From bcab8468dd398ae9ad71ac41c402be8ef5e34023 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Wed, 10 Jun 2020 10:28:38 -0400 Subject: [PATCH] [SIEM][Detections Engine] - Exceptions viewer cleanup (#68651) (#68755) ### Summary This PR is a follow up to #68027 where some feedback didn't make it in. It cleans up the and_or_badge component, updates some css, and cleans up stories. --- .../__examples__/index.stories.tsx | 37 ++++---- .../components/and_or_badge/index.test.tsx | 18 +++- .../common/components/and_or_badge/index.tsx | 93 +------------------ .../and_or_badge/rounded_badge.test.tsx | 34 +++++++ .../components/and_or_badge/rounded_badge.tsx | 42 +++++++++ .../rounded_badge_antenna.test.tsx | 46 +++++++++ .../and_or_badge/rounded_badge_antenna.tsx | 62 +++++++++++++ .../__examples__/exception_item.stories.tsx | 89 +++++++++--------- .../components/exceptions/helpers.test.tsx | 8 +- .../common/components/exceptions/helpers.tsx | 4 +- .../exception_item/exception_details.tsx | 51 +++++----- .../exception_item/exception_entries.test.tsx | 34 +++---- .../exception_item/exception_entries.tsx | 28 ++++-- .../viewer/exception_item/index.test.tsx | 36 +++---- .../viewer/exception_item/index.tsx | 32 +++---- .../components/exceptions/viewer/index.tsx | 8 +- .../security_solution/public/graphql/types.ts | 2 + .../security_solution/scripts/storybook.js | 2 +- 18 files changed, 371 insertions(+), 255 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx index f939cf81d1bd3..7465d3ca1e63a 100644 --- a/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { storiesOf } from '@storybook/react'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { ThemeProvider } from 'styled-components'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; @@ -14,26 +14,21 @@ import { AndOrBadge } from '..'; const sampleText = 'Doggo ipsum i am bekom fat snoot wow such tempt waggy wags floofs, ruff heckin good boys and girls mlem. Ruff heckin good boys and girls mlem stop it fren borkf borking doggo very hand that feed shibe, you are doing me the shock big ol heck smol borking doggo with a long snoot for pats heckin good boys. You are doing me the shock smol borking doggo with a long snoot for pats wow very biscit, length boy. Doggo ipsum i am bekom fat snoot wow such tempt waggy wags floofs, ruff heckin good boys and girls mlem. Ruff heckin good boys and girls mlem stop it fren borkf borking doggo very hand that feed shibe, you are doing me the shock big ol heck smol borking doggo with a long snoot for pats heckin good boys.'; +const withTheme = (storyFn: () => ReactNode) => ( + ({ eui: euiLightVars, darkMode: true })}>{storyFn()} +); + storiesOf('components/AndOrBadge', module) - .add('and', () => ( - ({ eui: euiLightVars, darkMode: true })}> - - - )) - .add('or', () => ( - ({ eui: euiLightVars, darkMode: true })}> - - - )) + .addDecorator(withTheme) + .add('and', () => ) + .add('or', () => ) .add('antennas', () => ( - ({ eui: euiLightVars, darkMode: true })}> - - - - - -

{sampleText}

-
-
-
+ + + + + +

{sampleText}

+
+
)); diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.test.tsx index ed918a59a514a..f2c7d6884bae3 100644 --- a/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.test.tsx @@ -20,8 +20,20 @@ describe('AndOrBadge', () => { ); expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('AND'); - expect(wrapper.find('EuiFlexItem[data-test-subj="andOrBadgeBarTop"]')).toHaveLength(1); - expect(wrapper.find('EuiFlexItem[data-test-subj="andOrBadgeBarBottom"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="andOrBadgeBarTop"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="andOrBadgeBarBottom"]').exists()).toBeTruthy(); + }); + + test('it does not render top and bottom antenna bars when "includeAntennas" is false', () => { + const wrapper = mount( + ({ eui: euiLightVars, darkMode: false })}> + + + ); + + expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('OR'); + expect(wrapper.find('[data-test-subj="andOrBadgeBarTop"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="andOrBadgeBarBottom"]').exists()).toBeFalsy(); }); test('it renders "and" when "type" is "and"', () => { @@ -32,7 +44,6 @@ describe('AndOrBadge', () => { ); expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('AND'); - expect(wrapper.find('EuiFlexItem[data-test-subj="and-or-badge-bar"]')).toHaveLength(0); }); test('it renders "or" when "type" is "or"', () => { @@ -43,6 +54,5 @@ describe('AndOrBadge', () => { ); expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('OR'); - expect(wrapper.find('EuiFlexItem[data-test-subj="and-or-badge-bar"]')).toHaveLength(0); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.tsx index ba3f880d9757e..e427e57a2c616 100644 --- a/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/index.tsx @@ -3,70 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { EuiFlexGroup, EuiBadge, EuiFlexItem } from '@elastic/eui'; import React from 'react'; -import styled, { css } from 'styled-components'; - -import * as i18n from './translations'; - -const AndOrBadgeAntenna = styled(EuiFlexItem)` - ${({ theme }) => css` - background: ${theme.eui.euiColorLightShade}; - position: relative; - width: 2px; - &:after { - background: ${theme.eui.euiColorLightShade}; - content: ''; - height: 8px; - right: -4px; - position: absolute; - width: 9px; - clip-path: circle(); - } - &.topAndOrBadgeAntenna { - &:after { - top: -1px; - } - } - &.bottomAndOrBadgeAntenna { - &:after { - bottom: -1px; - } - } - &.euiFlexItem { - margin: 0 12px 0 0; - } - `} -`; - -const EuiFlexItemWrapper = styled(EuiFlexItem)` - &.euiFlexItem { - margin: 0 12px 0 0; - } -`; -const RoundedBadge = (styled(EuiBadge)` - align-items: center; - border-radius: 100%; - display: inline-flex; - font-size: 9px; - height: 34px; - justify-content: center; - margin: 0 5px 0 5px; - padding: 7px 6px 4px 6px; - user-select: none; - width: 34px; - .euiBadge__content { - position: relative; - top: -1px; - } - .euiBadge__text { - text-overflow: clip; - } -` as unknown) as typeof EuiBadge; - -RoundedBadge.displayName = 'RoundedBadge'; +import { RoundedBadge } from './rounded_badge'; +import { RoundedBadgeAntenna } from './rounded_badge_antenna'; export type AndOr = 'and' | 'or'; @@ -74,34 +14,7 @@ export type AndOr = 'and' | 'or'; // Ref: https://github.com/elastic/eui/issues/1655 export const AndOrBadge = React.memo<{ type: AndOr; includeAntennas?: boolean }>( ({ type, includeAntennas = false }) => { - const getBadge = () => ( - - {type === 'and' ? i18n.AND : i18n.OR} - - ); - - const getBadgeWithAntennas = () => ( - - - {getBadge()} - - - ); - - return includeAntennas ? getBadgeWithAntennas() : getBadge(); + return includeAntennas ? : ; } ); diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.test.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.test.tsx new file mode 100644 index 0000000000000..14d9627d48ad7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { RoundedBadge } from './rounded_badge'; + +describe('RoundedBadge', () => { + test('it renders "and" when "type" is "and"', () => { + const wrapper = mount( + ({ eui: euiLightVars, darkMode: false })}> + + + ); + + expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('AND'); + }); + + test('it renders "or" when "type" is "or"', () => { + const wrapper = mount( + ({ eui: euiLightVars, darkMode: false })}> + + + ); + + expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('OR'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx new file mode 100644 index 0000000000000..1a03e8c73f252 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiBadge } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +import * as i18n from './translations'; +import { AndOr } from '.'; + +const RoundBadge = (styled(EuiBadge)` + align-items: center; + border-radius: 100%; + display: inline-flex; + font-size: 9px; + height: 34px; + justify-content: center; + margin: 0 5px 0 5px; + padding: 7px 6px 4px 6px; + user-select: none; + width: 34px; + .euiBadge__content { + position: relative; + top: -1px; + } + .euiBadge__text { + text-overflow: clip; + } +` as unknown) as typeof EuiBadge; + +RoundBadge.displayName = 'RoundBadge'; + +export const RoundedBadge: React.FC<{ type: AndOr }> = ({ type }) => ( + + {type === 'and' ? i18n.AND : i18n.OR} + +); + +RoundedBadge.displayName = 'RoundedBadge'; diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.test.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.test.tsx new file mode 100644 index 0000000000000..e6362f8798fa6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.test.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { RoundedBadgeAntenna } from './rounded_badge_antenna'; + +describe('RoundedBadgeAntenna', () => { + test('it renders top and bottom antenna bars', () => { + const wrapper = mount( + ({ eui: euiLightVars, darkMode: false })}> + + + ); + + expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('AND'); + expect(wrapper.find('[data-test-subj="andOrBadgeBarTop"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="andOrBadgeBarBottom"]').exists()).toBeTruthy(); + }); + + test('it renders "and" when "type" is "and"', () => { + const wrapper = mount( + ({ eui: euiLightVars, darkMode: false })}> + + + ); + + expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('AND'); + }); + + test('it renders "or" when "type" is "or"', () => { + const wrapper = mount( + ({ eui: euiLightVars, darkMode: false })}> + + + ); + + expect(wrapper.find('[data-test-subj="and-or-badge"]').at(0).text()).toEqual('OR'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.tsx new file mode 100644 index 0000000000000..1076d8b41b955 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge_antenna.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +import { RoundedBadge } from './rounded_badge'; +import { AndOr } from '.'; + +const antennaStyles = css` + background: ${({ theme }) => theme.eui.euiColorLightShade}; + position: relative; + width: 2px; + margin: 0 12px 0 0; + &:after { + background: ${({ theme }) => theme.eui.euiColorLightShade}; + content: ''; + height: 8px; + right: -4px; + position: absolute; + width: 10px; + clip-path: circle(); + } +`; + +const TopAntenna = styled(EuiFlexItem)` + ${antennaStyles} + &:after { + top: 0; + } +`; +const BottomAntenna = styled(EuiFlexItem)` + ${antennaStyles} + &:after { + bottom: 0; + } +`; + +const EuiFlexItemWrapper = styled(EuiFlexItem)` + margin: 0 12px 0 0; +`; + +export const RoundedBadgeAntenna: React.FC<{ type: AndOr }> = ({ type }) => ( + + + + + + + +); + +RoundedBadgeAntenna.displayName = 'RoundedBadgeAntenna'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/__examples__/exception_item.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/__examples__/exception_item.stories.tsx index 8942832798a5e..5f2b0b93e9df0 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/__examples__/exception_item.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/__examples__/exception_item.stories.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { storiesOf } from '@storybook/react'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { ThemeProvider } from 'styled-components'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; @@ -12,8 +12,13 @@ import { ExceptionItem } from '../viewer/exception_item'; import { Operator } from '../types'; import { getExceptionItemMock } from '../mocks'; +const withTheme = (storyFn: () => ReactNode) => ( + ({ eui: euiLightVars, darkMode: false })}>{storyFn()} +); + storiesOf('ExceptionItem', module) - .add('with os', () => { + .addDecorator(withTheme) + .add('ExceptionItem/with os', () => { const payload = getExceptionItemMock(); payload.description = ''; payload.comment = []; @@ -27,15 +32,13 @@ storiesOf('ExceptionItem', module) ]; return ( - ({ eui: euiLightVars, darkMode: false })}> - {}} - handleEdit={() => {}} - /> - + {}} + onEditException={() => {}} + /> ); }) .add('with description', () => { @@ -52,15 +55,13 @@ storiesOf('ExceptionItem', module) ]; return ( - ({ eui: euiLightVars, darkMode: false })}> - {}} - handleEdit={() => {}} - /> - + {}} + onEditException={() => {}} + /> ); }) .add('with comments', () => { @@ -77,15 +78,13 @@ storiesOf('ExceptionItem', module) ]; return ( - ({ eui: euiLightVars, darkMode: false })}> - {}} - handleEdit={() => {}} - /> - + {}} + onEditException={() => {}} + /> ); }) .add('with nested entries', () => { @@ -95,29 +94,25 @@ storiesOf('ExceptionItem', module) payload.comment = []; return ( - ({ eui: euiLightVars, darkMode: false })}> - {}} - handleEdit={() => {}} - /> - + {}} + onEditException={() => {}} + /> ); }) .add('with everything', () => { const payload = getExceptionItemMock(); return ( - ({ eui: euiLightVars, darkMode: false })}> - {}} - handleEdit={() => {}} - /> - + {}} + onEditException={() => {}} + /> ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index 7698605588e76..2893c7dc961f2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -10,7 +10,7 @@ import moment from 'moment-timezone'; import { getOperatorType, getExceptionOperatorSelect, - determineIfIsNested, + isEntryNested, getFormattedEntries, formatEntry, getOperatingSystems, @@ -159,21 +159,21 @@ describe('Exception helpers', () => { }); }); - describe('#determineIfIsNested', () => { + describe('#isEntryNested', () => { test('it returns true if type NestedExceptionEntry', () => { const payload: NestedExceptionEntry = { field: 'actingProcess.file.signer', type: 'nested', entries: [], }; - const result = determineIfIsNested(payload); + const result = isEntryNested(payload); expect(result).toBeTruthy(); }); test('it returns false if NOT type NestedExceptionEntry', () => { const payload = getExceptionItemEntryMock(); - const result = determineIfIsNested(payload); + const result = isEntryNested(payload); expect(result).toBeFalsy(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx index bd22de636bf6c..155c8a3e2926c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx @@ -56,7 +56,7 @@ export const getExceptionOperatorSelect = (entry: ExceptionEntry): OperatorOptio return foundOperator ?? isOperator; }; -export const determineIfIsNested = ( +export const isEntryNested = ( tbd: ExceptionEntry | NestedExceptionEntry ): tbd is NestedExceptionEntry => { if (tbd.type === 'nested') { @@ -75,7 +75,7 @@ export const getFormattedEntries = ( entries: Array ): FormattedEntry[] => { const formattedEntries = entries.map((entry) => { - if (determineIfIsNested(entry)) { + if (isEntryNested(entry)) { const parent = { fieldName: entry.field, operator: null, value: null, isNested: false }; return entry.entries.reduce( (acc, nestedEntry) => { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx index 6f418808b239a..12287f7cd0fa9 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx @@ -4,28 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexItem, EuiFlexGroup, EuiDescriptionList, EuiButtonEmpty } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import { + EuiFlexItem, + EuiFlexGroup, + EuiDescriptionList, + EuiButtonEmpty, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import React, { useMemo, Fragment } from 'react'; import styled, { css } from 'styled-components'; -import { transparentize } from 'polished'; -import { ExceptionListItemSchema } from '../../types'; +import { DescriptionListItem, ExceptionListItemSchema } from '../../types'; import { getDescriptionListContent } from '../../helpers'; import * as i18n from '../../translations'; const StyledExceptionDetails = styled(EuiFlexItem)` ${({ theme }) => css` - background-color: ${transparentize(0.95, theme.eui.euiColorPrimary)}; + background-color: ${theme.eui.euiColorLightestShade}; padding: ${theme.eui.euiSize}; + `} +`; - .euiDescriptionList__title.listTitle--width { - width: 40%; - } +const MyDescriptionListTitle = styled(EuiDescriptionListTitle)` + width: 40%; +`; - .euiDescriptionList__description.listDescription--width { - width: 60%; - } - `} +const MyDescriptionListDescription = styled(EuiDescriptionListDescription)` + width: 60%; `; const ExceptionDetailsComponent = ({ @@ -37,7 +43,10 @@ const ExceptionDetailsComponent = ({ exceptionItem: ExceptionListItemSchema; onCommentsClick: () => void; }): JSX.Element => { - const descriptionList = useMemo(() => getDescriptionListContent(exceptionItem), [exceptionItem]); + const descriptionListItems = useMemo( + (): DescriptionListItem[] => getDescriptionListContent(exceptionItem), + [exceptionItem] + ); const commentsSection = useMemo((): JSX.Element => { // TODO: return back to exceptionItem.comments once updated @@ -62,14 +71,14 @@ const ExceptionDetailsComponent = ({ - + + {descriptionListItems.map((item) => ( + + {item.title} + {item.description} + + ))} + {commentsSection} diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx index 10f11231ace01..b2408a654b1c6 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx @@ -20,8 +20,8 @@ describe('ExceptionEntries', () => { ); @@ -35,8 +35,8 @@ describe('ExceptionEntries', () => { ); @@ -45,39 +45,39 @@ describe('ExceptionEntries', () => { }); test('it invokes "handlEdit" when edit button clicked', () => { - const mockHandleEdit = jest.fn(); + const mockOnEdit = jest.fn(); const wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> ); const editBtn = wrapper.find('[data-test-subj="exceptionsViewerEditBtn"] button').at(0); editBtn.simulate('click'); - expect(mockHandleEdit).toHaveBeenCalledTimes(1); + expect(mockOnEdit).toHaveBeenCalledTimes(1); }); - test('it invokes "handleDelete" when delete button clicked', () => { - const mockHandleDelete = jest.fn(); + test('it invokes "onDelete" when delete button clicked', () => { + const mockOnDelete = jest.fn(); const wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> ); const deleteBtn = wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button').at(0); deleteBtn.simulate('click'); - expect(mockHandleDelete).toHaveBeenCalledTimes(1); + expect(mockOnDelete).toHaveBeenCalledTimes(1); }); test('it renders nested entry', () => { @@ -90,8 +90,8 @@ describe('ExceptionEntries', () => { ); @@ -132,8 +132,8 @@ describe('ExceptionEntries', () => { ); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx index 6f29875784e61..8c758e3b84f42 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiButton, EuiTableFieldDataColumnType, + EuiHideFor, } from '@elastic/eui'; import React, { useMemo } from 'react'; import styled, { css } from 'styled-components'; @@ -48,15 +49,15 @@ const AndOrBadgeContainer = styled(EuiFlexItem)` interface ExceptionEntriesComponentProps { entries: FormattedEntry[]; disableDelete: boolean; - handleDelete: () => void; - handleEdit: () => void; + onDelete: () => void; + onEdit: () => void; } const ExceptionEntriesComponent = ({ entries, disableDelete, - handleDelete, - handleEdit, + onDelete, + onEdit, }: ExceptionEntriesComponentProps): JSX.Element => { const columns = useMemo( (): Array> => [ @@ -65,6 +66,7 @@ const ExceptionEntriesComponent = ({ name: 'Field', sortable: false, truncateText: true, + textOnly: true, 'data-test-subj': 'exceptionFieldNameCell', width: '30%', render: (value: string | null, data: FormattedEntry) => { @@ -121,9 +123,15 @@ const ExceptionEntriesComponent = ({ {entries.length > 1 && ( - - - + + + + + )} - + @@ -153,7 +161,7 @@ const ExceptionEntriesComponent = ({ diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx index 784fc4336a5ff..dca3afe4f9069 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx @@ -21,8 +21,8 @@ describe('ExceptionItem', () => { @@ -32,8 +32,8 @@ describe('ExceptionItem', () => { expect(wrapper.find('ExceptionEntries')).toHaveLength(1); }); - it('it invokes "handleEdit" when edit button clicked', () => { - const mockHandleEdit = jest.fn(); + it('it invokes "onEditException" when edit button clicked', () => { + const mockOnEditException = jest.fn(); const exceptionItem = getExceptionItemMock(); const wrapper = mount( @@ -41,8 +41,8 @@ describe('ExceptionItem', () => { @@ -51,11 +51,11 @@ describe('ExceptionItem', () => { const editBtn = wrapper.find('[data-test-subj="exceptionsViewerEditBtn"] button').at(0); editBtn.simulate('click'); - expect(mockHandleEdit).toHaveBeenCalledTimes(1); + expect(mockOnEditException).toHaveBeenCalledTimes(1); }); - it('it invokes "handleDelete" when delete button clicked', () => { - const mockHandleDelete = jest.fn(); + it('it invokes "onDeleteException" when delete button clicked', () => { + const mockOnDeleteException = jest.fn(); const exceptionItem = getExceptionItemMock(); const wrapper = mount( @@ -63,8 +63,8 @@ describe('ExceptionItem', () => { @@ -73,11 +73,11 @@ describe('ExceptionItem', () => { const editBtn = wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button').at(0); editBtn.simulate('click'); - expect(mockHandleDelete).toHaveBeenCalledTimes(1); + expect(mockOnDeleteException).toHaveBeenCalledTimes(1); }); it('it renders comment accordion closed to begin with', () => { - const mockHandleDelete = jest.fn(); + const mockOnDeleteException = jest.fn(); const exceptionItem = getExceptionItemMock(); const wrapper = mount( @@ -85,8 +85,8 @@ describe('ExceptionItem', () => { @@ -96,7 +96,7 @@ describe('ExceptionItem', () => { }); it('it renders comment accordion open when showComments is true', () => { - const mockHandleDelete = jest.fn(); + const mockOnDeleteException = jest.fn(); const exceptionItem = getExceptionItemMock(); const wrapper = mount( @@ -104,8 +104,8 @@ describe('ExceptionItem', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx index 386ab6f3c3c7c..ba6ffb109be04 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx @@ -21,26 +21,26 @@ import { getFormattedEntries, getFormattedComments } from '../../helpers'; import { FormattedEntry, ExceptionListItemSchema, ApiProps } from '../../types'; const MyFlexItem = styled(EuiFlexItem)` - &.comments--show { - padding: ${({ theme }) => theme.eui.euiSize}; - border-top: ${({ theme }) => `${theme.eui.euiBorderThin}`} - + &.comments--show { + padding: ${({ theme }) => theme.eui.euiSize}; + border-top: ${({ theme }) => `${theme.eui.euiBorderThin}`}; + } `; interface ExceptionItemProps { loadingItemIds: ApiProps[]; exceptionItem: ExceptionListItemSchema; commentsAccordionId: string; - handleDelete: (arg: ApiProps) => void; - handleEdit: (item: ExceptionListItemSchema) => void; + onDeleteException: (arg: ApiProps) => void; + onEditException: (item: ExceptionListItemSchema) => void; } const ExceptionItemComponent = ({ loadingItemIds, exceptionItem, commentsAccordionId, - handleDelete, - handleEdit, + onDeleteException, + onEditException, }: ExceptionItemProps): JSX.Element => { const [entryItems, setEntryItems] = useState([]); const [showComments, setShowComments] = useState(false); @@ -50,13 +50,13 @@ const ExceptionItemComponent = ({ setEntryItems(formattedEntries); }, [exceptionItem.entries]); - const onDelete = useCallback((): void => { - handleDelete({ id: exceptionItem.id, namespaceType: exceptionItem.namespace_type }); - }, [handleDelete, exceptionItem]); + const handleDelete = useCallback((): void => { + onDeleteException({ id: exceptionItem.id, namespaceType: exceptionItem.namespace_type }); + }, [onDeleteException, exceptionItem]); - const onEdit = useCallback((): void => { - handleEdit(exceptionItem); - }, [handleEdit, exceptionItem]); + const handleEdit = useCallback((): void => { + onEditException(exceptionItem); + }, [onEditException, exceptionItem]); const onCommentsClick = useCallback((): void => { setShowComments(!showComments); @@ -85,8 +85,8 @@ const ExceptionItemComponent = ({ diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index ff52e395c3b1e..3cf59c7dda023 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -195,7 +195,7 @@ const ExceptionsViewerComponent = ({ [setIsModalOpen] ); - const onEditExceptionItem = useCallback( + const handleEditException = useCallback( (exception: ExceptionListItemSchema): void => { // TODO: Added this just for testing. Update // modal state logic as needed once ready @@ -234,7 +234,7 @@ const ExceptionsViewerComponent = ({ [dispatch] ); - const onDeleteException = useCallback( + const handleDeleteException = useCallback( ({ id, namespaceType }: ApiProps) => { deleteExceptionItem({ id, @@ -395,8 +395,8 @@ const ExceptionsViewerComponent = ({ loadingItemIds={loadingItemIds} commentsAccordionId={commentsAccordionId} exceptionItem={exception} - handleDelete={onDeleteException} - handleEdit={onEditExceptionItem} + onDeleteException={handleDeleteException} + onEditException={handleEditException} /> ))} diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index 628d56ebb647c..dc4a8ae78bf46 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -4340,6 +4340,8 @@ export namespace GetAllTimeline { pinnedEventIds: Maybe; + status: Maybe; + title: Maybe; timelineType: Maybe; diff --git a/x-pack/plugins/security_solution/scripts/storybook.js b/x-pack/plugins/security_solution/scripts/storybook.js index 5f06f2a4ebb12..cd4d16d89c48d 100644 --- a/x-pack/plugins/security_solution/scripts/storybook.js +++ b/x-pack/plugins/security_solution/scripts/storybook.js @@ -9,5 +9,5 @@ import { join } from 'path'; // eslint-disable-next-line require('@kbn/storybook').runStorybookCli({ name: 'siem', - storyGlobs: [join(__dirname, '..', 'public', '**', 'components', '**', '*.stories.tsx')], + storyGlobs: [join(__dirname, '..', 'public', '**', '*.stories.tsx')], });