From ebfba81ba54b4b25f07a98fc3eefe7a7927a3e02 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 27 Aug 2020 13:25:01 -0400 Subject: [PATCH] [Security Solution][Resolver] break/wrap for process detail (#76095) * [Security Solution][Resolver]break/wrap for process detail * add an enzyme test to check for the breakers --- .../public/resolver/mocks/resolver_tree.ts | 2 +- .../public/resolver/view/panel.test.tsx | 17 ++++++++-- .../view/panels/panel_content_utilities.tsx | 32 +++++++++++++++++++ .../resolver/view/panels/process_details.tsx | 16 +++++++--- .../view/panels/related_event_detail.tsx | 30 +---------------- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts index 7edf4f8071ed..8bd5953e9cb4 100644 --- a/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts @@ -177,7 +177,7 @@ export function mockTreeWithNoAncestorsAnd2Children({ const origin: ResolverEvent = mockEndpointEvent({ pid: 0, entityID: originID, - name: 'c', + name: 'c.ext', parentEntityId: 'none', timestamp: 0, }); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx index 037337fb2f86..1add907ae933 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx @@ -81,7 +81,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children, an }; }) ).toYieldEqualTo({ - title: 'c', + title: 'c.ext', titleIcon: 'Running Process', detailEntries: [ ['process.executable', 'executable'], @@ -94,6 +94,19 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children, an ], }); }); + it('should have breaking opportunities (s) in node titles to allow wrapping', async () => { + await expect( + simulator().map(() => { + const titleWrapper = simulator().testSubject('resolver:node-detail:title'); + return { + wordBreaks: titleWrapper.find('wbr').length, + }; + }) + ).toYieldEqualTo({ + // The GeneratedText component adds 1 after the period and one at the end + wordBreaks: 2, + }); + }); }); const queryStringWithFirstChildSelected = urlSearch(resolverComponentInstanceID, { @@ -174,7 +187,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children, an .testSubject('resolver:node-list:node-link:title') .map((node) => node.text()); }) - ).toYieldEqualTo(['c', 'd', 'e']); + ).toYieldEqualTo(['c.ext', 'd', 'e']); }); }); }); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 5c7a4a476efb..19f0aa3fe1d6 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -43,6 +43,38 @@ const betaBadgeLabel = i18n.translate( } ); +/** + * A component that renders an element with breaking opportunities (``s) + * spliced into text children at word boundaries. + */ +export const GeneratedText = React.memo(function ({ children }) { + return <>{processedValue()}; + + function processedValue() { + return React.Children.map(children, (child) => { + if (typeof child === 'string') { + const valueSplitByWordBoundaries = child.split(/\b/); + + if (valueSplitByWordBoundaries.length < 2) { + return valueSplitByWordBoundaries[0]; + } + + return [ + valueSplitByWordBoundaries[0], + ...valueSplitByWordBoundaries + .splice(1) + .reduce(function (generatedTextMemo: Array, value, index) { + return [...generatedTextMemo, value, ]; + }, []), + ]; + } else { + return child; + } + }); + } +}); +GeneratedText.displayName = 'GeneratedText'; + /** * A component to keep time representations in blocks so they don't wrap * and look bad. diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx index 15711909c4c9..e86e3e6baf4a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx @@ -19,7 +19,7 @@ import { FormattedMessage } from 'react-intl'; import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import * as selectors from '../../store/selectors'; import * as event from '../../../../common/endpoint/models/event'; -import { formatDate, StyledBreadcrumbs } from './panel_content_utilities'; +import { formatDate, StyledBreadcrumbs, GeneratedText } from './panel_content_utilities'; import { processPath, processPid, @@ -39,6 +39,10 @@ const StyledDescriptionList = styled(EuiDescriptionList)` } `; +const StyledTitle = styled('h4')` + overflow-wrap: break-word; +`; + /** * A description list view of all the Metadata that goes with a particular process event, like: * Created, PID, User/Domain, etc. @@ -114,7 +118,7 @@ export const ProcessDetails = memo(function ProcessDetails({ .map((entry) => { return { ...entry, - description: String(entry.description), + description: {String(entry.description)}, }; }); @@ -163,13 +167,15 @@ export const ProcessDetails = memo(function ProcessDetails({ -

+ - {processName} -

+ + {processName} + +
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx index da4cd3c9daca..6aacf91c5617 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx @@ -10,7 +10,7 @@ import { EuiSpacer, EuiText, EuiDescriptionList, EuiTextColor, EuiTitle } from ' import styled from 'styled-components'; import { useSelector } from 'react-redux'; import { FormattedMessage } from 'react-intl'; -import { StyledBreadcrumbs, BoldCode, StyledTime } from './panel_content_utilities'; +import { StyledBreadcrumbs, BoldCode, StyledTime, GeneratedText } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; import { ResolverEvent } from '../../../../common/endpoint/types'; import * as selectors from '../../store/selectors'; @@ -57,34 +57,6 @@ const TitleHr = memo(() => { }); TitleHr.displayName = 'TitleHR'; -const GeneratedText = React.memo(function ({ children }) { - return <>{processedValue()}; - - function processedValue() { - return React.Children.map(children, (child) => { - if (typeof child === 'string') { - const valueSplitByWordBoundaries = child.split(/\b/); - - if (valueSplitByWordBoundaries.length < 2) { - return valueSplitByWordBoundaries[0]; - } - - return [ - valueSplitByWordBoundaries[0], - ...valueSplitByWordBoundaries - .splice(1) - .reduce(function (generatedTextMemo: Array, value, index) { - return [...generatedTextMemo, value, ]; - }, []), - ]; - } else { - return child; - } - }); - } -}); -GeneratedText.displayName = 'GeneratedText'; - /** * Take description list entries and prepare them for display by * seeding with `` tags.