From 35c83bac5a22fe79e8b9cafa1fd99817cdc36552 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 13 Sep 2021 16:04:56 -0400 Subject: [PATCH] [SECURITY SOLUTION] Bring back Drag drop in query tabs of active timeline (#111908) * bring back drag drop in active timeline detail view * only allow drag and drop in query tab * fix snapshot * fix bug on alert summary to remove dragdrop --- .../event_details/alert_summary_view.tsx | 18 ++-- .../components/event_details/columns.tsx | 3 + .../event_details/event_details.tsx | 11 ++- .../event_details/event_fields_browser.tsx | 5 +- .../components/event_details/helpers.tsx | 1 + .../event_details/table/field_value_cell.tsx | 4 +- .../network/components/details/index.tsx | 13 ++- .../components/host_overview/index.tsx | 12 ++- .../field_renderers/field_renderers.tsx | 23 +++-- .../__snapshots__/index.test.tsx.snap | 6 ++ .../event_details/expandable_event.tsx | 13 ++- .../side_panel/event_details/index.tsx | 7 +- .../expandable_host.test.tsx.snap | 45 ++++++++++ .../host_details/expandable_host.tsx | 4 +- .../side_panel/host_details/index.tsx | 9 +- .../components/side_panel/index.test.tsx | 89 ++++++++++++++++++- .../timelines/components/side_panel/index.tsx | 6 +- .../network_details/expandable_network.tsx | 4 +- .../side_panel/network_details/index.tsx | 15 +++- 19 files changed, 247 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index 03c420914d170..e7816fd1daaa8 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -117,6 +117,7 @@ const getDescription = ({ data, eventId, fieldFromBrowserField, + isDraggable, linkValue, timelineId, values, @@ -133,6 +134,7 @@ const getDescription = ({ eventId={eventId} fieldFromBrowserField={fieldFromBrowserField} linkValue={linkValue} + isDraggable={isDraggable} values={values} /> { const eventCategoryField = find({ category: 'event', field: 'event.category' }, data); @@ -206,6 +210,7 @@ export const getSummaryRows = ({ const initialDescription = { contextId: timelineId, eventId, + isDraggable, value: null, fieldType: 'string', linkValue: undefined, @@ -304,15 +309,14 @@ const AlertSummaryViewComponent: React.FC<{ browserFields: BrowserFields; data: TimelineEventsDetailsItem[]; eventId: string; + isDraggable?: boolean; timelineId: string; title?: string; -}> = ({ browserFields, data, eventId, timelineId, title }) => { - const summaryRows = useMemo(() => getSummaryRows({ browserFields, data, eventId, timelineId }), [ - browserFields, - data, - eventId, - timelineId, - ]); +}> = ({ browserFields, data, eventId, isDraggable, timelineId, title }) => { + const summaryRows = useMemo( + () => getSummaryRows({ browserFields, data, eventId, isDraggable, timelineId }), + [browserFields, data, eventId, isDraggable, timelineId] + ); const ruleId = useMemo(() => { const item = data.find((d) => d.field === 'signal.rule.id'); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx index 5a0e0a0633e00..b91978489f64e 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx @@ -48,6 +48,7 @@ export const getColumns = ({ timelineId, toggleColumn, getLinkValue, + isDraggable, }: { browserFields: BrowserFields; columnHeaders: ColumnHeaderOptions[]; @@ -57,6 +58,7 @@ export const getColumns = ({ timelineId: string; toggleColumn: (column: ColumnHeaderOptions) => void; getLinkValue: (field: string) => string | null; + isDraggable?: boolean; }) => [ { field: 'values', @@ -138,6 +140,7 @@ export const getColumns = ({ eventId={eventId} fieldFromBrowserField={fieldFromBrowserField} getLinkValue={getLinkValue} + isDraggable={isDraggable} values={values} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 2265a0c17c4dd..d058efe7c904d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -57,6 +57,7 @@ interface Props { data: TimelineEventsDetailsItem[]; id: string; isAlert: boolean; + isDraggable?: boolean; timelineTabType: TimelineTabs | 'flyout'; timelineId: string; } @@ -95,6 +96,7 @@ const EventDetailsComponent: React.FC = ({ data, id, isAlert, + isDraggable, timelineId, timelineTabType, }) => { @@ -144,6 +146,7 @@ const EventDetailsComponent: React.FC = ({ data, eventId: id, browserFields, + isDraggable, timelineId, title: i18n.DUCOMENT_SUMMARY, }} @@ -168,13 +171,14 @@ const EventDetailsComponent: React.FC = ({ : undefined, [ isAlert, - data, id, + data, browserFields, + isDraggable, timelineId, - isEnrichmentsLoading, enrichmentCount, allEnrichments, + isEnrichmentsLoading, ] ); @@ -238,13 +242,14 @@ const EventDetailsComponent: React.FC = ({ browserFields={browserFields} data={data} eventId={id} + isDraggable={isDraggable} timelineId={timelineId} timelineTabType={timelineTabType} /> ), }), - [browserFields, data, id, timelineId, timelineTabType] + [browserFields, data, id, isDraggable, timelineId, timelineTabType] ); const jsonTab = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index 5af6502ae7cd6..b0e818d08678e 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -34,6 +34,7 @@ interface Props { browserFields: BrowserFields; data: TimelineEventsDetailsItem[]; eventId: string; + isDraggable?: boolean; timelineId: string; timelineTabType: TimelineTabs | 'flyout'; } @@ -136,7 +137,7 @@ const StyledEuiInMemoryTable = styled(EuiInMemoryTable as any)` /** Renders a table view or JSON view of the `ECS` `data` */ export const EventFieldsBrowser = React.memo( - ({ browserFields, data, eventId, timelineTabType, timelineId }) => { + ({ browserFields, data, eventId, isDraggable, timelineTabType, timelineId }) => { const containerElement = useRef(null); const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -217,6 +218,7 @@ export const EventFieldsBrowser = React.memo( timelineId, toggleColumn, getLinkValue, + isDraggable, }), [ browserFields, @@ -227,6 +229,7 @@ export const EventFieldsBrowser = React.memo( timelineTabType, toggleColumn, getLinkValue, + isDraggable, ] ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx index fea3fff4f0555..a1c74a603fbc3 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx @@ -59,6 +59,7 @@ export interface AlertSummaryRow { description: { data: FieldsData; eventId: string; + isDraggable?: boolean; fieldFromBrowserField?: BrowserField; linkValue: string | undefined; timelineId: string; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx index bf19384b0d0aa..ef7b9da696028 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx @@ -19,6 +19,7 @@ export interface FieldValueCellProps { eventId: string; fieldFromBrowserField?: BrowserField; getLinkValue?: (field: string) => string | null; + isDraggable?: boolean; linkValue?: string | null | undefined; values: string[] | null | undefined; } @@ -30,6 +31,7 @@ export const FieldValueCell = React.memo( eventId, fieldFromBrowserField, getLinkValue, + isDraggable = false, linkValue, values, }: FieldValueCellProps) => { @@ -55,7 +57,7 @@ export const FieldValueCell = React.memo( fieldFormat={data.format} fieldName={data.field} fieldType={data.type} - isDraggable={false} + isDraggable={isDraggable} isObjectArray={data.isObjectArray} value={value} linkValue={(getLinkValue && getLinkValue(data.field)) ?? linkValue} diff --git a/x-pack/plugins/security_solution/public/network/components/details/index.tsx b/x-pack/plugins/security_solution/public/network/components/details/index.tsx index 851197a78520b..7658a6a76230c 100644 --- a/x-pack/plugins/security_solution/public/network/components/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/details/index.tsx @@ -43,6 +43,7 @@ export interface IpOverviewProps { flowTarget: FlowTarget; id: string; ip: string; + isDraggable?: boolean; isInDetailsSidePanel: boolean; isLoadingAnomaliesData: boolean; loading: boolean; @@ -57,6 +58,7 @@ export const IpOverview = React.memo( id, ip, data, + isDraggable = false, isInDetailsSidePanel = false, // Rather than duplicate the component, alter the structure based on it's location loading, flowTarget, @@ -76,13 +78,14 @@ export const IpOverview = React.memo( description: locationRenderer( [`${flowTarget}.geo.city_name`, `${flowTarget}.geo.region_name`], data, - contextID + contextID, + isDraggable ), }, { title: i18n.AUTONOMOUS_SYSTEM, description: typeData - ? autonomousSystemRenderer(typeData.autonomousSystem, flowTarget, contextID) + ? autonomousSystemRenderer(typeData.autonomousSystem, flowTarget, contextID, isDraggable) : getEmptyTagValue(), }, ]; @@ -122,13 +125,15 @@ export const IpOverview = React.memo( title: i18n.HOST_ID, description: typeData && data.host - ? hostIdRenderer({ host: data.host, ipFilter: ip, contextID }) + ? hostIdRenderer({ host: data.host, isDraggable, ipFilter: ip, contextID }) : getEmptyTagValue(), }, { title: i18n.HOST_NAME, description: - typeData && data.host ? hostNameRenderer(data.host, ip, contextID) : getEmptyTagValue(), + typeData && data.host + ? hostNameRenderer(data.host, ip, contextID, isDraggable) + : getEmptyTagValue(), }, ], [ diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index 75723e0d3af84..e73096aa3babf 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -42,6 +42,7 @@ interface HostSummaryProps { data: HostItem; docValueFields: DocValueFields[]; id: string; + isDraggable?: boolean; isInDetailsSidePanel: boolean; loading: boolean; isLoadingAnomaliesData: boolean; @@ -60,6 +61,7 @@ export const HostOverview = React.memo( docValueFields, endDate, id, + isDraggable = false, isInDetailsSidePanel = false, // Rather than duplicate the component, alter the structure based on it's location isLoadingAnomaliesData, indexNames, @@ -77,9 +79,10 @@ export const HostOverview = React.memo( rowItems={getOr([], fieldName, fieldData)} attrName={fieldName} idPrefix={contextID ? `host-overview-${contextID}` : 'host-overview'} + isDraggable={isDraggable} /> ), - [contextID] + [contextID, isDraggable] ); const column: DescriptionList[] = useMemo( @@ -88,7 +91,7 @@ export const HostOverview = React.memo( title: i18n.HOST_ID, description: data && data.host - ? hostIdRenderer({ host: data.host, noLink: true }) + ? hostIdRenderer({ host: data.host, isDraggable, noLink: true }) : getEmptyTagValue(), }, { @@ -120,7 +123,7 @@ export const HostOverview = React.memo( ), }, ], - [data, docValueFields, indexNames] + [data, docValueFields, indexNames, isDraggable] ); const firstColumn = useMemo( () => @@ -163,6 +166,7 @@ export const HostOverview = React.memo( rowItems={getOr([], 'host.ip', data)} attrName={'host.ip'} idPrefix={contextID ? `host-overview-${contextID}` : 'host-overview'} + isDraggable={isDraggable} render={(ip) => (ip != null ? : getEmptyTagValue())} /> ), @@ -198,7 +202,7 @@ export const HostOverview = React.memo( }, ], ], - [contextID, data, firstColumn, getDefaultRenderer] + [contextID, data, firstColumn, getDefaultRenderer, isDraggable] ); return ( <> diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx index d6dee8a8406b9..af5596be0732f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx @@ -42,7 +42,8 @@ export const DEFAULT_MORE_MAX_HEIGHT = '200px'; export const locationRenderer = ( fieldNames: string[], data: NetworkDetailsStrategyResponse['networkDetails'], - contextID?: string + contextID?: string, + isDraggable?: boolean ): React.ReactElement => fieldNames.length > 0 && fieldNames.every((fieldName) => getOr(null, fieldName, data)) ? ( @@ -56,7 +57,7 @@ export const locationRenderer = ( id={`location-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }${fieldName}`} - isDraggable={false} + isDraggable={isDraggable ?? false} field={fieldName} value={locationValue} /> @@ -76,7 +77,8 @@ export const dateRenderer = (timestamp?: string | null): React.ReactElement => ( export const autonomousSystemRenderer = ( as: AutonomousSystem, flowTarget: FlowTarget, - contextID?: string + contextID?: string, + isDraggable?: boolean ): React.ReactElement => as && as.organization && as.organization.name && as.number ? ( @@ -85,7 +87,7 @@ export const autonomousSystemRenderer = ( id={`autonomous-system-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }${flowTarget}.as.organization.name`} - isDraggable={false} + isDraggable={isDraggable ?? false} field={`${flowTarget}.as.organization.name`} value={as.organization.name} /> @@ -110,12 +112,14 @@ interface HostIdRendererTypes { contextID?: string; host: HostEcs; ipFilter?: string; + isDraggable?: boolean; noLink?: boolean; } export const hostIdRenderer = ({ contextID, host, + isDraggable = false, ipFilter, noLink, }: HostIdRendererTypes): React.ReactElement => @@ -126,7 +130,7 @@ export const hostIdRenderer = ({ id={`host-id-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }host-id`} - isDraggable={false} + isDraggable={isDraggable} field="host.id" value={host.id[0]} > @@ -147,7 +151,8 @@ export const hostIdRenderer = ({ export const hostNameRenderer = ( host?: HostEcs, ipFilter?: string, - contextID?: string + contextID?: string, + isDraggable?: boolean ): React.ReactElement => host && host.name && @@ -158,7 +163,7 @@ export const hostNameRenderer = ( id={`host-name-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }host-name`} - isDraggable={false} + isDraggable={isDraggable ?? false} field={'host.name'} value={host.name[0]} > @@ -180,6 +185,7 @@ interface DefaultFieldRendererProps { rowItems: string[] | null | undefined; attrName: string; idPrefix: string; + isDraggable?: boolean; render?: (item: string) => JSX.Element; displayCount?: number; moreMaxHeight?: string; @@ -191,6 +197,7 @@ export const DefaultFieldRendererComponent: React.FC attrName, displayCount = 1, idPrefix, + isDraggable = false, moreMaxHeight = DEFAULT_MORE_MAX_HEIGHT, render, rowItems, @@ -209,7 +216,7 @@ export const DefaultFieldRendererComponent: React.FC )} {typeof rowItem === 'string' && ( - + {render ? render(rowItem) : rowItem} )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap index e3cf7fed14abd..1608120095f8d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap @@ -197,6 +197,7 @@ exports[`Details Panel Component DetailsPanel:EventDetails: rendering it should } } handleOnEventClosed={[Function]} + isDraggable={false} isFlyoutView={false} tabType="query" timelineId="test" @@ -435,6 +436,7 @@ exports[`Details Panel Component DetailsPanel:EventDetails: rendering it should } } isAlert={false} + isDraggable={false} loading={true} timelineId="test" timelineTabType="query" @@ -737,6 +739,7 @@ Array [ } } handleOnEventClosed={[Function]} + isDraggable={false} isFlyoutView={true} tabType="query" timelineId="test" @@ -952,6 +955,7 @@ Array [ } } isAlert={false} + isDraggable={false} loading={true} timelineId="test" timelineTabType="flyout" @@ -1596,6 +1600,7 @@ Array [ } } handleOnEventClosed={[Function]} + isDraggable={false} isFlyoutView={true} tabType="query" timelineId="test" @@ -1811,6 +1816,7 @@ Array [ } } isAlert={false} + isDraggable={false} loading={true} timelineId="test" timelineTabType="flyout" diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx index 31cc61d4996a8..b3d7b869c0699 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx @@ -29,6 +29,7 @@ interface Props { detailsData: TimelineEventsDetailsItem[] | null; event: { eventId: string; indexName: string }; isAlert: boolean; + isDraggable?: boolean; loading: boolean; messageHeight?: number; timelineTabType: TimelineTabs | 'flyout'; @@ -80,7 +81,16 @@ export const ExpandableEventTitle = React.memo( ExpandableEventTitle.displayName = 'ExpandableEventTitle'; export const ExpandableEvent = React.memo( - ({ browserFields, event, timelineId, timelineTabType, isAlert, loading, detailsData }) => { + ({ + browserFields, + event, + timelineId, + timelineTabType, + isAlert, + isDraggable, + loading, + detailsData, + }) => { if (!event.eventId) { return {i18n.EVENT_DETAILS_PLACEHOLDER}; } @@ -97,6 +107,7 @@ export const ExpandableEvent = React.memo( data={detailsData ?? []} id={event.eventId!} isAlert={isAlert} + isDraggable={isDraggable} timelineId={timelineId} timelineTabType={timelineTabType} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 0866a927182f5..fee4646e88186 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -61,6 +61,7 @@ interface EventDetailsPanelProps { refetch?: () => void; }; handleOnEventClosed: () => void; + isDraggable?: boolean; isFlyoutView?: boolean; tabType: TimelineTabs; timelineId: string; @@ -72,6 +73,7 @@ const EventDetailsPanelComponent: React.FC = ({ entityType = 'events', // Default to events so only alerts have to pass entityType in expandedEvent, handleOnEventClosed, + isDraggable, isFlyoutView, tabType, timelineId, @@ -186,6 +188,7 @@ const EventDetailsPanelComponent: React.FC = ({ detailsData={detailsData} event={expandedEvent} isAlert={isAlert} + isDraggable={isDraggable} loading={loading} timelineId={timelineId} timelineTabType="flyout" @@ -217,6 +220,7 @@ const EventDetailsPanelComponent: React.FC = ({ detailsData={detailsData} event={expandedEvent} isAlert={isAlert} + isDraggable={isDraggable} loading={loading} timelineId={timelineId} timelineTabType={tabType} @@ -231,5 +235,6 @@ export const EventDetailsPanel = React.memo( deepEqual(prevProps.browserFields, nextProps.browserFields) && deepEqual(prevProps.docValueFields, nextProps.docValueFields) && deepEqual(prevProps.expandedEvent, nextProps.expandedEvent) && - prevProps.timelineId === nextProps.timelineId + prevProps.timelineId === nextProps.timelineId && + prevProps.isDraggable === nextProps.isDraggable ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap index 2cd3d333798d4..01ef89cd35c9f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap @@ -108,6 +108,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "IShouldBeUsed", ] } + isDraggable={false} isInDetailsSidePanel={true} isLoadingAnomaliesData={false} loading={true} @@ -301,6 +302,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , @@ -310,6 +312,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "MAC addresses", @@ -318,6 +321,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Platform", @@ -339,6 +343,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , @@ -348,6 +353,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "MAC addresses", @@ -356,6 +362,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Platform", @@ -371,6 +378,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , @@ -380,6 +388,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "MAC addresses", @@ -388,6 +397,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Platform", @@ -416,6 +426,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -447,6 +458,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -477,6 +489,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -502,6 +515,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Operating system", @@ -510,6 +524,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Family", @@ -518,6 +533,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Version", @@ -526,6 +542,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Architecture", @@ -547,6 +564,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Operating system", @@ -555,6 +573,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Family", @@ -563,6 +582,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Version", @@ -571,6 +591,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Architecture", @@ -586,6 +607,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Operating system", @@ -594,6 +616,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Family", @@ -602,6 +625,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Version", @@ -610,6 +634,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Architecture", @@ -638,6 +663,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -668,6 +694,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -698,6 +725,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -728,6 +756,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -753,6 +782,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Cloud provider", @@ -761,6 +791,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Region", @@ -769,6 +800,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Instance ID", @@ -777,6 +809,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Machine type", @@ -798,6 +831,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Cloud provider", @@ -806,6 +840,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Region", @@ -814,6 +849,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Instance ID", @@ -822,6 +858,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Machine type", @@ -837,6 +874,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Cloud provider", @@ -845,6 +883,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Region", @@ -853,6 +892,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Instance ID", @@ -861,6 +901,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re "description": , "title": "Machine type", @@ -889,6 +930,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -919,6 +961,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -949,6 +992,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re @@ -979,6 +1023,7 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx index f18f1eb993ee2..3fe8c2dcc8e08 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx @@ -54,7 +54,8 @@ export const ExpandableHostDetailsPageLink = ({ hostName }: ExpandableHostProps) export const ExpandableHostDetails = ({ contextID, hostName, -}: ExpandableHostProps & { contextID: string }) => { + isDraggable = false, +}: ExpandableHostProps & { contextID: string; isDraggable?: boolean }) => { const { to, from, isInitializing } = useGlobalTime(); const { docValueFields } = useSourcererScope(); /* @@ -92,6 +93,7 @@ export const ExpandableHostDetails = ({ isInDetailsSidePanel data={hostOverview as HostItem} anomaliesData={anomaliesData} + isDraggable={isDraggable} isLoadingAnomaliesData={isLoadingAnomaliesData} indexNames={allPatterns} loading={loading} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx index dd1bf3ddb5ffe..0a06810bb1249 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx @@ -61,10 +61,11 @@ interface HostDetailsProps { expandedHost: { hostName: string }; handleOnHostClosed: () => void; isFlyoutView?: boolean; + isDraggable?: boolean; } export const HostDetailsPanel: React.FC = React.memo( - ({ contextID, expandedHost, handleOnHostClosed, isFlyoutView }) => { + ({ contextID, expandedHost, handleOnHostClosed, isDraggable, isFlyoutView }) => { const { hostName } = expandedHost; if (!hostName) { @@ -108,7 +109,11 @@ export const HostDetailsPanel: React.FC = React.memo( - + ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx index 04601fcd50769..511887dc93cdf 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx @@ -19,13 +19,27 @@ import { } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { DetailsPanel } from './index'; -import { TimelineExpandedDetail, TimelineTabs } from '../../../../common/types/timeline'; +import { + TimelineExpandedDetail, + TimelineId, + TimelineTabs, +} from '../../../../common/types/timeline'; import { FlowTarget } from '../../../../common/search_strategy/security_solution/network'; +import { EventDetailsPanel } from './event_details'; jest.mock('../../../common/lib/kibana'); describe('Details Panel Component', () => { - const state: State = { ...mockGlobalState }; + const state: State = { + ...mockGlobalState, + timeline: { + ...mockGlobalState.timeline, + timelineById: { + ...mockGlobalState.timeline.timelineById, + [TimelineId.active]: mockGlobalState.timeline.timelineById.test, + }, + }, + }; const { storage } = createSecuritySolutionStorageMock(); let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); @@ -67,6 +81,17 @@ describe('Details Panel Component', () => { }, }; + const eventPinnedExpandedDetail: TimelineExpandedDetail = { + [TimelineTabs.pinned]: { + panelView: 'eventDetail', + params: { + eventId: 'my-id', + indexName: 'my-index', + ecsData: mockEcsDataWithAlert, + }, + }, + }; + const mockProps = { browserFields: {}, docValueFields: [], @@ -105,8 +130,10 @@ describe('Details Panel Component', () => { describe('DetailsPanel:EventDetails: rendering', () => { beforeEach(() => { - state.timeline.timelineById.test.expandedDetail = eventExpandedDetail; - store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + const mockState = { ...state }; + mockState.timeline.timelineById[TimelineId.active].expandedDetail = eventExpandedDetail; + mockState.timeline.timelineById.test.expandedDetail = eventExpandedDetail; + store = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); }); test('it should render the Details Panel when the panelView is set and the associated params are set', () => { @@ -139,6 +166,60 @@ describe('Details Panel Component', () => { expect(wrapper.find('EventDetails')).toMatchSnapshot(); }); + + test('it should have the attributes isDraggable to be false when timelineId !== "active" and activeTab === "query"', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(EventDetailsPanel).props().isDraggable).toBeFalsy(); + }); + + test('it should have the attributes isDraggable to be true when timelineId === "active" and activeTab === "query"', () => { + const currentProps = { ...mockProps, timelineId: TimelineId.active }; + const wrapper = mount( + + + + ); + expect(wrapper.find(EventDetailsPanel).props().isDraggable).toBeTruthy(); + }); + }); + + describe('DetailsPanel:EventDetails: rendering in pinned tab', () => { + beforeEach(() => { + const mockState = { ...state }; + mockState.timeline.timelineById[TimelineId.active].activeTab = TimelineTabs.pinned; + mockState.timeline.timelineById[TimelineId.active].expandedDetail = eventPinnedExpandedDetail; + mockState.timeline.timelineById.test.expandedDetail = eventPinnedExpandedDetail; + mockState.timeline.timelineById.test.activeTab = TimelineTabs.pinned; + store = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + }); + + test('it should have the attributes isDraggable to be false when timelineId !== "active" and activeTab === "pinned"', () => { + const currentProps = { ...mockProps, tabType: TimelineTabs.pinned }; + const wrapper = mount( + + + + ); + expect(wrapper.find(EventDetailsPanel).props().isDraggable).toBeFalsy(); + }); + + test('it should have the attributes isDraggable to be false when timelineId === "active" and activeTab === "pinned"', () => { + const currentProps = { + ...mockProps, + tabType: TimelineTabs.pinned, + timelineId: TimelineId.active, + }; + const wrapper = mount( + + + + ); + expect(wrapper.find(EventDetailsPanel).props().isDraggable).toBeFalsy(); + }); }); describe('DetailsPanel:HostDetails: rendering', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx index 97d9e4b492d6b..a9b1126edcaeb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx @@ -12,7 +12,7 @@ import { EuiFlyout, EuiFlyoutProps } from '@elastic/eui'; import { timelineActions, timelineSelectors } from '../../store/timeline'; import { timelineDefaults } from '../../store/timeline/defaults'; import { BrowserFields, DocValueFields } from '../../../common/containers/source'; -import { TimelineTabs } from '../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../common/types/timeline'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { EventDetailsPanel } from './event_details'; import { HostDetailsPanel } from './host_details'; @@ -70,6 +70,7 @@ export const DetailsPanel = React.memo( let visiblePanel = null; // store in variable to make return statement more readable let panelSize: EuiFlyoutProps['size'] = 's'; const contextID = `${timelineId}-${activeTab}`; + const isDraggable = timelineId === TimelineId.active && activeTab === TimelineTabs.query; if (currentTabDetail?.panelView === 'eventDetail' && currentTabDetail?.params?.eventId) { panelSize = 'm'; visiblePanel = ( @@ -79,6 +80,7 @@ export const DetailsPanel = React.memo( entityType={entityType} expandedEvent={currentTabDetail?.params} handleOnEventClosed={closePanel} + isDraggable={isDraggable} isFlyoutView={isFlyoutView} tabType={activeTab} timelineId={timelineId} @@ -92,6 +94,7 @@ export const DetailsPanel = React.memo( contextID={contextID} expandedHost={currentTabDetail?.params} handleOnHostClosed={closePanel} + isDraggable={isDraggable} isFlyoutView={isFlyoutView} /> ); @@ -103,6 +106,7 @@ export const DetailsPanel = React.memo( contextID={contextID} expandedNetwork={currentTabDetail?.params} handleOnNetworkClosed={closePanel} + isDraggable={isDraggable} isFlyoutView={isFlyoutView} /> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx index e53e835cfd882..184379cd366b1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx @@ -66,7 +66,8 @@ export const ExpandableNetworkDetailsPageLink = ({ export const ExpandableNetworkDetails = ({ contextID, expandedNetwork, -}: ExpandableNetworkProps & { contextID: string }) => { + isDraggable, +}: ExpandableNetworkProps & { contextID: string; isDraggable?: boolean }) => { const { ip, flowTarget } = expandedNetwork; const dispatch = useDispatch(); const { to, from, isInitializing } = useGlobalTime(); @@ -132,6 +133,7 @@ export const ExpandableNetworkDetails = ({ loading={loading} isInDetailsSidePanel isLoadingAnomaliesData={isLoadingAnomaliesData} + isDraggable={isDraggable} type={type} flowTarget={flowTarget} startDate={from} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/index.tsx index ea857da926f84..b091e499c9269 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/index.tsx @@ -62,10 +62,17 @@ interface NetworkDetailsProps { expandedNetwork: { ip: string; flowTarget: FlowTarget }; handleOnNetworkClosed: () => void; isFlyoutView?: boolean; + isDraggable?: boolean; } export const NetworkDetailsPanel = React.memo( - ({ contextID, expandedNetwork, handleOnNetworkClosed, isFlyoutView }: NetworkDetailsProps) => { + ({ + contextID, + expandedNetwork, + handleOnNetworkClosed, + isFlyoutView, + isDraggable, + }: NetworkDetailsProps) => { const { ip } = expandedNetwork; return isFlyoutView ? ( @@ -105,7 +112,11 @@ export const NetworkDetailsPanel = React.memo( - + );