[8.8] [Cases] Fix lens visualization in comment and description markdown (#155897) (#156189)

# Backport

This will backport the following commits from `main` to `8.8`:
- [[Cases] Fix lens visualization in comment and description markdown
(#155897)](https://github.com/elastic/kibana/pull/155897)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Janki
Salvi","email":"117571355+js-jankisalvi@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-04-28T15:21:00Z","message":"[Cases]
Fix lens visualization in comment and description markdown
(#155897)\n\n## Summary\r\n\r\nThis PR fixes issues with lens
visualization in case view page for\r\ncomment and
description.\r\n\r\nFixes:
#155631\r\n\r\n**Description:**\r\n\r\n\r\n234858215-feaac6c7-5579-4a00-bb97-00e0b23f5cfe.mov\r\n\r\n###
Checklist\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n**Flakey Test
runner:**\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2182\r\n\r\nNext
one after
update:\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2183\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"0c0e02277f6c0d52dd4906e7421edcbd4c0dd962","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:skip","Team:ResponseOps","Feature:Cases","v8.7.0","v8.8.0","v8.9.0"],"number":155897,"url":"https://github.com/elastic/kibana/pull/155897","mergeCommit":{"message":"[Cases]
Fix lens visualization in comment and description markdown
(#155897)\n\n## Summary\r\n\r\nThis PR fixes issues with lens
visualization in case view page for\r\ncomment and
description.\r\n\r\nFixes:
#155631\r\n\r\n**Description:**\r\n\r\n\r\n234858215-feaac6c7-5579-4a00-bb97-00e0b23f5cfe.mov\r\n\r\n###
Checklist\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n**Flakey Test
runner:**\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2182\r\n\r\nNext
one after
update:\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2183\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"0c0e02277f6c0d52dd4906e7421edcbd4c0dd962"}},"sourceBranch":"main","suggestedTargetBranches":["8.7","8.8"],"targetPullRequestStates":[{"branch":"8.7","label":"v8.7.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.8","label":"v8.8.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/155897","number":155897,"mergeCommit":{"message":"[Cases]
Fix lens visualization in comment and description markdown
(#155897)\n\n## Summary\r\n\r\nThis PR fixes issues with lens
visualization in case view page for\r\ncomment and
description.\r\n\r\nFixes:
#155631\r\n\r\n**Description:**\r\n\r\n\r\n234858215-feaac6c7-5579-4a00-bb97-00e0b23f5cfe.mov\r\n\r\n###
Checklist\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n**Flakey Test
runner:**\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2182\r\n\r\nNext
one after
update:\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2183\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"0c0e02277f6c0d52dd4906e7421edcbd4c0dd962"}}]}]
BACKPORT-->

Co-authored-by: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com>
Co-authored-by: Lisa Cawley <lcawley@elastic.co>
This commit is contained in:
Kibana Machine 2023-05-01 21:13:54 -04:00 committed by GitHub
parent edd78ba55e
commit 52c79188e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 55 deletions

View file

@ -17,8 +17,6 @@ import { schema } from './schema';
import type { AppMockRenderer } from '../../common/mock';
import { createAppMockRenderer } from '../../common/mock';
jest.mock('../markdown_editor/plugins/lens/use_lens_draft_comment');
describe('Description', () => {
let globalForm: FormHook;
let appMockRender: AppMockRenderer;

View file

@ -5,14 +5,9 @@
* 2.0.
*/
import React, { memo, useEffect, useRef } from 'react';
import {
UseField,
useFormContext,
useFormData,
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import React, { memo, useRef } from 'react';
import { UseField, useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { MarkdownEditorForm } from '../markdown_editor';
import { useLensDraftComment } from '../markdown_editor/plugins/lens/use_lens_draft_comment';
import { ID as LensPluginId } from '../markdown_editor/plugins/lens/constants';
interface Props {
@ -23,33 +18,10 @@ interface Props {
export const fieldName = 'description';
const DescriptionComponent: React.FC<Props> = ({ isLoading, draftStorageKey }) => {
const { draftComment, hasIncomingLensState, openLensModal, clearDraftComment } =
useLensDraftComment();
const { setFieldValue } = useFormContext();
const [{ title, tags }] = useFormData({ watch: ['title', 'tags'] });
const editorRef = useRef<Record<string, unknown>>();
const disabledUiPlugins = [LensPluginId];
useEffect(() => {
if (draftComment?.commentId === fieldName && editorRef.current) {
setFieldValue(fieldName, draftComment.comment);
if (draftComment.caseTitle) {
setFieldValue('title', draftComment.caseTitle);
}
if (draftComment.caseTags && draftComment.caseTags.length > 0) {
setFieldValue('tags', draftComment.caseTags);
}
if (hasIncomingLensState) {
openLensModal({ editorRef: editorRef.current });
} else {
clearDraftComment();
}
}
}, [clearDraftComment, draftComment, hasIncomingLensState, openLensModal, setFieldValue]);
return (
<UseField
path={fieldName}

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useCallback, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import {
@ -21,12 +21,17 @@ import { getMarkdownEditorStorageKey } from '../markdown_editor/utils';
import * as i18n from '../user_actions/translations';
import { useCasesContext } from '../cases_context/use_cases_context';
import { useLensDraftComment } from '../markdown_editor/plugins/lens/use_lens_draft_comment';
import type { EditableMarkdownRefObject, EuiMarkdownEditorRef } from '../markdown_editor';
import { EditableMarkdown, ScrollableMarkdown } from '../markdown_editor';
import type { Case } from '../../containers/types';
import type { OnUpdateFields } from '../case_view/types';
import { schema } from './schema';
const DESCRIPTION_ID = 'description';
export interface DescriptionMarkdownRefObject extends EditableMarkdownRefObject {
editor: EuiMarkdownEditorRef | null;
}
export interface DescriptionProps {
caseData: Case;
isLoadingDescription: boolean;
@ -73,6 +78,13 @@ const getDraftDescription = (
return sessionStorage.getItem(draftStorageKey);
};
const isCommentRef = (
ref: EditableMarkdownRefObject | null | undefined
): ref is EditableMarkdownRefObject => {
const commentRef = ref as EditableMarkdownRefObject;
return commentRef?.setComment != null;
};
export const Description = ({
caseData,
onUpdateField,
@ -82,6 +94,8 @@ export const Description = ({
const [isEditable, setIsEditable] = useState<boolean>(false);
const descriptionRef = useRef(null);
const descriptionMarkdownRef = useRef<DescriptionMarkdownRefObject | null>(null);
const { euiTheme } = useEuiTheme();
const { appId, permissions } = useCasesContext();
@ -89,6 +103,7 @@ export const Description = ({
clearDraftComment: clearLensDraftComment,
draftComment: lensDraftComment,
hasIncomingLensState,
openLensModal,
} = useLensDraftComment();
const handleOnChangeEditable = useCallback(() => {
@ -117,6 +132,22 @@ export const Description = ({
setIsEditable(true);
}
useEffect(() => {
if (
isCommentRef(descriptionMarkdownRef.current) &&
descriptionMarkdownRef.current.editor?.textarea &&
lensDraftComment &&
lensDraftComment.commentId === DESCRIPTION_ID
) {
descriptionMarkdownRef.current.setComment(lensDraftComment.comment);
if (hasIncomingLensState) {
openLensModal({ editorRef: descriptionMarkdownRef.current.editor });
} else {
clearLensDraftComment();
}
}
}, [clearLensDraftComment, lensDraftComment, hasIncomingLensState, openLensModal]);
const hasUnsavedChanges =
draftDescription && draftDescription !== caseData.description && !isLoadingDescription;
@ -131,6 +162,7 @@ export const Description = ({
editorRef={descriptionRef}
fieldName="content"
formSchema={schema}
ref={descriptionMarkdownRef}
/>
) : (
<Panel hasShadow={false} hasBorder={true} data-test-subj="description">

View file

@ -46,10 +46,17 @@ const MyEuiCommentFooter = styled(EuiText)`
`}
`;
const hasDraftComment = (appId = '', caseId: string, commentId: string): boolean => {
const draftStorageKey = getMarkdownEditorStorageKey(appId, caseId, commentId);
const hasDraftComment = (
applicationId = '',
caseId: string,
commentId: string,
comment: string
): boolean => {
const draftStorageKey = getMarkdownEditorStorageKey(applicationId, caseId, commentId);
return Boolean(sessionStorage.getItem(draftStorageKey));
const sessionValue = sessionStorage.getItem(draftStorageKey);
return Boolean(sessionValue && sessionValue !== comment);
};
export const createUserAttachmentUserActionBuilder = ({
@ -78,7 +85,8 @@ export const createUserAttachmentUserActionBuilder = ({
className: classNames('userAction__comment', {
outlined,
isEdit,
draftFooter: !isEdit && !isLoading && hasDraftComment(appId, caseId, comment.id),
draftFooter:
!isEdit && !isLoading && hasDraftComment(appId, caseId, comment.id, comment.comment),
}),
children: (
<>
@ -95,7 +103,7 @@ export const createUserAttachmentUserActionBuilder = ({
version: comment.version,
})}
/>
{!isEdit && !isLoading && hasDraftComment(appId, caseId, comment.id) ? (
{!isEdit && !isLoading && hasDraftComment(appId, caseId, comment.id, comment.comment) ? (
<MyEuiCommentFooter>
<EuiText color="subdued" size="xs" data-test-subj="user-action-comment-unsaved-draft">
{i18n.UNSAVED_DRAFT_COMMENT}

View file

@ -37,6 +37,13 @@ const isAddCommentRef = (
return commentRef?.addQuote != null;
};
const isSetCommentRef = (
ref: AddCommentRefObject | UserActionMarkdownRefObject | null | undefined
): ref is AddCommentRefObject => {
const commentRef = ref as UserActionMarkdownRefObject;
return commentRef?.setComment != null;
};
export const useUserActionsHandler = (): UseUserActionsHandler => {
const { detailName: caseId } = useCaseViewParams();
const { clearDraftComment, draftComment, hasIncomingLensState, openLensModal } =
@ -122,7 +129,7 @@ export const useUserActionsHandler = (): UseUserActionsHandler => {
);
useEffect(() => {
if (draftComment?.commentId) {
if (draftComment?.commentId && draftComment?.commentId !== 'description') {
setManageMarkdownEditIds((prevManageMarkdownEditIds) => {
if (
NEW_COMMENT_ID !== draftComment?.commentId &&
@ -135,7 +142,7 @@ export const useUserActionsHandler = (): UseUserActionsHandler => {
const ref = commentRefs?.current?.[draftComment.commentId];
if (isAddCommentRef(ref) && ref.editor?.textarea) {
if (isSetCommentRef(ref) && ref.editor?.textarea) {
ref.setComment(draftComment.comment);
if (hasIncomingLensState) {
openLensModal({ editorRef: ref.editor });

View file

@ -41,7 +41,7 @@ export function CasesSingleViewServiceProvider({ getService, getPageObject }: Ft
},
async getCommentCount(): Promise<number> {
const commentsContainer = await testSubjects.find('user-actions');
const commentsContainer = await testSubjects.find('user-actions-list');
const comments = await commentsContainer.findAllByClassName('euiComment');
return comments.length - 1; // don't count the element for adding a new comment
},
@ -58,13 +58,22 @@ export function CasesSingleViewServiceProvider({ getService, getPageObject }: Ft
});
},
async addVisualization(visName: string) {
async addVisualizationToNewComment(visName: string) {
// open saved object finder
const addCommentElement = await testSubjects.find('add-comment');
const addVisualizationButton = await addCommentElement.findByCssSelector(
'[data-test-subj="euiMarkdownEditorToolbarButton"][aria-label="Visualization"]'
);
await addVisualizationButton.click();
await this.findAndSaveVisualization(visName);
await testSubjects.existOrFail('cases-app', { timeout: 10 * 1000 });
await this.submitComment();
},
async findAndSaveVisualization(visName: string) {
await testSubjects.existOrFail('savedObjectsFinderTable', { timeout: 10 * 1000 });
// select visualization
@ -78,8 +87,6 @@ export function CasesSingleViewServiceProvider({ getService, getPageObject }: Ft
// save and return to cases app, add comment
await lensPage.saveAndReturn();
await testSubjects.existOrFail('cases-app', { timeout: 10 * 1000 });
await this.submitComment();
},
async openVisualizationButtonTooltip() {

View file

@ -272,16 +272,6 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
it('shows unsaved comment message when page is refreshed', async () => {
const commentArea = await find.byCssSelector(
'[data-test-subj="add-comment"] textarea.euiMarkdownEditorTextArea'
);
await commentArea.focus();
await commentArea.type('Test comment from automation');
await testSubjects.click('submit-comment');
await header.waitUntilLoadingHasFinished();
await testSubjects.click('property-actions-user-action-ellipses');
await header.waitUntilLoadingHasFinished();
@ -299,6 +289,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await editCommentTextArea.focus();
await editCommentTextArea.type('Edited comment');
await header.waitUntilLoadingHasFinished();
await browser.refresh();
await header.waitUntilLoadingHasFinished();
@ -341,6 +333,104 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
});
describe('Lens visualization', () => {
before(async () => {
await cases.testResources.installKibanaSampleData('logs');
});
after(async () => {
await cases.testResources.removeKibanaSampleData('logs');
});
createOneCaseBeforeDeleteAllAfter(getPageObject, getService);
it('adds lens visualization in description', async () => {
await testSubjects.click('description-edit-icon');
await header.waitUntilLoadingHasFinished();
const editCommentTextArea = await find.byCssSelector(
'[data-test-subj*="editable-markdown-form"] textarea.euiMarkdownEditorTextArea'
);
await header.waitUntilLoadingHasFinished();
await editCommentTextArea.focus();
const editableDescription = await testSubjects.find('editable-markdown-form');
const addVisualizationButton = await editableDescription.findByCssSelector(
'[data-test-subj="euiMarkdownEditorToolbarButton"][aria-label="Visualization"]'
);
await addVisualizationButton.click();
await cases.singleCase.findAndSaveVisualization('[Logs] Bytes distribution');
await header.waitUntilLoadingHasFinished();
await testSubjects.click('editable-save-markdown');
await header.waitUntilLoadingHasFinished();
const description = await find.byCssSelector('[data-test-subj="description"]');
await description.findByCssSelector('[data-test-subj="xyVisChart"]');
});
it('adds lens visualization in existing comment', async () => {
const commentArea = await find.byCssSelector(
'[data-test-subj="add-comment"] textarea.euiMarkdownEditorTextArea'
);
await commentArea.focus();
await commentArea.type('Test comment from automation');
await header.waitUntilLoadingHasFinished();
await testSubjects.click('submit-comment');
await header.waitUntilLoadingHasFinished();
await testSubjects.click('property-actions-user-action-ellipses');
await header.waitUntilLoadingHasFinished();
await testSubjects.click('property-actions-user-action-pencil');
await header.waitUntilLoadingHasFinished();
const editComment = await find.byCssSelector('[data-test-subj*="editable-markdown-form"]');
const addVisualizationButton = await editComment.findByCssSelector(
'[data-test-subj="euiMarkdownEditorToolbarButton"][aria-label="Visualization"]'
);
await addVisualizationButton.click();
await cases.singleCase.findAndSaveVisualization('[Logs] Bytes distribution');
await header.waitUntilLoadingHasFinished();
await testSubjects.click('editable-save-markdown');
await header.waitUntilLoadingHasFinished();
const createdComment = await find.byCssSelector(
'[data-test-subj*="comment-create-action"] [data-test-subj="scrollable-markdown"]'
);
await createdComment.findByCssSelector('[data-test-subj="xyVisChart"]');
});
it('adds lens visualization in new comment', async () => {
await cases.singleCase.addVisualizationToNewComment('[Logs] Bytes distribution');
await header.waitUntilLoadingHasFinished();
const newComment = await find.byCssSelector('[data-test-subj*="comment-create-action"]');
await newComment.findByCssSelector('[data-test-subj="xyVisChart"]');
});
});
describe('Severity field', () => {
createOneCaseBeforeDeleteAllAfter(getPageObject, getService);

View file

@ -31,7 +31,7 @@ export default function ({ getService }: FtrProviderContext) {
it('cases visualization screenshot', async () => {
await cases.navigation.navigateToApp();
await cases.navigation.navigateToSingleCase('cases', CASE_ID);
await cases.singleCase.addVisualization('[Logs] Bytes distribution');
await cases.singleCase.addVisualizationToNewComment('[Logs] Bytes distribution');
await cases.singleCase.openVisualizationButtonTooltip();
await commonScreenshots.takeScreenshot(