diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts index 5e851cecbd86b..a89ddf3e0b250 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts @@ -44,7 +44,7 @@ describe('timeline data providers', () => { closeTimeline(); }); - it.skip('renders the data provider of a host dragged from the All Hosts widget on the hosts page', () => { + it('renders the data provider of a host dragged from the All Hosts widget on the hosts page', () => { dragAndDropFirstHostToTimeline(); openTimelineUsingToggle(); cy.get(`${TIMELINE_FLYOUT} ${TIMELINE_DROPPED_DATA_PROVIDERS}`) @@ -78,7 +78,7 @@ describe('timeline data providers', () => { }); }); - it.skip('sets correct classes when the user starts dragging a host, but is not hovering over the data providers', () => { + it('sets correct classes when the user starts dragging a host, but is not hovering over the data providers', () => { dragFirstHostToTimeline(); cy.get(IS_DRAGGING_DATA_PROVIDERS) @@ -87,7 +87,7 @@ describe('timeline data providers', () => { .should('have.class', 'drop-target-data-providers'); }); - it.skip('render an extra highlighted area in dataProvider when the user starts dragging a host AND is hovering over the data providers', () => { + it('render an extra highlighted area in dataProvider when the user starts dragging a host AND is hovering over the data providers', () => { dragFirstHostToEmptyTimelineDataProviders(); cy.get(IS_DRAGGING_DATA_PROVIDERS) diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts index ac34d65f0fd0a..38c6f41f1049c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts @@ -79,7 +79,7 @@ describe('timeline flyout button', () => { closeTimelineUsingCloseButton(); }); - it.skip('sets correct classes when the user starts dragging a host, but is not hovering over the data providers', () => { + it('sets correct classes when the user starts dragging a host, but is not hovering over the data providers', () => { dragFirstHostToTimeline(); cy.get(IS_DRAGGING_DATA_PROVIDERS) diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts index cf1bac421b447..615421b1ef323 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts @@ -7,6 +7,6 @@ export const ALL_HOSTS_TABLE = '[data-test-subj="table-allHosts-loading-false"]'; -export const HOSTS_NAMES = '[data-test-subj="render-content-host.name"] a.euiLink'; +export const HOSTS_NAMES = '[data-test-subj="draggable-content-host.name"] a.euiLink'; -export const HOSTS_NAMES_DRAGGABLE = '[data-test-subj="render-content-host.name"]'; +export const HOSTS_NAMES_DRAGGABLE = '[data-test-subj="draggable-content-host.name"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts index f2a712f868850..536379654862c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts @@ -5,6 +5,6 @@ * 2.0. */ -export const PROCESS_NAME_FIELD = '[data-test-subj="render-content-process.name"]'; +export const PROCESS_NAME_FIELD = '[data-test-subj="draggable-content-process.name"]'; export const UNCOMMON_PROCESSES_TABLE = '[data-test-subj="table-uncommonProcesses-loading-false"]'; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx index ad15f0a5fa9fb..6d87b5d3a68b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx @@ -340,9 +340,13 @@ describe.each(chartDataSets)('BarChart with stackByField', () => { const dataProviderId = `draggableId.content.draggable-legend-item-uuid_v4()-${escapeDataProviderId( stackByField )}-${escapeDataProviderId(datum.key)}`; - expect(wrapper.find(`div[data-provider-id="${dataProviderId}"]`).first().text()).toEqual( - datum.key - ); + + expect( + wrapper + .find(`[draggableId="${dataProviderId}"] [data-test-subj="providerContainer"]`) + .first() + .text() + ).toEqual(datum.key); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx index 493ce4da78eba..6017501f87dcc 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx @@ -46,7 +46,6 @@ const DraggableLegendItemComponent: React.FC<{ data-test-subj={`legend-item-${dataProviderId}`} field={field} id={dataProviderId} - isDraggable={false} timelineId={timelineId} value={value} /> diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap index 0b25ff2c8c5ee..aa8214938c2b0 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap @@ -17,7 +17,6 @@ exports[`DraggableWrapper rendering it renders against the snapshot 1`] = ` }, } } - isDraggable={true} render={[Function]} /> `; diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx index d27ad96ff3c4f..bdc5545880e1c 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx @@ -42,11 +42,7 @@ describe('DraggableWrapper', () => { const wrapper = shallow( - message} - /> + message} /> ); @@ -58,11 +54,7 @@ describe('DraggableWrapper', () => { const wrapper = mount( - message} - /> + message} /> ); @@ -74,27 +66,19 @@ describe('DraggableWrapper', () => { const wrapper = mount( - message} - /> + message} /> ); - expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBe(false); + expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(false); }); test('it renders hover actions when the mouse is over the text of draggable wrapper', async () => { const wrapper = mount( - message} - /> + message} /> ); @@ -104,7 +88,7 @@ describe('DraggableWrapper', () => { wrapper.update(); jest.runAllTimers(); wrapper.update(); - expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); }); }); }); @@ -114,12 +98,7 @@ describe('DraggableWrapper', () => { const wrapper = mount( - message} - truncate - /> + message} truncate /> ); @@ -133,11 +112,7 @@ describe('DraggableWrapper', () => { const wrapper = mount( - message} - /> + message} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx index d008aad213392..9db5b3899d8bc 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx @@ -7,7 +7,7 @@ import { EuiScreenReaderOnly } from '@elastic/eui'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '@kbn/securitysolution-t-grid'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; import { Draggable, DraggableProvided, @@ -25,13 +25,12 @@ import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/com import { TruncatableText } from '../truncatable_text'; import { WithHoverActions } from '../with_hover_actions'; - +import { DraggableWrapperHoverContent, useGetTimelineId } from './draggable_wrapper_hover_content'; import { getDraggableId, getDroppableId } from './helpers'; import { ProviderContainer } from './provider_container'; import * as i18n from './translations'; import { useKibana } from '../../lib/kibana'; -import { useHoverActions } from '../hover_actions/use_hover_actions'; // As right now, we do not know what we want there, we will keep it as a placeholder export const DragEffects = styled.div``; @@ -81,7 +80,7 @@ const Wrapper = styled.div` Wrapper.displayName = 'Wrapper'; -export const ProviderContentWrapper = styled.span` +const ProviderContentWrapper = styled.span` > span.euiToolTipAnchor { display: block; /* allow EuiTooltip content to be truncatable */ } @@ -96,7 +95,6 @@ type RenderFunctionProp = ( interface Props { dataProvider: DataProvider; disabled?: boolean; - isDraggable?: boolean; inline?: boolean; render: RenderFunctionProp; timelineId?: string; @@ -123,35 +121,55 @@ export const getStyle = ( }; }; -const DraggableOnWrapperComponent: React.FC = ({ +const draggableContainsLinks = (draggableElement: HTMLDivElement | null) => { + const links = draggableElement?.querySelectorAll('.euiLink') ?? []; + return links.length > 0; +}; + +const DraggableWrapperComponent: React.FC = ({ dataProvider, onFilterAdded, render, timelineId, truncate, }) => { + const keyboardHandlerRef = useRef(null); + const draggableRef = useRef(null); + const [closePopOverTrigger, setClosePopOverTrigger] = useState(false); + const [showTopN, setShowTopN] = useState(false); + const [goGetTimelineId, setGoGetTimelineId] = useState(false); + const timelineIdFind = useGetTimelineId(draggableRef, goGetTimelineId); const [providerRegistered, setProviderRegistered] = useState(false); const isDisabled = dataProvider.id.includes(`-${ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID}-`); + const [hoverActionsOwnFocus, setHoverActionsOwnFocus] = useState(false); const dispatch = useDispatch(); const { timelines } = useKibana().services; - const { - closePopOverTrigger, - handleClosePopOverTrigger, - hoverActionsOwnFocus, - hoverContent, - keyboardHandlerRef, - onCloseRequested, - openPopover, - onFocus, - setContainerRef, - showTopN, - } = useHoverActions({ - dataProvider, - onFilterAdded, - render, - timelineId, - truncate, - }); + + const handleClosePopOverTrigger = useCallback(() => { + setClosePopOverTrigger((prevClosePopOverTrigger) => !prevClosePopOverTrigger); + setHoverActionsOwnFocus((prevHoverActionsOwnFocus) => { + if (prevHoverActionsOwnFocus) { + setTimeout(() => { + keyboardHandlerRef.current?.focus(); + }, 0); + } + return false; // always give up ownership + }); + + setTimeout(() => { + setHoverActionsOwnFocus(false); + }, 0); // invoked on the next tick, because we want to restore focus first + }, [keyboardHandlerRef]); + + const toggleTopN = useCallback(() => { + setShowTopN((prevShowTopN) => { + const newShowTopN = !prevShowTopN; + if (newShowTopN === false) { + handleClosePopOverTrigger(); + } + return newShowTopN; + }); + }, [handleClosePopOverTrigger]); const registerProvider = useCallback(() => { if (!isDisabled) { @@ -174,6 +192,49 @@ const DraggableOnWrapperComponent: React.FC = ({ [unRegisterProvider] ); + const hoverContent = useMemo(() => { + // display links as additional content in the hover menu to enable keyboard + // navigation of links (when the draggable contains them): + const additionalContent = + hoverActionsOwnFocus && !showTopN && draggableContainsLinks(draggableRef.current) ? ( + + {render(dataProvider, null, { isDragging: false, isDropAnimating: false })} + + ) : null; + + return ( + + ); + }, [ + dataProvider, + handleClosePopOverTrigger, + hoverActionsOwnFocus, + onFilterAdded, + render, + showTopN, + timelineId, + timelineIdFind, + toggleTopN, + ]); + const RenderClone = useCallback( (provided, snapshot) => ( @@ -203,7 +264,7 @@ const DraggableOnWrapperComponent: React.FC = ({ {...provided.dragHandleProps} ref={(e: HTMLDivElement) => { provided.innerRef(e); - setContainerRef(e); + draggableRef.current = e; }} data-test-subj="providerContainer" isDragging={snapshot.isDragging} @@ -231,9 +292,13 @@ const DraggableOnWrapperComponent: React.FC = ({ )} ), - [dataProvider, registerProvider, render, setContainerRef, truncate] + [dataProvider, registerProvider, render, truncate] ); + const openPopover = useCallback(() => { + setHoverActionsOwnFocus(true); + }, []); + const { onBlur, onKeyDown } = timelines.getUseDraggableKeyboardWrapper()({ closePopover: handleClosePopOverTrigger, draggableId: getDraggableId(dataProvider.id), @@ -242,6 +307,24 @@ const DraggableOnWrapperComponent: React.FC = ({ openPopover, }); + const onFocus = useCallback(() => { + if (!hoverActionsOwnFocus) { + keyboardHandlerRef.current?.focus(); + } + }, [hoverActionsOwnFocus, keyboardHandlerRef]); + + const onCloseRequested = useCallback(() => { + setShowTopN(false); + + if (hoverActionsOwnFocus) { + setHoverActionsOwnFocus(false); + + setTimeout(() => { + onFocus(); // return focus to this draggable on the next tick, because we owned focus + }, 0); + } + }, [onFocus, hoverActionsOwnFocus]); + const DroppableContent = useCallback( (droppableProvided) => (
@@ -267,7 +350,7 @@ const DraggableOnWrapperComponent: React.FC = ({ {droppableProvided.placeholder}
), - [DraggableContent, dataProvider.id, isDisabled, keyboardHandlerRef, onBlur, onFocus, onKeyDown] + [DraggableContent, dataProvider.id, isDisabled, onBlur, onFocus, onKeyDown] ); const content = useMemo( @@ -302,75 +385,6 @@ const DraggableOnWrapperComponent: React.FC = ({ ); }; -const DraggableWrapperComponent: React.FC = ({ - dataProvider, - isDraggable = false, - onFilterAdded, - render, - timelineId, - truncate, -}) => { - const { - closePopOverTrigger, - hoverActionsOwnFocus, - hoverContent, - onCloseRequested, - setContainerRef, - showTopN, - } = useHoverActions({ - dataProvider, - isDraggable, - onFilterAdded, - render, - timelineId, - truncate, - }); - const renderContent = useCallback( - () => ( -
{ - setContainerRef(e); - }} - tabIndex={-1} - data-provider-id={getDraggableId(dataProvider.id)} - > - {truncate ? ( - - {render(dataProvider, null, { isDragging: false, isDropAnimating: false })} - - ) : ( - - {render(dataProvider, null, { isDragging: false, isDropAnimating: false })} - - )} -
- ), - [dataProvider, render, setContainerRef, truncate] - ); - if (!isDraggable) { - return ( - - ); - } - return ( - - ); -}; - export const DraggableWrapper = React.memo(DraggableWrapperComponent); DraggableWrapper.displayName = 'DraggableWrapper'; diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx new file mode 100644 index 0000000000000..2531780ec4bd5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx @@ -0,0 +1,564 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import { mount, ReactWrapper } from 'enzyme'; + +import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { mockBrowserFields } from '../../containers/source/mock'; +import '../../mock/match_media'; +import { useKibana } from '../../lib/kibana'; +import { TestProviders } from '../../mock'; +import { FilterManager } from '../../../../../../../src/plugins/data/public'; +import { useSourcererScope } from '../../containers/sourcerer'; +import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content'; +import { TimelineId } from '../../../../common/types/timeline'; +import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; + +jest.mock('../link_to'); +jest.mock('../../lib/kibana'); +jest.mock('../../containers/sourcerer', () => { + const original = jest.requireActual('../../containers/sourcerer'); + + return { + ...original, + useSourcererScope: jest.fn(), + }; +}); + +jest.mock('uuid', () => { + return { + v1: jest.fn(() => 'uuid.v1()'), + v4: jest.fn(() => 'uuid.v4()'), + }; +}); +const mockStartDragToTimeline = jest.fn(); +jest.mock('../../../../../timelines/public/hooks/use_add_to_timeline', () => { + const original = jest.requireActual('../../../../../timelines/public/hooks/use_add_to_timeline'); + return { + ...original, + useAddToTimeline: () => ({ startDragToTimeline: mockStartDragToTimeline }), + }; +}); +const mockAddFilters = jest.fn(); +jest.mock('../../../common/hooks/use_selector', () => ({ + useShallowEqualSelector: jest.fn(), + useDeepEqualSelector: jest.fn(), +})); +jest.mock('../../../common/hooks/use_invalid_filter_query.tsx'); + +const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; +const timelineId = TimelineId.active; +const field = 'process.name'; +const value = 'nice'; +const toggleTopN = jest.fn(); +const goGetTimelineId = jest.fn(); +const defaultProps = { + field, + goGetTimelineId, + ownFocus: false, + showTopN: false, + timelineId, + toggleTopN, + value, +}; + +describe('DraggableWrapperHoverContent', () => { + beforeAll(() => { + mockStartDragToTimeline.mockReset(); + (useDeepEqualSelector as jest.Mock).mockReturnValue({ + filterManager: { addFilters: mockAddFilters }, + }); + (useSourcererScope as jest.Mock).mockReturnValue({ + browserFields: mockBrowserFields, + selectedPatterns: [], + indexPattern: {}, + }); + }); + + /** + * The tests for "Filter for value" and "Filter out value" are similar enough + * to combine them into "table tests" using this array + */ + const forOrOut = ['for', 'out']; + + forOrOut.forEach((hoverAction) => { + describe(`Filter ${hoverAction} value`, () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + test(`it renders the 'Filter ${hoverAction} value' button when showTopN is false`, () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().exists() + ).toBe(true); + }); + + test(`it does NOT render the 'Filter ${hoverAction} value' button when showTopN is true`, () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().exists() + ).toBe(false); + }); + + test(`it should call goGetTimelineId when user is over the 'Filter ${hoverAction} value' button`, () => { + const wrapper = mount( + + + + ); + const button = wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first(); + button.simulate('mouseenter'); + expect(goGetTimelineId).toHaveBeenCalledWith(true); + }); + + describe('when run in the context of a timeline', () => { + let wrapper: ReactWrapper; + let onFilterAdded: () => void; + + beforeEach(() => { + onFilterAdded = jest.fn(); + + wrapper = mount( + + + + ); + }); + + test('when clicked, it adds a filter to the timeline when running in the context of a timeline', () => { + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().simulate('click'); + wrapper.update(); + + expect(mockAddFilters).toBeCalledWith({ + meta: { + alias: null, + disabled: false, + key: 'process.name', + negate: hoverAction === 'out' ? true : false, + params: { query: 'nice' }, + type: 'phrase', + value: 'nice', + }, + query: { match: { 'process.name': { query: 'nice', type: 'phrase' } } }, + }); + }); + + test('when clicked, invokes onFilterAdded when running in the context of a timeline', () => { + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().simulate('click'); + wrapper.update(); + + expect(onFilterAdded).toBeCalled(); + }); + }); + + describe('when NOT run in the context of a timeline', () => { + let wrapper: ReactWrapper; + let onFilterAdded: () => void; + const kibana = useKibana(); + + beforeEach(() => { + kibana.services.data.query.filterManager.addFilters = jest.fn(); + onFilterAdded = jest.fn(); + + wrapper = mount( + + + + ); + }); + + test('when clicked, it adds a filter to the global filters when NOT running in the context of a timeline', () => { + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().simulate('click'); + wrapper.update(); + + expect(kibana.services.data.query.filterManager.addFilters).toBeCalledWith({ + meta: { + alias: null, + disabled: false, + key: 'process.name', + negate: hoverAction === 'out' ? true : false, + params: { query: 'nice' }, + type: 'phrase', + value: 'nice', + }, + query: { match: { 'process.name': { query: 'nice', type: 'phrase' } } }, + }); + }); + + test('when clicked, invokes onFilterAdded when NOT running in the context of a timeline', () => { + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().simulate('click'); + wrapper.update(); + + expect(onFilterAdded).toBeCalled(); + }); + }); + + describe('an empty string value when run in the context of a timeline', () => { + let filterManager: FilterManager; + let wrapper: ReactWrapper; + let onFilterAdded: () => void; + + beforeEach(() => { + filterManager = new FilterManager(mockUiSettingsForFilterManager); + filterManager.addFilters = jest.fn(); + onFilterAdded = jest.fn(); + + wrapper = mount( + + + + ); + }); + + const expectedFilterTypeDescription = + hoverAction === 'for' ? 'a "NOT exists"' : 'an "exists"'; + test(`when clicked, it adds ${expectedFilterTypeDescription} filter to the timeline when run in the context of a timeline`, () => { + const expected = + hoverAction === 'for' + ? { + exists: { field: 'process.name' }, + meta: { + alias: null, + disabled: false, + key: 'process.name', + negate: true, + type: 'exists', + value: 'exists', + }, + } + : { + exists: { field: 'process.name' }, + meta: { + alias: null, + disabled: false, + key: 'process.name', + negate: false, + type: 'exists', + value: 'exists', + }, + }; + + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().simulate('click'); + wrapper.update(); + + expect(mockAddFilters).toBeCalledWith(expected); + }); + }); + + describe('an empty string value when NOT run in the context of a timeline', () => { + let wrapper: ReactWrapper; + let onFilterAdded: () => void; + const kibana = useKibana(); + + beforeEach(() => { + kibana.services.data.query.filterManager.addFilters = jest.fn(); + onFilterAdded = jest.fn(); + + wrapper = mount( + + + + ); + }); + + const expectedFilterTypeDescription = + hoverAction === 'for' ? 'a "NOT exists"' : 'an "exists"'; + test(`when clicked, it adds ${expectedFilterTypeDescription} filter to the global filters when NOT running in the context of a timeline`, () => { + const expected = + hoverAction === 'for' + ? { + exists: { field: 'process.name' }, + meta: { + alias: null, + disabled: false, + key: 'process.name', + negate: true, + type: 'exists', + value: 'exists', + }, + } + : { + exists: { field: 'process.name' }, + meta: { + alias: null, + disabled: false, + key: 'process.name', + negate: false, + type: 'exists', + value: 'exists', + }, + }; + + wrapper.find(`[data-test-subj="filter-${hoverAction}-value"]`).first().simulate('click'); + wrapper.update(); + + expect(kibana.services.data.query.filterManager.addFilters).toBeCalledWith(expected); + }); + }); + }); + }); + + describe('Add to timeline', () => { + const aggregatableStringField = 'cloud.account.id'; + const draggableId = 'draggable.id'; + + [false, true].forEach((showTopN) => { + [value, null].forEach((maybeValue) => { + [draggableId, undefined].forEach((maybeDraggableId) => { + const shouldRender = !showTopN && maybeValue != null && maybeDraggableId != null; + const assertion = shouldRender ? 'should render' : 'should NOT render'; + + test(`it ${assertion} the 'Add to timeline investigation' button when showTopN is ${showTopN}, value is ${maybeValue}, and a draggableId is ${maybeDraggableId}`, () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="add-to-timeline"]').first().exists()).toBe( + shouldRender + ); + }); + }); + }); + }); + + test('when clicked, it invokes the `startDragToTimeline` function returned by the `useAddToTimeline` hook', async () => { + const wrapper = mount( + + + + ); + + wrapper.find('[data-test-subj="add-to-timeline"]').first().simulate('click'); + + await waitFor(() => { + wrapper.update(); + expect(mockStartDragToTimeline).toHaveBeenCalled(); + }); + }); + }); + + describe('Top N', () => { + test(`it renders the 'Show top field' button when showTopN is false and an aggregatable string field is provided`, () => { + const aggregatableStringField = 'cloud.account.id'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + expect(wrapper.find('[data-test-subj="show-top-field"]').first().exists()).toBe(true); + }); + + test(`it renders the 'Show top field' button when showTopN is false and a allowlisted signal field is provided`, () => { + const allowlistedField = 'signal.rule.name'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + expect(wrapper.find('[data-test-subj="show-top-field"]').first().exists()).toBe(true); + }); + + test(`it does NOT render the 'Show top field' button when showTopN is false and a field not known to BrowserFields is provided`, () => { + const notKnownToBrowserFields = 'unknown.field'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + expect(wrapper.find('[data-test-subj="show-top-field"]').first().exists()).toBe(false); + }); + + test(`it should invokes goGetTimelineId when user is over the 'Show top field' button`, async () => { + const allowlistedField = 'signal.rule.name'; + const wrapper = mount( + + + + ); + const button = wrapper.find(`[data-test-subj="show-top-field"]`).first(); + button.simulate('mouseenter'); + await waitFor(() => { + expect(goGetTimelineId).toHaveBeenCalledWith(true); + }); + }); + + test(`invokes the toggleTopN function when the 'Show top field' button is clicked`, () => { + const allowlistedField = 'signal.rule.name'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + wrapper.find('[data-test-subj="show-top-field"]').first().simulate('click'); + wrapper.update(); + + expect(toggleTopN).toBeCalled(); + }); + + test(`it does NOT render the Top N histogram when when showTopN is false`, () => { + const allowlistedField = 'signal.rule.name'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + expect(wrapper.find('[data-test-subj="eventsByDatasetOverviewPanel"]').first().exists()).toBe( + false + ); + }); + + test(`it does NOT render the 'Show top field' button when showTopN is true`, () => { + const allowlistedField = 'signal.rule.name'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + expect(wrapper.find('[data-test-subj="show-top-field"]').first().exists()).toBe(false); + }); + + test(`it renders the Top N histogram when when showTopN is true`, () => { + const allowlistedField = 'signal.rule.name'; + const wrapper = mount( + + + + ); + + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]').first().exists() + ).toBe(true); + }); + }); + + describe('Copy to Clipboard', () => { + test(`it renders the 'Copy to Clipboard' button when showTopN is false`, () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="copy-to-clipboard"]`).first().exists()).toBe(true); + }); + + test(`it does NOT render the 'Copy to Clipboard' button when showTopN is true`, () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="copy-to-clipboard"]`).first().exists()).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx new file mode 100644 index 0000000000000..71c3114015a03 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx @@ -0,0 +1,425 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiButtonIcon, + EuiFocusTrap, + EuiPanel, + EuiScreenReaderOnly, + EuiToolTip, +} from '@elastic/eui'; + +import React, { useCallback, useEffect, useRef, useMemo, useState } from 'react'; +import { DraggableId } from 'react-beautiful-dnd'; +import styled from 'styled-components'; + +import { getAllFieldsByName } from '../../containers/source'; +import { COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME } from '../../lib/clipboard/clipboard'; +import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard'; +import { useKibana } from '../../lib/kibana'; +import { createFilter } from '../add_filter_to_global_search_bar'; +import { StatefulTopN } from '../top_n'; + +import { allowTopN } from './helpers'; +import * as i18n from './translations'; +import { useDeepEqualSelector } from '../../hooks/use_selector'; +import { TimelineId } from '../../../../common/types/timeline'; +import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { useSourcererScope } from '../../containers/sourcerer'; +import { timelineSelectors } from '../../../timelines/store/timeline'; +import { stopPropagationAndPreventDefault } from '../../../../../timelines/public'; +import { TooltipWithKeyboardShortcut } from '../accessibility'; + +export const AdditionalContent = styled.div` + padding: 2px; +`; + +AdditionalContent.displayName = 'AdditionalContent'; + +const getAdditionalScreenReaderOnlyContext = ({ + field, + value, +}: { + field: string; + value?: string[] | string | null; +}): string => { + if (value == null) { + return field; + } + + return Array.isArray(value) ? `${field} ${value.join(' ')}` : `${field} ${value}`; +}; + +const FILTER_FOR_VALUE_KEYBOARD_SHORTCUT = 'f'; +const FILTER_OUT_VALUE_KEYBOARD_SHORTCUT = 'o'; +const ADD_TO_TIMELINE_KEYBOARD_SHORTCUT = 'a'; +const SHOW_TOP_N_KEYBOARD_SHORTCUT = 't'; +const COPY_TO_CLIPBOARD_KEYBOARD_SHORTCUT = 'c'; + +interface Props { + additionalContent?: React.ReactNode; + closePopOver?: () => void; + draggableId?: DraggableId; + field: string; + goGetTimelineId?: (args: boolean) => void; + onFilterAdded?: () => void; + ownFocus: boolean; + showTopN: boolean; + timelineId?: string | null; + toggleTopN: () => void; + value?: string[] | string | null; +} + +/** Returns a value for the `disabled` prop of `EuiFocusTrap` */ +const isFocusTrapDisabled = ({ + ownFocus, + showTopN, +}: { + ownFocus: boolean; + showTopN: boolean; +}): boolean => { + if (showTopN) { + return false; // we *always* want to trap focus when showing Top N + } + + return !ownFocus; +}; + +const DraggableWrapperHoverContentComponent: React.FC = ({ + additionalContent = null, + closePopOver, + draggableId, + field, + goGetTimelineId, + onFilterAdded, + ownFocus, + showTopN, + timelineId, + toggleTopN, + value, +}) => { + const kibana = useKibana(); + const { timelines } = kibana.services; + const { startDragToTimeline } = timelines.getUseAddToTimeline()({ + draggableId, + fieldName: field, + }); + const filterManagerBackup = useMemo(() => kibana.services.data.query.filterManager, [ + kibana.services.data.query.filterManager, + ]); + const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []); + const { filterManager: activeFilterMananager } = useDeepEqualSelector((state) => + getManageTimeline(state, timelineId ?? '') + ); + const defaultFocusedButtonRef = useRef(null); + const panelRef = useRef(null); + + const filterManager = useMemo( + () => (timelineId === TimelineId.active ? activeFilterMananager : filterManagerBackup), + [timelineId, activeFilterMananager, filterManagerBackup] + ); + + // Regarding data from useManageTimeline: + // * `indexToAdd`, which enables the alerts index to be appended to + // the `indexPattern` returned by `useWithSource`, may only be populated when + // this component is rendered in the context of the active timeline. This + // behavior enables the 'All events' view by appending the alerts index + // to the index pattern. + const activeScope: SourcererScopeName = + timelineId === TimelineId.active + ? SourcererScopeName.timeline + : timelineId != null && + [TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage].includes( + timelineId as TimelineId + ) + ? SourcererScopeName.detections + : SourcererScopeName.default; + const { browserFields, indexPattern } = useSourcererScope(activeScope); + const handleStartDragToTimeline = useCallback(() => { + startDragToTimeline(); + if (closePopOver != null) { + closePopOver(); + } + }, [closePopOver, startDragToTimeline]); + + const filterForValue = useCallback(() => { + const filter = + value?.length === 0 ? createFilter(field, undefined) : createFilter(field, value); + const activeFilterManager = filterManager; + + if (activeFilterManager != null) { + activeFilterManager.addFilters(filter); + if (closePopOver != null) { + closePopOver(); + } + if (onFilterAdded != null) { + onFilterAdded(); + } + } + }, [closePopOver, field, value, filterManager, onFilterAdded]); + + const filterOutValue = useCallback(() => { + const filter = + value?.length === 0 ? createFilter(field, null, false) : createFilter(field, value, true); + const activeFilterManager = filterManager; + + if (activeFilterManager != null) { + activeFilterManager.addFilters(filter); + + if (closePopOver != null) { + closePopOver(); + } + if (onFilterAdded != null) { + onFilterAdded(); + } + } + }, [closePopOver, field, value, filterManager, onFilterAdded]); + + const isInit = useRef(true); + + useEffect(() => { + if (isInit.current && goGetTimelineId != null && timelineId == null) { + isInit.current = false; + goGetTimelineId(true); + } + }, [goGetTimelineId, timelineId]); + + useEffect(() => { + if (ownFocus) { + setTimeout(() => { + defaultFocusedButtonRef.current?.focus(); + }, 0); + } + }, [ownFocus]); + + const onKeyDown = useCallback( + (keyboardEvent: React.KeyboardEvent) => { + if (!ownFocus) { + return; + } + + switch (keyboardEvent.key) { + case FILTER_FOR_VALUE_KEYBOARD_SHORTCUT: + stopPropagationAndPreventDefault(keyboardEvent); + filterForValue(); + break; + case FILTER_OUT_VALUE_KEYBOARD_SHORTCUT: + stopPropagationAndPreventDefault(keyboardEvent); + filterOutValue(); + break; + case ADD_TO_TIMELINE_KEYBOARD_SHORTCUT: + stopPropagationAndPreventDefault(keyboardEvent); + handleStartDragToTimeline(); + break; + case SHOW_TOP_N_KEYBOARD_SHORTCUT: + stopPropagationAndPreventDefault(keyboardEvent); + toggleTopN(); + break; + case COPY_TO_CLIPBOARD_KEYBOARD_SHORTCUT: + stopPropagationAndPreventDefault(keyboardEvent); + const copyToClipboardButton = panelRef.current?.querySelector( + `.${COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME}` + ); + if (copyToClipboardButton != null) { + copyToClipboardButton.click(); + if (closePopOver != null) { + closePopOver(); + } + } + break; + case 'Enter': + break; + case 'Escape': + stopPropagationAndPreventDefault(keyboardEvent); + if (closePopOver != null) { + closePopOver(); + } + break; + default: + break; + } + }, + + [closePopOver, filterForValue, filterOutValue, handleStartDragToTimeline, ownFocus, toggleTopN] + ); + + return ( + + + +

{i18n.YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS(field)}

+
+ + {additionalContent != null && {additionalContent}} + + {!showTopN && value != null && ( + + } + > + + + )} + + {!showTopN && value != null && ( + + } + > + + + )} + + {!showTopN && value != null && draggableId != null && ( + + } + > + + + )} + + <> + {allowTopN({ + browserField: getAllFieldsByName(browserFields)[field], + fieldName: field, + }) && ( + <> + {!showTopN && ( + + } + > + + + )} + + {showTopN && ( + + )} + + )} + + + {!showTopN && ( + + )} +
+
+ ); +}; + +DraggableWrapperHoverContentComponent.displayName = 'DraggableWrapperHoverContentComponent'; + +export const DraggableWrapperHoverContent = React.memo(DraggableWrapperHoverContentComponent); + +export const useGetTimelineId = function ( + elem: React.MutableRefObject, + getTimelineId: boolean = false +) { + const [timelineId, setTimelineId] = useState(null); + + useEffect(() => { + let startElem: Element | (Node & ParentNode) | null = elem.current; + if (startElem != null && getTimelineId) { + for (; startElem && startElem !== document; startElem = startElem.parentNode) { + const myElem: Element = startElem as Element; + if ( + myElem != null && + myElem.classList != null && + myElem.classList.contains(SELECTOR_TIMELINE_GLOBAL_CONTAINER) && + myElem.hasAttribute('data-timeline-id') + ) { + setTimelineId(myElem.getAttribute('data-timeline-id')); + break; + } + } + } + }, [elem, getTimelineId]); + + return timelineId; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/use_get_timeline_id_from_dom.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/use_get_timeline_id_from_dom.tsx deleted file mode 100644 index fcb547842aec4..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/use_get_timeline_id_from_dom.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect, useState } from 'react'; - -import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles'; - -export const useGetTimelineId = function ( - elem: React.MutableRefObject, - getTimelineId: boolean = false -) { - const [timelineId, setTimelineId] = useState(null); - - useEffect(() => { - let startElem: Element | (Node & ParentNode) | null = elem.current; - if (startElem != null && getTimelineId) { - for (; startElem && startElem !== document; startElem = startElem.parentNode) { - const myElem: Element = startElem as Element; - if ( - myElem != null && - myElem.classList != null && - myElem.classList.contains(SELECTOR_TIMELINE_GLOBAL_CONTAINER) && - myElem.hasAttribute('data-timeline-id') - ) { - setTimelineId(myElem.getAttribute('data-timeline-id')); - break; - } - } - } - }, [elem, getTimelineId]); - - return timelineId; -}; diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap index 6b27cf5969f1a..93608a181adff 100644 --- a/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap @@ -36,7 +36,6 @@ exports[`draggables rendering it renders the default DefaultDraggable 1`] = ` }, } } - isDraggable={true} render={[Function]} /> `; diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx index 6ac1746d77709..df92da0c7d056 100644 --- a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx @@ -20,7 +20,6 @@ import { Provider } from '../../../timelines/components/timeline/data_providers/ export interface DefaultDraggableType { id: string; - isDraggable?: boolean; field: string; value?: string | null; name?: string | null; @@ -80,7 +79,6 @@ Content.displayName = 'Content'; * that's only displayed when the specified value is non-`null`. * * @param id - a unique draggable id, which typically follows the format `${contextId}-${eventId}-${field}-${value}` - * @param isDraggable - optional prop to disable drag & drop and it will defaulted to true * @param field - the name of the field, e.g. `network.transport` * @param value - value of the field e.g. `tcp` * @param name - defaulting to `field`, this optional human readable name is used by the `DataProvider` that represents the data @@ -90,17 +88,7 @@ Content.displayName = 'Content'; * @param queryValue - defaults to `value`, this query overrides the `queryMatch.value` used by the `DataProvider` that represents the data */ export const DefaultDraggable = React.memo( - ({ - id, - isDraggable = true, - field, - value, - name, - children, - timelineId, - tooltipContent, - queryValue, - }) => { + ({ id, field, value, name, children, timelineId, tooltipContent, queryValue }) => { const dataProviderProp: DataProvider = useMemo( () => ({ and: [], @@ -137,7 +125,6 @@ export const DefaultDraggable = React.memo( return ( @@ -168,7 +155,6 @@ export type BadgeDraggableType = Omit & { * @param field - the name of the field, e.g. `network.transport` * @param value - value of the field e.g. `tcp` * @param iconType -the (optional) type of icon e.g. `snowflake` to display on the badge - * @param isDraggable * @param name - defaulting to `field`, this optional human readable name is used by the `DataProvider` that represents the data * @param color - defaults to `hollow`, optionally overwrite the color of the badge icon * @param children - defaults to displaying `value`, this allows an arbitrary visualization to be displayed in lieu of the default behavior @@ -182,7 +168,6 @@ const DraggableBadgeComponent: React.FC = ({ field, value, iconType, - isDraggable, name, color = 'hollow', children, @@ -192,7 +177,6 @@ const DraggableBadgeComponent: React.FC = ({ value != null ? ( = ({ key={key} contextId={key} eventId={eventId} - isDraggable={false} fieldName={fieldName || 'unknown'} value={value} /> diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx index 28a90e94c0ca4..80c014771ae68 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx @@ -245,7 +245,7 @@ describe('EventFieldsBrowser', () => { /> ); - expect(wrapper.find('[data-test-subj="localized-date-tool-tip"]').at(0).text()).toEqual( + expect(wrapper.find('[data-test-subj="draggable-content-@timestamp"]').at(0).text()).toEqual( 'Feb 28, 2019 @ 16:50:54.621' ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx index 67b1874eea0a0..f5cf600e281ad 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx @@ -6,10 +6,11 @@ */ import React, { useCallback, useState, useRef } from 'react'; +import { getDraggableId } from '@kbn/securitysolution-t-grid'; import { HoverActions } from '../../hover_actions'; import { useActionCellDataProvider } from './use_action_cell_data_provider'; import { EventFieldsData } from '../types'; -import { useGetTimelineId } from '../../drag_and_drop/use_get_timeline_id_from_dom'; +import { useGetTimelineId } from '../../drag_and_drop/draggable_wrapper_hover_content'; import { ColumnHeaderOptions } from '../../../../../common/types/timeline'; import { BrowserField } from '../../../containers/source'; @@ -65,10 +66,11 @@ export const ActionCell: React.FC = React.memo( }); }, []); + const draggableIds = actionCellConfig?.idList.map((id) => getDraggableId(id)); return ( ({ - and: [], - enabled: true, - id: escapeDataProviderId(id), - name: field, - excluded: false, - kqlQuery: '', - queryMatch: { - field, - value, - operator: IS_OPERATOR, - }, -}); - export const useActionCellDataProvider = ({ contextId, eventId, @@ -66,90 +50,72 @@ export const useActionCellDataProvider = ({ isObjectArray, linkValue, values, -}: UseActionCellDataProvider): { - stringValues: string[]; - dataProvider: DataProvider[]; -} | null => { - const cellData = useMemo(() => { - if (values === null || values === undefined) return null; - const arrayValues = Array.isArray(values) ? values : [values]; - return arrayValues.reduce<{ - stringValues: string[]; - dataProvider: DataProvider[]; - }>( - (memo, value, index) => { - let id: string = ''; - let valueAsString: string = isString(value) ? value : `${values}`; - const appendedUniqueId = `${contextId}-${eventId}-${field}-${index}-${value}`; - if (fieldFromBrowserField == null) { - memo.stringValues.push(valueAsString); - return memo; - } +}: UseActionCellDataProvider): { idList: string[]; stringValues: string[] } | null => { + if (values === null || values === undefined) return null; - if (isObjectArray || fieldType === GEO_FIELD_TYPE || [MESSAGE_FIELD_NAME].includes(field)) { - memo.stringValues.push(valueAsString); - return memo; - } else if (fieldType === IP_FIELD_TYPE) { - id = `formatted-ip-data-provider-${contextId}-${field}-${value}-${eventId}`; - if (isString(value) && !isEmpty(value)) { - try { - const addresses = JSON.parse(value); - if (isArray(addresses)) { - valueAsString = addresses.join(','); - addresses.forEach((ip) => memo.dataProvider.push(getDataProvider(field, id, ip))); - } - } catch (_) { - // Default to keeping the existing string value - } - memo.stringValues.push(valueAsString); - return memo; + const stringifiedValues: string[] = []; + const arrayValues = Array.isArray(values) ? values : [values]; + + const idList: string[] = arrayValues.reduce((memo, value, index) => { + let id = null; + let valueAsString: string = isString(value) ? value : `${values}`; + if (fieldFromBrowserField == null) { + stringifiedValues.push(valueAsString); + return memo; + } + const appendedUniqueId = `${contextId}-${eventId}-${field}-${index}-${value}-${eventId}-${field}-${value}`; + if (isObjectArray || fieldType === GEO_FIELD_TYPE || [MESSAGE_FIELD_NAME].includes(field)) { + stringifiedValues.push(valueAsString); + return memo; + } else if (fieldType === IP_FIELD_TYPE) { + id = `formatted-ip-data-provider-${contextId}-${field}-${value}-${eventId}`; + if (isString(value) && !isEmpty(value)) { + try { + const addresses = JSON.parse(value); + if (isArray(addresses)) { + valueAsString = addresses.join(','); } - } else if (PORT_NAMES.some((portName) => field === portName)) { - id = `port-default-draggable-${appendedUniqueId}`; - } else if (field === EVENT_DURATION_FIELD_NAME) { - id = `duration-default-draggable-${appendedUniqueId}`; - } else if (field === HOST_NAME_FIELD_NAME) { - id = `event-details-value-default-draggable-${appendedUniqueId}`; - } else if (fieldFormat === BYTES_FORMAT) { - id = `bytes-default-draggable-${appendedUniqueId}`; - } else if (field === SIGNAL_RULE_NAME_FIELD_NAME) { - id = `event-details-value-default-draggable-${appendedUniqueId}-${linkValue}`; - } else if (field === EVENT_MODULE_FIELD_NAME) { - id = `event-details-value-default-draggable-${appendedUniqueId}-${value}`; - } else if (field === SIGNAL_STATUS_FIELD_NAME) { - id = `alert-details-value-default-draggable-${appendedUniqueId}`; - } else if (field === AGENT_STATUS_FIELD_NAME) { - const valueToUse = typeof value === 'string' ? value : ''; - id = `event-details-value-default-draggable-${appendedUniqueId}`; - valueAsString = valueToUse; - } else if ( - [ - RULE_REFERENCE_FIELD_NAME, - REFERENCE_URL_FIELD_NAME, - EVENT_URL_FIELD_NAME, - INDICATOR_REFERENCE, - ].includes(field) - ) { - id = `event-details-value-default-draggable-${appendedUniqueId}-${value}`; - } else { - id = `event-details-value-default-draggable-${appendedUniqueId}`; + } catch (_) { + // Default to keeping the existing string value } - memo.stringValues.push(valueAsString); - memo.dataProvider.push(getDataProvider(field, id, value)); - return memo; - }, - { stringValues: [], dataProvider: [] } - ); - }, [ - contextId, - eventId, - field, - fieldFormat, - fieldFromBrowserField, - fieldType, - isObjectArray, - linkValue, - values, - ]); - return cellData; + } + } else if (PORT_NAMES.some((portName) => field === portName)) { + id = `port-default-draggable-${appendedUniqueId}`; + } else if (field === EVENT_DURATION_FIELD_NAME) { + id = `duration-default-draggable-${appendedUniqueId}`; + } else if (field === HOST_NAME_FIELD_NAME) { + id = `event-details-value-default-draggable-${appendedUniqueId}`; + } else if (fieldFormat === BYTES_FORMAT) { + id = `bytes-default-draggable-${appendedUniqueId}`; + } else if (field === SIGNAL_RULE_NAME_FIELD_NAME) { + id = `event-details-value-default-draggable-${appendedUniqueId}-${linkValue}`; + } else if (field === EVENT_MODULE_FIELD_NAME) { + id = `event-details-value-default-draggable-${appendedUniqueId}-${value}`; + } else if (field === SIGNAL_STATUS_FIELD_NAME) { + id = `alert-details-value-default-draggable-${appendedUniqueId}`; + } else if (field === AGENT_STATUS_FIELD_NAME) { + const valueToUse = typeof value === 'string' ? value : ''; + id = `event-details-value-default-draggable-${appendedUniqueId}`; + valueAsString = valueToUse; + } else if ( + [ + RULE_REFERENCE_FIELD_NAME, + REFERENCE_URL_FIELD_NAME, + EVENT_URL_FIELD_NAME, + INDICATOR_REFERENCE, + ].includes(field) + ) { + id = `event-details-value-default-draggable-${appendedUniqueId}-${value}`; + } else { + id = `event-details-value-default-draggable-${appendedUniqueId}`; + } + stringifiedValues.push(valueAsString); + memo.push(escapeDataProviderId(id)); + return memo; + }, [] as string[]); + + return { + idList, + stringValues: stringifiedValues, + }; }; diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/index.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/index.tsx index a1ba33f30cc55..31bdf78626e7c 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/index.tsx @@ -6,17 +6,16 @@ */ import { EuiFocusTrap, EuiScreenReaderOnly } from '@elastic/eui'; -import React, { useCallback, useEffect, useRef, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useMemo } from 'react'; import { DraggableId } from 'react-beautiful-dnd'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; -import { isEmpty } from 'lodash'; - -import { useKibana } from '../../lib/kibana'; import { getAllFieldsByName } from '../../containers/source'; +import { COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME } from '../../lib/clipboard/clipboard'; +import { useKibana } from '../../lib/kibana'; import { allowTopN } from './utils'; import { useDeepEqualSelector } from '../../hooks/use_selector'; -import { ColumnHeaderOptions, DataProvider, TimelineId } from '../../../../common/types/timeline'; +import { ColumnHeaderOptions, TimelineId } from '../../../../common/types/timeline'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { useSourcererScope } from '../../containers/sourcerer'; import { timelineSelectors } from '../../../timelines/store/timeline'; @@ -39,51 +38,43 @@ export const AdditionalContent = styled.div` AdditionalContent.displayName = 'AdditionalContent'; -const StyledHoverActionsContainer = styled.div<{ $showTopN: boolean; $showOwnFocus: boolean }>` +const StyledHoverActionsContainer = styled.div<{ $showTopN: boolean }>` padding: ${(props) => `0 ${props.theme.eui.paddingSizes.s}`}; display: flex; - ${(props) => - props.$showOwnFocus - ? ` - &:focus-within { - .timelines__hoverActionButton, - .securitySolution__hoverActionButton { - opacity: 1; - } + &:focus-within { + .timelines__hoverActionButton, + .securitySolution__hoverActionButton { + opacity: 1; } + } - &:hover { - .timelines__hoverActionButton, - .securitySolution__hoverActionButton { - opacity: 1; - } + &:hover { + .timelines__hoverActionButton, + .securitySolution__hoverActionButton { + opacity: 1; } + } .timelines__hoverActionButton, .securitySolution__hoverActionButton { - opacity: ${props.$showTopN ? 1 : 0}; + opacity: ${(props) => (props.$showTopN ? 1 : 0)}; - &:focus { - opacity: 1; - } + &:focus { + opacity: 1; } - ` - : ''} + } `; interface Props { additionalContent?: React.ReactNode; - closePopOver?: () => void; - dataProvider?: DataProvider | DataProvider[]; dataType?: string; - draggableId?: DraggableId; + draggableIds?: DraggableId[]; field: string; goGetTimelineId?: (args: boolean) => void; isObjectArray: boolean; onFilterAdded?: () => void; ownFocus: boolean; - showOwnFocus?: boolean; showTopN: boolean; timelineId?: string | null; toggleColumn?: (column: ColumnHeaderOptions) => void; @@ -109,15 +100,13 @@ const isFocusTrapDisabled = ({ export const HoverActions: React.FC = React.memo( ({ additionalContent = null, - dataProvider, dataType, - draggableId, + draggableIds, field, goGetTimelineId, isObjectArray, onFilterAdded, ownFocus, - showOwnFocus = true, showTopN, timelineId, toggleColumn, @@ -128,13 +117,29 @@ export const HoverActions: React.FC = React.memo( const { timelines } = kibana.services; // Common actions used by the alert table and alert flyout const { - getAddToTimelineButton, - getColumnToggleButton, - getCopyButton, - getFilterForValueButton, - getFilterOutValueButton, + addToTimeline: { + AddToTimelineButton, + keyboardShortcut: addToTimelineKeyboardShortcut, + useGetHandleStartDragToTimeline, + }, + columnToggle: { + ColumnToggleButton, + columnToggleFn, + keyboardShortcut: columnToggleKeyboardShortcut, + }, + copy: { CopyButton, keyboardShortcut: copyKeyboardShortcut }, + filterForValue: { + FilterForValueButton, + filterForValueFn, + keyboardShortcut: filterForValueKeyboardShortcut, + }, + filterOutValue: { + FilterOutValueButton, + filterOutValueFn, + keyboardShortcut: filterOutValueKeyboardShortcut, + }, } = timelines.getHoverActions(); - const [stKeyboardEvent, setStKeyboardEvent] = useState(); + const filterManagerBackup = useMemo(() => kibana.services.data.query.filterManager, [ kibana.services.data.query.filterManager, ]); @@ -164,8 +169,30 @@ export const HoverActions: React.FC = React.memo( : SourcererScopeName.default; const { browserFields } = useSourcererScope(activeScope); + const handleStartDragToTimeline = (() => { + const handleStartDragToTimelineFns = draggableIds?.map((draggableId) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + return useGetHandleStartDragToTimeline({ draggableId, field }); + }); + return () => handleStartDragToTimelineFns?.forEach((dragFn) => dragFn()); + })(); + + const handleFilterForValue = useCallback(() => { + filterForValueFn({ field, value: values, filterManager, onFilterAdded }); + }, [filterForValueFn, field, values, filterManager, onFilterAdded]); + + const handleFilterOutValue = useCallback(() => { + filterOutValueFn({ field, value: values, filterManager, onFilterAdded }); + }, [filterOutValueFn, field, values, filterManager, onFilterAdded]); + + const handleToggleColumn = useCallback( + () => (toggleColumn ? columnToggleFn({ toggleColumn, field }) : null), + [columnToggleFn, field, toggleColumn] + ); + const isInit = useRef(true); const defaultFocusedButtonRef = useRef(null); + const panelRef = useRef(null); useEffect(() => { if (isInit.current && goGetTimelineId != null && timelineId == null) { @@ -188,6 +215,31 @@ export const HoverActions: React.FC = React.memo( return; } switch (keyboardEvent.key) { + case addToTimelineKeyboardShortcut: + stopPropagationAndPreventDefault(keyboardEvent); + handleStartDragToTimeline(); + break; + case columnToggleKeyboardShortcut: + stopPropagationAndPreventDefault(keyboardEvent); + handleToggleColumn(); + break; + case copyKeyboardShortcut: + stopPropagationAndPreventDefault(keyboardEvent); + const copyToClipboardButton = panelRef.current?.querySelector( + `.${COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME}` + ); + if (copyToClipboardButton != null) { + copyToClipboardButton.click(); + } + break; + case filterForValueKeyboardShortcut: + stopPropagationAndPreventDefault(keyboardEvent); + handleFilterForValue(); + break; + case filterOutValueKeyboardShortcut: + stopPropagationAndPreventDefault(keyboardEvent); + handleFilterOutValue(); + break; case SHOW_TOP_N_KEYBOARD_SHORTCUT: stopPropagationAndPreventDefault(keyboardEvent); toggleTopN(); @@ -198,26 +250,33 @@ export const HoverActions: React.FC = React.memo( stopPropagationAndPreventDefault(keyboardEvent); break; default: - setStKeyboardEvent(keyboardEvent); break; } }, - [ownFocus, toggleTopN] + [ + addToTimelineKeyboardShortcut, + columnToggleKeyboardShortcut, + copyKeyboardShortcut, + filterForValueKeyboardShortcut, + filterOutValueKeyboardShortcut, + handleFilterForValue, + handleFilterOutValue, + handleStartDragToTimeline, + handleToggleColumn, + ownFocus, + toggleTopN, + ] ); const showFilters = values != null; return ( - - +

{YOU_ARE_IN_A_DIALOG_CONTAINING_OPTIONS(field)}

@@ -227,58 +286,46 @@ export const HoverActions: React.FC = React.memo( {showFilters && ( <> -
- {getFilterForValueButton({ - defaultFocusedButtonRef, - field, - filterManager, - keyboardEvent: stKeyboardEvent, - onFilterAdded, - ownFocus, - showTooltip: true, - value: values, - })} -
-
- {getFilterOutValueButton({ - field, - filterManager, - keyboardEvent: stKeyboardEvent, - onFilterAdded, - ownFocus, - showTooltip: true, - value: values, - })} -
+ + )} {toggleColumn && ( -
- {getColumnToggleButton({ - field, - isDisabled: isObjectArray && dataType !== 'geo_point', - isObjectArray, - keyboardEvent: stKeyboardEvent, - ownFocus, - showTooltip: true, - toggleColumn, - value: values, - })} -
+ )} - {showFilters && (draggableId != null || !isEmpty(dataProvider)) && ( -
- {getAddToTimelineButton({ - dataProvider, - draggableId, - field, - keyboardEvent: stKeyboardEvent, - ownFocus, - showTooltip: true, - value: values, - })} -
+ {showFilters && draggableIds != null && ( + )} {allowTopN({ browserField: getAllFieldsByName(browserFields)[field], @@ -295,20 +342,18 @@ export const HoverActions: React.FC = React.memo( value={values} /> )} - {field != null && ( -
- {getCopyButton({ - field, - isHoverAction: true, - keyboardEvent: stKeyboardEvent, - ownFocus, - showTooltip: true, - value: values, - })} -
+ {showFilters && ( + )} -
-
+ + ); } ); diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_actions.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_actions.tsx deleted file mode 100644 index 373f944b70a81..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_actions.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useMemo, useState, useRef } from 'react'; -import { DraggableProvided, DraggableStateSnapshot } from 'react-beautiful-dnd'; -import { HoverActions } from '.'; - -import { DataProvider } from '../../../../common/types'; -import { ProviderContentWrapper } from '../drag_and_drop/draggable_wrapper'; -import { getDraggableId } from '../drag_and_drop/helpers'; -import { useGetTimelineId } from '../drag_and_drop/use_get_timeline_id_from_dom'; - -const draggableContainsLinks = (draggableElement: HTMLDivElement | null) => { - const links = draggableElement?.querySelectorAll('.euiLink') ?? []; - return links.length > 0; -}; - -type RenderFunctionProp = ( - props: DataProvider, - provided: DraggableProvided | null, - state: DraggableStateSnapshot -) => React.ReactNode; - -interface Props { - dataProvider: DataProvider; - disabled?: boolean; - isDraggable?: boolean; - inline?: boolean; - render: RenderFunctionProp; - timelineId?: string; - truncate?: boolean; - onFilterAdded?: () => void; -} - -export const useHoverActions = ({ - dataProvider, - isDraggable, - onFilterAdded, - render, - timelineId, -}: Props) => { - const containerRef = useRef(null); - const keyboardHandlerRef = useRef(null); - const [closePopOverTrigger, setClosePopOverTrigger] = useState(false); - const [showTopN, setShowTopN] = useState(false); - const [hoverActionsOwnFocus, setHoverActionsOwnFocus] = useState(false); - const [goGetTimelineId, setGoGetTimelineId] = useState(false); - const timelineIdFind = useGetTimelineId(containerRef, goGetTimelineId); - - const handleClosePopOverTrigger = useCallback(() => { - setClosePopOverTrigger((prevClosePopOverTrigger) => !prevClosePopOverTrigger); - setHoverActionsOwnFocus((prevHoverActionsOwnFocus) => { - if (prevHoverActionsOwnFocus) { - setTimeout(() => { - keyboardHandlerRef.current?.focus(); - }, 0); - } - return false; // always give up ownership - }); - - setTimeout(() => { - setHoverActionsOwnFocus(false); - }, 0); // invoked on the next tick, because we want to restore focus first - }, [keyboardHandlerRef]); - - const toggleTopN = useCallback(() => { - setShowTopN((prevShowTopN) => { - const newShowTopN = !prevShowTopN; - if (newShowTopN === false) { - handleClosePopOverTrigger(); - } - return newShowTopN; - }); - }, [handleClosePopOverTrigger]); - - const hoverContent = useMemo(() => { - // display links as additional content in the hover menu to enable keyboard - // navigation of links (when the draggable contains them): - const additionalContent = - hoverActionsOwnFocus && !showTopN && draggableContainsLinks(containerRef.current) ? ( - - {render(dataProvider, null, { isDragging: false, isDropAnimating: false })} - - ) : null; - - return ( - - ); - }, [ - dataProvider, - handleClosePopOverTrigger, - hoverActionsOwnFocus, - isDraggable, - onFilterAdded, - render, - showTopN, - timelineId, - timelineIdFind, - toggleTopN, - ]); - - const setContainerRef = useCallback((e: HTMLDivElement) => { - containerRef.current = e; - }, []); - - const onFocus = useCallback(() => { - if (!hoverActionsOwnFocus) { - keyboardHandlerRef.current?.focus(); - } - }, [hoverActionsOwnFocus, keyboardHandlerRef]); - - const onCloseRequested = useCallback(() => { - setShowTopN(false); - - if (hoverActionsOwnFocus) { - setHoverActionsOwnFocus(false); - - setTimeout(() => { - onFocus(); // return focus to this draggable on the next tick, because we owned focus - }, 0); - } - }, [onFocus, hoverActionsOwnFocus]); - - const openPopover = useCallback(() => { - setHoverActionsOwnFocus(true); - }, []); - - return useMemo( - () => ({ - closePopOverTrigger, - handleClosePopOverTrigger, - hoverActionsOwnFocus, - hoverContent, - keyboardHandlerRef, - onCloseRequested, - onFocus, - openPopover, - setContainerRef, - showTopN, - }), - [ - closePopOverTrigger, - handleClosePopOverTrigger, - hoverActionsOwnFocus, - hoverContent, - onCloseRequested, - onFocus, - openPopover, - setContainerRef, - showTopN, - ] - ); -}; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx index 45883019b9ff8..2ecda8482e340 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx @@ -70,6 +70,67 @@ describe('get_anomalies_host_table_columns', () => { expect(columns.some((col) => col.name === i18n.HOST_NAME)).toEqual(false); }); + test('on host page, we should escape the draggable id', () => { + const columns = getAnomaliesHostTableColumnsCurated( + HostsType.page, + startDate, + endDate, + interval, + narrowDateRange + ); + const column = columns.find((col) => col.name === i18n.SCORE) as Columns< + string, + AnomaliesByHost + >; + const anomaly: AnomaliesByHost = { + hostName: 'host.name', + anomaly: { + detectorIndex: 0, + entityName: 'entity-name-1', + entityValue: 'entity-value-1', + influencers: [], + jobId: 'job-1', + rowId: 'row-1', + severity: 100, + time: new Date('01/01/2000').valueOf(), + source: { + job_id: 'job-1', + result_type: 'result-1', + probability: 50, + multi_bucket_impact: 0, + record_score: 0, + initial_record_score: 0, + bucket_span: 0, + detector_index: 0, + is_interim: true, + timestamp: new Date('01/01/2000').valueOf(), + by_field_name: 'some field name', + by_field_value: 'some field value', + partition_field_name: 'partition field name', + partition_field_value: 'partition field value', + function: 'function-1', + function_description: 'description-1', + typical: [5, 3], + actual: [7, 4], + influencers: [], + }, + }, + }; + if (column != null && column.render != null) { + const wrapper = mount({column.render('', anomaly)}); + expect( + wrapper + .find( + '[draggableId="draggableId.content.anomalies-host-table-severity-host_name-entity-name-1-entity-value-1-100-job-1"]' + ) + .first() + .exists() + ).toBe(true); + } else { + expect(column).not.toBe(null); + } + }); + test('on host page, undefined influencers should turn into an empty column string', () => { const columns = getAnomaliesHostTableColumnsCurated( HostsType.page, diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx index 817205ce22808..48c2ec3ee38d8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx @@ -43,6 +43,62 @@ describe('get_anomalies_network_table_columns', () => { expect(columns.some((col) => col.name === i18n.NETWORK_NAME)).toEqual(false); }); + test('on network page, we should escape the draggable id', () => { + const columns = getAnomaliesNetworkTableColumnsCurated(NetworkType.page, startDate, endDate); + const column = columns.find((col) => col.name === i18n.SCORE) as Columns< + string, + AnomaliesByNetwork + >; + const anomaly: AnomaliesByNetwork = { + type: 'source.ip', + ip: '127.0.0.1', + anomaly: { + detectorIndex: 0, + entityName: 'entity-name-1', + entityValue: 'entity-value-1', + influencers: [], + jobId: 'job-1', + rowId: 'row-1', + severity: 100, + time: new Date('01/01/2000').valueOf(), + source: { + job_id: 'job-1', + result_type: 'result-1', + probability: 50, + multi_bucket_impact: 0, + record_score: 0, + initial_record_score: 0, + bucket_span: 0, + detector_index: 0, + is_interim: true, + timestamp: new Date('01/01/2000').valueOf(), + by_field_name: 'some field name', + by_field_value: 'some field value', + partition_field_name: 'partition field name', + partition_field_value: 'partition field value', + function: 'function-1', + function_description: 'description-1', + typical: [5, 3], + actual: [7, 4], + influencers: [], + }, + }, + }; + if (column != null && column.render != null) { + const wrapper = mount({column.render('', anomaly)}); + expect( + wrapper + .find( + '[draggableId="draggableId.content.anomalies-network-table-severity-127_0_0_1-entity-name-1-entity-value-1-100-job-1"]' + ) + .first() + .exists() + ).toBe(true); + } else { + expect(column).not.toBe(null); + } + }); + test('on network page, undefined influencers should turn into an empty column string', () => { const columns = getAnomaliesNetworkTableColumnsCurated(NetworkType.page, startDate, endDate); const column = columns.find((col) => col.name === i18n.INFLUENCED_BY) as Columns< diff --git a/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx index 10e4538c802ad..c122138f9547a 100644 --- a/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx @@ -55,7 +55,7 @@ describe('Table Helpers', () => { displayCount: 0, }); const wrapper = mount({rowItem}); - expect(wrapper.find('[data-test-subj="render-content-attrName"]').first().text()).toBe( + expect(wrapper.find('[data-test-subj="draggable-content-attrName"]').first().text()).toBe( '(Empty String)' ); }); @@ -81,7 +81,7 @@ describe('Table Helpers', () => { render: renderer, }); const wrapper = mount({rowItem}); - expect(wrapper.find('[data-test-subj="render-content-attrName"]').first().text()).toBe( + expect(wrapper.find('[data-test-subj="draggable-content-attrName"]').first().text()).toBe( 'Hi item1 renderer' ); }); @@ -116,7 +116,7 @@ describe('Table Helpers', () => { idPrefix: 'idPrefix', }); const wrapper = mount({rowItems}); - expect(wrapper.find('[data-test-subj="render-content-attrName"]').first().text()).toBe( + expect(wrapper.find('[data-test-subj="draggable-content-attrName"]').first().text()).toBe( '(Empty String)' ); }); @@ -163,7 +163,7 @@ describe('Table Helpers', () => { displayCount: 2, }); const wrapper = mount({rowItems}); - expect(wrapper.find('[data-test-subj="withHoverActionsButton"]').hostNodes().length).toBe(2); + expect(wrapper.find('[data-test-subj="draggableWrapperDiv"]').hostNodes().length).toBe(2); }); test('it uses custom renderer', () => { @@ -175,7 +175,7 @@ describe('Table Helpers', () => { render: renderer, }); const wrapper = mount({rowItems}); - expect(wrapper.find('[data-test-subj="render-content-attrName"]').first().text()).toBe( + expect(wrapper.find('[data-test-subj="draggable-content-attrName"]').first().text()).toBe( 'Hi item1 renderer' ); }); diff --git a/x-pack/plugins/security_solution/public/network/components/direction/index.tsx b/x-pack/plugins/security_solution/public/network/components/direction/index.tsx index d87756cb9bbab..7c6bb50378d9c 100644 --- a/x-pack/plugins/security_solution/public/network/components/direction/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/direction/index.tsx @@ -60,15 +60,13 @@ export const DirectionBadge = React.memo<{ contextId: string; direction?: string | null; eventId: string; - isDraggable?: boolean; -}>(({ contextId, eventId, direction, isDraggable }) => ( +}>(({ contextId, eventId, direction }) => ( )); diff --git a/x-pack/plugins/security_solution/public/network/components/ip/index.tsx b/x-pack/plugins/security_solution/public/network/components/ip/index.tsx index 2fa3e988784ca..a08b8003f142c 100644 --- a/x-pack/plugins/security_solution/public/network/components/ip/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/ip/index.tsx @@ -22,15 +22,13 @@ export const Ip = React.memo<{ contextId: string; eventId: string; fieldName: string; - isDraggable?: boolean; value?: string | null; -}>(({ contextId, eventId, fieldName, isDraggable, value }) => ( +}>(({ contextId, eventId, fieldName, value }) => ( diff --git a/x-pack/plugins/security_solution/public/network/components/port/index.tsx b/x-pack/plugins/security_solution/public/network/components/port/index.tsx index 4afd9bc7b892a..df288c1abfb06 100644 --- a/x-pack/plugins/security_solution/public/network/components/port/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/port/index.tsx @@ -29,7 +29,7 @@ export const Port = React.memo<{ contextId: string; eventId: string; fieldName: string; - isDraggable?: boolean; + isDraggable: boolean; value: string | undefined | null; }>(({ contextId, eventId, fieldName, isDraggable, value }) => isDraggable ? ( @@ -37,7 +37,6 @@ export const Port = React.memo<{ data-test-subj="port" field={fieldName} id={`port-default-draggable-${contextId}-${eventId}-${fieldName}-${value}`} - isDraggable={isDraggable} tooltipContent={fieldName} value={value} > diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/geo_fields.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/geo_fields.tsx index 65bd3bf1ec154..1f6111cd0bb07 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/geo_fields.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/geo_fields.tsx @@ -73,9 +73,8 @@ const GeoFieldValues = React.memo<{ contextId: string; eventId: string; fieldName: string; - isDraggable?: boolean; values?: string[] | null; -}>(({ contextId, eventId, fieldName, isDraggable, values }) => +}>(({ contextId, eventId, fieldName, values }) => values != null ? ( <> {uniq(values).map((value) => ( @@ -93,7 +92,6 @@ const GeoFieldValues = React.memo<{ data-test-subj={fieldName} field={fieldName} id={`geo-field-values-default-draggable-${contextId}-${eventId}-${fieldName}-${value}`} - isDraggable={isDraggable} tooltipContent={fieldName} value={value} /> @@ -116,7 +114,7 @@ GeoFieldValues.displayName = 'GeoFieldValues'; * - `source|destination.geo.city_name` */ export const GeoFields = React.memo((props) => { - const { contextId, eventId, isDraggable, type } = props; + const { contextId, eventId, type } = props; const propNameToFieldName = getGeoFieldPropNameToFieldNameMap(type); return ( @@ -126,7 +124,6 @@ export const GeoFields = React.memo((props) => { contextId={contextId} eventId={eventId} fieldName={geo.fieldName} - isDraggable={isDraggable} key={geo.fieldName} values={get(geo.prop, props)} /> diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/index.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/index.tsx index d7bcf9f6c5297..57e302d2911fa 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/index.tsx @@ -36,7 +36,6 @@ export const SourceDestination = React.memo( destinationPackets, destinationPort, eventId, - isDraggable, networkBytes, networkCommunityId, networkDirection, @@ -60,9 +59,8 @@ export const SourceDestination = React.memo( packets={networkPackets} communityId={networkCommunityId} contextId={contextId} - direction={networkDirection} eventId={eventId} - isDraggable={isDraggable} + direction={networkDirection} protocol={networkProtocol} transport={transport} /> @@ -81,7 +79,6 @@ export const SourceDestination = React.memo( destinationPackets={destinationPackets} destinationPort={destinationPort} eventId={eventId} - isDraggable={isDraggable} sourceBytes={sourceBytes} sourceGeoContinentName={sourceGeoContinentName} sourceGeoCountryName={sourceGeoCountryName} diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/ip_with_port.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/ip_with_port.tsx index e99aecbc535e7..17b55c4229fcc 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/ip_with_port.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/ip_with_port.tsx @@ -25,10 +25,9 @@ IpPortSeparator.displayName = 'IpPortSeparator'; const PortWithSeparator = React.memo<{ contextId: string; eventId: string; - isDraggable?: boolean; port?: string | null; portFieldName: string; -}>(({ contextId, eventId, isDraggable, port, portFieldName }) => { +}>(({ contextId, eventId, port, portFieldName }) => { return port != null ? ( @@ -40,7 +39,7 @@ const PortWithSeparator = React.memo<{ data-test-subj="port" eventId={eventId} fieldName={portFieldName} - isDraggable={isDraggable} + isDraggable={true} value={port} /> @@ -59,10 +58,9 @@ export const IpWithPort = React.memo<{ eventId: string; ip?: string | null; ipFieldName: string; - isDraggable?: boolean; port?: string | null; portFieldName: string; -}>(({ contextId, eventId, ip, ipFieldName, isDraggable, port, portFieldName }) => ( +}>(({ contextId, eventId, ip, ipFieldName, port, portFieldName }) => ( @@ -78,7 +75,6 @@ export const IpWithPort = React.memo<{ diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/network.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/network.tsx index 88bfd19b7066e..c1b454892fddf 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/network.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/network.tsx @@ -45,120 +45,97 @@ export const Network = React.memo<{ contextId: string; direction?: string[] | null; eventId: string; - isDraggable?: boolean; packets?: string[] | null; protocol?: string[] | null; transport?: string[] | null; -}>( - ({ - bytes, - communityId, - contextId, - direction, - eventId, - isDraggable, - packets, - protocol, - transport, - }) => ( - - {direction != null - ? uniq(direction).map((dir) => ( - - - - )) - : null} - - {protocol != null - ? uniq(protocol).map((proto) => ( - - - - )) - : null} +}>(({ bytes, communityId, contextId, direction, eventId, packets, protocol, transport }) => ( + + {direction != null + ? uniq(direction).map((dir) => ( + + + + )) + : null} - {bytes != null - ? uniq(bytes).map((b) => - !isNaN(Number(b)) ? ( - - - - - - - - - - ) : null - ) - : null} + {protocol != null + ? uniq(protocol).map((proto) => ( + + + + )) + : null} - {packets != null - ? uniq(packets).map((p) => ( - + {bytes != null + ? uniq(bytes).map((b) => + !isNaN(Number(b)) ? ( + - {`${p} ${i18n.PACKETS}`} + + + - )) - : null} + ) : null + ) + : null} - {transport != null - ? uniq(transport).map((trans) => ( - - - - )) - : null} + {packets != null + ? uniq(packets).map((p) => ( + + + + {`${p} ${i18n.PACKETS}`} + + + + )) + : null} + + {transport != null + ? uniq(transport).map((trans) => ( + + + + )) + : null} - {communityId != null - ? uniq(communityId).map((trans) => ( - - - - )) - : null} - - ) -); + {communityId != null + ? uniq(communityId).map((trans) => ( + + + + )) + : null} + +)); Network.displayName = 'Network'; diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_arrows.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_arrows.tsx index 6858520340aae..ff9edff39b3ad 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_arrows.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_arrows.tsx @@ -56,11 +56,10 @@ Data.displayName = 'Data'; const SourceArrow = React.memo<{ contextId: string; eventId: string; - isDraggable?: boolean; sourceBytes: string | undefined; sourceBytesPercent: number | undefined; sourcePackets: string | undefined; -}>(({ contextId, eventId, isDraggable, sourceBytes, sourceBytesPercent, sourcePackets }) => { +}>(({ contextId, eventId, sourceBytes, sourceBytesPercent, sourcePackets }) => { const sourceArrowHeight = sourceBytesPercent != null ? getArrowHeightFromPercent(sourceBytesPercent) @@ -77,7 +76,6 @@ const SourceArrow = React.memo<{ @@ -103,7 +101,6 @@ const SourceArrow = React.memo<{ @@ -132,85 +129,73 @@ SourceArrow.displayName = 'SourceArrow'; */ const DestinationArrow = React.memo<{ contextId: string; + eventId: string; destinationBytes: string | undefined; destinationBytesPercent: number | undefined; destinationPackets: string | undefined; - eventId: string; - isDraggable?: boolean; -}>( - ({ - contextId, - destinationBytes, - destinationBytesPercent, - destinationPackets, - eventId, - isDraggable, - }) => { - const destinationArrowHeight = - destinationBytesPercent != null - ? getArrowHeightFromPercent(destinationBytesPercent) - : DEFAULT_ARROW_HEIGHT; - - return ( - - - - +}>(({ contextId, eventId, destinationBytes, destinationBytesPercent, destinationPackets }) => { + const destinationArrowHeight = + destinationBytesPercent != null + ? getArrowHeightFromPercent(destinationBytesPercent) + : DEFAULT_ARROW_HEIGHT; - - - + return ( + + + + - {destinationBytes != null && !isNaN(Number(destinationBytes)) ? ( - - - - {destinationBytesPercent != null ? ( - - {`(${numeral(destinationBytesPercent).format('0.00')}%)`} - - ) : null} - - - - - - - ) : null} + + + + {destinationBytes != null && !isNaN(Number(destinationBytes)) ? ( - + + + {destinationBytesPercent != null ? ( + + {`(${numeral(destinationBytesPercent).format('0.00')}%)`} + + ) : null} + + + + + + ) : null} - {destinationPackets != null && !isNaN(Number(destinationPackets)) ? ( - - - - {`${numeral(destinationPackets).format( - '0,0' - )} ${i18n.PACKETS}`} - - - - ) : null} + + + + {destinationPackets != null && !isNaN(Number(destinationPackets)) ? ( - + + + {`${numeral(destinationPackets).format( + '0,0' + )} ${i18n.PACKETS}`} + + - - ); - } -); + ) : null} + + + + + + ); +}); DestinationArrow.displayName = 'DestinationArrow'; @@ -223,79 +208,67 @@ export const SourceDestinationArrows = React.memo<{ destinationBytes?: string[] | null; destinationPackets?: string[] | null; eventId: string; - isDraggable?: boolean; sourceBytes?: string[] | null; sourcePackets?: string[] | null; -}>( - ({ - contextId, - destinationBytes, - destinationPackets, - eventId, - isDraggable, - sourceBytes, - sourcePackets, - }) => { - const maybeSourceBytes = - sourceBytes != null && hasOneValue(sourceBytes) ? sourceBytes[0] : undefined; - - const maybeSourcePackets = - sourcePackets != null && hasOneValue(sourcePackets) ? sourcePackets[0] : undefined; - - const maybeDestinationBytes = - destinationBytes != null && hasOneValue(destinationBytes) ? destinationBytes[0] : undefined; - - const maybeDestinationPackets = - destinationPackets != null && hasOneValue(destinationPackets) - ? destinationPackets[0] - : undefined; - - const maybeSourceBytesPercent = - maybeSourceBytes != null && maybeDestinationBytes != null - ? getPercent({ - numerator: Number(maybeSourceBytes), - denominator: Number(maybeSourceBytes) + Number(maybeDestinationBytes), - }) - : undefined; - - const maybeDestinationBytesPercent = - maybeSourceBytesPercent != null ? 100 - maybeSourceBytesPercent : undefined; - - return ( - - {maybeSourceBytes != null ? ( - - - - ) : null} - {maybeDestinationBytes != null ? ( - - - - ) : null} - - ); - } -); +}>(({ contextId, destinationBytes, destinationPackets, eventId, sourceBytes, sourcePackets }) => { + const maybeSourceBytes = + sourceBytes != null && hasOneValue(sourceBytes) ? sourceBytes[0] : undefined; + + const maybeSourcePackets = + sourcePackets != null && hasOneValue(sourcePackets) ? sourcePackets[0] : undefined; + + const maybeDestinationBytes = + destinationBytes != null && hasOneValue(destinationBytes) ? destinationBytes[0] : undefined; + + const maybeDestinationPackets = + destinationPackets != null && hasOneValue(destinationPackets) + ? destinationPackets[0] + : undefined; + + const maybeSourceBytesPercent = + maybeSourceBytes != null && maybeDestinationBytes != null + ? getPercent({ + numerator: Number(maybeSourceBytes), + denominator: Number(maybeSourceBytes) + Number(maybeDestinationBytes), + }) + : undefined; + + const maybeDestinationBytesPercent = + maybeSourceBytesPercent != null ? 100 - maybeSourceBytesPercent : undefined; + + return ( + + {maybeSourceBytes != null ? ( + + + + ) : null} + + {maybeDestinationBytes != null ? ( + + + + ) : null} + + ); +}); SourceDestinationArrows.displayName = 'SourceDestinationArrows'; diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx index 824b9fd11f242..91f7ea3d7ac7a 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx @@ -958,7 +958,6 @@ describe('SourceDestinationIp', () => { destinationIp={asArrayIfExists(get(DESTINATION_IP_FIELD_NAME, getMockNetflowData()))} destinationPort={asArrayIfExists(get(DESTINATION_PORT_FIELD_NAME, getMockNetflowData()))} eventId={get(ID_FIELD_NAME, getMockNetflowData())} - isDraggable={true} sourceGeoContinentName={asArrayIfExists( get(SOURCE_GEO_CONTINENT_NAME_FIELD_NAME, getMockNetflowData()) )} @@ -980,6 +979,7 @@ describe('SourceDestinationIp', () => { /> ); + expect( removeExternalLinkText( wrapper.find('[data-test-subj="draggable-content-source.port"]').first().text() @@ -1011,7 +1011,6 @@ describe('SourceDestinationIp', () => { destinationIp={asArrayIfExists(get(DESTINATION_IP_FIELD_NAME, getMockNetflowData()))} destinationPort={asArrayIfExists(get(DESTINATION_PORT_FIELD_NAME, getMockNetflowData()))} eventId={get(ID_FIELD_NAME, getMockNetflowData())} - isDraggable={true} sourceGeoContinentName={asArrayIfExists( get(SOURCE_GEO_CONTINENT_NAME_FIELD_NAME, getMockNetflowData()) )} @@ -1065,7 +1064,6 @@ describe('SourceDestinationIp', () => { destinationIp={asArrayIfExists(get(DESTINATION_IP_FIELD_NAME, getMockNetflowData()))} destinationPort={asArrayIfExists(get(DESTINATION_PORT_FIELD_NAME, getMockNetflowData()))} eventId={get(ID_FIELD_NAME, getMockNetflowData())} - isDraggable={true} sourceGeoContinentName={asArrayIfExists( get(SOURCE_GEO_CONTINENT_NAME_FIELD_NAME, getMockNetflowData()) )} @@ -1120,7 +1118,6 @@ describe('SourceDestinationIp', () => { destinationIp={undefined} destinationPort={asArrayIfExists(get(DESTINATION_PORT_FIELD_NAME, getMockNetflowData()))} eventId={get(ID_FIELD_NAME, getMockNetflowData())} - isDraggable={true} sourceGeoContinentName={asArrayIfExists( get(SOURCE_GEO_CONTINENT_NAME_FIELD_NAME, getMockNetflowData()) )} @@ -1274,7 +1271,6 @@ describe('SourceDestinationIp', () => { destinationIp={asArrayIfExists(get(DESTINATION_IP_FIELD_NAME, getMockNetflowData()))} destinationPort={asArrayIfExists(get(DESTINATION_PORT_FIELD_NAME, getMockNetflowData()))} eventId={get(ID_FIELD_NAME, getMockNetflowData())} - isDraggable={true} sourceGeoContinentName={asArrayIfExists( get(SOURCE_GEO_CONTINENT_NAME_FIELD_NAME, getMockNetflowData()) )} diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.tsx index 31bae6880fcbe..db9773789bf54 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.tsx @@ -88,67 +88,54 @@ const IpAdressesWithPorts = React.memo<{ destinationIp?: string[] | null; destinationPort?: Array | null; eventId: string; - isDraggable?: boolean; sourceIp?: string[] | null; sourcePort?: Array | null; type: SourceDestinationType; -}>( - ({ - contextId, - destinationIp, - destinationPort, - eventId, - isDraggable, - sourceIp, - sourcePort, - type, - }) => { - const ip = type === 'source' ? sourceIp : destinationIp; - const ipFieldName = type === 'source' ? SOURCE_IP_FIELD_NAME : DESTINATION_IP_FIELD_NAME; - const port = type === 'source' ? sourcePort : destinationPort; - const portFieldName = type === 'source' ? SOURCE_PORT_FIELD_NAME : DESTINATION_PORT_FIELD_NAME; - - if (ip == null) { - return null; // if ip is not populated as an array, ports will be ignored - } - - // IMPORTANT: The ip and port arrays are parallel arrays; the port at - // index `i` corresponds with the ip address at index `i`. We must - // preserve the relationships between the parallel arrays: - const ipPortPairs: IpPortPair[] = - port != null && ip.length === port.length - ? ip.map((address, i) => ({ - ip: address, - port: port[i] != null ? `${port[i]}` : null, // use the corresponding port in the parallel array - })) - : ip.map((address) => ({ - ip: address, - port: null, // drop the port, because the length of the parallel ip and port arrays is different - })); - - return ( - - {uniqWith(deepEqual, ipPortPairs).map( - (ipPortPair) => - ipPortPair.ip != null && ( - - - - ) - )} - - ); +}>(({ contextId, destinationIp, destinationPort, eventId, sourceIp, sourcePort, type }) => { + const ip = type === 'source' ? sourceIp : destinationIp; + const ipFieldName = type === 'source' ? SOURCE_IP_FIELD_NAME : DESTINATION_IP_FIELD_NAME; + const port = type === 'source' ? sourcePort : destinationPort; + const portFieldName = type === 'source' ? SOURCE_PORT_FIELD_NAME : DESTINATION_PORT_FIELD_NAME; + + if (ip == null) { + return null; // if ip is not populated as an array, ports will be ignored } -); + + // IMPORTANT: The ip and port arrays are parallel arrays; the port at + // index `i` corresponds with the ip address at index `i`. We must + // preserve the relationships between the parallel arrays: + const ipPortPairs: IpPortPair[] = + port != null && ip.length === port.length + ? ip.map((address, i) => ({ + ip: address, + port: port[i] != null ? `${port[i]}` : null, // use the corresponding port in the parallel array + })) + : ip.map((address) => ({ + ip: address, + port: null, // drop the port, because the length of the parallel ip and port arrays is different + })); + + return ( + + {uniqWith(deepEqual, ipPortPairs).map( + (ipPortPair) => + ipPortPair.ip != null && ( + + + + ) + )} + + ); +}); IpAdressesWithPorts.displayName = 'IpAdressesWithPorts'; @@ -172,7 +159,6 @@ export const SourceDestinationIp = React.memo( destinationIp, destinationPort, eventId, - isDraggable, sourceGeoContinentName, sourceGeoCountryName, sourceGeoCountryIsoCode, @@ -203,7 +189,6 @@ export const SourceDestinationIp = React.memo( destinationIp={destinationIp} destinationPort={destinationPort} eventId={eventId} - isDraggable={isDraggable} sourceIp={sourceIp} sourcePort={sourcePort} type={type} @@ -217,7 +202,7 @@ export const SourceDestinationIp = React.memo( data-test-subj="port" eventId={eventId} fieldName={`${type}.port`} - isDraggable={isDraggable} + isDraggable={true} value={port} /> @@ -234,7 +219,6 @@ export const SourceDestinationIp = React.memo( destinationGeoRegionName={destinationGeoRegionName} destinationGeoCityName={destinationGeoCityName} eventId={eventId} - isDraggable={isDraggable} sourceGeoContinentName={sourceGeoContinentName} sourceGeoCountryName={sourceGeoCountryName} sourceGeoCountryIsoCode={sourceGeoCountryIsoCode} diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_with_arrows.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_with_arrows.tsx index a010d674291ba..3d6189118ecb0 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_with_arrows.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_with_arrows.tsx @@ -32,7 +32,6 @@ export const SourceDestinationWithArrows = React.memo @@ -88,7 +85,6 @@ export const SourceDestinationWithArrows = React.memo | null; eventId: string; - isDraggable?: boolean; sourceGeoContinentName?: string[] | null; sourceGeoCountryName?: string[] | null; sourceGeoCountryIsoCode?: string[] | null; @@ -88,7 +85,6 @@ export interface SourceDestinationWithArrowsProps { destinationPackets?: string[] | null; destinationPort?: string[] | null; eventId: string; - isDraggable?: boolean; sourceBytes?: string[] | null; sourceGeoContinentName?: string[] | null; sourceGeoCountryName?: string[] | null; diff --git a/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx index 296faf208ac91..29775067478a5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx @@ -40,9 +40,8 @@ export const CertificateFingerprint = React.memo<{ certificateType: CertificateType; contextId: string; fieldName: string; - isDraggable?: boolean; value?: string | null; -}>(({ eventId, certificateType, contextId, fieldName, isDraggable, value }) => { +}>(({ eventId, certificateType, contextId, fieldName, value }) => { return ( {fieldName} diff --git a/x-pack/plugins/security_solution/public/timelines/components/duration/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/duration/index.tsx index 7500fdb122fae..421ba5941eaef 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/duration/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/duration/index.tsx @@ -26,7 +26,6 @@ export const Duration = React.memo<{ isDraggable ? ( @@ -25,7 +24,6 @@ exports[`Field Renderers #autonomousSystemRenderer it renders correctly against @@ -60,7 +58,6 @@ exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1 }, } } - isDraggable={false} render={[Function]} /> `; @@ -82,7 +79,6 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot }, } } - isDraggable={false} render={[Function]} /> `; @@ -98,7 +94,6 @@ exports[`Field Renderers #locationRenderer it renders correctly against snapshot @@ -85,7 +84,6 @@ export const autonomousSystemRenderer = ( id={`autonomous-system-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }${flowTarget}.as.organization.name`} - isDraggable={false} field={`${flowTarget}.as.organization.name`} value={as.organization.name} /> @@ -96,7 +94,6 @@ export const autonomousSystemRenderer = ( id={`autonomous-system-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }${flowTarget}.as.number`} - isDraggable={false} field={`${flowTarget}.as.number`} value={`${as.number}`} /> @@ -126,7 +123,6 @@ export const hostIdRenderer = ({ id={`host-id-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }host-id`} - isDraggable={false} field="host.id" value={host.id[0]} > @@ -158,7 +154,6 @@ export const hostNameRenderer = ( id={`host-name-renderer-default-draggable-${IpOverviewId}-${ contextID ? `${contextID}-` : '' }host-name`} - isDraggable={false} field={'host.name'} value={host.name[0]} > @@ -209,7 +204,7 @@ export const DefaultFieldRendererComponent: React.FC )} {typeof rowItem === 'string' && ( - + {render ? render(rowItem) : rowItem} )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx index 5acc0ef9aa46b..5014a198e8bd5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx @@ -59,11 +59,11 @@ describe('FieldName', () => { ); await waitFor(() => { - wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); + wrapper.find('[data-test-subj="withHoverActionsButton"]').at(0).simulate('mouseenter'); wrapper.update(); jest.runAllTimers(); wrapper.update(); - expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx index 1e081d249cc00..2e76e43227506 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx @@ -11,9 +11,11 @@ import styled from 'styled-components'; import { OnUpdateColumns } from '../timeline/events'; import { WithHoverActions } from '../../../common/components/with_hover_actions'; -import { useGetTimelineId } from '../../../common/components/drag_and_drop/use_get_timeline_id_from_dom'; +import { + DraggableWrapperHoverContent, + useGetTimelineId, +} from '../../../common/components/drag_and_drop/draggable_wrapper_hover_content'; import { ColumnHeaderOptions } from '../../../../common'; -import { HoverActions } from '../../../common/components/hover_actions'; /** * The name of a (draggable) field @@ -110,10 +112,9 @@ export const FieldName = React.memo<{ const hoverContent = useMemo( () => ( - = ({ return ( (({ contextId, eventId, fieldName, isDraggable, value }) => ( +}>(({ contextId, eventId, fieldName, value }) => ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/fingerprints/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/netflow/fingerprints/index.tsx index 328d310524070..16ea48890778e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/fingerprints/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/fingerprints/index.tsx @@ -23,7 +23,6 @@ import { JA3_HASH_FIELD_NAME, Ja3Fingerprint } from '../../ja3_fingerprint'; export const Fingerprints = React.memo<{ contextId: string; eventId: string; - isDraggable?: boolean; tlsClientCertificateFingerprintSha1?: string[] | null; tlsFingerprintsJa3Hash?: string[] | null; tlsServerCertificateFingerprintSha1?: string[] | null; @@ -31,7 +30,6 @@ export const Fingerprints = React.memo<{ ({ contextId, eventId, - isDraggable, tlsClientCertificateFingerprintSha1, tlsFingerprintsJa3Hash, tlsServerCertificateFingerprintSha1, @@ -50,7 +48,6 @@ export const Fingerprints = React.memo<{ eventId={eventId} fieldName={JA3_HASH_FIELD_NAME} contextId={contextId} - isDraggable={isDraggable} value={ja3} /> @@ -64,7 +61,6 @@ export const Fingerprints = React.memo<{ certificateType="client" contextId={contextId} fieldName={TLS_CLIENT_CERTIFICATE_FINGERPRINT_SHA1_FIELD_NAME} - isDraggable={isDraggable} value={clientCert} /> @@ -78,7 +74,6 @@ export const Fingerprints = React.memo<{ certificateType="server" contextId={contextId} fieldName={TLS_SERVER_CERTIFICATE_FINGERPRINT_SHA1_FIELD_NAME} - isDraggable={isDraggable} value={serverCert} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/netflow/index.tsx index a755aa54fca7b..05bfe56d1df42 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/index.tsx @@ -37,7 +37,6 @@ export const Netflow = React.memo( eventId, eventEnd, eventStart, - isDraggable, networkBytes, networkCommunityId, networkDirection, @@ -83,7 +82,6 @@ export const Netflow = React.memo( eventId={eventId} eventEnd={eventEnd} eventStart={eventStart} - isDraggable={isDraggable} networkBytes={networkBytes} networkCommunityId={networkCommunityId} networkDirection={networkDirection} @@ -107,7 +105,6 @@ export const Netflow = React.memo( (({ contextId, eventDuration, eventId, eventEnd, eventStart, isDraggable }) => ( +}>(({ contextId, eventDuration, eventId, eventEnd, eventStart }) => ( @@ -97,7 +94,6 @@ export const DurationEventStartEnd = React.memo<{ data-test-subj="event-end" field={EVENT_END_FIELD_NAME} id={`duration-event-start-end-default-draggable-${contextId}-${eventId}-${EVENT_END_FIELD_NAME}-${end}`} - isDraggable={isDraggable} tooltipContent={null} value={end} > diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/index.tsx index e319e803e63fe..4714b561f036b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/index.tsx @@ -48,7 +48,6 @@ export const NetflowColumns = React.memo( eventId, eventEnd, eventStart, - isDraggable, networkBytes, networkCommunityId, networkDirection, @@ -77,7 +76,6 @@ export const NetflowColumns = React.memo( @@ -90,7 +88,6 @@ export const NetflowColumns = React.memo( eventId={eventId} eventEnd={eventEnd} eventStart={eventStart} - isDraggable={isDraggable} /> @@ -107,7 +104,6 @@ export const NetflowColumns = React.memo( destinationPackets={destinationPackets} destinationPort={destinationPort} eventId={eventId} - isDraggable={isDraggable} networkBytes={networkBytes} networkCommunityId={networkCommunityId} networkDirection={networkDirection} diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/types.ts b/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/types.ts index 801df93bfcf37..532b35f4cffd0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/types.ts @@ -21,7 +21,6 @@ export interface NetflowColumnsProps { eventId: string; eventEnd?: string[] | null; eventStart?: string[] | null; - isDraggable?: boolean; networkBytes?: string[] | null; networkCommunityId?: string[] | null; networkDirection?: string[] | null; diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/user_process.tsx b/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/user_process.tsx index 72de537fee588..e6931baeb7017 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/user_process.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/netflow_columns/user_process.tsx @@ -22,10 +22,9 @@ export const USER_NAME_FIELD_NAME = 'user.name'; export const UserProcess = React.memo<{ contextId: string; eventId: string; - isDraggable?: boolean; processName?: string[] | null; userName?: string[] | null; -}>(({ contextId, eventId, isDraggable, processName, userName }) => ( +}>(({ contextId, eventId, processName, userName }) => ( @@ -57,7 +55,6 @@ export const UserProcess = React.memo<{ data-test-subj="process-name" eventId={eventId} field={PROCESS_NAME_FIELD_NAME} - isDraggable={isDraggable} value={process} iconType="console" /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/types.ts b/x-pack/plugins/security_solution/public/timelines/components/netflow/types.ts index 0798345c61da9..a28334e2d45fb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/types.ts @@ -20,7 +20,6 @@ export interface NetflowProps { eventId: string; eventEnd?: string[] | null; eventStart?: string[] | null; - isDraggable?: boolean; networkBytes?: string[] | null; networkCommunityId?: string[] | null; networkDirection?: string[] | null; diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/alerts.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/alerts.tsx index d6aa34f2528e5..b0384155c5c10 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/alerts.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/alerts.tsx @@ -26,7 +26,6 @@ const AlertsExampleComponent: React.FC = () => { {alertsRowRenderer.renderRow({ browserFields: {}, data: mockEndpointProcessExecutionMalwarePreventionAlert, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd.tsx index 2c6ce5886462b..703621bc4c666 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd.tsx @@ -23,7 +23,6 @@ const AuditdExampleComponent: React.FC = () => { {auditdRowRenderer.renderRow({ browserFields: {}, data: mockTimelineData[26].ecs, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd_file.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd_file.tsx index a525b26571dc8..265a71ef264d1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd_file.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/auditd_file.tsx @@ -23,7 +23,6 @@ const AuditdFileExampleComponent: React.FC = () => { {auditdFileRowRenderer.renderRow({ browserFields: {}, data: mockTimelineData[27].ecs, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/library.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/library.tsx index f8704b63fe47e..6198225fcb87d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/library.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/library.tsx @@ -23,7 +23,6 @@ const LibraryExampleComponent: React.FC = () => { {libraryRowRenderer.renderRow({ browserFields: {}, data: mockEndpointLibraryLoadEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/netflow.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/netflow.tsx index c5a0f09440899..cd20b28203246 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/netflow.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/netflow.tsx @@ -16,7 +16,6 @@ const NetflowExampleComponent: React.FC = () => ( {netflowRowRenderer.renderRow({ browserFields: {}, data: getMockNetflowData(), - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/registry.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/registry.tsx index 67859db1a5ea4..f00db0d94eed8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/registry.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/registry.tsx @@ -23,7 +23,6 @@ const RegistryExampleComponent: React.FC = () => { {registryRowRenderer.renderRow({ browserFields: {}, data: mockEndpointRegistryModificationEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/suricata.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/suricata.tsx index 1e6caca2effa9..f22ac0dca6f9d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/suricata.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/suricata.tsx @@ -16,7 +16,6 @@ const SuricataExampleComponent: React.FC = () => ( {suricataRowRenderer.renderRow({ browserFields: {}, data: mockTimelineData[2].ecs, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system.tsx index 7d38f8feaace6..909d5224fb351 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system.tsx @@ -23,7 +23,6 @@ const SystemExampleComponent: React.FC = () => { {systemRowRenderer.renderRow({ browserFields: {}, data: mockEndgameTerminationEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_dns.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_dns.tsx index 72c5060b27701..0f413eed811be 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_dns.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_dns.tsx @@ -19,7 +19,6 @@ const SystemDnsExampleComponent: React.FC = () => { {systemDnsRowRenderer.renderRow({ browserFields: {}, data: mockEndgameDnsRequest, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_endgame_process.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_endgame_process.tsx index 6103746b3238b..0e5fe1768c787 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_endgame_process.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_endgame_process.tsx @@ -23,7 +23,6 @@ const SystemEndgameProcessExampleComponent: React.FC = () => { {systemEndgameProcessRowRenderer.renderRow({ browserFields: {}, data: mockEndgameCreationEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_file.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_file.tsx index cb8668536f8d2..3db9a93fc37c9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_file.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_file.tsx @@ -23,7 +23,6 @@ const SystemFileExampleComponent: React.FC = () => { {systemFileRowRenderer.renderRow({ browserFields: {}, data: mockEndgameFileDeleteEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_fim.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_fim.tsx index 12ad132131d1b..08ff6a5ddc7c9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_fim.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_fim.tsx @@ -23,7 +23,6 @@ const SystemFimExampleComponent: React.FC = () => { {systemFimRowRenderer.renderRow({ browserFields: {}, data: mockEndgameFileCreateEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_security_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_security_event.tsx index 8dfb0bf998738..59b5fedbc82fa 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_security_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_security_event.tsx @@ -21,7 +21,6 @@ const SystemSecurityEventExampleComponent: React.FC = () => { {systemSecurityEventRowRenderer.renderRow({ browserFields: {}, data: mockEndgameUserLogon, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_socket.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_socket.tsx index 7fa430e812625..5175145bae9d9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_socket.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/system_socket.tsx @@ -22,7 +22,6 @@ const SystemSocketExampleComponent: React.FC = () => { {systemSocketRowRenderer.renderRow({ browserFields: {}, data: mockEndgameIpv4ConnectionAcceptEvent, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/threat_match.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/threat_match.tsx index 73d458a23ca17..9d7e5d48315e3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/threat_match.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/threat_match.tsx @@ -16,7 +16,6 @@ const ThreatMatchExampleComponent: React.FC = () => ( {threatMatchRowRenderer.renderRow({ browserFields: {}, data: mockTimelineData[31].ecs, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/zeek.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/zeek.tsx index 83d9e0122e971..b84942ea8b2a8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/zeek.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/examples/zeek.tsx @@ -16,7 +16,6 @@ const ZeekExampleComponent: React.FC = () => ( {zeekRowRenderer.renderRow({ browserFields: {}, data: mockTimelineData[13].ecs, - isDraggable: false, timelineId: ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx index cf1f4a26c709d..19abd6841e7e8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx @@ -80,7 +80,6 @@ export const StatefulRowRenderer = ({ {rowRenderer.renderRow({ browserFields, data: event.ecs, - isDraggable: true, timelineId, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/empty_column_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/empty_column_renderer.test.tsx.snap index 722c7a7aebb00..92816d499b029 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/empty_column_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/empty_column_renderer.test.tsx.snap @@ -19,7 +19,6 @@ exports[`empty_column_renderer renders correctly against snapshot 1`] = ` }, } } - isDraggable={true} key="empty-column-renderer-draggable-wrapper-test-source.ip-1-source.ip" render={[Function]} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx index edec2d0d823fa..417cf0ceee184 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx @@ -42,7 +42,6 @@ export const AgentStatuses = React.memo( @@ -61,7 +60,6 @@ export const AgentStatuses = React.memo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/args.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/args.tsx index fbb2c2edf8ae3..bdb650585bdb0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/args.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/args.tsx @@ -15,10 +15,9 @@ interface Props { contextId: string; eventId: string; processTitle: string | null | undefined; - isDraggable?: boolean; } -export const ArgsComponent = ({ args, contextId, eventId, processTitle, isDraggable }: Props) => { +export const ArgsComponent = ({ args, contextId, eventId, processTitle }: Props) => { if (isNillEmptyOrNotFinite(args) && isNillEmptyOrNotFinite(processTitle)) { return null; } @@ -32,7 +31,6 @@ export const ArgsComponent = ({ args, contextId, eventId, processTitle, isDragga contextId={`${contextId}-args-${i}-${arg}`} eventId={eventId} field="process.args" - isDraggable={isDraggable} value={arg} /> @@ -44,7 +42,6 @@ export const ArgsComponent = ({ args, contextId, eventId, processTitle, isDragga contextId={contextId} eventId={eventId} field="process.title" - isDraggable={isDraggable} value={processTitle} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap index 684764e39848f..63b65d3cf36be 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap @@ -98,7 +98,6 @@ exports[`GenericRowRenderer #createGenericAuditRowRenderer renders correctly aga }, } } - isDraggable={true} text="connected using" timelineId="test" /> @@ -223,7 +222,6 @@ exports[`GenericRowRenderer #createGenericFileRowRenderer renders correctly agai } } fileIcon="document" - isDraggable={true} text="opened file using" timelineId="test" /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_details.tsx index fb14d44995c95..737d0b74bfbf9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_details.tsx @@ -36,7 +36,6 @@ interface Props { workingDirectory: string | null | undefined; args: string[] | null | undefined; session: string | null | undefined; - isDraggable?: boolean; } export const AuditdGenericLine = React.memo( @@ -56,7 +55,6 @@ export const AuditdGenericLine = React.memo( result, session, text, - isDraggable, }) => ( ( secondary={secondary} workingDirectory={workingDirectory} session={session} - isDraggable={isDraggable} /> {processExecutable != null && ( @@ -84,16 +81,9 @@ export const AuditdGenericLine = React.memo( processPid={processPid} processName={processName} processExecutable={processExecutable} - isDraggable={isDraggable} /> - + {result != null && ( {i18n.WITH_RESULT} @@ -104,7 +94,6 @@ export const AuditdGenericLine = React.memo( contextId={contextId} eventId={id} field="auditd.result" - isDraggable={isDraggable} queryValue={result} value={result} /> @@ -118,14 +107,13 @@ AuditdGenericLine.displayName = 'AuditdGenericLine'; interface GenericDetailsProps { browserFields: BrowserFields; data: Ecs; - isDraggable?: boolean; contextId: string; text: string; timelineId: string; } export const AuditdGenericDetails = React.memo( - ({ data, contextId, isDraggable, text, timelineId }) => { + ({ data, contextId, text, timelineId }) => { const id = data._id; const session: string | null | undefined = get('auditd.session[0]', data); const hostName: string | null | undefined = get('host.name[0]', data); @@ -158,10 +146,9 @@ export const AuditdGenericDetails = React.memo( primary={primary} result={result} secondary={secondary} - isDraggable={isDraggable} /> - + ); } else { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_file_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_file_details.tsx index 89fbbf751b0ee..efab1a433c0bb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_file_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_file_details.tsx @@ -38,7 +38,6 @@ interface Props { workingDirectory: string | null | undefined; args: string[] | null | undefined; session: string | null | undefined; - isDraggable?: boolean; } export const AuditdGenericFileLine = React.memo( @@ -60,7 +59,6 @@ export const AuditdGenericFileLine = React.memo( session, text, fileIcon, - isDraggable, }) => ( ( secondary={secondary} workingDirectory={workingDirectory} session={session} - isDraggable={isDraggable} /> {(filePath != null || processExecutable != null) && ( @@ -84,7 +81,6 @@ export const AuditdGenericFileLine = React.memo( contextId={contextId} eventId={id} field="file.path" - isDraggable={isDraggable} value={filePath} iconType={fileIcon} /> @@ -100,19 +96,12 @@ export const AuditdGenericFileLine = React.memo( endgamePid={undefined} endgameProcessName={undefined} eventId={id} - isDraggable={isDraggable} processPid={processPid} processName={processName} processExecutable={processExecutable} /> - + {result != null && ( {i18n.WITH_RESULT} @@ -123,7 +112,6 @@ export const AuditdGenericFileLine = React.memo( contextId={contextId} eventId={id} field="auditd.result" - isDraggable={isDraggable} queryValue={result} value={result} /> @@ -136,16 +124,15 @@ AuditdGenericFileLine.displayName = 'AuditdGenericFileLine'; interface GenericDetailsProps { browserFields: BrowserFields; - contextId: string; data: Ecs; + contextId: string; text: string; fileIcon: IconType; timelineId: string; - isDraggable?: boolean; } export const AuditdGenericFileDetails = React.memo( - ({ data, contextId, text, fileIcon = 'document', timelineId, isDraggable }) => { + ({ data, contextId, text, fileIcon = 'document', timelineId }) => { const id = data._id; const session: string | null | undefined = get('auditd.session[0]', data); const hostName: string | null | undefined = get('host.name[0]', data); @@ -182,10 +169,9 @@ export const AuditdGenericFileDetails = React.memo( secondary={secondary} fileIcon={fileIcon} result={result} - isDraggable={isDraggable} /> - + ); } else { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx index 1f44feb3b394f..74a5ff472b581 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx @@ -55,7 +55,6 @@ describe('GenericRowRenderer', () => { const children = connectedToRenderer.renderRow({ browserFields, data: auditd, - isDraggable: true, timelineId: 'test', }); @@ -85,7 +84,6 @@ describe('GenericRowRenderer', () => { const children = connectedToRenderer.renderRow({ browserFields: mockBrowserFields, data: auditd, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -119,7 +117,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields, data: auditdFile, - isDraggable: true, timelineId: 'test', }); @@ -149,7 +146,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields: mockBrowserFields, data: auditdFile, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.tsx index d0522e97157ab..765bfd3d21351 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.tsx @@ -36,12 +36,11 @@ export const createGenericAuditRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( ( + renderRow: ({ browserFields, data, timelineId }) => ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/primary_secondary_user_info.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/primary_secondary_user_info.tsx index 5857dc1e30182..8fd8cfd5af9da 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/primary_secondary_user_info.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/primary_secondary_user_info.tsx @@ -21,77 +21,69 @@ interface Props { eventId: string; primary: string | null | undefined; secondary: string | null | undefined; - isDraggable?: boolean; } -export const PrimarySecondary = React.memo( - ({ contextId, eventId, primary, secondary, isDraggable }) => { - if (nilOrUnSet(primary) && nilOrUnSet(secondary)) { - return null; - } else if (!nilOrUnSet(primary) && nilOrUnSet(secondary)) { - return ( - - ); - } else if (nilOrUnSet(primary) && !nilOrUnSet(secondary)) { - return ( - - ); - } else if (primary === secondary) { - return ( - - ); - } else { - return ( - - - - - - {i18n.AS} - - - - - - ); - } +export const PrimarySecondary = React.memo(({ contextId, eventId, primary, secondary }) => { + if (nilOrUnSet(primary) && nilOrUnSet(secondary)) { + return null; + } else if (!nilOrUnSet(primary) && nilOrUnSet(secondary)) { + return ( + + ); + } else if (nilOrUnSet(primary) && !nilOrUnSet(secondary)) { + return ( + + ); + } else if (primary === secondary) { + return ( + + ); + } else { + return ( + + + + + + {i18n.AS} + + + + + + ); } -); +}); PrimarySecondary.displayName = 'PrimarySecondary'; @@ -101,11 +93,10 @@ interface PrimarySecondaryUserInfoProps { userName: string | null | undefined; primary: string | null | undefined; secondary: string | null | undefined; - isDraggable?: boolean; } export const PrimarySecondaryUserInfo = React.memo( - ({ contextId, eventId, userName, primary, secondary, isDraggable }) => { + ({ contextId, eventId, userName, primary, secondary }) => { if (nilOrUnSet(userName) && nilOrUnSet(primary) && nilOrUnSet(secondary)) { return null; } else if ( @@ -120,7 +111,6 @@ export const PrimarySecondaryUserInfo = React.memo @@ -131,7 +121,6 @@ export const PrimarySecondaryUserInfo = React.memo @@ -141,7 +130,6 @@ export const PrimarySecondaryUserInfo = React.memo diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/session_user_host_working_dir.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/session_user_host_working_dir.tsx index f90407b882fdf..a7252064d9774 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/session_user_host_working_dir.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/session_user_host_working_dir.tsx @@ -23,21 +23,10 @@ interface Props { secondary: string | null | undefined; workingDirectory: string | null | undefined; session: string | null | undefined; - isDraggable?: boolean; } export const SessionUserHostWorkingDir = React.memo( - ({ - eventId, - contextId, - hostName, - userName, - primary, - secondary, - workingDirectory, - session, - isDraggable, - }) => ( + ({ eventId, contextId, hostName, userName, primary, secondary, workingDirectory, session }) => ( <> {i18n.SESSION} @@ -49,7 +38,6 @@ export const SessionUserHostWorkingDir = React.memo( field="auditd.session" value={session} iconType="number" - isDraggable={isDraggable} /> @@ -59,7 +47,6 @@ export const SessionUserHostWorkingDir = React.memo( userName={userName} primary={primary} secondary={secondary} - isDraggable={isDraggable} /> {hostName != null && ( @@ -72,7 +59,6 @@ export const SessionUserHostWorkingDir = React.memo( eventId={eventId} workingDirectory={workingDirectory} hostName={hostName} - isDraggable={isDraggable} /> ) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx index 8859c601ad56d..e2418334dfc80 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx @@ -26,7 +26,6 @@ export const Bytes = React.memo<{ isDraggable ? ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/indicator_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/indicator_details.tsx index f2e4555147c50..11846632f740e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/indicator_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/indicator_details.tsx @@ -26,7 +26,6 @@ interface IndicatorDetailsProps { indicatorProvider: string | undefined; indicatorReference: string | undefined; indicatorType: string | undefined; - isDraggable?: boolean; } export const IndicatorDetails: React.FC = ({ @@ -36,7 +35,6 @@ export const IndicatorDetails: React.FC = ({ indicatorProvider, indicatorReference, indicatorType, - isDraggable, }) => ( = ({ data-test-subj="threat-match-indicator-details-indicator-type" eventId={eventId} field={INDICATOR_MATCHED_TYPE} - isDraggable={isDraggable} value={indicatorType} /> @@ -74,7 +71,6 @@ export const IndicatorDetails: React.FC = ({ data-test-subj="threat-match-indicator-details-indicator-dataset" eventId={eventId} field={INDICATOR_DATASET} - isDraggable={isDraggable} value={indicatorDataset} /> @@ -96,7 +92,6 @@ export const IndicatorDetails: React.FC = ({ data-test-subj="threat-match-indicator-details-indicator-provider" eventId={eventId} field={INDICATOR_PROVIDER} - isDraggable={isDraggable} value={indicatorProvider} /> @@ -113,7 +108,6 @@ export const IndicatorDetails: React.FC = ({ data-test-subj="threat-match-indicator-details-indicator-reference" eventId={eventId} fieldName={INDICATOR_REFERENCE} - isDraggable={isDraggable} value={indicatorReference} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/match_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/match_details.tsx index 31c5065cde59a..2195421301d31 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/match_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/match_details.tsx @@ -16,7 +16,6 @@ import { HorizontalSpacer } from './helpers'; interface MatchDetailsProps { contextId: string; eventId: string; - isDraggable?: boolean; sourceField: string; sourceValue: string; } @@ -24,7 +23,6 @@ interface MatchDetailsProps { export const MatchDetails: React.FC = ({ contextId, eventId, - isDraggable, sourceField, sourceValue, }) => ( @@ -42,7 +40,6 @@ export const MatchDetails: React.FC = ({ data-test-subj="threat-match-details-source-field" eventId={eventId} field={INDICATOR_MATCHED_FIELD} - isDraggable={isDraggable} value={sourceField} /> @@ -60,7 +57,6 @@ export const MatchDetails: React.FC = ({ data-test-subj="threat-match-details-source-value" eventId={eventId} field={sourceField} - isDraggable={isDraggable} value={sourceValue} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row.tsx index 94ed19e218d74..ba5b0127df526 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row.tsx @@ -28,7 +28,6 @@ export interface ThreatMatchRowProps { indicatorProvider: string | undefined; indicatorReference: string | undefined; indicatorType: string | undefined; - isDraggable?: boolean; sourceField: string; sourceValue: string; } @@ -37,12 +36,10 @@ export const ThreatMatchRow = ({ contextId, data, eventId, - isDraggable, }: { contextId: string; data: Fields; eventId: string; - isDraggable?: boolean; }) => { const props = { contextId, @@ -51,7 +48,6 @@ export const ThreatMatchRow = ({ indicatorReference: get(data, EVENT_REFERENCE)[0] as string | undefined, indicatorProvider: get(data, PROVIDER)[0] as string | undefined, indicatorType: get(data, MATCHED_TYPE)[0] as string | undefined, - isDraggable, sourceField: get(data, MATCHED_FIELD)[0] as string, sourceValue: get(data, MATCHED_ATOMIC)[0] as string, }; @@ -66,7 +62,6 @@ export const ThreatMatchRowView = ({ indicatorProvider, indicatorReference, indicatorType, - isDraggable, sourceField, sourceValue, }: ThreatMatchRowProps) => { @@ -81,7 +76,6 @@ export const ThreatMatchRowView = ({ @@ -94,7 +88,6 @@ export const ThreatMatchRowView = ({ indicatorProvider={indicatorProvider} indicatorReference={indicatorReference} indicatorType={indicatorType} - isDraggable={isDraggable} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx index 78972442f5018..6687179e5b887 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx @@ -56,7 +56,6 @@ describe('threatMatchRowRenderer', () => { const children = threatMatchRowRenderer.renderRow({ browserFields: {}, data: threatMatchData, - isDraggable: true, timelineId: 'test', }); const wrapper = shallow({children}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx index d2c1f09d903c1..f6feb6dd1b126 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx @@ -20,7 +20,7 @@ const SpacedContainer = styled.div` margin: ${({ theme }) => theme.eui.paddingSizes.s} 0; `; -export const ThreatMatchRows: RowRenderer['renderRow'] = ({ data, isDraggable, timelineId }) => { +export const ThreatMatchRows: RowRenderer['renderRow'] = ({ data, timelineId }) => { const indicators = get(data, 'threat.indicator') as Fields[]; const eventId = get(data, ID_FIELD_NAME); @@ -31,12 +31,7 @@ export const ThreatMatchRows: RowRenderer['renderRow'] = ({ data, isDraggable, t const contextId = `threat-match-row-${timelineId}-${eventId}-${index}`; return ( - + {index < indicators.length - 1 && } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details.tsx index 90d68eceb7fb3..3a53db2196d8c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details.tsx @@ -20,50 +20,46 @@ interface Props { browserFields: BrowserFields; contextId: string; data: Ecs; - isDraggable?: boolean; timelineId: string; } -export const DnsRequestEventDetails = React.memo( - ({ data, contextId, isDraggable, timelineId }) => { - const dnsQuestionName: string | null | undefined = get('dns.question.name[0]', data); - const dnsQuestionType: string | null | undefined = get('dns.question.type[0]', data); - const dnsResolvedIp: string | null | undefined = get('dns.resolved_ip[0]', data); - const dnsResponseCode: string | null | undefined = get('dns.response_code[0]', data); - const eventCode: string | null | undefined = get('event.code[0]', data); - const hostName: string | null | undefined = get('host.name[0]', data); - const id = data._id; - const processExecutable: string | null | undefined = get('process.executable[0]', data); - const processName: string | null | undefined = get('process.name[0]', data); - const processPid: number | null | undefined = get('process.pid[0]', data); - const userDomain: string | null | undefined = get('user.domain[0]', data); - const userName: string | null | undefined = get('user.name[0]', data); - const winlogEventId: string | null | undefined = get('winlog.event_id[0]', data); +export const DnsRequestEventDetails = React.memo(({ data, contextId, timelineId }) => { + const dnsQuestionName: string | null | undefined = get('dns.question.name[0]', data); + const dnsQuestionType: string | null | undefined = get('dns.question.type[0]', data); + const dnsResolvedIp: string | null | undefined = get('dns.resolved_ip[0]', data); + const dnsResponseCode: string | null | undefined = get('dns.response_code[0]', data); + const eventCode: string | null | undefined = get('event.code[0]', data); + const hostName: string | null | undefined = get('host.name[0]', data); + const id = data._id; + const processExecutable: string | null | undefined = get('process.executable[0]', data); + const processName: string | null | undefined = get('process.name[0]', data); + const processPid: number | null | undefined = get('process.pid[0]', data); + const userDomain: string | null | undefined = get('user.domain[0]', data); + const userName: string | null | undefined = get('user.name[0]', data); + const winlogEventId: string | null | undefined = get('winlog.event_id[0]', data); - return ( -
- - - -
- ); - } -); + return ( +
+ + + +
+ ); +}); DnsRequestEventDetails.displayName = 'DnsRequestEventDetails'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx index ff85336bd47f8..549abcf6a6d35 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx @@ -24,7 +24,6 @@ interface Props { eventCode: string | null | undefined; hostName: string | null | undefined; id: string; - isDraggable?: boolean; processExecutable: string | null | undefined; processName: string | null | undefined; processPid: number | null | undefined; @@ -43,7 +42,6 @@ export const DnsRequestEventDetailsLine = React.memo( eventCode, hostName, id, - isDraggable, processExecutable, processName, processPid, @@ -58,7 +56,6 @@ export const DnsRequestEventDetailsLine = React.memo( contextId={contextId} eventId={id} hostName={hostName} - isDraggable={isDraggable} userDomain={userDomain} userName={userName} workingDirectory={undefined} @@ -74,7 +71,6 @@ export const DnsRequestEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="dns.question.name" - isDraggable={isDraggable} value={dnsQuestionName} />
@@ -91,7 +87,6 @@ export const DnsRequestEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="dns.question.type" - isDraggable={isDraggable} value={dnsQuestionType} />
@@ -108,7 +103,6 @@ export const DnsRequestEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="dns.resolved_ip" - isDraggable={isDraggable} value={dnsResolvedIp} /> @@ -128,7 +122,6 @@ export const DnsRequestEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="dns.response_code" - isDraggable={isDraggable} value={dnsResponseCode} /> @@ -148,7 +141,6 @@ export const DnsRequestEventDetailsLine = React.memo( endgamePid={undefined} endgameProcessName={undefined} eventId={id} - isDraggable={isDraggable} processPid={processPid} processName={processName} processExecutable={processExecutable} @@ -163,7 +155,6 @@ export const DnsRequestEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="event.code" - isDraggable={isDraggable} value={eventCode} /> @@ -174,7 +165,6 @@ export const DnsRequestEventDetailsLine = React.memo( eventId={id} iconType="logoWindows" field="winlog.event_id" - isDraggable={isDraggable} value={winlogEventId} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx index db568726f1b20..8e2335a2f149b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx @@ -60,7 +60,6 @@ export const emptyColumnRenderer: ColumnRenderer = { kqlQuery: '', and: [], }} - isDraggable={isDraggable} key={`empty-column-renderer-draggable-wrapper-${timelineId}-${columnName}-${eventId}-${field.id}`} render={(dataProvider, _, snapshot) => snapshot.isDragging ? ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx index 8f39cf933570f..515db45e9fcd4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx @@ -20,75 +20,65 @@ interface Props { browserFields: BrowserFields; contextId: string; data: Ecs; - isDraggable?: boolean; timelineId: string; } -export const EndgameSecurityEventDetails = React.memo( - ({ data, contextId, isDraggable, timelineId }) => { - const endgameLogonType: number | null | undefined = get('endgame.logon_type[0]', data); - const endgameSubjectDomainName: string | null | undefined = get( - 'endgame.subject_domain_name[0]', - data - ); - const endgameSubjectLogonId: string | null | undefined = get( - 'endgame.subject_logon_id[0]', - data - ); - const endgameSubjectUserName: string | null | undefined = get( - 'endgame.subject_user_name[0]', - data - ); - const endgameTargetLogonId: string | null | undefined = get('endgame.target_logon_id[0]', data); - const endgameTargetDomainName: string | null | undefined = get( - 'endgame.target_domain_name[0]', - data - ); - const endgameTargetUserName: string | null | undefined = get( - 'endgame.target_user_name[0]', - data - ); - const eventAction: string | null | undefined = get('event.action[0]', data); - const eventCode: string | null | undefined = get('event.code[0]', data); - const eventOutcome: string | null | undefined = get('event.outcome[0]', data); - const hostName: string | null | undefined = get('host.name[0]', data); - const id = data._id; - const processExecutable: string | null | undefined = get('process.executable[0]', data); - const processName: string | null | undefined = get('process.name[0]', data); - const processPid: number | null | undefined = get('process.pid[0]', data); - const userDomain: string | null | undefined = get('user.domain[0]', data); - const userName: string | null | undefined = get('user.name[0]', data); - const winlogEventId: string | null | undefined = get('winlog.event_id[0]', data); +export const EndgameSecurityEventDetails = React.memo(({ data, contextId, timelineId }) => { + const endgameLogonType: number | null | undefined = get('endgame.logon_type[0]', data); + const endgameSubjectDomainName: string | null | undefined = get( + 'endgame.subject_domain_name[0]', + data + ); + const endgameSubjectLogonId: string | null | undefined = get('endgame.subject_logon_id[0]', data); + const endgameSubjectUserName: string | null | undefined = get( + 'endgame.subject_user_name[0]', + data + ); + const endgameTargetLogonId: string | null | undefined = get('endgame.target_logon_id[0]', data); + const endgameTargetDomainName: string | null | undefined = get( + 'endgame.target_domain_name[0]', + data + ); + const endgameTargetUserName: string | null | undefined = get('endgame.target_user_name[0]', data); + const eventAction: string | null | undefined = get('event.action[0]', data); + const eventCode: string | null | undefined = get('event.code[0]', data); + const eventOutcome: string | null | undefined = get('event.outcome[0]', data); + const hostName: string | null | undefined = get('host.name[0]', data); + const id = data._id; + const processExecutable: string | null | undefined = get('process.executable[0]', data); + const processName: string | null | undefined = get('process.name[0]', data); + const processPid: number | null | undefined = get('process.pid[0]', data); + const userDomain: string | null | undefined = get('user.domain[0]', data); + const userName: string | null | undefined = get('user.name[0]', data); + const winlogEventId: string | null | undefined = get('winlog.event_id[0]', data); - return ( -
- - - -
- ); - } -); + return ( +
+ + + +
+ ); +}); EndgameSecurityEventDetails.displayName = 'EndgameSecurityEventDetails'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx index 7e5a6dd08765b..aba6f7346271d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx @@ -38,7 +38,6 @@ interface Props { eventOutcome: string | null | undefined; hostName: string | null | undefined; id: string; - isDraggable?: boolean; processExecutable: string | null | undefined; processName: string | null | undefined; processPid: number | null | undefined; @@ -62,7 +61,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( eventOutcome, hostName, id, - isDraggable, processExecutable, processName, processPid, @@ -97,7 +95,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( eventId={id} hostName={hostName} hostNameSeparator={hostNameSeparator} - isDraggable={isDraggable} userDomain={domain} userDomainField={userDomainField} userName={user} @@ -119,7 +116,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="endgame.logon_type" - isDraggable={isDraggable} queryValue={String(endgameLogonType)} value={`${endgameLogonType} - ${getHumanReadableLogonType(endgameLogonType)}`} /> @@ -140,7 +136,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="endgame.target_logon_id" - isDraggable={isDraggable} value={endgameTargetLogonId} /> @@ -160,7 +155,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( endgamePid={undefined} endgameProcessName={undefined} eventId={id} - isDraggable={isDraggable} processPid={processPid} processName={processName} processExecutable={processExecutable} @@ -182,7 +176,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="endgame.subject_user_name" - isDraggable={isDraggable} iconType="user" value={endgameSubjectUserName} /> @@ -204,7 +197,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="endgame.subject_domain_name" - isDraggable={isDraggable} value={endgameSubjectDomainName} /> @@ -224,7 +216,6 @@ export const EndgameSecurityEventDetailsLine = React.memo( contextId={contextId} eventId={id} field="endgame.subject_logon_id" - isDraggable={isDraggable} value={endgameSubjectLogonId} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/exit_code_draggable.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/exit_code_draggable.tsx index 1f1862daa4e55..7ac9fe290893f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/exit_code_draggable.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/exit_code_draggable.tsx @@ -15,13 +15,12 @@ interface Props { contextId: string; endgameExitCode: string | null | undefined; eventId: string; - isDraggable?: boolean; processExitCode: number | null | undefined; text: string | null | undefined; } export const ExitCodeDraggable = React.memo( - ({ contextId, endgameExitCode, eventId, isDraggable, processExitCode, text }) => { + ({ contextId, endgameExitCode, eventId, processExitCode, text }) => { if (isNillEmptyOrNotFinite(processExitCode) && isNillEmptyOrNotFinite(endgameExitCode)) { return null; } @@ -40,7 +39,6 @@ export const ExitCodeDraggable = React.memo( contextId={contextId} eventId={eventId} field="process.exit_code" - isDraggable={isDraggable} value={`${processExitCode}`} /> @@ -52,7 +50,6 @@ export const ExitCodeDraggable = React.memo( contextId={contextId} eventId={eventId} field="endgame.exit_code" - isDraggable={isDraggable} value={endgameExitCode} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_draggable.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_draggable.tsx index 7ff5a0f73ab30..703b38e627e55 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_draggable.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_draggable.tsx @@ -20,7 +20,6 @@ interface Props { fileName: string | null | undefined; filePath: string | null | undefined; fileExtOriginalPath: string | null | undefined; - isDraggable?: boolean; } export const FileDraggable = React.memo( @@ -32,7 +31,6 @@ export const FileDraggable = React.memo( fileExtOriginalPath, fileName, filePath, - isDraggable, }) => { if ( isNillEmptyOrNotFinite(fileName) && @@ -54,7 +52,6 @@ export const FileDraggable = React.memo( contextId={contextId} eventId={eventId} field="file.name" - isDraggable={isDraggable} value={fileName} iconType="document" /> @@ -65,7 +62,6 @@ export const FileDraggable = React.memo( contextId={contextId} eventId={eventId} field="endgame.file_name" - isDraggable={isDraggable} value={endgameFileName} iconType="document" /> @@ -84,7 +80,6 @@ export const FileDraggable = React.memo( contextId={contextId} eventId={eventId} field="file.path" - isDraggable={isDraggable} value={filePath} iconType="document" /> @@ -95,7 +90,6 @@ export const FileDraggable = React.memo( contextId={contextId} eventId={eventId} field="endgame.file_path" - isDraggable={isDraggable} value={endgameFilePath} iconType="document" /> @@ -112,7 +106,6 @@ export const FileDraggable = React.memo( contextId={contextId} eventId={eventId} field="file.Ext.original.path" - isDraggable={isDraggable} value={fileExtOriginalPath} iconType="document" /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_hash.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_hash.tsx index 13b024e0a5359..9e624ba17c921 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_hash.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/file_hash.tsx @@ -21,10 +21,9 @@ interface Props { contextId: string; eventId: string; fileHashSha256: string | null | undefined; - isDraggable?: boolean; } -export const FileHash = React.memo(({ contextId, eventId, fileHashSha256, isDraggable }) => { +export const FileHash = React.memo(({ contextId, eventId, fileHashSha256 }) => { if (isNillEmptyOrNotFinite(fileHashSha256)) { return null; } @@ -36,7 +35,6 @@ export const FileHash = React.memo(({ contextId, eventId, fileHashSha256, contextId={contextId} eventId={eventId} field="file.hash.sha256" - isDraggable={isDraggable} iconType="number" value={fileHashSha256} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index 06ed901110962..aa6c7beb9139e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -86,7 +86,6 @@ const FormattedFieldValueComponent: React.FC<{ @@ -215,7 +214,6 @@ const FormattedFieldValueComponent: React.FC<{ = ({ @@ -96,7 +95,6 @@ export const RenderRuleName: React.FC = ({ @@ -152,7 +150,6 @@ export const renderEventModule = ({ @@ -221,7 +218,6 @@ export const renderUrl = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx index 6b76aba92678d..104550f138f16 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx @@ -54,7 +54,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: nonSuricata, - isDraggable: true, timelineId: 'test', }); @@ -67,7 +66,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: nonSuricata, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -83,7 +81,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: suricata, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -102,7 +99,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: suricata, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -121,7 +117,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: zeek, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -140,7 +135,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: system, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -159,7 +153,6 @@ describe('get_column_renderer', () => { const row = rowRenderer?.renderRow({ browserFields: mockBrowserFields, data: auditd, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx index 060b539950d83..abd4731ec4b66 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx @@ -93,7 +93,6 @@ const HostNameComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_working_dir.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_working_dir.tsx index fef9a5d5c0201..de307d1af7f93 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_working_dir.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_working_dir.tsx @@ -17,11 +17,10 @@ interface Props { eventId: string; hostName: string | null | undefined; workingDirectory: string | null | undefined; - isDraggable?: boolean; } export const HostWorkingDir = React.memo( - ({ contextId, eventId, hostName, workingDirectory, isDraggable }) => ( + ({ contextId, eventId, hostName, workingDirectory }) => ( <> ( eventId={eventId} field="host.name" value={hostName} - isDraggable={isDraggable} /> {workingDirectory != null && ( @@ -44,7 +42,6 @@ export const HostWorkingDir = React.memo( field="process.working_directory" value={workingDirectory} iconType="folderOpen" - isDraggable={isDraggable} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow.tsx index d6ea939c966ac..18f56d8b03066 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow.tsx @@ -60,58 +60,52 @@ import { interface NetflowRendererProps { data: Ecs; timelineId: string; - isDraggable?: boolean; } -export const NetflowRenderer = React.memo( - ({ data, timelineId, isDraggable }) => ( - - ) -); +export const NetflowRenderer = React.memo(({ data, timelineId }) => ( + +)); NetflowRenderer.displayName = 'NetflowRenderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap index a9ecbe8428aee..d7bdacbcc61ef 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap @@ -67,7 +67,6 @@ exports[`netflowRowRenderer renders correctly against snapshot 1`] = ` "2018-11-12T19:03:25.836Z", ] } - isDraggable={true} networkBytes={ Array [ 100, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx index 01e05bbc365e9..fc97624dbfc96 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx @@ -38,7 +38,6 @@ describe('netflowRowRenderer', () => { const children = netflowRowRenderer.renderRow({ browserFields, data: getMockNetflowData(), - isDraggable: true, timelineId: 'test', }); @@ -108,7 +107,6 @@ describe('netflowRowRenderer', () => { const children = netflowRowRenderer.renderRow({ browserFields: mockBrowserFields, data: getMockNetflowData(), - isDraggable: true, timelineId: 'test', }); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx index 272912b855af0..35406dce6ff72 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx @@ -90,7 +90,7 @@ export const netflowRowRenderer: RowRenderer = { isInstance: (ecs) => eventCategoryMatches(get(EVENT_CATEGORY_FIELD, ecs)) || eventActionMatches(get(EVENT_ACTION_FIELD, ecs)), - renderRow: ({ data, isDraggable, timelineId }) => ( + renderRow: ({ data, timelineId }) => (
( contextId, endgameParentProcessName, eventId, - isDraggable, processParentName, processParentPid, processPpid, @@ -58,7 +56,6 @@ export const ParentProcessDraggable = React.memo( contextId={contextId} eventId={eventId} field="process.parent.name" - isDraggable={isDraggable} value={processParentName} /> @@ -70,7 +67,6 @@ export const ParentProcessDraggable = React.memo( contextId={contextId} eventId={eventId} field="endgame.parent_process_name" - isDraggable={isDraggable} value={endgameParentProcessName} /> @@ -82,7 +78,6 @@ export const ParentProcessDraggable = React.memo( contextId={contextId} eventId={eventId} field="process.parent.pid" - isDraggable={isDraggable} queryValue={String(processParentPid)} value={`(${String(processParentPid)})`} /> @@ -95,7 +90,6 @@ export const ParentProcessDraggable = React.memo( contextId={contextId} eventId={eventId} field="process.ppid" - isDraggable={isDraggable} queryValue={String(processPpid)} value={`(${String(processPpid)})`} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_row_renderer.test.tsx index f28c72253a4e7..666fb254aaa2c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_row_renderer.test.tsx @@ -24,7 +24,6 @@ describe('plain_row_renderer', () => { const children = plainRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockDatum, - isDraggable: true, timelineId: 'test', }); const wrapper = shallow({children}); @@ -39,7 +38,6 @@ describe('plain_row_renderer', () => { const children = plainRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockDatum, - isDraggable: true, timelineId: 'test', }); const wrapper = mount({children}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_draggable.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_draggable.tsx index db7e3ae6f06c9..705eff8873204 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_draggable.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_draggable.tsx @@ -21,7 +21,6 @@ interface Props { processExecutable: string | undefined | null; processPid: number | undefined | null; processName: string | undefined | null; - isDraggable?: boolean; } export const ProcessDraggable = React.memo( @@ -33,7 +32,6 @@ export const ProcessDraggable = React.memo( processExecutable, processName, processPid, - isDraggable, }) => { if ( isNillEmptyOrNotFinite(processName) && @@ -55,7 +53,6 @@ export const ProcessDraggable = React.memo( field="process.name" value={processName} iconType="console" - isDraggable={isDraggable} /> ) : !isNillEmptyOrNotFinite(processExecutable) ? ( @@ -66,7 +63,6 @@ export const ProcessDraggable = React.memo( field="process.executable" value={processExecutable} iconType="console" - isDraggable={isDraggable} /> ) : !isNillEmptyOrNotFinite(endgameProcessName) ? ( @@ -77,7 +73,6 @@ export const ProcessDraggable = React.memo( field="endgame.process_name" value={endgameProcessName} iconType="console" - isDraggable={isDraggable} /> ) : null} @@ -90,7 +85,6 @@ export const ProcessDraggable = React.memo( field="process.pid" queryValue={String(processPid)} value={`(${String(processPid)})`} - isDraggable={isDraggable} /> ) : !isNillEmptyOrNotFinite(endgamePid) ? ( @@ -101,7 +95,6 @@ export const ProcessDraggable = React.memo( field="endgame.pid" queryValue={String(endgamePid)} value={`(${String(endgamePid)})`} - isDraggable={isDraggable} /> ) : null} @@ -121,7 +114,6 @@ export const ProcessDraggableWithNonExistentProcess = React.memo( processExecutable, processName, processPid, - isDraggable, }) => { if ( endgamePid == null && @@ -141,7 +133,6 @@ export const ProcessDraggableWithNonExistentProcess = React.memo( processExecutable={processExecutable} processName={processName} processPid={processPid} - isDraggable={isDraggable} /> ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_hash.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_hash.tsx index dd4f588a14bb7..32432afbf205c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_hash.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/process_hash.tsx @@ -20,31 +20,27 @@ const HashFlexGroup = styled(EuiFlexGroup)` interface Props { contextId: string; eventId: string; - isDraggable?: boolean; processHashSha256: string | null | undefined; } -export const ProcessHash = React.memo( - ({ contextId, eventId, isDraggable, processHashSha256 }) => { - if (isNillEmptyOrNotFinite(processHashSha256)) { - return null; - } - - return ( - - - - - - ); +export const ProcessHash = React.memo(({ contextId, eventId, processHashSha256 }) => { + if (isNillEmptyOrNotFinite(processHashSha256)) { + return null; } -); + + return ( + + + + + + ); +}); ProcessHash.displayName = 'ProcessHash'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details.tsx index da31f75e2fa10..0bfb03168019a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details.tsx @@ -18,11 +18,10 @@ interface Props { browserFields: BrowserFields; contextId: string; data: Ecs; - isDraggable?: boolean; text: string; } -const RegistryEventDetailsComponent: React.FC = ({ contextId, data, isDraggable, text }) => { +const RegistryEventDetailsComponent: React.FC = ({ contextId, data, text }) => { const hostName: string | null | undefined = get('host.name[0]', data); const id = data._id; const processName: string | null | undefined = get('process.name[0]', data); @@ -42,7 +41,6 @@ const RegistryEventDetailsComponent: React.FC = ({ contextId, data, isDra contextId={contextId} hostName={hostName} id={id} - isDraggable={isDraggable} processName={processName} processPid={processPid} registryKey={registryKey} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details_line.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details_line.tsx index 8d9f52da88fdd..b85ae25ed2509 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details_line.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/registry/registry_event_details_line.tsx @@ -19,7 +19,6 @@ interface Props { contextId: string; hostName: string | null | undefined; id: string; - isDraggable?: boolean; processName: string | null | undefined; processPid: number | null | undefined; registryKey: string | null | undefined; @@ -33,7 +32,6 @@ const RegistryEventDetailsLineComponent: React.FC = ({ contextId, hostName, id, - isDraggable, processName, processPid, registryKey, @@ -73,7 +71,6 @@ const RegistryEventDetailsLineComponent: React.FC = ({ contextId={contextId} eventId={id} hostName={hostName} - isDraggable={isDraggable} userDomain={userDomain} userName={userName} workingDirectory={undefined} @@ -89,7 +86,6 @@ const RegistryEventDetailsLineComponent: React.FC = ({ contextId={contextId} eventId={id} field="registry.key" - isDraggable={isDraggable} tooltipContent={registryKeyTooltipContent} value={registryKey} /> @@ -107,7 +103,6 @@ const RegistryEventDetailsLineComponent: React.FC = ({ contextId={contextId} eventId={id} field="registry.path" - isDraggable={isDraggable} tooltipContent={registryPathTooltipContent} value={registryPath} /> @@ -125,7 +120,6 @@ const RegistryEventDetailsLineComponent: React.FC = ({ endgamePid={undefined} endgameProcessName={undefined} eventId={id} - isDraggable={isDraggable} processPid={processPid} processName={processName} processExecutable={undefined} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx index 09248b832490a..126bfae996ef7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx @@ -42,7 +42,6 @@ const RuleStatusComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap index eeb8786b2cfa3..2934d35dc184d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap @@ -525,7 +525,6 @@ exports[`suricata_row_renderer renders correctly against snapshot 1`] = ` }, } } - isDraggable={true} timelineId="test" /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.tsx index f096cd906f619..82eb11d455543 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.tsx @@ -26,9 +26,8 @@ Details.displayName = 'Details'; export const SuricataDetails = React.memo<{ browserFields: BrowserFields; data: Ecs; - isDraggable?: boolean; timelineId: string; -}>(({ data, isDraggable, timelineId }) => { +}>(({ data, timelineId }) => { const signature: string | null | undefined = get('suricata.eve.alert.signature[0]', data); const signatureId: number | null | undefined = get('suricata.eve.alert.signature_id[0]', data); @@ -38,13 +37,12 @@ export const SuricataDetails = React.memo<{ - +
); } else { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx index 661fc562cc34c..998233b2278c9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx @@ -45,7 +45,6 @@ describe('suricata_row_renderer', () => { const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: nonSuricata, - isDraggable: true, timelineId: 'test', }); @@ -65,7 +64,6 @@ describe('suricata_row_renderer', () => { const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -83,7 +81,6 @@ describe('suricata_row_renderer', () => { const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx index 0faa6a4fbba74..aa482926bf007 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx @@ -21,14 +21,9 @@ export const suricataRowRenderer: RowRenderer = { const module: string | null | undefined = get('event.module[0]', ecs); return module != null && module.toLowerCase() === 'suricata'; }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( - + ), }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx index 2a5b57d77498f..a4e16c66f4fef 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx @@ -57,69 +57,65 @@ export const Tokens = React.memo<{ tokens: string[] }>(({ tokens }) => ( Tokens.displayName = 'Tokens'; -export const DraggableSignatureId = React.memo<{ - id: string; - isDraggable?: boolean; - signatureId: number; -}>(({ id, isDraggable, signatureId }) => { - const dataProviderProp = useMemo( - () => ({ - and: [], - enabled: true, - id: escapeDataProviderId(`suricata-draggable-signature-id-${id}-sig-${signatureId}`), - name: String(signatureId), - excluded: false, - kqlQuery: '', - queryMatch: { - field: SURICATA_SIGNATURE_ID_FIELD_NAME, - value: signatureId, - operator: IS_OPERATOR as QueryOperator, - }, - }), - [id, signatureId] - ); - - const render = useCallback( - (dataProvider, _, snapshot) => - snapshot.isDragging ? ( - - - - ) : ( - - - {signatureId} - - - ), - [signatureId] - ); - - return ( - - - - ); -}); +export const DraggableSignatureId = React.memo<{ id: string; signatureId: number }>( + ({ id, signatureId }) => { + const dataProviderProp = useMemo( + () => ({ + and: [], + enabled: true, + id: escapeDataProviderId(`suricata-draggable-signature-id-${id}-sig-${signatureId}`), + name: String(signatureId), + excluded: false, + kqlQuery: '', + queryMatch: { + field: SURICATA_SIGNATURE_ID_FIELD_NAME, + value: signatureId, + operator: IS_OPERATOR as QueryOperator, + }, + }), + [id, signatureId] + ); + + const render = useCallback( + (dataProvider, _, snapshot) => + snapshot.isDragging ? ( + + + + ) : ( + + + {signatureId} + + + ), + [signatureId] + ); + + return ( + + + + ); + } +); DraggableSignatureId.displayName = 'DraggableSignatureId'; export const SuricataSignature = React.memo<{ contextId: string; id: string; - isDraggable?: boolean; signature: string; signatureId: number; -}>(({ contextId, id, isDraggable, signature, signatureId }) => { +}>(({ contextId, id, signature, signatureId }) => { const tokens = getBeginningTokens(signature); return ( @@ -128,7 +124,6 @@ export const SuricataSignature = React.memo<{ data-test-subj="draggable-signature-link" field={SURICATA_SIGNATURE_FIELD_NAME} id={`suricata-signature-default-draggable-${contextId}-${id}-${SURICATA_SIGNATURE_FIELD_NAME}`} - isDraggable={isDraggable} value={signature} >
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap index 15443058f434e..d2c405a46acf8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap @@ -56,7 +56,6 @@ exports[`GenericRowRenderer #createGenericFileRowRenderer renders correctly agai }, } } - isDraggable={true} text="some text" timelineId="test" /> @@ -120,7 +119,6 @@ exports[`GenericRowRenderer #createGenericSystemRowRenderer renders correctly ag }, } } - isDraggable={true} text="some text" timelineId="test" /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/auth_ssh.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/auth_ssh.tsx index 7de03a2ae2356..4dcb90637a817 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/auth_ssh.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/auth_ssh.tsx @@ -13,40 +13,35 @@ import { TokensFlexItem } from '../helpers'; interface Props { contextId: string; eventId: string; - isDraggable?: boolean; sshSignature: string | null | undefined; sshMethod: string | null | undefined; } -export const AuthSsh = React.memo( - ({ contextId, eventId, isDraggable, sshSignature, sshMethod }) => ( - <> - {sshSignature != null && ( - - - - )} - {sshMethod != null && ( - - - - )} - - ) -); +export const AuthSsh = React.memo(({ contextId, eventId, sshSignature, sshMethod }) => ( + <> + {sshSignature != null && ( + + + + )} + {sshMethod != null && ( + + + + )} + +)); AuthSsh.displayName = 'AuthSsh'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.tsx index 2cf42ecc9c670..19d5fd2a0dab1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.tsx @@ -27,7 +27,6 @@ interface Props { contextId: string; hostName: string | null | undefined; id: string; - isDraggable?: boolean; message: string | null | undefined; outcome: string | null | undefined; packageName: string | null | undefined; @@ -49,7 +48,6 @@ export const SystemGenericLine = React.memo( contextId, hostName, id, - isDraggable, message, outcome, packageName, @@ -70,10 +68,9 @@ export const SystemGenericLine = React.memo( @@ -85,7 +82,6 @@ export const SystemGenericLine = React.memo( endgamePid={undefined} endgameProcessName={undefined} eventId={id} - isDraggable={isDraggable} processPid={processPid} processName={processName} processExecutable={processExecutable} @@ -101,7 +97,6 @@ export const SystemGenericLine = React.memo( contextId={contextId} eventId={id} field="event.outcome" - isDraggable={isDraggable} queryValue={outcome} value={outcome} /> @@ -109,14 +104,12 @@ export const SystemGenericLine = React.memo( ( - ({ contextId, data, isDraggable, text, timelineId }) => { + ({ data, contextId, text, timelineId }) => { const id = data._id; const message: string | null = data.message != null ? data.message[0] : null; const hostName: string | null | undefined = get('host.name[0]', data); @@ -173,7 +165,6 @@ export const SystemGenericDetails = React.memo( contextId={contextId} hostName={hostName} id={id} - isDraggable={isDraggable} message={message} outcome={outcome} packageName={packageName} @@ -190,7 +181,7 @@ export const SystemGenericDetails = React.memo( workingDirectory={workingDirectory} /> - + ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx index ae31dbff7f063..6df583656ff2d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx @@ -45,7 +45,6 @@ interface Props { filePath: string | null | undefined; hostName: string | null | undefined; id: string; - isDraggable?: boolean; message: string | null | undefined; outcome: string | null | undefined; packageName: string | null | undefined; @@ -88,7 +87,6 @@ export const SystemGenericFileLine = React.memo( filePath, hostName, id, - isDraggable, message, outcome, packageName, @@ -118,7 +116,6 @@ export const SystemGenericFileLine = React.memo( ( fileExtOriginalPath={fileExtOriginalPath} fileName={fileName} filePath={filePath} - isDraggable={isDraggable} /> )} {showVia(eventAction) && ( @@ -151,7 +147,6 @@ export const SystemGenericFileLine = React.memo( endgamePid={endgamePid} endgameProcessName={endgameProcessName} eventId={id} - isDraggable={isDraggable} processPid={processPid} processName={processName} processExecutable={processExecutable} @@ -162,7 +157,6 @@ export const SystemGenericFileLine = React.memo( contextId={contextId} endgameExitCode={endgameExitCode} eventId={id} - isDraggable={isDraggable} processExitCode={processExitCode} text={i18n.WITH_EXIT_CODE} /> @@ -171,7 +165,6 @@ export const SystemGenericFileLine = React.memo( contextId={contextId} endgameParentProcessName={endgameParentProcessName} eventId={id} - isDraggable={isDraggable} processParentName={processParentName} processParentPid={processParentPid} processPpid={processPpid} @@ -188,7 +181,6 @@ export const SystemGenericFileLine = React.memo( contextId={contextId} eventId={id} field="event.outcome" - isDraggable={isDraggable} queryValue={outcome} value={outcome} /> @@ -196,34 +188,22 @@ export const SystemGenericFileLine = React.memo( {!skipRedundantFileDetails && ( - + )} {!skipRedundantProcessDetails && ( - + )} {message != null && showMessage && ( @@ -246,9 +226,8 @@ SystemGenericFileLine.displayName = 'SystemGenericFileLine'; interface GenericDetailsProps { browserFields: BrowserFields; - contextId: string; data: Ecs; - isDraggable?: boolean; + contextId: string; showMessage?: boolean; skipRedundantFileDetails?: boolean; skipRedundantProcessDetails?: boolean; @@ -258,9 +237,8 @@ interface GenericDetailsProps { export const SystemGenericFileDetails = React.memo( ({ - contextId, data, - isDraggable, + contextId, showMessage = true, skipRedundantFileDetails = false, skipRedundantProcessDetails = false, @@ -345,10 +323,9 @@ export const SystemGenericFileDetails = React.memo( sshSignature={sshSignature} sshMethod={sshMethod} outcome={outcome} - isDraggable={isDraggable} /> - + ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx index 516d279765904..6f5b225f0690b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx @@ -118,7 +118,6 @@ describe('GenericRowRenderer', () => { const children = connectedToRenderer.renderRow({ browserFields, data: system, - isDraggable: true, timelineId: 'test', }); @@ -148,7 +147,6 @@ describe('GenericRowRenderer', () => { const children = connectedToRenderer.renderRow({ browserFields: mockBrowserFields, data: system, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -182,7 +180,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields, data: systemFile, - isDraggable: true, timelineId: 'test', }); @@ -211,7 +208,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields: mockBrowserFields, data: systemFile, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( @@ -243,7 +239,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileCreationMalwarePreventionAlert, - isDraggable: true, timelineId: 'test', })} @@ -271,7 +266,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileCreationMalwareDetectionAlert, - isDraggable: true, timelineId: 'test', })} @@ -301,7 +295,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFilesEncryptedRansomwarePreventionAlert, - isDraggable: true, timelineId: 'test', })} @@ -331,7 +324,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFilesEncryptedRansomwareDetectionAlert, - isDraggable: true, timelineId: 'test', })} @@ -361,7 +353,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileModificationMalwarePreventionAlert, - isDraggable: true, timelineId: 'test', })} @@ -391,7 +382,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileModificationMalwareDetectionAlert, - isDraggable: true, timelineId: 'test', })} @@ -419,7 +409,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileRenameMalwarePreventionAlert, - isDraggable: true, timelineId: 'test', })} @@ -447,7 +436,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileRenameMalwareDetectionAlert, - isDraggable: true, timelineId: 'test', })} @@ -477,7 +465,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointProcessExecutionMalwarePreventionAlert, - isDraggable: true, timelineId: 'test', })} @@ -507,7 +494,6 @@ describe('GenericRowRenderer', () => { endpointAlertsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointProcessExecutionMalwareDetectionAlert, - isDraggable: true, timelineId: 'test', })} @@ -535,7 +521,6 @@ describe('GenericRowRenderer', () => { endpointProcessStartRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointProcessExecEvent, - isDraggable: true, timelineId: 'test', })} @@ -561,7 +546,6 @@ describe('GenericRowRenderer', () => { endpointProcessStartRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointProcessForkEvent, - isDraggable: true, timelineId: 'test', })} @@ -587,7 +571,6 @@ describe('GenericRowRenderer', () => { endpointProcessStartRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointProcessStartEvent, - isDraggable: true, timelineId: 'test', })} @@ -616,7 +599,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - isDraggable: true, timelineId: 'test', })} @@ -642,7 +624,6 @@ describe('GenericRowRenderer', () => { endpointProcessEndRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointProcessEndEvent, - isDraggable: true, timelineId: 'test', })} @@ -671,7 +652,6 @@ describe('GenericRowRenderer', () => { endgameProcessTerminationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameTerminationEvent, - isDraggable: true, timelineId: 'test', })} @@ -700,7 +680,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - isDraggable: true, timelineId: 'test', })} @@ -731,7 +710,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - isDraggable: true, timelineId: 'test', })} @@ -762,7 +740,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - isDraggable: true, timelineId: 'test', })} @@ -788,7 +765,6 @@ describe('GenericRowRenderer', () => { endpointFileCreationRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileCreationEvent, - isDraggable: true, timelineId: 'test', })} @@ -817,7 +793,6 @@ describe('GenericRowRenderer', () => { endgameFileCreateEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileCreateEvent, - isDraggable: true, timelineId: 'test', })} @@ -843,7 +818,6 @@ describe('GenericRowRenderer', () => { endpointFileDeletionRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileDeletionEvent, - isDraggable: true, timelineId: 'test', })} @@ -869,7 +843,6 @@ describe('GenericRowRenderer', () => { endpointFileModificationRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileModificationEvent, - isDraggable: true, timelineId: 'test', })} @@ -895,7 +868,6 @@ describe('GenericRowRenderer', () => { endpointFileOverwriteRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileOverwriteEvent, - isDraggable: true, timelineId: 'test', })} @@ -921,7 +893,6 @@ describe('GenericRowRenderer', () => { endpointFileRenameRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointFileRenameEvent, - isDraggable: true, timelineId: 'test', })} @@ -950,7 +921,6 @@ describe('GenericRowRenderer', () => { endgameFileDeleteEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileDeleteEvent, - isDraggable: true, timelineId: 'test', })} @@ -979,7 +949,6 @@ describe('GenericRowRenderer', () => { fileCreatedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: fimFileCreatedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1006,7 +975,6 @@ describe('GenericRowRenderer', () => { fileDeletedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: fimFileDeletedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1035,7 +1003,6 @@ describe('GenericRowRenderer', () => { endgameFileCreateEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileCreateEvent, - isDraggable: true, timelineId: 'test', })} @@ -1066,7 +1033,6 @@ describe('GenericRowRenderer', () => { endgameFileCreateEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileCreateEvent, - isDraggable: true, timelineId: 'test', })} @@ -1097,7 +1063,6 @@ describe('GenericRowRenderer', () => { fileCreatedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: fimFileCreatedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1125,7 +1090,6 @@ describe('GenericRowRenderer', () => { endpointConnectionAcceptedRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointNetworkConnectionAcceptedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1154,7 +1118,6 @@ describe('GenericRowRenderer', () => { endpointRegistryModificationRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointRegistryModificationEvent, - isDraggable: true, timelineId: 'test', })} @@ -1182,7 +1145,6 @@ describe('GenericRowRenderer', () => { endpointLibraryLoadRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointLibraryLoadEvent, - isDraggable: true, timelineId: 'test', })} @@ -1209,7 +1171,6 @@ describe('GenericRowRenderer', () => { endpointHttpRequestEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointNetworkHttpRequestEvent, - isDraggable: true, timelineId: 'test', })} @@ -1238,7 +1199,6 @@ describe('GenericRowRenderer', () => { endgameIpv4ConnectionAcceptEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv4ConnectionAcceptEvent, - isDraggable: true, timelineId: 'test', })} @@ -1267,7 +1227,6 @@ describe('GenericRowRenderer', () => { endgameIpv6ConnectionAcceptEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv6ConnectionAcceptEvent, - isDraggable: true, timelineId: 'test', })} @@ -1293,7 +1252,6 @@ describe('GenericRowRenderer', () => { endpointDisconnectReceivedRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointDisconnectReceivedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1322,7 +1280,6 @@ describe('GenericRowRenderer', () => { endgameIpv4DisconnectReceivedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv4DisconnectReceivedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1351,7 +1308,6 @@ describe('GenericRowRenderer', () => { endgameIpv6DisconnectReceivedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv6DisconnectReceivedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1380,7 +1336,6 @@ describe('GenericRowRenderer', () => { socketOpenedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: socketOpenedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1409,7 +1364,6 @@ describe('GenericRowRenderer', () => { socketClosedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: socketClosedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1438,7 +1392,6 @@ describe('GenericRowRenderer', () => { endgameIpv4ConnectionAcceptEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv4ConnectionAcceptEvent, - isDraggable: true, timelineId: 'test', })} @@ -1460,7 +1413,6 @@ describe('GenericRowRenderer', () => { securityLogOnRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointSecurityLogOnSuccessEvent, - isDraggable: true, timelineId: 'test', })} @@ -1482,7 +1434,6 @@ describe('GenericRowRenderer', () => { securityLogOnRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointSecurityLogOnFailureEvent, - isDraggable: true, timelineId: 'test', })} @@ -1507,7 +1458,6 @@ describe('GenericRowRenderer', () => { userLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: userLogonEvent, - isDraggable: true, timelineId: 'test', })} @@ -1532,7 +1482,6 @@ describe('GenericRowRenderer', () => { adminLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: adminLogonEvent, - isDraggable: true, timelineId: 'test', })} @@ -1557,7 +1506,6 @@ describe('GenericRowRenderer', () => { explicitUserLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: explicitUserLogonEvent, - isDraggable: true, timelineId: 'test', })} @@ -1579,7 +1527,6 @@ describe('GenericRowRenderer', () => { securityLogOffRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointSecurityLogOffEvent, - isDraggable: true, timelineId: 'test', })} @@ -1604,7 +1551,6 @@ describe('GenericRowRenderer', () => { userLogoffEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: userLogoffEvent, - isDraggable: true, timelineId: 'test', })} @@ -1629,7 +1575,6 @@ describe('GenericRowRenderer', () => { userLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: userLogonEvent, - isDraggable: true, timelineId: 'test', })} @@ -1649,7 +1594,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointNetworkLookupRequestedEvent, - isDraggable: true, timelineId: 'test', })} @@ -1669,7 +1613,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockEndpointNetworkLookupResultEvent, - isDraggable: true, timelineId: 'test', })} @@ -1693,7 +1636,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: requestEvent, - isDraggable: true, timelineId: 'test', })} @@ -1717,7 +1659,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: dnsEvent, - isDraggable: true, timelineId: 'test', })} @@ -1747,7 +1688,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: requestEvent, - isDraggable: true, timelineId: 'test', })} @@ -1775,7 +1715,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: requestEvent, - isDraggable: true, timelineId: 'test', })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.tsx index b1027bf12b7d2..c6845d7d672d2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.tsx @@ -40,13 +40,12 @@ export const createGenericSystemRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( @@ -72,13 +71,12 @@ export const createEndgameProcessRowRenderer = ({ action?.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( ( + renderRow: ({ browserFields, data, timelineId }) => ( ( + renderRow: ({ browserFields, data, timelineId }) => ( ( + renderRow: ({ browserFields, data, timelineId }) => ( ( + renderRow: ({ browserFields, data, timelineId }) => ( @@ -245,13 +239,12 @@ export const createSocketRowRenderer = ({ const action: string | null | undefined = get('event.action[0]', ecs); return action != null && action.toLowerCase() === actionName; }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( @@ -275,13 +268,12 @@ export const createSecurityEventRowRenderer = ({ action?.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( @@ -295,13 +287,12 @@ export const createDnsRowRenderer = (): RowRenderer => ({ const dnsQuestionName: string | null | undefined = get('dns.question.name[0]', ecs); return !isNillEmptyOrNotFinite(dnsQuestionType) && !isNillEmptyOrNotFinite(dnsQuestionName); }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( @@ -324,13 +315,12 @@ export const createEndpointRegistryRowRenderer = ({ dataset?.toLowerCase() === 'endpoint.events.registry' && action?.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/package.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/package.tsx index 296c099da22a4..7952154da1293 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/package.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/package.tsx @@ -13,14 +13,13 @@ import { TokensFlexItem } from '../helpers'; interface Props { contextId: string; eventId: string; - isDraggable?: boolean; packageName: string | null | undefined; packageSummary: string | null | undefined; packageVersion: string | null | undefined; } export const Package = React.memo( - ({ contextId, eventId, isDraggable, packageName, packageSummary, packageVersion }) => { + ({ contextId, eventId, packageName, packageSummary, packageVersion }) => { if (packageName != null || packageSummary != null || packageVersion != null) { return ( <> @@ -29,7 +28,6 @@ export const Package = React.memo( contextId={contextId} eventId={eventId} field="system.audit.package.name" - isDraggable={isDraggable} value={packageName} iconType="document" /> @@ -39,7 +37,6 @@ export const Package = React.memo( contextId={contextId} eventId={eventId} field="system.audit.package.version" - isDraggable={isDraggable} value={packageVersion} iconType="document" /> @@ -49,7 +46,6 @@ export const Package = React.memo( contextId={contextId} eventId={eventId} field="system.audit.package.summary" - isDraggable={isDraggable} value={packageSummary} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.test.tsx index b61e00f1752b8..7cff1166cd0de 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.test.tsx @@ -242,7 +242,6 @@ describe('UserHostWorkingDir', () => { { ); - expect(wrapper.find('[data-test-subj="render-content-user.domain"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="draggable-content-user.domain"]').exists()).toBe(true); }); test('it renders a draggable with an overridden field name when userDomain is provided, and userDomainField is also specified as a prop', () => { @@ -262,7 +261,6 @@ describe('UserHostWorkingDir', () => { { ); - expect(wrapper.find('[data-test-subj="render-content-overridden.field.name"]').exists()).toBe( - true - ); + expect( + wrapper.find('[data-test-subj="draggable-content-overridden.field.name"]').exists() + ).toBe(true); }); test('it renders a draggable `user.name` field (by default) when userName is provided, and userNameField is NOT specified as a prop', () => { @@ -285,7 +283,6 @@ describe('UserHostWorkingDir', () => { { ); - expect(wrapper.find('[data-test-subj="render-content-user.name"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="draggable-content-user.name"]').exists()).toBe(true); }); test('it renders a draggable with an overridden field name when userName is provided, and userNameField is also specified as a prop', () => { @@ -305,7 +302,6 @@ describe('UserHostWorkingDir', () => { { ); - expect(wrapper.find('[data-test-subj="render-content-overridden.field.name"]').exists()).toBe( - true - ); + expect( + wrapper.find('[data-test-subj="draggable-content-overridden.field.name"]').exists() + ).toBe(true); }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.tsx index 9e789cbd7aba2..0ab3624970c28 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_host_working_dir.tsx @@ -14,13 +14,12 @@ import { HostWorkingDir } from './host_working_dir'; interface Props { contextId: string; eventId: string; - isDraggable?: boolean; - hostName: string | null | undefined; - hostNameSeparator?: string; userDomain: string | null | undefined; userDomainField?: string; userName: string | null | undefined; userNameField?: string; + hostName: string | null | undefined; + hostNameSeparator?: string; workingDirectory: string | null | undefined; } @@ -30,7 +29,6 @@ export const UserHostWorkingDir = React.memo( eventId, hostName, hostNameSeparator = '@', - isDraggable, userDomain, userDomainField = 'user.domain', userName, @@ -44,7 +42,6 @@ export const UserHostWorkingDir = React.memo( contextId={contextId} eventId={eventId} field={userNameField} - isDraggable={isDraggable} value={userName} iconType="user" /> @@ -64,7 +61,6 @@ export const UserHostWorkingDir = React.memo( contextId={contextId} eventId={eventId} field={userDomainField} - isDraggable={isDraggable} value={userDomain} /> @@ -80,7 +76,6 @@ export const UserHostWorkingDir = React.memo( contextId={contextId} eventId={eventId} hostName={hostName} - isDraggable={isDraggable} workingDirectory={workingDirectory} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap index 94cbe43e93d2d..6c59df606cd36 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap @@ -525,7 +525,6 @@ exports[`zeek_row_renderer renders correctly against snapshot 1`] = ` }, } } - isDraggable={true} timelineId="test" /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.tsx index a4dbde1a5626d..7b44862040f2d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.tsx @@ -24,16 +24,15 @@ Details.displayName = 'Details'; interface ZeekDetailsProps { browserFields: BrowserFields; data: Ecs; - isDraggable?: boolean; timelineId: string; } -export const ZeekDetails = React.memo(({ data, isDraggable, timelineId }) => +export const ZeekDetails = React.memo(({ data, timelineId }) => data.zeek != null ? (
- + - +
) : null ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx index 12f2fd08163ba..6b154d4d32707 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx @@ -44,7 +44,6 @@ describe('zeek_row_renderer', () => { const children = zeekRowRenderer.renderRow({ browserFields: mockBrowserFields, data: nonZeek, - isDraggable: true, timelineId: 'test', }); @@ -64,7 +63,6 @@ describe('zeek_row_renderer', () => { const children = zeekRowRenderer.renderRow({ browserFields: mockBrowserFields, data: zeek, - isDraggable: true, timelineId: 'test', }); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx index 0a265fa7522b1..2b6311b8cae83 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx @@ -21,14 +21,9 @@ export const zeekRowRenderer: RowRenderer = { const module: string | null | undefined = get('event.module[0]', ecs); return module != null && module.toLowerCase() === 'zeek'; }, - renderRow: ({ browserFields, data, isDraggable, timelineId }) => ( + renderRow: ({ browserFields, data, timelineId }) => ( - + ), }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx index 412fd9d04fe7c..e4d5a6a86682d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx @@ -67,10 +67,9 @@ export const sha1StringRenderer: StringRenderer = (value: string) => export const DraggableZeekElement = React.memo<{ id: string; field: string; - isDraggable?: boolean; value: string | null | undefined; stringRenderer?: StringRenderer; -}>(({ id, field, isDraggable, value, stringRenderer = defaultStringRenderer }) => { +}>(({ id, field, value, stringRenderer = defaultStringRenderer }) => { const dataProviderProp = useMemo( () => ({ and: [], @@ -106,7 +105,7 @@ export const DraggableZeekElement = React.memo<{ return value != null ? ( - + ) : null; }); @@ -204,11 +203,10 @@ export const constructDroppedValue = (dropped: boolean | null | undefined): stri interface ZeekSignatureProps { data: Ecs; - isDraggable?: boolean; timelineId: string; } -export const ZeekSignature = React.memo(({ data, isDraggable, timelineId }) => { +export const ZeekSignature = React.memo(({ data, timelineId }) => { const id = `zeek-signature-draggable-zeek-element-${timelineId}-${data._id}`; const sessionId: string | null | undefined = get('zeek.session_id[0]', data); const dataSet: string | null | undefined = get('event.dataset[0]', data); @@ -236,92 +234,42 @@ export const ZeekSignature = React.memo(({ data, isDraggable return ( <> - + - - - - - - - - + + + + + + + + diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index ef47b474350c7..a68617536c6af 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { DataProvider } from '../../components/timeline/data_providers/data_provider'; import { EqlOptionsSelected } from '../../../../common/search_strategy/timeline'; import type { TimelineEventsType, @@ -25,6 +26,8 @@ export type TimelineModel = TGridModelForTimeline & { prevActiveTab: TimelineTabs; /** Timeline saved object owner */ createdBy?: string; + /** The sources of the event data shown in the timeline */ + dataProviders: DataProvider[]; /** A summary of the events and notes in this timeline */ description: string; eqlOptions: EqlOptionsSelected; diff --git a/x-pack/plugins/timelines/common/types/timeline/rows/index.ts b/x-pack/plugins/timelines/common/types/timeline/rows/index.ts index 089f886e1c221..b598d13273798 100644 --- a/x-pack/plugins/timelines/common/types/timeline/rows/index.ts +++ b/x-pack/plugins/timelines/common/types/timeline/rows/index.ts @@ -15,12 +15,10 @@ export interface RowRenderer { renderRow: ({ browserFields, data, - isDraggable, timelineId, }: { browserFields: BrowserFields; data: Ecs; - isDraggable: boolean; timelineId: string; }) => React.ReactNode; } diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx index dd5ef27c32a89..eb9c95f0998c6 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx @@ -5,20 +5,18 @@ * 2.0. */ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback } from 'react'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { DraggableId } from 'react-beautiful-dnd'; -import { useDispatch } from 'react-redux'; - -import { isEmpty } from 'lodash'; -import { DataProvider, stopPropagationAndPreventDefault, TimelineId } from '../../../../common'; import { TooltipWithKeyboardShortcut } from '../../tooltip_with_keyboard_shortcut'; import { getAdditionalScreenReaderOnlyContext } from '../utils'; import { useAddToTimeline } from '../../../hooks/use_add_to_timeline'; import { HoverActionComponentProps } from './types'; -import { tGridActions } from '../../..'; -import { useAppToasts } from '../../../hooks/use_app_toasts'; -import * as i18n from './translations'; + +const ADD_TO_TIMELINE = i18n.translate('xpack.timelines.hoverActions.addToTimeline', { + defaultMessage: 'Add to timeline investigation', +}); export const ADD_TO_TIMELINE_KEYBOARD_SHORTCUT = 'a'; @@ -27,7 +25,7 @@ export interface UseGetHandleStartDragToTimelineArgs { draggableId: DraggableId | undefined; } -const useGetHandleStartDragToTimeline = ({ +export const useGetHandleStartDragToTimeline = ({ field, draggableId, }: UseGetHandleStartDragToTimelineArgs): (() => void) => { @@ -43,59 +41,8 @@ const useGetHandleStartDragToTimeline = ({ return handleStartDragToTimeline; }; -export interface AddToTimelineButtonProps extends HoverActionComponentProps { - draggableId?: DraggableId; - dataProvider?: DataProvider[] | DataProvider; -} - -const AddToTimelineButton: React.FC = React.memo( - ({ - closePopOver, - dataProvider, - defaultFocusedButtonRef, - draggableId, - field, - keyboardEvent, - ownFocus, - showTooltip = false, - value, - }) => { - const dispatch = useDispatch(); - const { addSuccess } = useAppToasts(); - const startDragToTimeline = useGetHandleStartDragToTimeline({ draggableId, field }); - const handleStartDragToTimeline = useCallback(() => { - if (draggableId != null) { - startDragToTimeline(); - } else if (!isEmpty(dataProvider)) { - const addDataProvider = Array.isArray(dataProvider) ? dataProvider : [dataProvider]; - addDataProvider.forEach((provider) => { - if (provider) { - dispatch( - tGridActions.addProviderToTimeline({ - id: TimelineId.active, - dataProvider: provider, - }) - ); - addSuccess(i18n.ADDED_TO_TIMELINE_MESSAGE(provider.name)); - } - }); - } - - if (closePopOver != null) { - closePopOver(); - } - }, [addSuccess, closePopOver, dataProvider, dispatch, draggableId, startDragToTimeline]); - - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === ADD_TO_TIMELINE_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - handleStartDragToTimeline(); - } - }, [handleStartDragToTimeline, keyboardEvent, ownFocus]); - +export const AddToTimelineButton: React.FC = React.memo( + ({ field, onClick, ownFocus, showTooltip = false, value }) => { return showTooltip ? ( = React.memo( field, value, })} - content={i18n.ADD_TO_TIMELINE} + content={ADD_TO_TIMELINE} shortcut={ADD_TO_TIMELINE_KEYBOARD_SHORTCUT} showShortcut={ownFocus} /> } > ) : ( ); } ); AddToTimelineButton.displayName = 'AddToTimelineButton'; - -// eslint-disable-next-line import/no-default-export -export { AddToTimelineButton as default }; diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx index d59383b8553ea..52d8fb439526f 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx @@ -5,11 +5,9 @@ * 2.0. */ -import React, { useCallback, useEffect } from 'react'; +import React from 'react'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - -import { stopPropagationAndPreventDefault } from '../../../../common'; import { TooltipWithKeyboardShortcut } from '../../tooltip_with_keyboard_shortcut'; import { getAdditionalScreenReaderOnlyContext } from '../utils'; import { defaultColumnHeaderType } from '../../t_grid/body/column_headers/default_headers'; @@ -32,48 +30,28 @@ export const NESTED_COLUMN = (field: string) => export const COLUMN_TOGGLE_KEYBOARD_SHORTCUT = 'i'; +export interface ColumnToggleFnArgs { + toggleColumn: (column: ColumnHeaderOptions) => void; + field: string; +} + +export const columnToggleFn = ({ toggleColumn, field }: ColumnToggleFnArgs): void => { + return toggleColumn({ + columnHeaderType: defaultColumnHeaderType, + id: field, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }); +}; + export interface ColumnToggleProps extends HoverActionComponentProps { isDisabled: boolean; isObjectArray: boolean; - toggleColumn: (column: ColumnHeaderOptions) => void; } -const ColumnToggleButton: React.FC = React.memo( - ({ - closePopOver, - defaultFocusedButtonRef, - field, - isDisabled, - isObjectArray, - keyboardEvent, - ownFocus, - showTooltip = false, - toggleColumn, - value, - }) => { +export const ColumnToggleButton: React.FC = React.memo( + ({ field, isDisabled, isObjectArray, onClick, ownFocus, showTooltip = false, value }) => { const label = isObjectArray ? NESTED_COLUMN(field) : COLUMN_TOGGLE(field); - const handleToggleColumn = useCallback(() => { - toggleColumn({ - columnHeaderType: defaultColumnHeaderType, - id: field, - initialWidth: DEFAULT_COLUMN_MIN_WIDTH, - }); - if (closePopOver != null) { - closePopOver(); - } - }, [closePopOver, field, toggleColumn]); - - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === COLUMN_TOGGLE_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - handleToggleColumn(); - } - }, [handleToggleColumn, keyboardEvent, ownFocus]); - return showTooltip ? ( = React.memo( > = React.memo( id={field} iconSize="s" iconType="listAdd" - onClick={handleToggleColumn} + onClick={onClick} /> ) : ( = React.memo( id={field} iconSize="s" iconType="listAdd" - onClick={handleToggleColumn} + onClick={onClick} /> ); } ); ColumnToggleButton.displayName = 'ColumnToggleButton'; - -// eslint-disable-next-line import/no-default-export -export { ColumnToggleButton as default }; diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/copy.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/copy.tsx index 1b567dee50683..33cc71e12c46f 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/copy.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/copy.tsx @@ -5,12 +5,10 @@ * 2.0. */ -import React, { useEffect, useRef } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { stopPropagationAndPreventDefault } from '../../../../common'; import { WithCopyToClipboard } from '../../clipboard/with_copy_to_clipboard'; import { HoverActionComponentProps } from './types'; -import { COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME } from '../../clipboard'; export const FIELD = i18n.translate('xpack.timelines.hoverActions.fieldLabel', { defaultMessage: 'Field', @@ -18,45 +16,22 @@ export const FIELD = i18n.translate('xpack.timelines.hoverActions.fieldLabel', { export const COPY_TO_CLIPBOARD_KEYBOARD_SHORTCUT = 'c'; -export interface CopyProps extends HoverActionComponentProps { +export type CopyProps = Omit & { isHoverAction?: boolean; -} +}; -const CopyButton: React.FC = React.memo( - ({ closePopOver, field, isHoverAction, keyboardEvent, ownFocus, value }) => { - const panelRef = useRef(null); - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === COPY_TO_CLIPBOARD_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - const copyToClipboardButton = panelRef.current?.querySelector( - `.${COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME}` - ); - if (copyToClipboardButton != null) { - copyToClipboardButton.click(); - } - if (closePopOver != null) { - closePopOver(); - } - } - }, [closePopOver, keyboardEvent, ownFocus]); +export const CopyButton: React.FC = React.memo( + ({ field, isHoverAction, ownFocus, value }) => { return ( -
- -
+ ); } ); CopyButton.displayName = 'CopyButton'; - -// eslint-disable-next-line import/no-default-export -export { CopyButton as default }; diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx index 58f7b4a831e51..421cd0089c1b7 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx @@ -5,11 +5,9 @@ * 2.0. */ -import React, { useCallback, useEffect } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; - -import { stopPropagationAndPreventDefault } from '../../../../common'; +import { EuiButtonIcon, EuiButtonIconPropsForButton, EuiToolTip } from '@elastic/eui'; import { TooltipWithKeyboardShortcut } from '../../tooltip_with_keyboard_shortcut'; import { createFilter, getAdditionalScreenReaderOnlyContext } from '../utils'; import { HoverActionComponentProps, FilterValueFnArgs } from './types'; @@ -19,50 +17,34 @@ export const FILTER_FOR_VALUE = i18n.translate('xpack.timelines.hoverActions.fil }); export const FILTER_FOR_VALUE_KEYBOARD_SHORTCUT = 'f'; -export type FilterForValueProps = HoverActionComponentProps & FilterValueFnArgs; - -const FilterForValueButton: React.FC = React.memo( - ({ - closePopOver, - defaultFocusedButtonRef, - field, - filterManager, - keyboardEvent, - onFilterAdded, - ownFocus, - showTooltip = false, - value, - }) => { - const filterForValueFn = useCallback(() => { - const makeFilter = (currentVal: string | null | undefined) => - currentVal?.length === 0 ? createFilter(field, undefined) : createFilter(field, currentVal); - const filters = Array.isArray(value) - ? value.map((currentVal: string | null | undefined) => makeFilter(currentVal)) - : makeFilter(value); +export const filterForValueFn = ({ + field, + value, + filterManager, + onFilterAdded, +}: FilterValueFnArgs): void => { + const makeFilter = (currentVal: string | null | undefined) => + currentVal?.length === 0 ? createFilter(field, undefined) : createFilter(field, currentVal); + const filters = Array.isArray(value) + ? value.map((currentVal: string | null | undefined) => makeFilter(currentVal)) + : makeFilter(value); - const activeFilterManager = filterManager; + const activeFilterManager = filterManager; - if (activeFilterManager != null) { - activeFilterManager.addFilters(filters); - if (onFilterAdded != null) { - onFilterAdded(); - } - } - if (closePopOver != null) { - closePopOver(); - } - }, [closePopOver, field, filterManager, onFilterAdded, value]); + if (activeFilterManager != null) { + activeFilterManager.addFilters(filters); + if (onFilterAdded != null) { + onFilterAdded(); + } + } +}; - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === FILTER_FOR_VALUE_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - filterForValueFn(); - } - }, [filterForValueFn, keyboardEvent, ownFocus]); +export interface FilterForValueProps extends HoverActionComponentProps { + defaultFocusedButtonRef: EuiButtonIconPropsForButton['buttonRef']; +} +export const FilterForValueButton: React.FC = React.memo( + ({ defaultFocusedButtonRef, field, onClick, ownFocus, showTooltip = false, value }) => { return showTooltip ? ( = React.memo( data-test-subj="filter-for-value" iconSize="s" iconType="plusInCircle" - onClick={filterForValueFn} + onClick={onClick} /> ) : ( @@ -95,13 +77,10 @@ const FilterForValueButton: React.FC = React.memo( data-test-subj="filter-for-value" iconSize="s" iconType="plusInCircle" - onClick={filterForValueFn} + onClick={onClick} /> ); } ); FilterForValueButton.displayName = 'FilterForValueButton'; - -// eslint-disable-next-line import/no-default-export -export { FilterForValueButton as default }; diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx index 03150d6371397..bfa7848025bf4 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx @@ -5,11 +5,9 @@ * 2.0. */ -import React, { useCallback, useEffect } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; - -import { stopPropagationAndPreventDefault } from '../../../../common'; import { TooltipWithKeyboardShortcut } from '../../tooltip_with_keyboard_shortcut'; import { createFilter, getAdditionalScreenReaderOnlyContext } from '../utils'; import { HoverActionComponentProps, FilterValueFnArgs } from './types'; @@ -20,50 +18,32 @@ export const FILTER_OUT_VALUE = i18n.translate('xpack.timelines.hoverActions.fil export const FILTER_OUT_VALUE_KEYBOARD_SHORTCUT = 'o'; -const FilterOutValueButton: React.FC = React.memo( - ({ - closePopOver, - defaultFocusedButtonRef, - field, - filterManager, - keyboardEvent, - onFilterAdded, - ownFocus, - showTooltip = false, - value, - }) => { - const filterOutValueFn = useCallback(() => { - const makeFilter = (currentVal: string | null | undefined) => - currentVal?.length === 0 - ? createFilter(field, null, false) - : createFilter(field, currentVal, true); - const filters = Array.isArray(value) - ? value.map((currentVal: string | null | undefined) => makeFilter(currentVal)) - : makeFilter(value); - - const activeFilterManager = filterManager; +export const filterOutValueFn = ({ + field, + value, + filterManager, + onFilterAdded, +}: FilterValueFnArgs) => { + const makeFilter = (currentVal: string | null | undefined) => + currentVal?.length === 0 + ? createFilter(field, null, false) + : createFilter(field, currentVal, true); + const filters = Array.isArray(value) + ? value.map((currentVal: string | null | undefined) => makeFilter(currentVal)) + : makeFilter(value); - if (activeFilterManager != null) { - activeFilterManager.addFilters(filters); - if (onFilterAdded != null) { - onFilterAdded(); - } - } - if (closePopOver != null) { - closePopOver(); - } - }, [closePopOver, field, filterManager, onFilterAdded, value]); + const activeFilterManager = filterManager; - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === FILTER_OUT_VALUE_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - filterOutValueFn(); - } - }, [filterOutValueFn, keyboardEvent, ownFocus]); + if (activeFilterManager != null) { + activeFilterManager.addFilters(filters); + if (onFilterAdded != null) { + onFilterAdded(); + } + } +}; +export const FilterOutValueButton: React.FC = React.memo( + ({ field, onClick, ownFocus, showTooltip = false, value }) => { return showTooltip ? ( ) : ( ); } ); FilterOutValueButton.displayName = 'FilterOutValueButton'; - -// eslint-disable-next-line import/no-default-export -export { FilterOutValueButton as default }; diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/translations.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/translations.tsx deleted file mode 100644 index 2f8587ddfab49..0000000000000 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/translations.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const ADD_TO_TIMELINE = i18n.translate('xpack.timelines.hoverActions.addToTimeline', { - defaultMessage: 'Add to timeline investigation', -}); - -export const ADDED_TO_TIMELINE_MESSAGE = (fieldOrValue: string) => - i18n.translate('xpack.timelines.hoverActions.addToTimeline.addedFieldMessage', { - values: { fieldOrValue }, - defaultMessage: `Added {fieldOrValue} to timeline`, - }); diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/types.ts b/x-pack/plugins/timelines/public/components/hover_actions/actions/types.ts index fdef1403e3dc2..4999638e0fe81 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/types.ts +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/types.ts @@ -4,8 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { EuiButtonIconPropsForButton } from '@elastic/eui'; import { FilterManager } from '../../../../../../../src/plugins/data/public'; export interface FilterValueFnArgs { @@ -16,10 +14,8 @@ export interface FilterValueFnArgs { } export interface HoverActionComponentProps { - closePopOver?: () => void; - defaultFocusedButtonRef?: EuiButtonIconPropsForButton['buttonRef']; field: string; - keyboardEvent?: React.KeyboardEvent; + onClick?: () => void; ownFocus: boolean; showTooltip?: boolean; value?: string[] | string | null; diff --git a/x-pack/plugins/timelines/public/components/hover_actions/index.tsx b/x-pack/plugins/timelines/public/components/hover_actions/index.tsx index fc8fcfa488a76..2329134d85626 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/index.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/index.tsx @@ -4,83 +4,86 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiLoadingSpinner } from '@elastic/eui'; -import { I18nProvider } from '@kbn/i18n/react'; -import React, { ReactElement } from 'react'; -import { Provider } from 'react-redux'; -import { Store } from 'redux'; -import type { AddToTimelineButtonProps } from './actions/add_to_timeline'; -import type { ColumnToggleProps } from './actions/column_toggle'; -import type { CopyProps } from './actions/copy'; -import type { HoverActionComponentProps, FilterValueFnArgs } from './actions/types'; +import React from 'react'; +import { + AddToTimelineButton, + ADD_TO_TIMELINE_KEYBOARD_SHORTCUT, + UseGetHandleStartDragToTimelineArgs, + useGetHandleStartDragToTimeline, +} from './actions/add_to_timeline'; +import { + ColumnToggleButton, + columnToggleFn, + ColumnToggleFnArgs, + ColumnToggleProps, + COLUMN_TOGGLE_KEYBOARD_SHORTCUT, +} from './actions/column_toggle'; +import { CopyButton, CopyProps, COPY_TO_CLIPBOARD_KEYBOARD_SHORTCUT } from './actions/copy'; +import { + FilterForValueButton, + filterForValueFn, + FilterForValueProps, + FILTER_FOR_VALUE_KEYBOARD_SHORTCUT, +} from './actions/filter_for_value'; +import { + FilterOutValueButton, + filterOutValueFn, + FILTER_OUT_VALUE_KEYBOARD_SHORTCUT, +} from './actions/filter_out_value'; +import { HoverActionComponentProps, FilterValueFnArgs } from './actions/types'; export interface HoverActionsConfig { - getAddToTimelineButton: ( - props: AddToTimelineButtonProps - ) => ReactElement; - getColumnToggleButton: (props: ColumnToggleProps) => ReactElement; - getCopyButton: (props: CopyProps) => ReactElement; - getFilterForValueButton: ( - props: HoverActionComponentProps & FilterValueFnArgs - ) => ReactElement; - getFilterOutValueButton: ( - props: HoverActionComponentProps & FilterValueFnArgs - ) => ReactElement; + addToTimeline: { + AddToTimelineButton: React.FC; + keyboardShortcut: string; + useGetHandleStartDragToTimeline: (args: UseGetHandleStartDragToTimelineArgs) => () => void; + }; + columnToggle: { + ColumnToggleButton: React.FC; + columnToggleFn: (args: ColumnToggleFnArgs) => void; + keyboardShortcut: string; + }; + copy: { + CopyButton: React.FC; + keyboardShortcut: string; + }; + filterForValue: { + FilterForValueButton: React.FC; + filterForValueFn: (args: FilterValueFnArgs) => void; + keyboardShortcut: string; + }; + filterOutValue: { + FilterOutValueButton: React.FC; + filterOutValueFn: (args: FilterValueFnArgs) => void; + keyboardShortcut: string; + }; } -const AddToTimelineButtonLazy = React.lazy(() => import('./actions/add_to_timeline')); -const getAddToTimelineButtonLazy = (store: Store, props: AddToTimelineButtonProps) => { - return ( - }> - - - - - - - ); +export const addToTimeline = { + AddToTimelineButton, + keyboardShortcut: ADD_TO_TIMELINE_KEYBOARD_SHORTCUT, + useGetHandleStartDragToTimeline, }; -const ColumnToggleButtonLazy = React.lazy(() => import('./actions/column_toggle')); -const getColumnToggleButtonLazy = (props: ColumnToggleProps) => { - return ( - }> - - - ); +export const columnToggle = { + ColumnToggleButton, + columnToggleFn, + keyboardShortcut: COLUMN_TOGGLE_KEYBOARD_SHORTCUT, }; -const CopyButtonLazy = React.lazy(() => import('./actions/copy')); -const getCopyButtonLazy = (props: CopyProps) => { - return ( - }> - - - ); +export const copy = { + CopyButton, + keyboardShortcut: COPY_TO_CLIPBOARD_KEYBOARD_SHORTCUT, }; -const FilterForValueButtonLazy = React.lazy(() => import('./actions/filter_for_value')); -const getFilterForValueButtonLazy = (props: HoverActionComponentProps & FilterValueFnArgs) => { - return ( - }> - - - ); +export const filterForValue = { + FilterForValueButton, + filterForValueFn, + keyboardShortcut: FILTER_FOR_VALUE_KEYBOARD_SHORTCUT, }; -const FilterOutValueButtonLazy = React.lazy(() => import('./actions/filter_out_value')); -const getFilterOutValueButtonLazy = (props: HoverActionComponentProps & FilterValueFnArgs) => { - return ( - }> - - - ); +export const filterOutValue = { + FilterOutValueButton, + filterOutValueFn, + keyboardShortcut: FILTER_OUT_VALUE_KEYBOARD_SHORTCUT, }; - -export const getHoverActions = (store?: Store): HoverActionsConfig => ({ - getAddToTimelineButton: getAddToTimelineButtonLazy.bind(null, store!), - getColumnToggleButton: getColumnToggleButtonLazy, - getCopyButton: getCopyButtonLazy, - getFilterForValueButton: getFilterForValueButtonLazy, - getFilterOutValueButton: getFilterOutValueButtonLazy, -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_row_renderer/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_row_renderer/index.tsx index 0d606ad28eff2..65762b93cd43f 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_row_renderer/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_row_renderer/index.tsx @@ -80,7 +80,6 @@ export const StatefulRowRenderer = ({ {rowRenderer.renderRow({ browserFields, data: event.ecs, - isDraggable: false, timelineId, })}
diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/renderers/plain_row_renderer.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/renderers/plain_row_renderer.test.tsx index 1a4bfcb0e4ab5..5cd709d2de3c7 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/renderers/plain_row_renderer.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/renderers/plain_row_renderer.test.tsx @@ -23,7 +23,6 @@ describe('plain_row_renderer', () => { const children = plainRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockDatum, - isDraggable: false, timelineId: 'test', }); const wrapper = shallow({children}); @@ -38,7 +37,6 @@ describe('plain_row_renderer', () => { const children = plainRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockDatum, - isDraggable: false, timelineId: 'test', }); const wrapper = mount({children}); diff --git a/x-pack/plugins/timelines/public/mock/global_state.ts b/x-pack/plugins/timelines/public/mock/global_state.ts index 610d1b26f2351..f7d3297738373 100644 --- a/x-pack/plugins/timelines/public/mock/global_state.ts +++ b/x-pack/plugins/timelines/public/mock/global_state.ts @@ -17,7 +17,6 @@ export const mockGlobalState: TimelineState = { start: '2020-07-07T08:20:18.966Z', end: '2020-07-08T08:20:18.966Z', }, - dataProviders: [], deletedEventIds: [], excludedRowRendererIds: [], expandedDetail: {}, diff --git a/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx b/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx index 5a8afb2036abf..52ea1fa827136 100644 --- a/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx +++ b/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx @@ -8,9 +8,28 @@ import React from 'react'; /* eslint-disable react/display-name */ export const mockHoverActions = { - getAddToTimelineButton: () => <>{'Add To Timeline'}, - getColumnToggleButton: () => <>{'Column Toggle'}, - getCopyButton: () => <>{'Copy button'}, - getFilterForValueButton: () => <>{'Filter button'}, - getFilterOutValueButton: () => <>{'Filter out button'}, + addToTimeline: { + AddToTimelineButton: () => <>{'Add To Timeline'}, + keyboardShortcut: 'timelineAddShortcut', + useGetHandleStartDragToTimeline: () => jest.fn, + }, + columnToggle: { + ColumnToggleButton: () => <>{'Column Toggle'}, + columnToggleFn: jest.fn, + keyboardShortcut: 'columnToggleShortcut', + }, + copy: { + CopyButton: () => <>{'Copy button'}, + keyboardShortcut: 'copyShortcut', + }, + filterForValue: { + FilterForValueButton: () => <>{'Filter button'}, + filterForValueFn: jest.fn, + keyboardShortcut: 'filterForShortcut', + }, + filterOutValue: { + FilterOutValueButton: () => <>{'Filter out button'}, + filterOutValueFn: jest.fn, + keyboardShortcut: 'filterOutShortcut', + }, }; diff --git a/x-pack/plugins/timelines/public/plugin.ts b/x-pack/plugins/timelines/public/plugin.ts index cb931ff53d445..29d84331cffaa 100644 --- a/x-pack/plugins/timelines/public/plugin.ts +++ b/x-pack/plugins/timelines/public/plugin.ts @@ -26,7 +26,7 @@ import type { TimelinesUIStart, TGridProps, TimelinesStartPlugins } from './type import { tGridReducer } from './store/t_grid/reducer'; import { useDraggableKeyboardWrapper } from './components/drag_and_drop/draggable_keyboard_wrapper_hook'; import { useAddToTimeline, useAddToTimelineSensor } from './hooks/use_add_to_timeline'; -import { getHoverActions } from './components/hover_actions'; +import * as hoverActions from './components/hover_actions'; export class TimelinesPlugin implements Plugin { constructor(private readonly initializerContext: PluginInitializerContext) {} private _store: Store | undefined; @@ -41,7 +41,7 @@ export class TimelinesPlugin implements Plugin { } return { getHoverActions: () => { - return getHoverActions(this._store!); + return hoverActions; }, getTGrid: (props: TGridProps) => { return getTGridLazy(props, { diff --git a/x-pack/plugins/timelines/public/store/t_grid/actions.ts b/x-pack/plugins/timelines/public/store/t_grid/actions.ts index 64c4d8a78c7ac..6d9e9e5bc7379 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/actions.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/actions.ts @@ -9,7 +9,6 @@ import actionCreatorFactory from 'typescript-fsa'; import type { TimelineNonEcsData } from '../../../common/search_strategy'; import type { ColumnHeaderOptions, - DataProvider, SortColumnTimeline, TimelineExpandedDetailType, } from '../../../common/types/timeline'; @@ -101,7 +100,3 @@ export const initializeTGridSettings = actionCreator('I export const setTGridSelectAll = actionCreator<{ id: string; selectAll: boolean }>( 'SET_TGRID_SELECT_ALL' ); - -export const addProviderToTimeline = actionCreator<{ id: string; dataProvider: DataProvider }>( - 'ADD_PROVIDER_TO_TIMELINE' -); diff --git a/x-pack/plugins/timelines/public/store/t_grid/helpers.ts b/x-pack/plugins/timelines/public/store/t_grid/helpers.ts index dd056f1e9237a..8bcf246dadb03 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/helpers.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/helpers.ts @@ -12,11 +12,7 @@ import type { ToggleDetailPanel } from './actions'; import { TGridPersistInput, TimelineById, TimelineId } from './types'; import type { TGridModel, TGridModelSettings } from './model'; -import type { - ColumnHeaderOptions, - DataProvider, - SortColumnTimeline, -} from '../../../common/types/timeline'; +import type { ColumnHeaderOptions, SortColumnTimeline } from '../../../common/types/timeline'; import { getTGridManageDefaults, tGridDefaults } from './defaults'; export const isNotNull = (value: T | null): value is T => value !== null; @@ -425,35 +421,3 @@ export const updateTimelineDetailsPanel = (action: ToggleDetailPanel) => { [expandedTabType]: {}, }; }; - -export const addProviderToTimelineHelper = ( - id: string, - provider: DataProvider, - timelineById: TimelineById -): TimelineById => { - const timeline = timelineById[id]; - const alreadyExistsAtIndex = timeline.dataProviders.findIndex((p) => p.id === provider.id); - - if (alreadyExistsAtIndex > -1 && !isEmpty(timeline.dataProviders[alreadyExistsAtIndex].and)) { - provider.id = `${provider.id}-${ - timeline.dataProviders.filter((p) => p.id === provider.id).length - }`; - } - - const dataProviders = - alreadyExistsAtIndex > -1 && isEmpty(timeline.dataProviders[alreadyExistsAtIndex].and) - ? [ - ...timeline.dataProviders.slice(0, alreadyExistsAtIndex), - provider, - ...timeline.dataProviders.slice(alreadyExistsAtIndex + 1), - ] - : [...timeline.dataProviders, provider]; - - return { - ...timelineById, - [id]: { - ...timeline, - dataProviders, - }, - }; -}; diff --git a/x-pack/plugins/timelines/public/store/t_grid/model.ts b/x-pack/plugins/timelines/public/store/t_grid/model.ts index 4ed4448f4cf35..2d4f9f0fca35f 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/model.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/model.ts @@ -10,7 +10,6 @@ import type { Filter, FilterManager } from '../../../../../../src/plugins/data/p import type { TimelineNonEcsData } from '../../../common/search_strategy'; import type { ColumnHeaderOptions, - DataProvider, TimelineExpandedDetail, SortColumnTimeline, SerializedFilterQuery, @@ -40,8 +39,6 @@ export interface TGridModel extends TGridModelSettings { Pick & ColumnHeaderOptions >; - /** The sources of the event data shown in the timeline */ - dataProviders: DataProvider[]; /** Specifies the granularity of the date range (e.g. 1 Day / Week / Month) applicable to the mini-map */ dateRange: { start: string; @@ -84,7 +81,6 @@ export interface TGridModel extends TGridModelSettings { export type TGridModelForTimeline = Pick< TGridModel, | 'columns' - | 'dataProviders' | 'dateRange' | 'deletedEventIds' | 'excludedRowRendererIds' diff --git a/x-pack/plugins/timelines/public/store/t_grid/reducer.ts b/x-pack/plugins/timelines/public/store/t_grid/reducer.ts index 751837691ea10..57c45f857554d 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/reducer.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/reducer.ts @@ -7,7 +7,6 @@ import { reducerWithInitialState } from 'typescript-fsa-reducers'; import { - addProviderToTimeline, applyDeltaToColumnWidth, clearEventsDeleted, clearEventsLoading, @@ -29,7 +28,6 @@ import { } from './actions'; import { - addProviderToTimelineHelper, applyDeltaToTimelineColumnWidth, createInitTGrid, setInitializeTgridSettings, @@ -211,8 +209,4 @@ export const tGridReducer = reducerWithInitialState(initialTGridState) }, }, })) - .case(addProviderToTimeline, (state, { id, dataProvider }) => ({ - ...state, - timelineById: addProviderToTimelineHelper(id, dataProvider, state.timelineById), - })) .build();