From 708789102ffd94c3615e03bc719f1134c4d855f6 Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Wed, 15 Jan 2025 16:18:19 +0100 Subject: [PATCH] [Security Solution] - remove styled-components and cleanup for event viewer and data table components (#206523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR originally aimed at replacing the usages `styled-components` with `@emotion/react` in the `security_solution/public/common/components/events_viewer` folder. I quickly realized removing some of these would require a small refactor. This lead to making a few more changes, as many properties were actually unused so a cleanup was welcome. Only 2 small UI changes are introduced in this PR: - the inspect icon on the top right corner of the tables are now always visible instead of only visible on hover. I'm aware that this is a different behavior from the alerts table in the alerts page, but we also have other tables (like the one on threat intelligence page) where the icon is always shown. Waiting on @codearos for confirmation here - the `Grid view` and `Additional filters` button are reversed due to the simplification of the code No other UI changes are introduced. No behavior logic has been changed either. The biggest code cleanup are: - removal of a bunch of unused properties and logic - deletion of the RightTopMenu component: it was used in both `StatefulEventsViewerComponent` and `getPersistentControlsHook` but none of the internal logic was overlapping. I don't know how we got there but its current implementation was overly complex and completely unnecessary... #### Alerts page ![Screenshot 2025-01-13 at 4 33 36 PM](https://github.com/user-attachments/assets/c6c588c1-16f1-49f8-bcc0-246fb05f7e10) #### Rule creation page ![Screenshot 2025-01-13 at 4 34 14 PM](https://github.com/user-attachments/assets/ea2332c3-425a-4960-8bd6-f2d7395cdf34) #### Host/User/Network events tab ![Screenshot 2025-01-13 at 4 34 27 PM](https://github.com/user-attachments/assets/4194e406-6bff-4a46-bc99-aadd1aea88d7) #### Host session view tab ![Screenshot 2025-01-13 at 4 34 42 PM](https://github.com/user-attachments/assets/045b3bb2-2681-4089-a303-a77f797f9b90) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../styled_components_files.js | 2 - .../components/data_table/index.tsx | 66 ++--- .../events_tab/events_query_tab_body.test.tsx | 12 +- .../events_tab/events_query_tab_body.tsx | 4 +- .../components/events_viewer/helpers.tsx | 42 --- .../components/events_viewer/index.test.tsx | 11 +- .../common/components/events_viewer/index.tsx | 247 +++++++----------- .../events_viewer/right_top_menu.tsx | 88 ------- .../components/events_viewer/styles.tsx | 92 ------- .../use_persistent_controls.test.tsx | 4 +- .../use_persistent_controls.tsx | 31 ++- 11 files changed, 155 insertions(+), 444 deletions(-) delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/right_top_menu.tsx delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/styles.tsx diff --git a/packages/kbn-babel-preset/styled_components_files.js b/packages/kbn-babel-preset/styled_components_files.js index 311b2f71da181..6739213b4e19d 100644 --- a/packages/kbn-babel-preset/styled_components_files.js +++ b/packages/kbn-babel-preset/styled_components_files.js @@ -196,8 +196,6 @@ module.exports = { /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]empty_value[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]endpoint[\/\\]agents[\/\\]agent_status[\/\\]agent_status.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]events_viewer[\/\\]index.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]events_viewer[\/\\]right_top_menu.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]events_viewer[\/\\]styles.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]events_viewer[\/\\]summary_view_select[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]field_selection[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]filters_global[\/\\]filters_global.tsx/, diff --git a/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx b/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx index 0b1a728284a7f..4c3bdd0dd5334 100644 --- a/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx +++ b/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx @@ -6,20 +6,19 @@ */ import type { - EuiDataGridRefProps, - EuiDataGridColumn, EuiDataGridCellValueElementProps, - EuiDataGridStyle, - EuiDataGridToolBarVisibilityOptions, + EuiDataGridColumn, EuiDataGridControlColumn, EuiDataGridPaginationProps, - EuiDataGridRowHeightsOptions, EuiDataGridProps, + EuiDataGridRefProps, + EuiDataGridStyle, + EuiDataGridToolBarVisibilityOptions, } from '@elastic/eui'; import { EuiDataGrid, EuiProgress } from '@elastic/eui'; import { getOr } from 'lodash/fp'; import memoizeOne from 'memoize-one'; -import React, { useCallback, useEffect, useMemo, useContext, useRef } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; import styled, { ThemeContext } from 'styled-components'; @@ -31,8 +30,8 @@ import type { import { i18n } from '@kbn/i18n'; import { BrowserFields, - DeprecatedCellValueElementProps, ColumnHeaderOptions, + DeprecatedCellValueElementProps, DeprecatedRowRenderer, TimelineItem, } from '@kbn/timelines-plugin/common'; @@ -88,12 +87,9 @@ interface BaseDataTableProps { loadPage: (newActivePage: number) => void; renderCellValue: (props: DeprecatedCellValueElementProps) => React.ReactNode; rowRenderers: DeprecatedRowRenderer[]; - hasCrudPermissions?: boolean; unitCountText: string; pagination: EuiDataGridPaginationProps & { pageSize: number }; totalItems: number; - rowHeightsOptions?: EuiDataGridRowHeightsOptions; - isEventRenderedView?: boolean; getFieldBrowser: GetFieldBrowser; getFieldSpec: (fieldName: string) => FieldSpec | undefined; cellActionsTriggerId?: string; @@ -103,12 +99,11 @@ export type DataTableProps = BaseDataTableProps & Omit ({ +const gridStyle: EuiDataGridStyle = { border: 'none', fontSize: 's', header: 'underline', - stripes: isEventRenderedView === true, -}); +}; const EuiDataGridContainer = styled.div<{ hideLastPage: boolean }>` ul.euiPagination__list { @@ -116,19 +111,23 @@ const EuiDataGridContainer = styled.div<{ hideLastPage: boolean }>` ${({ hideLastPage }) => `${hideLastPage ? 'display:none' : ''}`}; } } + div .euiDataGridRowCell { display: flex; align-items: center; } + div .euiDataGridRowCell > [data-focus-lock-disabled] { display: flex; align-items: center; flex-grow: 1; width: 100%; } + div .euiDataGridRowCell__content { flex-grow: 1; } + div .siemEventsTable__trSupplement--summary { display: block; } @@ -136,8 +135,7 @@ const EuiDataGridContainer = styled.div<{ hideLastPage: boolean }>` const memoizedGetColumnHeaders: ( headers: ColumnHeaderOptions[], - browserFields: BrowserFields, - isEventRenderedView: boolean + browserFields: BrowserFields ) => ColumnHeaderOptions[] = memoizeOne(getColumnHeaders); // eslint-disable-next-line react/display-name @@ -148,7 +146,6 @@ export const DataTableComponent = React.memo( bulkActions = true, data, fieldBrowserOptions, - hasCrudPermissions, id, leadingControlColumns, loadPage, @@ -157,8 +154,6 @@ export const DataTableComponent = React.memo( pagination, unitCountText, totalItems, - rowHeightsOptions, - isEventRenderedView = false, getFieldBrowser, getFieldSpec, cellActionsTriggerId, @@ -178,7 +173,7 @@ export const DataTableComponent = React.memo( dataViewId, } = dataTable; - const columnHeaders = memoizedGetColumnHeaders(columns, browserFields, isEventRenderedView); + const columnHeaders = memoizedGetColumnHeaders(columns, browserFields); const dataGridRef = useRef(null); @@ -189,10 +184,6 @@ export const DataTableComponent = React.memo( const theme: EuiTheme = useContext(ThemeContext); const showBulkActions = useMemo(() => { - if (!hasCrudPermissions) { - return false; - } - if (selectedCount === 0 || !showCheckboxes) { return false; } @@ -200,7 +191,7 @@ export const DataTableComponent = React.memo( return bulkActions; } return (bulkActions?.customBulkActions?.length || bulkActions?.alertStatusActions) ?? true; - }, [hasCrudPermissions, selectedCount, showCheckboxes, bulkActions]); + }, [selectedCount, showCheckboxes, bulkActions]); const onResetColumns = useCallback(() => { dispatch(dataTableActions.updateColumns({ id, columns: defaultColumns })); @@ -237,22 +228,18 @@ export const DataTableComponent = React.memo( {isLoading && } {unitCountText} {additionalControls ?? null} - {!isEventRenderedView ? ( - getFieldBrowser({ - browserFields, - options: fieldBrowserOptions, - columnIds: columnHeaders.map(({ id: columnId }) => columnId), - onResetColumns, - onToggleColumn, - }) - ) : ( - <> - )} + {getFieldBrowser({ + browserFields, + options: fieldBrowserOptions, + columnIds: columnHeaders.map(({ id: columnId }) => columnId), + onResetColumns, + onToggleColumn, + })} ), }, }, - ...(showBulkActions || isEventRenderedView + ...(showBulkActions ? { showColumnSelector: false, showSortSelector: false, @@ -269,7 +256,6 @@ export const DataTableComponent = React.memo( isLoading, unitCountText, additionalControls, - isEventRenderedView, getFieldBrowser, browserFields, fieldBrowserOptions, @@ -468,9 +454,9 @@ export const DataTableComponent = React.memo( id={'body-data-grid'} data-test-subj="body-data-grid" aria-label={DATA_TABLE_ARIA_LABEL} - columns={isEventRenderedView ? columnHeaders : columnsWithCellActions} + columns={columnsWithCellActions} columnVisibility={{ visibleColumns, setVisibleColumns: onSetVisibleColumns }} - gridStyle={gridStyle(isEventRenderedView)} + gridStyle={gridStyle} leadingControlColumns={leadingControlColumns} toolbarVisibility={toolbarVisibility} rowCount={totalItems} @@ -479,7 +465,7 @@ export const DataTableComponent = React.memo( onColumnResize={onColumnResize} pagination={pagination} ref={dataGridRef} - rowHeightsOptions={rowHeightsOptions} + rowHeightsOptions={undefined} /> diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx index 0a5156bddcc99..3d93dd19cf691 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx @@ -7,11 +7,11 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { TableId, dataTableActions } from '@kbn/securitysolution-data-table'; +import { dataTableActions, TableId } from '@kbn/securitysolution-data-table'; import { HostsType } from '../../../explore/hosts/store/model'; import { TestProviders } from '../../mock'; import type { EventsQueryTabBodyComponentProps } from './events_query_tab_body'; -import { EventsQueryTabBody, ALERTS_EVENTS_HISTOGRAM_ID } from './events_query_tab_body'; +import { ALERTS_EVENTS_HISTOGRAM_ID, EventsQueryTabBody } from './events_query_tab_body'; import { useGlobalFullScreen } from '../../containers/use_full_screen'; import { licenseService } from '../../hooks/use_license'; import { mockHistory } from '../../mock/router'; @@ -59,9 +59,13 @@ jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockReturnValue({ pathname: '/test' }), })); -const FakeStatefulEventsViewer = ({ additionalFilters }: { additionalFilters: JSX.Element }) => ( +const FakeStatefulEventsViewer = ({ + topRightMenuOptions, +}: { + topRightMenuOptions: JSX.Element; +}) => (
- {additionalFilters} + {topRightMenuOptions} {'MockedStatefulEventsViewer'}
); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx index 70e56f4a8b4d2..2bb51df635380 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx @@ -10,8 +10,8 @@ import { useDispatch } from 'react-redux'; import { EuiCheckbox } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; -import { dataTableActions } from '@kbn/securitysolution-data-table'; import type { TableId } from '@kbn/securitysolution-data-table'; +import { dataTableActions } from '@kbn/securitysolution-data-table'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; import type { CustomBulkAction } from '../../../../common/types'; import { RowRendererValues } from '../../../../common/api/timeline'; @@ -177,7 +177,7 @@ const EventsQueryTabBodyComponent: React.FC = /> )} - graphEventId != null && graphEventId !== ''; - -export const EVENTS_COUNT_BUTTON_CLASS_NAME = 'local-events-count-button'; - -/** Returns `true` when the element, or one of it's children has focus */ -export const elementOrChildrenHasFocus = (element: HTMLElement | null | undefined): boolean => - element === document.activeElement || element?.querySelector(':focus-within') != null; - -/** Returns true if the events table has focus */ -export const tableHasFocus = (containerElement: HTMLElement | null): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector(`.${EVENTS_TABLE_CLASS_NAME}`) - ); - -export const isSelectableView = (tableId: string): boolean => - tableId === TableId.alertsOnAlertsPage || tableId === TableId.alertsOnRuleDetailsPage; - -export const isViewSelection = (value: unknown): value is ViewSelection => - value === 'gridView' || value === 'eventRenderedView'; - -/** always returns a valid default `ViewSelection` */ -export const getDefaultViewSelection = ({ - tableId, - value, -}: { - tableId: string; - value: unknown; -}): ViewSelection => { - const defaultViewSelection = 'gridView'; - - if (!isSelectableView(tableId)) { - return defaultViewSelection; - } else { - return isViewSelection(value) ? value : defaultViewSelection; - } -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index 162bb6bee5ece..a9c23c415a44a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -12,7 +12,7 @@ import { render } from '@testing-library/react'; import { TestProviders } from '../../mock'; import { mockEventViewerResponse } from './mock'; -import { StatefulEventsViewer, type EventsViewerProps } from '.'; +import { type EventsViewerProps, StatefulEventsViewer } from '.'; import { eventsDefaultModel } from './default_model'; import { EntityType } from '@kbn/timelines-plugin/common'; import { SourcererScopeName } from '../../../sourcerer/store/model'; @@ -54,18 +54,17 @@ const to = '2019-08-26T22:10:56.791Z'; const ACTION_BUTTON_COUNT = 4; const testProps: EventsViewerProps = { + bulkActions: false, defaultModel: eventsDefaultModel, end: to, entityType: EntityType.EVENTS, indexNames: [], - tableId: TableId.test, leadingControlColumns: getDefaultControlColumn(ACTION_BUTTON_COUNT), renderCellValue: DefaultCellRenderer, rowRenderers: defaultRowRenderers, sourcererScope: SourcererScopeName.default, start: from, - bulkActions: false, - hasCrudPermissions: true, + tableId: TableId.test, }; describe('StatefulEventsViewer', () => { beforeAll(() => { @@ -93,7 +92,7 @@ describe('StatefulEventsViewer', () => { ); - expect(wrapper.find(`[data-test-subj="hoverVisibilityContainer"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBeTruthy(); }); test('it closes field editor when unmounted', () => { @@ -119,7 +118,7 @@ describe('StatefulEventsViewer', () => { ]} + topRightMenuOptions={[

]} /> ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx index adae56f752273..ac400984db6b9 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -5,20 +5,16 @@ * 2.0. */ +import { css } from '@emotion/react'; +import type { SubsetDataTableModel, TableId } from '@kbn/securitysolution-data-table'; import { dataTableActions, DataTableComponent, defaultHeaders, getEventIdToDataMapping, } from '@kbn/securitysolution-data-table'; -import type { - SubsetDataTableModel, - TableId, - ViewSelection, -} from '@kbn/securitysolution-data-table'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; import { AlertConsumers } from '@kbn/rule-data-utils'; -import React, { useRef, useCallback, useMemo, useEffect, useState, useContext } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import type { ConnectedProps } from 'react-redux'; import { connect, useDispatch, useSelector } from 'react-redux'; import { ThemeContext } from 'styled-components'; @@ -33,9 +29,9 @@ import type { import { isEmpty } from 'lodash'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; -import type { EuiDataGridRowHeightsOptions } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { RunTimeMappings } from '@kbn/timelines-plugin/common/search_strategy'; -import { ALERTS_TABLE_VIEW_SELECTION_KEY } from '../../../../common/constants'; +import { InspectButton } from '../inspect'; import type { ControlColumnProps, OnRowSelected, @@ -47,8 +43,6 @@ import type { RowRenderer, SortColumnTimeline as Sort } from '../../../../common import { InputsModelId } from '../../store/inputs/constants'; import type { State } from '../../store'; import { inputsActions } from '../../store/actions'; -import { InspectButtonContainer } from '../inspect'; -import { useGlobalFullScreen } from '../../containers/use_full_screen'; import { eventsViewerSelector } from './selectors'; import type { SourcererScopeName } from '../../../sourcerer/store/model'; import { useSourcererDataView } from '../../../sourcerer/containers'; @@ -58,55 +52,39 @@ import { GraphOverlay } from '../../../timelines/components/graph_overlay'; import type { FieldEditorActions } from '../../../timelines/components/fields_browser'; import { useFieldBrowserOptions } from '../../../timelines/components/fields_browser'; import { - useSessionViewNavigation, useSessionView, + useSessionViewNavigation, } from '../../../timelines/components/timeline/tabs/session/use_session_view'; -import { - EventsContainerLoading, - FullScreenContainer, - FullWidthFlexGroupTable, - ScrollableFlexItem, - StyledEuiPanel, -} from './styles'; -import { getDefaultViewSelection, getCombinedFilterQuery } from './helpers'; +import { getCombinedFilterQuery } from './helpers'; import { useTimelineEvents } from './use_timelines_events'; -import { TableContext, EmptyTable, TableLoading } from './shared'; -import type { AlertWorkflowStatus } from '../../types'; +import { EmptyTable, TableContext, TableLoading } from './shared'; import { useQueryInspector } from '../page/manage_query'; import type { SetQuery } from '../../containers/use_global_time/types'; import { checkBoxControlColumn, transformControlColumns } from '../control_columns'; -import { RightTopMenu } from './right_top_menu'; import { useAlertBulkActions } from './use_alert_bulk_actions'; import type { BulkActionsProp } from '../toolbar/bulk_actions/types'; import { StatefulEventContext } from './stateful_event_context'; import { defaultUnit } from '../toolbar/unit'; import { useGetFieldSpec } from '../../hooks/use_get_field_spec'; -const storage = new Storage(localStorage); - const SECURITY_ALERTS_CONSUMERS = [AlertConsumers.SIEM]; export interface EventsViewerProps { + bulkActions: boolean | BulkActionsProp; + cellActionsTriggerId?: string; defaultModel: SubsetDataTableModel; end: string; entityType?: EntityType; - tableId: TableId; + indexNames?: string[]; leadingControlColumns: ControlColumnProps[]; - sourcererScope: SourcererScopeName; - start: string; - showTotalCount?: boolean; // eslint-disable-line react/no-unused-prop-types pageFilters?: Filter[]; - currentFilter?: AlertWorkflowStatus; - onRuleChange?: () => void; renderCellValue: React.FC; rowRenderers: RowRenderer[]; - additionalFilters?: React.ReactNode; - hasCrudPermissions?: boolean; + sourcererScope: SourcererScopeName; + start: string; + tableId: TableId; + topRightMenuOptions?: React.ReactNode; unit?: (n: number) => string; - indexNames?: string[]; - bulkActions: boolean | BulkActionsProp; - additionalRightMenuOptions?: React.ReactNode[]; - cellActionsTriggerId?: string; } /** @@ -115,19 +93,14 @@ export interface EventsViewerProps { * NOTE: As of writting, it is not used in the Case_View component */ const StatefulEventsViewerComponent: React.FC = ({ - additionalFilters, - additionalRightMenuOptions, bulkActions, cellActionsTriggerId, clearSelected, - currentFilter, defaultModel, end, entityType = 'events', - hasCrudPermissions = true, indexNames, leadingControlColumns, - onRuleChange, pageFilters, renderCellValue, rowRenderers, @@ -135,6 +108,7 @@ const StatefulEventsViewerComponent: React.FC { const dispatch = useDispatch(); @@ -162,6 +136,7 @@ const StatefulEventsViewerComponent: React.FC eventsViewerSelector(state, tableId)); + const inspectModalTitle = useMemo(() => {title}, [title]); const { uiSettings, @@ -169,13 +144,6 @@ const StatefulEventsViewerComponent: React.FC( - getDefaultViewSelection({ - tableId, - value: storage.get(ALERTS_TABLE_VIEW_SELECTION_KEY), - }) - ); - const { browserFields, dataViewId, @@ -187,8 +155,6 @@ const StatefulEventsViewerComponent: React.FC(null); useEffect(() => { dispatch( @@ -215,14 +181,13 @@ const StatefulEventsViewerComponent: React.FC [...filters, ...(pageFilters ?? [])], [filters, pageFilters]); + // TODO remove this when session view is fully migrated to the flyout and the advanced settings is removed const { Navigation } = useSessionViewNavigation({ scopeId: tableId, }); - const { SessionView } = useSessionView({ scopeId: tableId, }); - const graphOverlay = useMemo(() => { const shouldShowOverlay = (graphEventId != null && graphEventId.length > 0) || sessionViewConfig != null; @@ -230,6 +195,7 @@ const StatefulEventsViewerComponent: React.FC ) : null; }, [graphEventId, tableId, sessionViewConfig, SessionView, Navigation]); + const setQuery = useCallback( ({ id, inspect, loading, refetch }: SetQuery) => dispatch( @@ -320,7 +286,7 @@ const StatefulEventsViewerComponent: React.FC { @@ -411,17 +377,12 @@ const StatefulEventsViewerComponent: React.FC { setSelected({ id: tableId, - eventIds: getEventIdToDataMapping( - nonDeletedEvents, - eventIds, - queryFields, - hasCrudPermissions - ), + eventIds: getEventIdToDataMapping(nonDeletedEvents, eventIds, queryFields, true), isSelected, isSelectAllChecked: isSelected && selectedCount + 1 === nonDeletedEvents.length, }); }, - [setSelected, tableId, nonDeletedEvents, queryFields, hasCrudPermissions, selectedCount] + [setSelected, tableId, nonDeletedEvents, queryFields, selectedCount] ); const onSelectPage: OnSelectAll = useCallback( @@ -433,13 +394,13 @@ const StatefulEventsViewerComponent: React.FC event._id), queryFields, - hasCrudPermissions + true ), isSelected, isSelectAllChecked: isSelected, }) : clearSelected({ id: tableId }), - [setSelected, tableId, nonDeletedEvents, queryFields, hasCrudPermissions, clearSelected] + [setSelected, tableId, nonDeletedEvents, queryFields, clearSelected] ); // Sync to selectAll so parent components can select all events @@ -460,7 +421,7 @@ const StatefulEventsViewerComponent: React.FC { - if (tableView === 'eventRenderedView') { - return { - defaultHeight: 'auto' as const, - }; - } - return undefined; - }, [tableView]); - const pagination = useMemo( () => ({ pageIndex: pageInfo.activePage, @@ -542,83 +493,79 @@ const StatefulEventsViewerComponent: React.FC - - - + {showFullLoading && } + + {graphOverlay} + + {canQueryTimeline && ( + +

- {showFullLoading && } - - {graphOverlay} - - {canQueryTimeline && ( - - - setTableView(selectedView)} - additionalFilters={additionalFilters} - hasRightOffset={tableView === 'gridView' && nonDeletedEvents.length > 0} - additionalMenuOptions={additionalRightMenuOptions} - /> - - {!hasAlerts && !loading && !graphOverlay && } - {hasAlerts && ( - - - - React.ReactNode - } - // TODO: migrate away from deprecated type - rowRenderers={rowRenderers as unknown as DeprecatedRowRenderer[]} - totalItems={totalCountMinusDeleted} - bulkActions={bulkActions} - fieldBrowserOptions={fieldBrowserOptions} - hasCrudPermissions={hasCrudPermissions} - leadingControlColumns={transformedLeadingControlColumns} - pagination={pagination} - isEventRenderedView={tableView === 'eventRenderedView'} - rowHeightsOptions={rowHeightsOptions} - getFieldBrowser={getFieldBrowser} - getFieldSpec={getFieldSpec} - /> - - - + {!loading && !graphOverlay && ( +
0 ? '72px' : theme.eui.euiSizeXS}; + `} + > + + + + + {topRightMenuOptions && ( + {topRightMenuOptions} )} - - + +
+ )} + + {!hasAlerts && !loading && !graphOverlay && } + + {hasAlerts && ( + + + React.ReactNode + } + // TODO: migrate away from deprecated type + rowRenderers={rowRenderers as unknown as DeprecatedRowRenderer[]} + unitCountText={unitCountText} + pagination={pagination} + totalItems={totalCountMinusDeleted} + getFieldBrowser={getFieldBrowser} + getFieldSpec={getFieldSpec} + cellActionsTriggerId={cellActionsTriggerId} + /> + + )} - - - - +
+ + )} + ); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/right_top_menu.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/right_top_menu.tsx deleted file mode 100644 index 40c6181f43a29..0000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/right_top_menu.tsx +++ /dev/null @@ -1,88 +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 type { ViewSelection } from '@kbn/securitysolution-data-table'; -import { TableId } from '@kbn/securitysolution-data-table'; -import React, { useMemo } from 'react'; -import type { CSSProperties } from 'styled-components'; -import styled from 'styled-components'; -import { InspectButton } from '../inspect'; -import { UpdatedFlexGroup, UpdatedFlexItem } from './styles'; -import { SummaryViewSelector } from './summary_view_select'; - -const TitleText = styled.span` - margin-right: 12px; -`; - -interface Props { - tableView: ViewSelection; - loading: boolean; - tableId: TableId; - title: string; - onViewChange: (viewSelection: ViewSelection) => void; - additionalFilters?: React.ReactNode; - hasRightOffset?: boolean; - showInspect?: boolean; - position?: CSSProperties['position']; - additionalMenuOptions?: React.ReactNode[]; -} - -export const RightTopMenu = ({ - tableView, - loading, - tableId, - title, - onViewChange, - additionalFilters, - hasRightOffset, - showInspect = true, - position = 'absolute', - additionalMenuOptions = [], -}: Props) => { - const alignItems = tableView === 'gridView' ? 'baseline' : 'center'; - const justTitle = useMemo(() => {title}, [title]); - - const menuOptions = useMemo( - () => - additionalMenuOptions.length - ? additionalMenuOptions.map((additionalMenuOption, i) => ( - - {additionalMenuOption} - - )) - : null, - [additionalMenuOptions, loading] - ); - - return ( - - {showInspect ? ( - - - - ) : null} - - {additionalFilters} - - {[TableId.alertsOnRuleDetailsPage, TableId.alertsOnAlertsPage].includes(tableId) && ( - - - - )} - {menuOptions} - - ); -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/styles.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/styles.tsx deleted file mode 100644 index e7eecc27c2255..0000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/styles.tsx +++ /dev/null @@ -1,92 +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 { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; -import type { CSSProperties } from 'styled-components'; -import styled from 'styled-components'; -export const SELECTOR_TIMELINE_GLOBAL_CONTAINER = 'securitySolutionTimeline__container'; -export const EVENTS_TABLE_CLASS_NAME = 'siemEventsTable'; - -export const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>` - height: ${({ $isFullScreen }) => ($isFullScreen ? '100%' : undefined)}; - flex: 1 1 auto; - display: flex; - width: 100%; -`; - -export const FullWidthFlexGroupTable = styled(EuiFlexGroup)<{ $visible: boolean }>` - overflow: hidden; - margin: 0; - display: ${({ $visible }) => ($visible ? 'flex' : 'none')}; -`; - -export const ScrollableFlexItem = styled(EuiFlexItem)` - overflow: auto; -`; - -export const FullWidthFlexGroup = styled(EuiFlexGroup)<{ $visible?: boolean }>` - overflow: hidden; - margin: 0; - min-height: 490px; - display: ${({ $visible = true }) => ($visible ? 'flex' : 'none')}; -`; - -export const UpdatedFlexGroup = styled(EuiFlexGroup)<{ - $hasRightOffset?: boolean; - position: CSSProperties['position']; -}>` - ${({ $hasRightOffset, theme, position }) => - position === 'relative' - ? `margin-right: ${theme.eui.euiSizeXS}; margin-left: ` - : $hasRightOffset && position === 'absolute' - ? `margin-right: ${theme.eui.euiSizeXL};` - : `margin-right: ${theme.eui.euiSizeXS};`} - ${({ position }) => { - return position === 'absolute' - ? `position: absolute` - : `display: flex; justify-content:center; align-items:center`; - }}; - display: inline-flex; - z-index: ${({ theme }) => theme.eui.euiZLevel1 - 3}; - ${({ $hasRightOffset, theme, position }) => - position === 'relative' - ? `right: 0;` - : $hasRightOffset && position === 'absolute' - ? `right: ${theme.eui.euiSizeXL};` - : `right: ${theme.eui.euiSizeL};`} -`; - -export const UpdatedFlexItem = styled(EuiFlexItem)<{ $show: boolean }>` - ${({ $show }) => ($show ? '' : 'visibility: hidden;')} -`; - -export const EventsContainerLoading = styled.div.attrs(({ className = '' }) => ({ - className: `${SELECTOR_TIMELINE_GLOBAL_CONTAINER} ${className}`, -}))` - position: relative; - width: 100%; - overflow: hidden; - flex: 1; - display: flex; - flex-direction: column; -`; - -export const StyledEuiPanel = styled(EuiPanel)<{ $isFullScreen: boolean }>` - display: flex; - flex-direction: column; - position: relative; - width: 100%; - - ${({ $isFullScreen }) => - $isFullScreen && - ` - border: 0; - box-shadow: none; - padding-top: 0; - padding-bottom: 0; -`} -`; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.test.tsx index 1fe2e617f3616..dfe69f6254873 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { getPersistentControlsHook } from './use_persistent_controls'; import { TableId } from '@kbn/securitysolution-data-table'; -import { render, fireEvent, renderHook } from '@testing-library/react'; +import { fireEvent, render, renderHook } from '@testing-library/react'; import { createMockStore, mockGlobalState, TestProviders } from '../../../common/mock'; import { useSourcererDataView } from '../../../sourcerer/containers'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; @@ -93,7 +93,7 @@ describe('usePersistentControls', () => { ), }); - const groupSelector = result.current.right.props.additionalMenuOptions[0]; + const groupSelector = result.current.right.props.children[2]; const { getByTestId } = render({groupSelector}); fireEvent.click(getByTestId('group-selector-dropdown')); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx index 484162cbf7d42..26628c860f5d2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx @@ -7,23 +7,25 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; +import type { ViewSelection } from '@kbn/securitysolution-data-table'; import { + dataTableActions, dataTableSelectors, tableDefaults, - dataTableActions, + TableId, } from '@kbn/securitysolution-data-table'; -import type { ViewSelection, TableId } from '@kbn/securitysolution-data-table'; import { useGetGroupSelectorStateless } from '@kbn/grouping/src/hooks/use_get_group_selector'; import { getTelemetryEvent } from '@kbn/grouping/src/telemetry/const'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { SummaryViewSelector } from '../../../common/components/events_viewer/summary_view_select'; import { groupIdSelector } from '../../../common/store/grouping/selectors'; import { useSourcererDataView } from '../../../sourcerer/containers'; import { SourcererScopeName } from '../../../sourcerer/store/model'; import { updateGroups } from '../../../common/store/grouping/actions'; import { useKibana } from '../../../common/lib/kibana'; -import { METRIC_TYPE, AlertsEventTypes, track } from '../../../common/lib/telemetry'; +import { AlertsEventTypes, METRIC_TYPE, track } from '../../../common/lib/telemetry'; import { useDataTableFilters } from '../../../common/hooks/use_data_table_filters'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; -import { RightTopMenu } from '../../../common/components/events_viewer/right_top_menu'; import { AdditionalFiltersAction } from '../../components/alerts_table/additional_filters_action'; const { changeViewMode } = dataTableActions; @@ -120,18 +122,15 @@ export const getPersistentControlsHook = (tableId: TableId) => { const rightTopMenu = useMemo( () => ( - + + {[TableId.alertsOnRuleDetailsPage, TableId.alertsOnAlertsPage].includes(tableId) && ( + + + + )} + {additionalFiltersComponent} + {groupSelector} + ), [tableView, handleChangeTableView, additionalFiltersComponent, groupSelector] );