Skip to content

Commit

Permalink
use description text area, remove session storage key for description…
Browse files Browse the repository at this point in the history
…, make description optional, show template tags options
  • Loading branch information
js-jankisalvi committed May 31, 2024
1 parent 82cd6af commit a468d94
Show file tree
Hide file tree
Showing 23 changed files with 219 additions and 110 deletions.
12 changes: 12 additions & 0 deletions x-pack/plugins/cases/common/types/api/configure/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,18 @@ describe('configure', () => {
});
});

it('does not throw when there is no description or tags', () => {
const newRequest = {
key: 'template_key_1',
name: 'Template 1',
caseFields: null,
};

expect(PathReporter.report(TemplateConfigurationRt.decode({ ...newRequest }))).toContain(
'No errors!'
);
});

it('limits name to 50 characters', () => {
const longName = 'x'.repeat(MAX_TEMPLATE_NAME_LENGTH + 1);

Expand Down
16 changes: 8 additions & 8 deletions x-pack/plugins/cases/common/types/api/configure/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,21 @@ export const TemplateConfigurationRt = rt.intersection([
* name of template
*/
name: limitedStringSchema({ fieldName: 'name', min: 1, max: MAX_TEMPLATE_NAME_LENGTH }),
/**
* description of templates
*/
description: limitedStringSchema({
fieldName: 'description',
min: 1,
max: MAX_TEMPLATE_DESCRIPTION_LENGTH,
}),
/**
* case fields
*/
caseFields: rt.union([rt.null, CaseBaseOptionalFieldsRequestRt]),
}),
rt.exact(
rt.partial({
/**
* description of templates
*/
description: limitedStringSchema({
fieldName: 'description',
min: 0,
max: MAX_TEMPLATE_DESCRIPTION_LENGTH,
}),
/**
* tags of templates
*/
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/cases/common/types/domain/configure/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ describe('configure', () => {
const templateWithFewCaseFields = {
key: 'template_sample_2',
name: 'Sample template 2',
description: 'this is second sample template',
tags: [],
caseFields: {
title: 'Case with sample template 2',
Expand All @@ -92,7 +91,6 @@ describe('configure', () => {
const templateWithNoCaseFields = {
key: 'template_sample_3',
name: 'Sample template 3',
description: 'this is third sample template',
caseFields: null,
};

Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/cases/common/types/domain/configure/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,17 @@ export const TemplateConfigurationRt = rt.intersection([
* name of template
*/
name: rt.string,
/**
* description of template
*/
description: rt.string,
/**
* case fields of template
*/
caseFields: rt.union([rt.null, CaseBaseOptionalFieldsRt]),
}),
rt.exact(
rt.partial({
/**
* description of template
*/
description: rt.string,
/**
* tags of template
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,19 @@ import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl';

jest.mock('../../containers/user_profiles/api');

const appId = 'securitySolution';
const draftKey = `cases.${appId}.createCaseTemplate.description.markdownEditor`;

describe('CaseFormFields', () => {
let appMock: AppMockRenderer;
const onSubmit = jest.fn();
const defaultProps = {
isLoading: false,
configurationCustomFields: [],
draftStorageKey: '',
};

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

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

it('renders correctly', async () => {
appMock.render(
<FormTestComponent onSubmit={onSubmit}>
Expand Down Expand Up @@ -83,7 +75,6 @@ describe('CaseFormFields', () => {
<CaseFormFields
isLoading={false}
configurationCustomFields={customFieldsConfigurationMock}
draftStorageKey=""
/>
</FormTestComponent>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,24 @@ import type { CasesConfigurationUI } from '../../containers/types';
interface Props {
isLoading: boolean;
configurationCustomFields: CasesConfigurationUI['customFields'];
draftStorageKey: string;
}

const CaseFormFieldsComponent: React.FC<Props> = ({
isLoading,
configurationCustomFields,
draftStorageKey,
}) => {
const CaseFormFieldsComponent: React.FC<Props> = ({ isLoading, configurationCustomFields }) => {
const { caseAssignmentAuthorized, isSyncAlertsEnabled } = useCasesFeatures();

return (
<EuiFlexGroup data-test-subj="case-form-fields" direction="column">
<Title isLoading={isLoading} />

{caseAssignmentAuthorized ? <Assignees isLoading={isLoading} /> : null}

<Tags isLoading={isLoading} />

<Category isLoading={isLoading} />

<Severity isLoading={isLoading} />

<Description isLoading={isLoading} draftStorageKey={draftStorageKey} />
<Description isLoading={isLoading} />

{isSyncAlertsEnabled ? <SyncAlertsToggle isLoading={isLoading} /> : null}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,14 @@ describe('CommonFlyout ', () => {
configCustomFields,
configConnectorId,
configConnectors,
configTemplateTags,
}: FlyOutBodyProps<TemplateFormProps | null>) => (
<TemplateForm
initialValue={initialValue}
connectors={configConnectors ?? []}
configurationConnectorId={configConnectorId ?? 'none'}
configurationCustomFields={configCustomFields ?? []}
configurationTemplateTags={configTemplateTags ?? []}
onChange={onChange}
/>
);
Expand All @@ -381,6 +383,7 @@ describe('CommonFlyout ', () => {
connectors: connectorsMock,
configurationConnectorId: 'none',
configurationCustomFields: [],
configurationTemplateTags: [],
renderBody,
};

Expand Down Expand Up @@ -466,12 +469,14 @@ describe('CommonFlyout ', () => {
configCustomFields,
configConnectorId,
configConnectors,
configTemplateTags,
}: FlyOutBodyProps<TemplateFormProps | null>) => (
<TemplateForm
initialValue={initialValue}
connectors={configConnectors ?? []}
configurationConnectorId={configConnectorId ?? 'none'}
configurationCustomFields={configCustomFields ?? []}
configurationTemplateTags={configTemplateTags ?? []}
onChange={onChange}
/>
);
Expand All @@ -481,6 +486,7 @@ describe('CommonFlyout ', () => {
connectors: [],
configurationConnectorId: 'none',
configurationCustomFields: customFieldsConfigurationMock,
configurationTemplateTags: [],
data: {
key: 'random_key',
name: 'Template 1',
Expand Down Expand Up @@ -525,12 +531,14 @@ describe('CommonFlyout ', () => {
configCustomFields,
configConnectorId,
configConnectors,
configTemplateTags,
}: FlyOutBodyProps<TemplateFormProps | null>) => (
<TemplateForm
initialValue={initialValue}
connectors={configConnectors ?? []}
configurationConnectorId={configConnectorId ?? 'none'}
configurationCustomFields={configCustomFields ?? []}
configurationTemplateTags={configTemplateTags ?? []}
onChange={onChange}
/>
);
Expand All @@ -540,6 +548,7 @@ describe('CommonFlyout ', () => {
connectors: connectorsMock,
configurationConnectorId: 'servicenow-1',
configurationCustomFields: [],
configurationTemplateTags: [],
data: {
key: 'random_key',
name: 'Template 1',
Expand Down Expand Up @@ -605,20 +614,6 @@ describe('CommonFlyout ', () => {
).toBeInTheDocument();
});

it('shows error when template description is empty', async () => {
appMockRender.render(<CommonFlyout {...newProps} />);

userEvent.paste(await screen.findByTestId('template-name-input'), 'Template name');

userEvent.click(await screen.findByTestId('common-flyout-save'));

await waitFor(() => {
expect(newProps.onSaveField).not.toHaveBeenCalled();
});

expect(await screen.findByText('A Description is required.')).toBeInTheDocument();
});

it('shows error if template description is too long', async () => {
appMockRender.render(<CommonFlyout {...newProps} />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface FlyOutBodyProps<T> {
configConnectors?: ActionConnector[];
configConnectorId?: string;
configCustomFields?: CasesConfigurationUI['customFields'];
configTemplateTags?: string[];
}

export interface FlyoutProps<T> {
Expand All @@ -42,13 +43,15 @@ export interface FlyoutProps<T> {
connectors?: ActionConnector[];
configurationConnectorId?: string;
configurationCustomFields?: CasesConfigurationUI['customFields'];
configurationTemplateTags?: string[];
renderHeader: () => React.ReactNode;
renderBody: ({
initialValue,
onChange,
configConnectors,
configConnectorId,
configCustomFields,
configTemplateTags,
}: FlyOutBodyProps<T>) => React.ReactNode;
}

Expand All @@ -63,6 +66,7 @@ export const CommonFlyout = <T extends CustomFieldConfiguration | TemplateFormPr
connectors,
configurationConnectorId,
configurationCustomFields,
configurationTemplateTags,
}: FlyoutProps<T>) => {
const [formState, setFormState] = useState<CustomFieldFormState | TemplateFormState>({
isValid: undefined,
Expand Down Expand Up @@ -95,6 +99,7 @@ export const CommonFlyout = <T extends CustomFieldConfiguration | TemplateFormPr
configConnectors: connectors,
configConnectorId: configurationConnectorId,
configCustomFields: configurationCustomFields,
configTemplateTags: configurationTemplateTags,
onChange: setFormState,
})}
</EuiFlyoutBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export const ConfigureCases: React.FC = React.memo(() => {
} = usePersistConfiguration();

const isLoadingCaseConfiguration = loadingCaseConfigure || isPersistingConfiguration;
const configurationTemplateTags = templates
.map((template) => (template?.tags?.length ? template.tags : []))
.flat();

const {
isLoading: isLoadingConnectors,
Expand Down Expand Up @@ -465,19 +468,22 @@ export const ConfigureCases: React.FC = React.memo(() => {
connectors={connectors}
configurationConnectorId={connector.id}
configurationCustomFields={customFields}
configurationTemplateTags={configurationTemplateTags}
renderHeader={() => <span>{i18n.CRATE_TEMPLATE}</span>}
renderBody={({
initialValue,
configConnectors,
configConnectorId,
configCustomFields,
configTemplateTags,
onChange,
}) => (
<TemplateForm
initialValue={initialValue}
connectors={configConnectors ?? []}
configurationConnectorId={configConnectorId ?? ''}
configurationCustomFields={configCustomFields ?? []}
configurationTemplateTags={configTemplateTags ?? []}
onChange={onChange}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ID as LensPluginId } from '../markdown_editor/plugins/lens/constants';

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

export const fieldName = 'description';
Expand Down
10 changes: 4 additions & 6 deletions x-pack/plugins/cases/public/components/create/tags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ import { useGetTags } from '../../containers/use_get_tags';
import * as i18n from './translations';
interface Props {
isLoading: boolean;
path?: string;
dataTestSubject?: string;
}

const TagsComponent: React.FC<Props> = ({ isLoading, path, dataTestSubject }) => {
const TagsComponent: React.FC<Props> = ({ isLoading }) => {
const { data: tagOptions = [], isLoading: isLoadingTags } = useGetTags();
const options = useMemo(
() =>
Expand All @@ -29,12 +27,12 @@ const TagsComponent: React.FC<Props> = ({ isLoading, path, dataTestSubject }) =>

return (
<UseField
path={path ?? 'tags'}
path="tags"
component={ComboBoxField}
defaultValue={[]}
componentProps={{
idAria: dataTestSubject ?? 'caseTags',
'data-test-subj': dataTestSubject ?? 'caseTags',
idAria: 'caseTags',
'data-test-subj': 'caseTags',
euiFieldProps: {
fullWidth: true,
placeholder: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type MarkdownEditorFormProps = EuiMarkdownEditorProps & {
bottomRightContent?: React.ReactNode;
caseTitle?: string;
caseTags?: string[];
draftStorageKey: string;
draftStorageKey?: string;
disabledUiPlugins?: string[];
initialValue?: string;
};
Expand All @@ -59,7 +59,7 @@ export const MarkdownEditorForm = React.memo(
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
const { hasConflicts } = useMarkdownSessionStorage({
field,
sessionKey: draftStorageKey,
sessionKey: draftStorageKey ?? '',
initialValue,
});
const { euiTheme } = useEuiTheme();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ describe('useMarkdownSessionStorage', () => {
});
});

it('should return hasConflicts as false when sessionKey is empty', async () => {
const { result, waitFor } = renderHook(() =>
useMarkdownSessionStorage({ field, sessionKey: '', initialValue })
);

await waitFor(() => {
expect(result.current.hasConflicts).toBe(false);
});
});

it('should update the session value with field value when it is first render', async () => {
const { waitFor } = renderHook<SessionStorageType, { hasConflicts: boolean }>(
(props) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const useMarkdownSessionStorage = ({

const [sessionValue, setSessionValue] = useSessionStorage(sessionKey, '', true);

if (!isEmpty(sessionValue) && isFirstRender.current) {
if (!isEmpty(sessionValue) && !isEmpty(sessionKey) && isFirstRender.current) {
field.setValue(sessionValue);
}

Expand Down
Loading

0 comments on commit a468d94

Please sign in to comment.