From 856743aebaa9f939826acbb9200c8148e9ac8e8e Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Wed, 20 Apr 2022 13:59:29 -0600 Subject: [PATCH 1/2] ## [Security Solution] [Investigations] [Tech Debt] removes `deepEqual` checks in column headers and data providers This tech debt PR is another entry in a series to remove `React.memo` `deepEqual` checks, per the details in - It removes `deepEqual` checks in Timeline's column headers and data providers - Files made redundant by the `timelines` plugin adopting `EuiDataGrid` are deleted ### Methodology The following techniques were used to ensure that removing the `deepEqual` checks did NOT result in unexpected re-renders: - To understand why components re-rendered, Timeline was profiled with the `Record why each component rendered wile profiling` setting in the React dev tools Profiler enabled, shown in the (illustrative) screenshot below: ![record_why_each_component_rendered](https://user-images.githubusercontent.com/4459398/158903740-8122e2d3-11a6-4927-916a-f895717835ae.png) - Components were temporarily instrumented with counters that incremented every time the component was rendered. Log statements prefixed with `[pre]` were observed before making changes, per the screenshot below: ![pre_change](https://user-images.githubusercontent.com/4459398/164310611-3837bd09-0b31-434e-8ef7-94434d35be48.png) - After removing the `deepEqual` checks, the log prefix was updated to `[POST]`, per the screenshot below: ![post_change](https://user-images.githubusercontent.com/4459398/164310656-f5c82443-2ff4-4e62-8c7b-8fa9dbce5dfd.png) The `[pre]` and `[POST]` counters were compared to verify removing the `deepEqual` checks did NOT introduce unexpected re-renders. --- .../body/column_headers/column_header.tsx | 13 +- .../timeline/body/column_headers/index.tsx | 19 +- .../timeline/data_providers/providers.tsx | 10 +- .../body/column_headers/column_header.tsx | 309 ----------------- .../t_grid/body/column_headers/index.test.tsx | 319 ------------------ .../t_grid/body/column_headers/index.tsx | 294 ---------------- 6 files changed, 4 insertions(+), 960 deletions(-) delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/body/column_headers/column_header.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.tsx diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx index 74593e40ddf4cd..67fcff7de13322 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx @@ -9,7 +9,6 @@ import { EuiContextMenu, EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover } fr import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Draggable } from 'react-beautiful-dnd'; import { Resizable, ResizeCallback } from 're-resizable'; -import deepEqual from 'fast-deep-equal'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '@kbn/securitysolution-t-grid'; @@ -298,14 +297,4 @@ const ColumnHeaderComponent: React.FC = ({ ); }; -export const ColumnHeader = React.memo( - ColumnHeaderComponent, - (prevProps, nextProps) => - prevProps.draggableIndex === nextProps.draggableIndex && - prevProps.tabType === nextProps.tabType && - prevProps.timelineId === nextProps.timelineId && - prevProps.isDragging === nextProps.isDragging && - prevProps.onFilterChange === nextProps.onFilterChange && - deepEqual(prevProps.sort, nextProps.sort) && - deepEqual(prevProps.header, nextProps.header) -); +export const ColumnHeader = React.memo(ColumnHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx index 56984452964de0..2bd5eda49bd98b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import deepEqual from 'fast-deep-equal'; + import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { Droppable, DraggableChildrenFn } from 'react-beautiful-dnd'; @@ -312,19 +312,4 @@ export const ColumnHeadersComponent = ({ ); }; -export const ColumnHeaders = React.memo( - ColumnHeadersComponent, - (prevProps, nextProps) => - prevProps.actionsColumnWidth === nextProps.actionsColumnWidth && - prevProps.isEventViewer === nextProps.isEventViewer && - prevProps.isSelectAllChecked === nextProps.isSelectAllChecked && - prevProps.onSelectAll === nextProps.onSelectAll && - prevProps.show === nextProps.show && - prevProps.showEventsSelect === nextProps.showEventsSelect && - prevProps.showSelectAllCheckbox === nextProps.showSelectAllCheckbox && - deepEqual(prevProps.sort, nextProps.sort) && - prevProps.timelineId === nextProps.timelineId && - deepEqual(prevProps.columnHeaders, nextProps.columnHeaders) && - prevProps.tabType === nextProps.tabType && - deepEqual(prevProps.browserFields, nextProps.browserFields) -); +export const ColumnHeaders = React.memo(ColumnHeadersComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx index 3db938f5efe07d..7e52267cd6f1b7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx @@ -11,7 +11,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Draggable, DraggingStyle, Droppable, NotDraggingStyle } from 'react-beautiful-dnd'; import styled from 'styled-components'; import { useDispatch } from 'react-redux'; -import deepEqual from 'fast-deep-equal'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME, @@ -357,14 +356,7 @@ export const DataProvidersGroupItem = React.memo( ); - }, - (prevProps, nextProps) => - prevProps.groupIndex === nextProps.groupIndex && - prevProps.index === nextProps.index && - prevProps.timelineId === nextProps.timelineId && - deepEqual(prevProps.browserFields, nextProps.browserFields) && - deepEqual(prevProps.group, nextProps.group) && - deepEqual(prevProps.dataProvider, nextProps.dataProvider) + } ); DataProvidersGroupItem.displayName = 'DataProvidersGroupItem'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/column_header.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/column_header.tsx deleted file mode 100644 index 4e6db10cc8bce7..00000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/column_header.tsx +++ /dev/null @@ -1,309 +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 { EuiContextMenu, EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover } from '@elastic/eui'; -import { - DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME, - getDraggableFieldId, -} from '@kbn/securitysolution-t-grid'; -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { Draggable } from 'react-beautiful-dnd'; -import { Resizable, ResizeCallback } from 're-resizable'; -import deepEqual from 'fast-deep-equal'; -import { useDispatch } from 'react-redux'; -import styled from 'styled-components'; - -import { DEFAULT_COLUMN_MIN_WIDTH } from '../constants'; - -import { ARIA_COLUMN_INDEX_OFFSET } from '../../helpers'; -import { EventsTh, EventsThContent, EventsHeadingHandle } from '../../styles'; -import { Sort } from '../sort'; - -import { Header } from './header'; - -import * as i18n from './translations'; -import { tGridActions } from '../../../../store/t_grid'; -import { TimelineTabs } from '../../../../../common/types/timeline'; -import type { ColumnHeaderOptions } from '../../../../../common/types/timeline'; - -import { Direction } from '../../../../../common/search_strategy'; -import { useDraggableKeyboardWrapper } from '../../../drag_and_drop'; - -const ContextMenu = styled(EuiContextMenu)` - width: 115px; - - & .euiContextMenuItem { - font-size: 12px; - padding: 4px 8px; - width: 115px; - } -`; - -const PopoverContainer = styled.div<{ $width: number }>` - & .euiPopover__anchor { - padding-right: 8px; - width: ${({ $width }) => $width}px; - } -`; - -const RESIZABLE_ENABLE = { right: true }; - -interface ColumneHeaderProps { - draggableIndex: number; - header: ColumnHeaderOptions; - isDragging: boolean; - sort: Sort[]; - tabType: TimelineTabs; - timelineId: string; -} - -const ColumnHeaderComponent: React.FC = ({ - draggableIndex, - header, - timelineId, - isDragging, - sort, - tabType, -}) => { - const keyboardHandlerRef = useRef(null); - const [hoverActionsOwnFocus, setHoverActionsOwnFocus] = useState(false); - const restoreFocus = useCallback(() => keyboardHandlerRef.current?.focus(), []); - - const dispatch = useDispatch(); - const resizableSize = useMemo( - () => ({ - width: header.initialWidth ?? DEFAULT_COLUMN_MIN_WIDTH, - height: 'auto', - }), - [header.initialWidth] - ); - const resizableStyle: { - position: 'absolute' | 'relative'; - } = useMemo( - () => ({ - position: isDragging ? 'absolute' : 'relative', - }), - [isDragging] - ); - const resizableHandleComponent = useMemo( - () => ({ - right: , - }), - [] - ); - const handleResizeStop: ResizeCallback = useCallback( - (e, direction, ref, delta) => { - dispatch( - tGridActions.applyDeltaToColumnWidth({ - columnId: header.id, - delta: delta.width, - id: timelineId, - }) - ); - }, - [dispatch, header.id, timelineId] - ); - const draggableId = useMemo( - () => - getDraggableFieldId({ - contextId: `timeline-column-headers-${tabType}-${timelineId}`, - fieldId: header.id, - }), - [tabType, timelineId, header.id] - ); - - const onColumnSort = useCallback( - (sortDirection: Direction) => { - const columnId = header.id; - const headerIndex = sort.findIndex((col) => col.columnId === columnId); - const newSort = - headerIndex === -1 - ? [ - ...sort, - { - columnId, - columnType: `${header.type}`, - sortDirection, - }, - ] - : [ - ...sort.slice(0, headerIndex), - { - columnId, - columnType: `${header.type}`, - sortDirection, - }, - ...sort.slice(headerIndex + 1), - ]; - - dispatch( - tGridActions.updateSort({ - id: timelineId, - sort: newSort, - }) - ); - }, - [dispatch, header, sort, timelineId] - ); - - const handleClosePopOverTrigger = useCallback(() => { - setHoverActionsOwnFocus(false); - restoreFocus(); - }, [restoreFocus]); - - const panels: EuiContextMenuPanelDescriptor[] = useMemo( - () => [ - { - id: 0, - items: [ - { - icon: , - name: i18n.REMOVE_COLUMN, - onClick: () => { - dispatch(tGridActions.removeColumn({ id: timelineId, columnId: header.id })); - handleClosePopOverTrigger(); - }, - }, - ...(tabType !== TimelineTabs.eql - ? [ - { - disabled: !header.aggregatable, - icon: , - name: i18n.SORT_AZ, - onClick: () => { - onColumnSort(Direction.asc); - handleClosePopOverTrigger(); - }, - }, - { - disabled: !header.aggregatable, - icon: , - name: i18n.SORT_ZA, - onClick: () => { - onColumnSort(Direction.desc); - handleClosePopOverTrigger(); - }, - }, - ] - : []), - ], - }, - ], - [ - dispatch, - handleClosePopOverTrigger, - header.aggregatable, - header.id, - onColumnSort, - tabType, - timelineId, - ] - ); - - const headerButton = useMemo( - () =>
, - [header, sort, timelineId] - ); - - const DraggableContent = useCallback( - (dragProvided) => ( - - - - - - - - - - ), - [handleClosePopOverTrigger, headerButton, header.initialWidth, hoverActionsOwnFocus, panels] - ); - - const onFocus = useCallback(() => { - keyboardHandlerRef.current?.focus(); - }, []); - - const openPopover = useCallback(() => { - setHoverActionsOwnFocus(true); - }, []); - - const { onBlur, onKeyDown } = useDraggableKeyboardWrapper({ - closePopover: handleClosePopOverTrigger, - draggableId, - fieldName: header.id, - keyboardHandlerRef, - openPopover, - }); - - const keyDownHandler = useCallback( - (keyboardEvent: React.KeyboardEvent) => { - if (!hoverActionsOwnFocus) { - onKeyDown(keyboardEvent); - } - }, - [hoverActionsOwnFocus, onKeyDown] - ); - - return ( - -
- - {DraggableContent} - -
-
- ); -}; - -export const ColumnHeader = React.memo( - ColumnHeaderComponent, - (prevProps, nextProps) => - prevProps.draggableIndex === nextProps.draggableIndex && - prevProps.tabType === nextProps.tabType && - prevProps.timelineId === nextProps.timelineId && - prevProps.isDragging === nextProps.isDragging && - deepEqual(prevProps.sort, nextProps.sort) && - deepEqual(prevProps.header, nextProps.header) -); diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.test.tsx deleted file mode 100644 index 084cd31d76e610..00000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.test.tsx +++ /dev/null @@ -1,319 +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 { shallow } from 'enzyme'; -import React from 'react'; - -import { getActionsColumnWidth } from './helpers'; - -import { defaultHeaders } from './default_headers'; -import { Sort } from '../sort'; - -import { ColumnHeadersComponent } from '.'; -import { cloneDeep } from 'lodash/fp'; -import { useMountAppended } from '../../../utils/use_mount_appended'; -import { mockBrowserFields } from '../../../../mock/browser_fields'; -import { Direction } from '../../../../../common/search_strategy'; -import { TimelineTabs } from '../../../../../common/types/timeline'; -import { tGridActions } from '../../../../store/t_grid'; -import { testTrailingControlColumns } from '../../../../mock/mock_timeline_control_columns'; -import { TestProviders } from '../../../../mock'; -import { mockGlobalState } from '../../../../mock/global_state'; - -const mockDispatch = jest.fn(); -jest.mock('../../../../hooks/use_selector', () => ({ - useShallowEqualSelector: () => mockGlobalState.timelineById.test, - useDeepEqualSelector: () => mockGlobalState.timelineById.test, -})); - -window.matchMedia = jest.fn().mockImplementation((query) => { - return { - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), - removeListener: jest.fn(), - }; -}); - -jest.mock('react-redux', () => { - const original = jest.requireActual('react-redux'); - - return { - ...original, - useDispatch: () => mockDispatch, - }; -}); -const timelineId = 'test'; - -describe('ColumnHeaders', () => { - const mount = useMountAppended(); - const ACTION_BUTTON_COUNT = 4; - const actionsColumnWidth = getActionsColumnWidth(ACTION_BUTTON_COUNT); - - describe('rendering', () => { - const sort: Sort[] = [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: Direction.desc, - }, - ]; - - test('renders correctly against snapshot', () => { - const wrapper = shallow( - - - - ); - expect(wrapper.find('ColumnHeadersComponent')).toMatchSnapshot(); - }); - - // TODO BrowserField When we bring back browser fields unskip - test.skip('it renders the field browser', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="field-browser"]').first().exists()).toEqual(true); - }); - - test('it renders every column header', () => { - const wrapper = mount( - - - - ); - - defaultHeaders.forEach((h) => { - expect(wrapper.find('[data-test-subj="headers-group"]').first().text()).toContain(h.id); - }); - }); - }); - - describe('#onColumnsSorted', () => { - let mockSort: Sort[] = [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: Direction.desc, - }, - { - columnId: 'host.name', - columnType: 'text', - sortDirection: Direction.asc, - }, - ]; - let mockDefaultHeaders = cloneDeep( - defaultHeaders.map((h) => (h.id === 'message' ? h : { ...h, aggregatable: true })) - ); - - beforeEach(() => { - mockDefaultHeaders = cloneDeep( - defaultHeaders.map((h) => (h.id === 'message' ? h : { ...h, aggregatable: true })) - ); - mockSort = [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: Direction.desc, - }, - { - columnId: 'host.name', - columnType: 'text', - sortDirection: Direction.asc, - }, - ]; - }); - - test('Add column `event.category` as desc sorting', () => { - const wrapper = mount( - - - - ); - - wrapper - .find('[data-test-subj="header-event.category"] [data-test-subj="header-sort-button"]') - .first() - .simulate('click'); - expect(mockDispatch).toHaveBeenCalledWith( - tGridActions.updateSort({ - id: timelineId, - sort: [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: Direction.desc, - }, - { - columnId: 'host.name', - columnType: 'text', - sortDirection: Direction.asc, - }, - { columnId: 'event.category', columnType: 'text', sortDirection: Direction.desc }, - ], - }) - ); - }); - - test('Change order of column `@timestamp` from desc to asc without changing index position', () => { - const wrapper = mount( - - - - ); - - wrapper - .find('[data-test-subj="header-@timestamp"] [data-test-subj="header-sort-button"]') - .first() - .simulate('click'); - expect(mockDispatch).toHaveBeenCalledWith( - tGridActions.updateSort({ - id: timelineId, - sort: [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: Direction.asc, - }, - { columnId: 'host.name', columnType: 'text', sortDirection: Direction.asc }, - ], - }) - ); - }); - - test('Change order of column `host.name` from asc to desc without changing index position', () => { - const wrapper = mount( - - - - ); - - wrapper - .find('[data-test-subj="header-host.name"] [data-test-subj="header-sort-button"]') - .first() - .simulate('click'); - expect(mockDispatch).toHaveBeenCalledWith( - tGridActions.updateSort({ - id: timelineId, - sort: [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: Direction.desc, - }, - { columnId: 'host.name', columnType: 'text', sortDirection: Direction.desc }, - ], - }) - ); - }); - test('Does not render the default leading action column header and renders a custom trailing header', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.exists('[data-test-subj="field-browser"]')).toBeFalsy(); - expect(wrapper.exists('[data-test-subj="test-header-action-cell"]')).toBeTruthy(); - }); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.tsx deleted file mode 100644 index 6a8568dcc6179c..00000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/index.tsx +++ /dev/null @@ -1,294 +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 { DRAG_TYPE_FIELD, droppableTimelineColumnsPrefix } from '@kbn/securitysolution-t-grid'; -import deepEqual from 'fast-deep-equal'; -import React, { useState, useEffect, useCallback, useMemo } from 'react'; -import { Droppable, DraggableChildrenFn } from 'react-beautiful-dnd'; - -import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; -import type { - ControlColumnProps, - ColumnHeaderOptions, - HeaderActionProps, -} from '../../../../../common/types/timeline'; - -import type { BrowserFields } from '../../../../../common/search_strategy/index_fields'; - -import type { OnSelectAll } from '../../types'; -import { - EventsTh, - EventsThead, - EventsThGroupData, - EventsTrHeader, - EventsThGroupActions, -} from '../../styles'; -import { Sort } from '../sort'; -import { ColumnHeader } from './column_header'; -import { DraggableFieldBadge } from '../../../draggables'; - -interface Props { - actionsColumnWidth: number; - browserFields: BrowserFields; - columnHeaders: ColumnHeaderOptions[]; - isEventViewer?: boolean; - isSelectAllChecked: boolean; - onSelectAll: OnSelectAll; - showEventsSelect: boolean; - showSelectAllCheckbox: boolean; - sort: Sort[]; - tabType: TimelineTabs; - timelineId: string; - leadingControlColumns: ControlColumnProps[]; - trailingControlColumns: ControlColumnProps[]; -} - -interface DraggableContainerProps { - children: React.ReactNode; - onMount: () => void; - onUnmount: () => void; -} - -export const DraggableContainer = React.memo( - ({ children, onMount, onUnmount }) => { - useEffect(() => { - onMount(); - - return () => onUnmount(); - }, [onMount, onUnmount]); - - return <>{children}; - } -); - -DraggableContainer.displayName = 'DraggableContainer'; - -export const isFullScreen = ({ - globalFullScreen, - timelineId, - timelineFullScreen, -}: { - globalFullScreen: boolean; - timelineId: string; - timelineFullScreen: boolean; -}) => - (timelineId === TimelineId.active && timelineFullScreen) || - (timelineId !== TimelineId.active && globalFullScreen); - -/** Renders the timeline header columns */ -export const ColumnHeadersComponent = ({ - actionsColumnWidth, - browserFields, - columnHeaders, - isEventViewer = false, - isSelectAllChecked, - onSelectAll, - showEventsSelect, - showSelectAllCheckbox, - sort, - tabType, - timelineId, - leadingControlColumns, - trailingControlColumns, -}: Props) => { - const [draggingIndex, setDraggingIndex] = useState(null); - - const renderClone: DraggableChildrenFn = useCallback( - (dragProvided, _dragSnapshot, rubric) => { - const index = rubric.source.index; - const header = columnHeaders[index]; - - const onMount = () => setDraggingIndex(index); - const onUnmount = () => setDraggingIndex(null); - - return ( - - - - - - ); - }, - [columnHeaders, setDraggingIndex] - ); - - const ColumnHeaderList = useMemo( - () => - columnHeaders.map((header, draggableIndex) => ( - - )), - [columnHeaders, timelineId, draggingIndex, sort, tabType] - ); - - const DroppableContent = useCallback( - (dropProvided, snapshot) => ( - <> - - {ColumnHeaderList} - - - ), - [ColumnHeaderList] - ); - - const leadingHeaderCells = useMemo( - () => - leadingControlColumns ? leadingControlColumns.map((column) => column.headerCellRender) : [], - [leadingControlColumns] - ); - - const trailingHeaderCells = useMemo( - () => - trailingControlColumns ? trailingControlColumns.map((column) => column.headerCellRender) : [], - [trailingControlColumns] - ); - - const LeadingHeaderActions = useMemo(() => { - return leadingHeaderCells.map( - (Header: React.ComponentType | React.ComponentType | undefined, index) => { - const passedWidth = leadingControlColumns[index] && leadingControlColumns[index].width; - const width = passedWidth ? passedWidth : actionsColumnWidth; - return ( - - {Header && ( -
- )} - - ); - } - ); - }, [ - leadingHeaderCells, - leadingControlColumns, - actionsColumnWidth, - browserFields, - columnHeaders, - isEventViewer, - isSelectAllChecked, - onSelectAll, - showEventsSelect, - showSelectAllCheckbox, - sort, - tabType, - timelineId, - ]); - - const TrailingHeaderActions = useMemo(() => { - return trailingHeaderCells.map( - (Header: React.ComponentType | React.ComponentType | undefined, index) => { - const passedWidth = trailingControlColumns[index] && trailingControlColumns[index].width; - const width = passedWidth ? passedWidth : actionsColumnWidth; - return ( - - {Header && ( -
- )} - - ); - } - ); - }, [ - trailingHeaderCells, - trailingControlColumns, - actionsColumnWidth, - browserFields, - columnHeaders, - isEventViewer, - isSelectAllChecked, - onSelectAll, - showEventsSelect, - showSelectAllCheckbox, - sort, - tabType, - timelineId, - ]); - return ( - - - {LeadingHeaderActions} - - {DroppableContent} - - {TrailingHeaderActions} - - - ); -}; - -export const ColumnHeaders = React.memo( - ColumnHeadersComponent, - (prevProps, nextProps) => - prevProps.actionsColumnWidth === nextProps.actionsColumnWidth && - prevProps.isEventViewer === nextProps.isEventViewer && - prevProps.isSelectAllChecked === nextProps.isSelectAllChecked && - prevProps.onSelectAll === nextProps.onSelectAll && - prevProps.showEventsSelect === nextProps.showEventsSelect && - prevProps.showSelectAllCheckbox === nextProps.showSelectAllCheckbox && - deepEqual(prevProps.sort, nextProps.sort) && - prevProps.timelineId === nextProps.timelineId && - deepEqual(prevProps.columnHeaders, nextProps.columnHeaders) && - prevProps.tabType === nextProps.tabType && - deepEqual(prevProps.browserFields, nextProps.browserFields) -); From dff43b214b0af357c9cde3b4f251d6e5235f12cb Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Thu, 21 Apr 2022 11:11:08 -0600 Subject: [PATCH 2/2] - remove obsolete snapshot --- .../__snapshots__/index.test.tsx.snap | 658 ------------------ 1 file changed, 658 deletions(-) delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 233e1c921cd50c..00000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,658 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = ` - -`;