Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
d121368
feat: store draft description and comment
js-jankisalvi Nov 21, 2022
58925e1
feat: save draft comments to session storage using storage kibana utils
js-jankisalvi Nov 23, 2022
5701728
Merge remote-tracking branch 'upstream/main' into save-draft-user-cmment
js-jankisalvi Nov 24, 2022
cffe533
feat: added unit test and e2e UI tests
js-jankisalvi Nov 24, 2022
21f7e8b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 24, 2022
9c88803
feat: added debounce, unit tests for draft comments
js-jankisalvi Nov 28, 2022
080decf
Merge branch 'save-draft-user-cmment' of https://github.com/js-jankis…
js-jankisalvi Nov 28, 2022
06c2da9
fixed conflict issue
js-jankisalvi Nov 28, 2022
ffbab09
fix: show version conflict error when user is editing a comment which…
js-jankisalvi Nov 29, 2022
5374a93
fix: update error message
js-jankisalvi Nov 29, 2022
66e3837
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Nov 29, 2022
8759595
test description updated
js-jankisalvi Nov 30, 2022
9e7d3e6
add constant for debounce time
js-jankisalvi Nov 30, 2022
8553dc5
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Nov 30, 2022
d20b100
fix: remove empty keys from session storage, prevent empty key on fir…
js-jankisalvi Dec 1, 2022
20dfeae
Merge branch 'save-draft-user-cmment' of https://github.com/js-jankis…
js-jankisalvi Dec 1, 2022
04942ae
fix: show version conflict warning instead of error
js-jankisalvi Dec 2, 2022
7e29808
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 2, 2022
8c26008
Merge branch 'main' into save-draft-user-cmment
kibanamachine Dec 5, 2022
9c68478
fix: create a hook to add draft comments to session storage, save des…
js-jankisalvi Dec 7, 2022
d97471d
fix: removed console logs
js-jankisalvi Dec 7, 2022
1f630e8
Merge remote-tracking branch 'upstream/main' into save-draft-user-cmment
js-jankisalvi Dec 7, 2022
02f076d
conflicts resolved
js-jankisalvi Dec 7, 2022
a75e768
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Dec 7, 2022
51b00aa
fix: remove mock values from jest
js-jankisalvi Dec 7, 2022
2a20281
Merge branch 'save-draft-user-cmment' of https://github.com/js-jankis…
js-jankisalvi Dec 7, 2022
ac7a777
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 7, 2022
15a273f
fix linting
js-jankisalvi Dec 7, 2022
92b7a60
Merge branch 'save-draft-user-cmment' of https://github.com/js-jankis…
js-jankisalvi Dec 7, 2022
e01c9fc
lint fix
js-jankisalvi Dec 7, 2022
7f25d73
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 8, 2022
2b70e7f
Fix FTR e2e test fail on CI pipeline
js-jankisalvi Dec 8, 2022
29f1a70
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Dec 8, 2022
d738484
fix: add single case addition and deletion to draft comments describe
js-jankisalvi Dec 8, 2022
f6bbdb9
Fix: added unit tests for more scenarios
js-jankisalvi Dec 12, 2022
e2b4cc6
Merge remote-tracking branch 'upstream/main' into save-draft-user-cmment
js-jankisalvi Dec 12, 2022
a2dc3a3
prevent useSessionStorage hook to JSON serialize stored values
js-jankisalvi Dec 12, 2022
d848b17
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 12, 2022
9775b94
fix: type check fixed for form_context
js-jankisalvi Dec 13, 2022
1a1d54f
remove session storage key after each test
js-jankisalvi Dec 13, 2022
6a39c82
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Dec 13, 2022
acbcdfe
fix: add existing storage key test case, weird issue of comment stays…
js-jankisalvi Dec 13, 2022
8781fcf
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 13, 2022
cbfb2c6
typos fixed
js-jankisalvi Dec 13, 2022
4879bc1
Merge branch 'save-draft-user-cmment' of https://github.com/js-jankis…
js-jankisalvi Dec 13, 2022
99c71cd
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 13, 2022
ee3d6f9
Merge branch 'main' into save-draft-user-cmment
js-jankisalvi Dec 13, 2022
426ab55
fix
js-jankisalvi Dec 13, 2022
520e3c5
Merge branch 'main' into save-draft-user-cmment
kibanamachine Dec 14, 2022
4de1551
fix: description text updated, isFristrender check removed from debou…
js-jankisalvi Dec 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions x-pack/plugins/cases/public/common/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,13 @@ export const ADD_TAG_CUSTOM_OPTION_LABEL = (searchValue: string) =>
values: { searchValue },
});

export const VERSION_CONFLICT_WARNING = (markdownId: string) =>
i18n.translate('xpack.cases.configure.commentVersionConflictWarning', {
defaultMessage:
'This {markdownId} was updated. Saving your changes will overwrite the updated value.',
values: { markdownId },
});

/**
* EUI checkbox replace {searchValue} with the current
* search value. We need to put the template variable
Expand Down
78 changes: 76 additions & 2 deletions x-pack/plugins/cases/public/components/add_comment/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

import React from 'react';
import { mount } from 'enzyme';
import { waitFor, act } from '@testing-library/react';
import { waitFor, act, fireEvent } from '@testing-library/react';
import { noop } from 'lodash/fp';

import { noCreateCasesPermissions, TestProviders } from '../../common/mock';
import { noCreateCasesPermissions, TestProviders, createAppMockRenderer } from '../../common/mock';

import { CommentType } from '../../../common/api';
import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
Expand All @@ -20,6 +20,7 @@ import { AddComment } from '.';
import { CasesTimelineIntegrationProvider } from '../timeline_context';
import { timelineIntegrationMock } from '../__mock__/timeline';
import type { CaseAttachmentWithoutOwner } from '../../types';
import type { AppMockRenderer } from '../../common/mock';

jest.mock('../../containers/use_create_attachments');

Expand Down Expand Up @@ -47,13 +48,19 @@ const sampleData: CaseAttachmentWithoutOwner = {
comment: 'what a cool comment',
type: CommentType.user as const,
};
const appId = 'testAppId';
const draftKey = `cases.${appId}.${addCommentProps.caseId}.${addCommentProps.id}.markdownEditor`;

describe('AddComment ', () => {
beforeEach(() => {
jest.clearAllMocks();
useCreateAttachmentsMock.mockImplementation(() => defaultResponse);
});

afterEach(() => {
sessionStorage.removeItem(draftKey);
});

it('should post comment on submit click', async () => {
const wrapper = mount(
<TestProviders>
Expand Down Expand Up @@ -209,3 +216,70 @@ describe('AddComment ', () => {
});
});
});

describe('draft comment ', () => {
let appMockRenderer: AppMockRenderer;

beforeEach(() => {
appMockRenderer = createAppMockRenderer();
jest.clearAllMocks();
});

beforeAll(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.clearAllTimers();
});

afterAll(() => {
jest.useRealTimers();
});

it('should clear session storage on submit', async () => {
Comment thread
cnasikas marked this conversation as resolved.
Comment thread
cnasikas marked this conversation as resolved.
const result = appMockRenderer.render(<AddComment {...addCommentProps} />);

fireEvent.change(result.getByLabelText('caseComment'), {
target: { value: sampleData.comment },
});

act(() => {
jest.advanceTimersByTime(1000);
});

await waitFor(() => {
expect(result.getByLabelText('caseComment')).toHaveValue(sessionStorage.getItem(draftKey));
});

fireEvent.click(result.getByTestId('submit-comment'));

await waitFor(() => {
expect(onCommentSaving).toBeCalled();
expect(createAttachments).toBeCalledWith({
caseId: addCommentProps.caseId,
caseOwner: SECURITY_SOLUTION_OWNER,
data: [sampleData],
updateCase: onCommentPosted,
});
expect(result.getByLabelText('caseComment').textContent).toBe('');
expect(sessionStorage.getItem(draftKey)).toBe('');
});
});

describe('existing storage key', () => {
beforeEach(() => {
sessionStorage.setItem(draftKey, 'value set in storage');
});

afterEach(() => {
sessionStorage.removeItem(draftKey);
});

it('should have draft comment same as existing session storage', async () => {
const result = appMockRenderer.render(<AddComment {...addCommentProps} />);

expect(result.getByLabelText('caseComment')).toHaveValue('value set in storage');
});
});
});
22 changes: 19 additions & 3 deletions x-pack/plugins/cases/public/components/add_comment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { useCreateAttachments } from '../../containers/use_create_attachments';
import type { Case } from '../../containers/types';
import type { EuiMarkdownEditorRef } from '../markdown_editor';
import { MarkdownEditorForm } from '../markdown_editor';
import { getMarkdownEditorStorageKey } from '../markdown_editor/utils';
import { removeItemFromSessionStorage } from '../utils';

import * as i18n from './translations';
import type { AddCommentFormSchema } from './schema';
Expand Down Expand Up @@ -68,8 +70,9 @@ export const AddComment = React.memo(
) => {
const editorRef = useRef<EuiMarkdownEditorRef>(null);
const [focusOnContext, setFocusOnContext] = useState(false);
const { permissions, owner } = useCasesContext();
const { permissions, owner, appId } = useCasesContext();
const { isLoading, createAttachments } = useCreateAttachments();
const draftStorageKey = getMarkdownEditorStorageKey(appId, caseId, id);

const { form } = useForm<AddCommentFormSchema>({
defaultValue: initialCommentValue,
Expand Down Expand Up @@ -116,9 +119,21 @@ export const AddComment = React.memo(
data: [{ ...data, type: CommentType.user }],
updateCase: onCommentPosted,
});
reset();

removeItemFromSessionStorage(draftStorageKey);

reset({ defaultValue: {} });
}
}, [submit, onCommentSaving, createAttachments, caseId, owner, onCommentPosted, reset]);
}, [
submit,
onCommentSaving,
createAttachments,
caseId,
owner,
onCommentPosted,
reset,
draftStorageKey,
]);

/**
* Focus on the text area when a quote has been added.
Expand Down Expand Up @@ -163,6 +178,7 @@ export const AddComment = React.memo(
componentProps={{
ref: editorRef,
id,
draftStorageKey,
idAria: 'caseComment',
isDisabled: isLoading,
dataTestSubj: 'add-comment',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ jest.mock('../markdown_editor/plugins/lens/use_lens_draft_comment');
describe('Description', () => {
let globalForm: FormHook;
let appMockRender: AppMockRenderer;
const draftStorageKey = `cases.caseView.createCase.description.markdownEditor`;
const defaultProps = {
draftStorageKey,
isLoading: false,
};

const MockHookWrapperComponent: React.FC = ({ children }) => {
const { form } = useForm<FormProps>({
Expand All @@ -44,7 +49,7 @@ describe('Description', () => {
it('it renders', async () => {
const result = appMockRender.render(
<MockHookWrapperComponent>
<Description isLoading={false} />
<Description {...defaultProps} />
</MockHookWrapperComponent>
);

Expand All @@ -54,7 +59,7 @@ describe('Description', () => {
it('it changes the description', async () => {
const result = appMockRender.render(
<MockHookWrapperComponent>
<Description isLoading={false} />
<Description {...defaultProps} />
</MockHookWrapperComponent>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import { ID as LensPluginId } from '../markdown_editor/plugins/lens/constants';

interface Props {
isLoading: boolean;
draftStorageKey: string;
}

export const fieldName = 'description';

const DescriptionComponent: React.FC<Props> = ({ isLoading }) => {
const DescriptionComponent: React.FC<Props> = ({ isLoading, draftStorageKey }) => {
const { draftComment, hasIncomingLensState, openLensModal, clearDraftComment } =
useLensDraftComment();
const { setFieldValue } = useFormContext();
Expand Down Expand Up @@ -62,6 +63,7 @@ const DescriptionComponent: React.FC<Props> = ({ isLoading }) => {
caseTitle: title,
caseTags: tags,
disabledUiPlugins,
draftStorageKey,
}}
/>
);
Expand Down
50 changes: 49 additions & 1 deletion x-pack/plugins/cases/public/components/create/form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import React from 'react';
import { mount } from 'enzyme';
import { act, render, within } from '@testing-library/react';
import { act, render, within, fireEvent } from '@testing-library/react';
import { waitFor } from '@testing-library/dom';
import { licensingMock } from '@kbn/licensing-plugin/public/mocks';

import { NONE_CONNECTOR_ID } from '../../../common/api';
Expand Down Expand Up @@ -53,6 +54,7 @@ const casesFormProps: CreateCaseFormProps = {

describe('CreateCaseForm', () => {
let globalForm: FormHook;
const draftStorageKey = `cases.caseView.createCase.description.markdownEditor`;

const MockHookWrapperComponent: React.FC<{ testProviderProps?: unknown }> = ({
children,
Expand Down Expand Up @@ -80,6 +82,10 @@ describe('CreateCaseForm', () => {
useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse);
});

afterEach(() => {
sessionStorage.removeItem(draftStorageKey);
});

it('renders with steps', async () => {
const wrapper = mount(
<MockHookWrapperComponent>
Expand Down Expand Up @@ -212,4 +218,46 @@ describe('CreateCaseForm', () => {
expect(titleInput).toHaveValue('title');
expect(descriptionInput).toHaveValue('description');
});

describe('draft comment ', () => {
it('should clear session storage key on cancel', () => {
const result = render(
<MockHookWrapperComponent>
<CreateCaseForm
{...casesFormProps}
initialValue={{ title: 'title', description: 'description' }}
/>
</MockHookWrapperComponent>
);

const cancelBtn = result.getByTestId('create-case-cancel');

fireEvent.click(cancelBtn);

fireEvent.click(result.getByTestId('confirmModalConfirmButton'));

expect(casesFormProps.onCancel).toHaveBeenCalled();
expect(sessionStorage.getItem(draftStorageKey)).toBe(null);
});

it('should clear session storage key on submit', () => {
const result = render(
<MockHookWrapperComponent>
<CreateCaseForm
{...casesFormProps}
initialValue={{ title: 'title', description: 'description' }}
/>
</MockHookWrapperComponent>
);

const submitBtn = result.getByTestId('create-case-submit');

fireEvent.click(submitBtn);

waitFor(() => {
expect(casesFormProps.onSuccess).toHaveBeenCalled();
expect(sessionStorage.getItem(draftStorageKey)).toBe(null);
});
});
});
});
Loading