Added description to trusted app card. (#79503)

* Added description to trusted app card.

* Fixed the test and increased the specificity of styling for item details card styles as opposed to using !important.

* Added useMemo.
This commit is contained in:
Bohdan Tsymbala 2020-10-05 22:04:07 +02:00 committed by GitHub
parent cd383800e3
commit 96d3b7710a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 183 additions and 36 deletions

View file

@ -29,7 +29,7 @@ export const GetTrustedAppsRequestSchema = {
export const PostTrustedAppCreateRequestSchema = {
body: schema.object({
name: schema.string({ minLength: 1 }),
description: schema.maybe(schema.string({ minLength: 0, defaultValue: '' })),
description: schema.maybe(schema.string({ minLength: 0, maxLength: 256, defaultValue: '' })),
os: schema.oneOf([schema.literal('linux'), schema.literal('macos'), schema.literal('windows')]),
entries: schema.arrayOf(
schema.object({

View file

@ -200,7 +200,11 @@ exports[`item_details_card ItemDetailsPropertySummary should render correctly 1`
name 1
</Styled(EuiDescriptionListTitle)>
<Styled(EuiDescriptionListDescription)>
value 1
<span
title=""
>
value 1
</span>
</Styled(EuiDescriptionListDescription)>
</Fragment>
`;

View file

@ -52,23 +52,30 @@ const DetailsSection = styled(EuiFlexItem)`
`;
const DescriptionListTitle = styled(EuiDescriptionListTitle)`
width: 40%;
&&& {
width: 40%;
}
`;
const DescriptionListDescription = styled(EuiDescriptionListDescription)`
width: 60%;
&&& {
width: 60%;
}
`;
interface ItemDetailsPropertySummaryProps {
name: ReactNode | ReactNode[];
value: ReactNode | ReactNode[];
title?: string;
}
export const ItemDetailsPropertySummary: FC<ItemDetailsPropertySummaryProps> = memo(
({ name, value }) => (
({ name, value, title = '' }) => (
<>
<DescriptionListTitle>{name}</DescriptionListTitle>
<DescriptionListDescription>{value}</DescriptionListDescription>
<DescriptionListDescription>
<span title={title}>{value}</span>
</DescriptionListDescription>
</>
)
);

View file

@ -494,11 +494,11 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
padding: 16px;
}
.c1 {
.c1.c1.c1 {
width: 40%;
}
.c2 {
.c2.c2.c2 {
width: 60%;
}
@ -791,7 +791,11 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
<dd
class="euiDescriptionList__description c2"
>
trusted app 0
<span
title=""
>
trusted app 0
</span>
</dd>
<dt
class="euiDescriptionList__title c1"
@ -801,7 +805,11 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
<dd
class="euiDescriptionList__description c2"
>
Windows
<span
title=""
>
Windows
</span>
</dd>
<dt
class="euiDescriptionList__title c1"
@ -811,7 +819,11 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
<dd
class="euiDescriptionList__description c2"
>
1 minute ago
<span
title=""
>
1 minute ago
</span>
</dd>
<dt
class="euiDescriptionList__title c1"
@ -821,7 +833,25 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
<dd
class="euiDescriptionList__description c2"
>
someone
<span
title=""
>
someone
</span>
</dd>
<dt
class="euiDescriptionList__title c1"
>
Description
</dt>
<dd
class="euiDescriptionList__description c2"
>
<span
title="Trusted App 0"
>
Trusted App 0
</span>
</dd>
</dl>
</div>

View file

@ -410,6 +410,7 @@ export const CreateTrustedAppForm = memo<CreateTrustedAppFormProps>(
value={formValues.description}
onChange={handleDomChangeEvents}
fullWidth
maxLength={256}
data-test-subj={getTestId('descriptionField')}
/>
</EuiFormRow>

View file

@ -24,6 +24,81 @@ exports[`trusted_app_card TrustedAppCard should render correctly 1`] = `
name="Created By"
value="someone"
/>
<ItemPropertySummary
name="Description"
title="Trusted App 4"
value="Trusted App 4"
/>
<ConditionsTable
badge="and"
columns={
Array [
Object {
"field": "field",
"name": "Field",
"sortable": false,
"textOnly": true,
"truncateText": true,
"width": "30%",
},
Object {
"field": "operator",
"name": "Operator",
"sortable": false,
"truncateText": true,
"width": "20%",
},
Object {
"field": "value",
"name": "Value",
"sortable": false,
"truncateText": true,
"width": "60%",
},
]
}
items={Array []}
responsive={true}
/>
<ItemDetailsAction
color="danger"
onClick={[Function]}
size="s"
>
Remove
</ItemDetailsAction>
</ItemDetailsCard>
`;
exports[`trusted_app_card TrustedAppCard should trim long descriptions 1`] = `
<ItemDetailsCard>
<ItemPropertySummary
name="Name"
value="trusted app 4"
/>
<ItemPropertySummary
name="OS"
value="Mac OS"
/>
<ItemPropertySummary
name="Date Created"
value={
<Memo(FormattedDate)
className="eui-textTruncate"
fieldName="Date Created"
value="1 minute ago"
/>
}
/>
<ItemPropertySummary
name="Created By"
value="someone"
/>
<ItemPropertySummary
name="Description"
title="item0 item1 item2 item3 item4 item5 item6 item7 item8 item9 item10 item11 item12 item13 item14 item15 item16 item17 item18 item19 item20 item21 item22 item23 item24 item25 item26 item27 item28 item29 item30 item31 item32 item33 item34 item35 item36 item37 item38 item39"
value="item0 item1 item2 item3 item4 item5 item6 item7 item8 item9 item10 item11 item12 item13 item14 item1..."
/>
<ConditionsTable
badge="and"
columns={

View file

@ -10,7 +10,7 @@ import { action } from '@storybook/addon-actions';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { KibanaContextProvider } from '../../../../../../../../../../src/plugins/kibana_react/public';
import { TrustedApp } from '../../../../../../../common/endpoint/types';
import { TrustedApp, WindowsConditionEntry } from '../../../../../../../common/endpoint/types';
import { createSampleTrustedApp } from '../../../test_utils';
@ -24,38 +24,40 @@ addDecorator((storyFn) => (
</KibanaContextProvider>
));
const PATH_CONDITION: WindowsConditionEntry = {
field: 'process.executable.caseless',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
};
const SIGNER_CONDITION: WindowsConditionEntry = {
field: 'process.code_signature',
operator: 'included',
type: 'match',
value: 'Elastic',
};
storiesOf('TrustedApps|TrustedAppCard', module)
.add('default', () => {
const trustedApp: TrustedApp = createSampleTrustedApp(5);
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [
{
field: 'process.executable.caseless',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
},
];
trustedApp.entries = [PATH_CONDITION];
return <TrustedAppCard trustedApp={trustedApp} onDelete={action('onClick')} />;
})
.add('multiple entries', () => {
const trustedApp: TrustedApp = createSampleTrustedApp(5);
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [
{
field: 'process.executable.caseless',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
},
{
field: 'process.code_signature',
operator: 'included',
type: 'match',
value: 'Elastic',
},
];
trustedApp.entries = [PATH_CONDITION, SIGNER_CONDITION];
return <TrustedAppCard trustedApp={trustedApp} onDelete={action('onClick')} />;
})
.add('trim description', () => {
const trustedApp: TrustedApp = createSampleTrustedApp(5);
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [PATH_CONDITION, SIGNER_CONDITION];
trustedApp.description = [...new Array(40).keys()].map((index) => `item${index}`).join(' ');
return <TrustedAppCard trustedApp={trustedApp} onDelete={action('onClick')} />;
});

View file

@ -18,5 +18,15 @@ describe('trusted_app_card', () => {
expect(element).toMatchSnapshot();
});
it('should trim long descriptions', () => {
const trustedApp = {
...createSampleTrustedApp(4),
description: [...new Array(40).keys()].map((index) => `item${index}`).join(' '),
};
const element = shallow(<TrustedAppCard trustedApp={trustedApp} onDelete={() => {}} />);
expect(element).toMatchSnapshot();
});
});
});

View file

@ -27,6 +27,14 @@ import { OS_TITLES, PROPERTY_TITLES, ENTRY_PROPERTY_TITLES } from '../../transla
type Entry = MacosLinuxConditionEntry | WindowsConditionEntry;
const trimTextOverflow = (text: string, maxSize: number) => {
if (text.length > maxSize) {
return `${text.substr(0, maxSize)}...`;
} else {
return text;
}
};
const getEntriesColumnDefinitions = (): Array<EuiTableFieldDataColumnType<Entry>> => [
{
field: 'field',
@ -75,6 +83,13 @@ export const TrustedAppCard = memo(({ trustedApp, onDelete }: TrustedAppCardProp
}
/>
<ItemDetailsPropertySummary name={PROPERTY_TITLES.created_by} value={trustedApp.created_by} />
<ItemDetailsPropertySummary
name={PROPERTY_TITLES.description}
value={useMemo(() => trimTextOverflow(trustedApp.description || '', 100), [
trustedApp.description,
])}
title={trustedApp.description}
/>
<ConditionsTable
columns={useMemo(() => getEntriesColumnDefinitions(), [])}

View file

@ -24,7 +24,7 @@ export const OS_TITLES: Readonly<{ [K in TrustedApp['os']]: string }> = {
};
export const PROPERTY_TITLES: Readonly<
{ [K in keyof Omit<TrustedApp, 'id' | 'description' | 'entries'>]: string }
{ [K in keyof Omit<TrustedApp, 'id' | 'entries'>]: string }
> = {
name: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.name', {
defaultMessage: 'Name',
@ -38,6 +38,9 @@ export const PROPERTY_TITLES: Readonly<
created_by: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.createdBy', {
defaultMessage: 'Created By',
}),
description: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.description', {
defaultMessage: 'Description',
}),
};
export const ENTRY_PROPERTY_TITLES: Readonly<