diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx index 1a131871dc4fe74..21713d10b61f155 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx @@ -22,6 +22,7 @@ export interface UnifiedTimelineBodyProps extends ComponentProps { const { header, + isSortEnabled, pageInfo, columns, rowRenderers, @@ -68,6 +69,7 @@ export const UnifiedTimelineBody = (props: UnifiedTimelineBodyProps) => { ColumnHeaderOptions[] = memoizeOne(getColumnHeaders); +import { useTimelineColumns } from '../shared/use-timeline-columns'; export type Props = TimelineTabCommonProps & PropsFromRedux; -const NO_SORTING: Sort[] = []; - -const trailingControlColumns: ControlColumnProps[] = []; // stable reference - export const EqlTabContentComponent: React.FC = ({ activeTab, columns, @@ -100,9 +85,8 @@ export const EqlTabContentComponent: React.FC = ({ runtimeMappings, selectedPatterns, } = useSourcererDataView(SourcererScopeName.timeline); - - const isEnterprisePlus = useLicense().isEnterprise(); - const ACTION_BUTTON_COUNT = isEnterprisePlus ? 6 : 5; + const { augmentedColumnHeaders, getTimelineQueryFieldsFromColumns, leadingControlColumns } = + useTimelineColumns(columns); const unifiedComponentsInTimelineEnabled = useIsExperimentalFeatureEnabled( 'unifiedComponentsInTimelineEnabled' @@ -118,31 +102,16 @@ export const EqlTabContentComponent: React.FC = ({ const isBlankTimeline: boolean = isEmpty(eqlQuery); - const canQueryTimeline = () => - loadingSourcerer != null && - !loadingSourcerer && - !isEmpty(start) && - !isEmpty(end) && - !isBlankTimeline; - - const defaultColumns = useMemo( - () => (unifiedComponentsInTimelineEnabled ? defaultUdtHeaders : defaultHeaders), - [unifiedComponentsInTimelineEnabled] - ); - - const localColumns = useMemo( - () => (isEmpty(columns) ? defaultColumns : columns), - [columns, defaultColumns] + const canQueryTimeline = useCallback( + () => + loadingSourcerer != null && + !loadingSourcerer && + !isEmpty(start) && + !isEmpty(end) && + !isBlankTimeline, + [end, isBlankTimeline, loadingSourcerer, start] ); - const augumentedColumnHeaders = memoizedGetColumnHeaders(localColumns, browserFields, false); - - const getTimelineQueryFields = () => { - const columnFields = augumentedColumnHeaders.map((c) => c.id); - - return [...columnFields, ...requiredFieldsForActions]; - }; - const [ dataLoadingState, { events, inspect, totalCount, pageInfo, loadPage, refreshedAt, refetch }, @@ -150,7 +119,7 @@ export const EqlTabContentComponent: React.FC = ({ dataViewId, endDate: end, eqlOptions: restEqlOption, - fields: getTimelineQueryFields(), + fields: getTimelineQueryFieldsFromColumns(), filterQuery: eqlQuery ?? '', id: timelineId, indexNames: selectedPatterns, @@ -182,15 +151,6 @@ export const EqlTabContentComponent: React.FC = ({ ); }, [loadingSourcerer, timelineId, isQueryLoading, dispatch]); - const leadingControlColumns = useMemo( - () => - getDefaultControlColumn(ACTION_BUTTON_COUNT).map((x) => ({ - ...x, - headerCellRender: HeaderActions, - })), - [ACTION_BUTTON_COUNT] - ); - const unifiedHeader = useMemo( () => ( @@ -216,12 +176,13 @@ export const EqlTabContentComponent: React.FC = ({ = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx index 7e798865c86b641..c2dc6793f66efc9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx @@ -8,26 +8,34 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useMemo } from 'react'; import type { FilterManager } from '@kbn/data-plugin/public'; - +import { InPortal } from 'react-reverse-portal'; import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; import styled from '@emotion/styled'; import { euiThemeVars } from '@kbn/ui-theme'; - +import { useIsExperimentalFeatureEnabled } from '../../../../../../common/hooks/use_experimental_features'; +import { useTimelineEventsCountPortal } from '../../../../../../common/hooks/use_timeline_events_count'; +import { useTimelineFullScreen } from '../../../../../../common/containers/use_full_screen'; +import { ExitFullScreen } from '../../../../../../common/components/exit_full_screen'; import type { TimelineStatusLiteralWithNull } from '../../../../../../../common/api/timeline'; import { TimelineStatus, TimelineType } from '../../../../../../../common/api/timeline'; +import type { TimelineTabs } from '../../../../../../../common/types/timeline'; import { timelineSelectors } from '../../../../../store'; import { useDeepEqualSelector } from '../../../../../../common/hooks/use_selector'; import { timelineDefaults } from '../../../../../store/defaults'; import * as i18n from './translations'; import { StatefulSearchOrFilter } from '../../../search_or_filter'; import { DataProviders } from '../../../data_providers'; +import { StyledEuiFlyoutHeader, EventsCountBadge, TabHeaderContainer } from '../../shared/layout'; interface Props { + activeTab: TimelineTabs; filterManager: FilterManager; show: boolean; showCallOutUnauthorizedMsg: boolean; + showEventsCountBadge: boolean; status: TimelineStatusLiteralWithNull; timelineId: string; + totalCount: number; } const DataProvidersContainer = styled.div<{ $shouldShowQueryBuilder: boolean }>` @@ -50,12 +58,20 @@ const DataProvidersContainer = styled.div<{ $shouldShowQueryBuilder: boolean }>` `; const QueryTabHeaderComponent: React.FC = ({ + activeTab, filterManager, show, showCallOutUnauthorizedMsg, status, timelineId, + showEventsCountBadge, + totalCount, }) => { + const unifiedComponentsInTimelineEnabled = useIsExperimentalFeatureEnabled( + 'unifiedComponentsInTimelineEnabled' + ); + const { portalNode: timelineEventsCountPortalNode } = useTimelineEventsCountPortal(); + const { setTimelineFullScreen, timelineFullScreen } = useTimelineFullScreen(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const getIsDataProviderVisible = useMemo( @@ -74,41 +90,69 @@ const QueryTabHeaderComponent: React.FC = ({ const shouldShowQueryBuilder = isDataProviderVisible || timelineType === TimelineType.template; return ( - - - - - {showCallOutUnauthorizedMsg && ( - - - - )} - {status === TimelineStatus.immutable && ( - - + + + {showEventsCountBadge ? {totalCount} : null} + + + {!unifiedComponentsInTimelineEnabled && + timelineFullScreen && + setTimelineFullScreen != null && ( + + + + + + )} + + + + + + + {showCallOutUnauthorizedMsg && ( + + + + )} + {status === TimelineStatus.immutable && ( + + + + )} + {show ? ( + + + + ) : null} + + - )} - {show ? ( - - - - ) : null} - + {/* TODO: This is a temporary solution to hide the KPIs until lens components play nicely with timelines */} + {/* https://github.com/elastic/kibana/issues/17156 */} + {/* */} + {/* */} + {/* */} + + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx index 472aa17a389d0af..0cae8a899a2d2ec 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx @@ -5,29 +5,22 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React, { useMemo, useEffect, useCallback } from 'react'; import type { Dispatch } from 'redux'; import type { ConnectedProps } from 'react-redux'; import { connect, useDispatch } from 'react-redux'; import deepEqual from 'fast-deep-equal'; -import { InPortal } from 'react-reverse-portal'; - import { getEsQueryConfig } from '@kbn/data-plugin/common'; import { DataLoadingState } from '@kbn/unified-data-table'; -import type { BrowserFields, ColumnHeaderOptions } from '@kbn/timelines-plugin/common'; -import memoizeOne from 'memoize-one'; import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; -import type { ControlColumnProps } from '../../../../../../common/types'; import { InputsModelId } from '../../../../../common/store/inputs/constants'; import { useInvalidFilterQuery } from '../../../../../common/hooks/use_invalid_filter_query'; import { timelineActions, timelineSelectors } from '../../../../store'; import type { Direction } from '../../../../../../common/search_strategy'; import { useTimelineEvents } from '../../../../containers'; import { useKibana } from '../../../../../common/lib/kibana'; -import { defaultHeaders } from '../../body/column_headers/default_headers'; import { StatefulBody } from '../../body'; import { Footer, footerHeight } from '../../footer'; import { QueryTabHeader } from './header'; @@ -39,7 +32,6 @@ import type { ToggleDetailPanel, } from '../../../../../../common/types/timeline'; import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; -import { requiredFieldsForActions } from '../../../../../detections/components/alerts_table/default_config'; import { EventDetailsWidthProvider } from '../../../../../common/components/events_viewer/event_details_width_context'; import type { inputsModel, State } from '../../../../../common/store'; import { inputsSelectors } from '../../../../../common/store'; @@ -50,31 +42,21 @@ import { useTimelineEventsCountPortal } from '../../../../../common/hooks/use_ti import type { TimelineModel } from '../../../../store/model'; import { useTimelineFullScreen } from '../../../../../common/containers/use_full_screen'; import { DetailsPanel } from '../../../side_panel'; -import { ExitFullScreen } from '../../../../../common/components/exit_full_screen'; -import { getDefaultControlColumn } from '../../body/control_columns'; -import { useLicense } from '../../../../../common/hooks/use_license'; -import { HeaderActions } from '../../../../../common/components/header_actions/header_actions'; -import { defaultUdtHeaders } from '../../unified_components/default_headers'; import { UnifiedTimelineBody } from '../../body/unified_timeline_body'; -import { getColumnHeaders } from '../../body/column_headers/helpers'; import { - StyledEuiFlyoutHeader, - EventsCountBadge, FullWidthFlexGroup, ScrollableFlexItem, StyledEuiFlyoutBody, StyledEuiFlyoutFooter, VerticalRule, - TabHeaderContainer, } from '../shared/layout'; -import { EMPTY_EVENTS, isTimerangeSame } from '../shared/utils'; +import { + TIMELINE_EMPTY_EVENTS, + isTimerangeSame, + timelineEmptyTrailingControlColumns, +} from '../shared/utils'; import type { TimelineTabCommonProps } from '../shared/types'; - -const memoizedGetColumnHeaders: ( - headers: ColumnHeaderOptions[], - browserFields: BrowserFields, - isEventRenderedView: boolean -) => ColumnHeaderOptions[] = memoizeOne(getColumnHeaders); +import { useTimelineColumns } from '../shared/use-timeline-columns'; const compareQueryProps = (prevProps: Props, nextProps: Props) => prevProps.kqlMode === nextProps.kqlMode && @@ -83,8 +65,6 @@ const compareQueryProps = (prevProps: Props, nextProps: Props) => export type Props = TimelineTabCommonProps & PropsFromRedux; -const trailingControlColumns: ControlColumnProps[] = []; // stable reference - export const QueryTabContentComponent: React.FC = ({ activeTab, columns, @@ -111,8 +91,6 @@ export const QueryTabContentComponent: React.FC = ({ expandedDetail, }) => { const dispatch = useDispatch(); - const { portalNode: timelineEventsCountPortalNode } = useTimelineEventsCountPortal(); - const { setTimelineFullScreen, timelineFullScreen } = useTimelineFullScreen(); const { browserFields, dataViewId, @@ -123,11 +101,14 @@ export const QueryTabContentComponent: React.FC = ({ // in order to include the exclude filters in the search that are not stored in the timeline selectedPatterns, } = useSourcererDataView(SourcererScopeName.timeline); + const { + augmentedColumnHeaders, + defaultColumns, + getTimelineQueryFieldsFromColumns, + leadingControlColumns, + } = useTimelineColumns(columns); const { uiSettings, timelineFilterManager } = useKibana().services; - const isEnterprisePlus = useLicense().isEnterprise(); - const ACTION_BUTTON_COUNT = isEnterprisePlus ? 6 : 5; - const unifiedComponentsInTimelineEnabled = useIsExperimentalFeatureEnabled( 'unifiedComponentsInTimelineEnabled' ); @@ -185,24 +166,6 @@ export const QueryTabContentComponent: React.FC = ({ [combinedQueries, end, loadingSourcerer, start] ); - const defaultColumns = useMemo( - () => (unifiedComponentsInTimelineEnabled ? defaultUdtHeaders : defaultHeaders), - [unifiedComponentsInTimelineEnabled] - ); - - const localColumns = useMemo( - () => (isEmpty(columns) ? defaultColumns : columns), - [columns, defaultColumns] - ); - - const augumentedColumnHeaders = memoizedGetColumnHeaders(localColumns, browserFields, false); - - const getTimelineQueryFields = () => { - const columnFields = augumentedColumnHeaders.map((c) => c.id); - - return [...columnFields, ...requiredFieldsForActions]; - }; - const timelineQuerySortField = sort.map(({ columnId, columnType, esTypes, sortDirection }) => ({ field: columnId, direction: sortDirection as Direction, @@ -225,7 +188,7 @@ export const QueryTabContentComponent: React.FC = ({ ] = useTimelineEvents({ dataViewId, endDate: end, - fields: getTimelineQueryFields(), + fields: getTimelineQueryFieldsFromColumns(), filterQuery: combinedQueries?.filterQuery, id: timelineId, indexNames: selectedPatterns, @@ -256,79 +219,27 @@ export const QueryTabContentComponent: React.FC = ({ ); }, [loadingSourcerer, timelineId, isQueryLoading, dispatch]); - const leadingControlColumns = useMemo( - () => - getDefaultControlColumn(ACTION_BUTTON_COUNT).map((x) => ({ - ...x, - headerCellRender: HeaderActions, - })), - [ACTION_BUTTON_COUNT] - ); - // NOTE: The timeline is blank after browser FORWARD navigation (after using back button to navigate to // the previous page from the timeline), yet we still see total count. This is because the timeline // is not getting refreshed when using browser navigation. const showEventsCountBadge = !isBlankTimeline && totalCount >= 0; - const header = useMemo( - () => ( - - - {showEventsCountBadge ? {totalCount} : null} - - - {!unifiedComponentsInTimelineEnabled && - timelineFullScreen && - setTimelineFullScreen != null && ( - - - - - - )} - - - - - - {/* TODO: This is a temporary solution to hide the KPIs until lens components play nicely with timelines */} - {/* https://github.com/elastic/kibana/issues/17156 */} - {/* */} - {/* */} - {/* */} - - - ), - [ - activeTab, - timelineFilterManager, - show, - showCallOutUnauthorizedMsg, - status, - timelineId, - setTimelineFullScreen, - timelineFullScreen, - unifiedComponentsInTimelineEnabled, - timelineEventsCountPortalNode, - showEventsCountBadge, - totalCount, - ] - ); - if (unifiedComponentsInTimelineEnabled) { return ( + } + columns={augmentedColumnHeaders} rowRenderers={rowRenderers} timelineId={timelineId} itemsPerPage={itemsPerPage} @@ -362,7 +273,16 @@ export const QueryTabContentComponent: React.FC = ({ /> - {header} + = ({ = ({ itemsPerPage, })} leadingControlColumns={leadingControlColumns} - trailingControlColumns={trailingControlColumns} + trailingControlColumns={timelineEmptyTrailingControlColumns} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use-timeline-columns.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use-timeline-columns.ts new file mode 100644 index 000000000000000..ed82fb52de915c9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use-timeline-columns.ts @@ -0,0 +1,79 @@ +/* + * 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 { isEmpty } from 'lodash/fp'; +import { useCallback, useMemo } from 'react'; +import { useLicense } from '../../../../../common/hooks/use_license'; +import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; +import { useSourcererDataView } from '../../../../../common/containers/sourcerer'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; +import { defaultHeaders } from '../../body/column_headers/default_headers'; +import { requiredFieldsForActions } from '../../../../../detections/components/alerts_table/default_config'; +import { getDefaultControlColumn } from '../../body/control_columns'; +import { HeaderActions } from '../../../../../common/components/header_actions/header_actions'; +import { defaultUdtHeaders } from '../../unified_components/default_headers'; +import type { ColumnHeaderOptions } from '../../../../../../common/types'; +import { memoizedGetTimelineColumnHeaders } from './utils'; + +export const useTimelineColumns = (columns: ColumnHeaderOptions[]) => { + const { browserFields } = useSourcererDataView(SourcererScopeName.timeline); + + const unifiedComponentsInTimelineEnabled = useIsExperimentalFeatureEnabled( + 'unifiedComponentsInTimelineEnabled' + ); + + const isEnterprisePlus = useLicense().isEnterprise(); + const ACTION_BUTTON_COUNT = isEnterprisePlus ? 6 : 5; + + const defaultColumns = useMemo( + () => (unifiedComponentsInTimelineEnabled ? defaultUdtHeaders : defaultHeaders), + [unifiedComponentsInTimelineEnabled] + ); + + const localColumns = useMemo( + () => (isEmpty(columns) ? defaultColumns : columns), + [columns, defaultColumns] + ); + + const augmentedColumnHeaders = memoizedGetTimelineColumnHeaders( + localColumns, + browserFields, + false + ); + + const getTimelineQueryFieldsFromColumns = useCallback(() => { + const columnFields = augmentedColumnHeaders.map((c) => c.id); + + return [...columnFields, ...requiredFieldsForActions]; + }, [augmentedColumnHeaders]); + + const leadingControlColumns = useMemo( + () => + getDefaultControlColumn(ACTION_BUTTON_COUNT).map((x) => ({ + ...x, + headerCellRender: HeaderActions, + })), + [ACTION_BUTTON_COUNT] + ); + + return useMemo( + () => ({ + defaultColumns, + localColumns, + augmentedColumnHeaders, + getTimelineQueryFieldsFromColumns, + leadingControlColumns, + }), + [ + augmentedColumnHeaders, + defaultColumns, + getTimelineQueryFieldsFromColumns, + leadingControlColumns, + localColumns, + ] + ); +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/utils.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/utils.ts index 716f9e7441b41df..879e4b140a61a53 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/utils.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/utils.ts @@ -4,9 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { BrowserFields, ColumnHeaderOptions } from '@kbn/timelines-plugin/common'; +import memoizeOne from 'memoize-one'; +import type { ControlColumnProps } from '../../../../../../common/types'; +import type { Sort } from '../../body/sort'; import type { TimelineItem } from '../../../../../../common/search_strategy'; import type { inputsModel } from '../../../../../common/store'; - +import { getColumnHeaders } from '../../body/column_headers/helpers'; interface TimerangeSimilarityProps { end: inputsModel.InputsRange['timerange']['to']; start: inputsModel.InputsRange['timerange']['from']; @@ -21,4 +25,14 @@ export const isTimerangeSame = ( prevProps.start === nextProps.start && prevProps.timerangeKind === nextProps.timerangeKind; -export const EMPTY_EVENTS: TimelineItem[] = []; +export const TIMELINE_EMPTY_EVENTS: TimelineItem[] = []; + +export const TIMELINE_NO_SORTING: Sort[] = []; + +export const timelineEmptyTrailingControlColumns: ControlColumnProps[] = []; + +export const memoizedGetTimelineColumnHeaders: ( + headers: ColumnHeaderOptions[], + browserFields: BrowserFields, + isEventRenderedView: boolean +) => ColumnHeaderOptions[] = memoizeOne(getColumnHeaders); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx index b823f3715c56650..a3f458841550348 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx @@ -26,8 +26,9 @@ import type { OnChangePage, RowRenderer, ToggleDetailPanel, + TimelineTabs, } from '../../../../../../common/types/timeline'; -import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; +import { TimelineId } from '../../../../../../common/types/timeline'; import type { State, inputsModel } from '../../../../../common/store'; import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../../../../common/containers/sourcerer'; @@ -65,7 +66,7 @@ type CommonDataTableProps = { dataLoadingState: DataLoadingState; updatedAt: number; isTextBasedQuery?: boolean; -} & Pick; +} & Pick; interface DataTableProps extends CommonDataTableProps { dataView: DataView; @@ -83,6 +84,7 @@ export const TimelineDataTableComponent: React.FC = memo( rowRenderers, sort, events, + isSortEnabled = true, onFieldEdited, refetch, dataLoadingState, @@ -149,27 +151,27 @@ export const TimelineDataTableComponent: React.FC = memo( dispatch( timelineActions.toggleDetailPanel({ ...updatedExpandedDetail, - tabType: TimelineTabs.query, + tabType: activeTab, id: timelineId, }) ); activeTimeline.toggleExpandedDetail({ ...updatedExpandedDetail }); }, - [dispatch, refetch, timelineId] + [activeTab, dispatch, refetch, timelineId] ); const handleOnPanelClosed = useCallback(() => { if ( - expandedDetail[TimelineTabs.query]?.panelView && + expandedDetail[activeTab]?.panelView && timelineId === TimelineId.active && showExpandedDetails ) { activeTimeline.toggleExpandedDetail({}); } setExpandedDoc(undefined); - onEventClosed({ tabType: TimelineTabs.query, id: timelineId }); - }, [onEventClosed, timelineId, expandedDetail, showExpandedDetails]); + onEventClosed({ tabType: activeTab, id: timelineId }); + }, [expandedDetail, activeTab, timelineId, showExpandedDetails, onEventClosed]); const onSetExpandedDoc = useCallback( (newDoc?: DataTableRecord) => { @@ -364,7 +366,7 @@ export const TimelineDataTableComponent: React.FC = memo( onUpdateSampleSize={onUpdateSampleSize} setExpandedDoc={onSetExpandedDoc} showTimeCol={showTimeCol} - isSortEnabled={true} + isSortEnabled={isSortEnabled} sort={sort} rowHeightState={rowHeight} isPlainRecord={isTextBasedQuery} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.tsx index 6121dd1f36b7b78..527b0146dfdbe3d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.tsx @@ -97,6 +97,7 @@ export const HIDE_FOR_SIZES = ['xs', 's']; interface Props { columns: ColumnHeaderOptions[]; + isSortEnabled?: boolean; rowRenderers: RowRenderer[]; timelineId: string; itemsPerPage: number; @@ -118,6 +119,7 @@ interface Props { const UnifiedTimelineComponent: React.FC = ({ columns, + isSortEnabled, activeTab, timelineId, itemsPerPage, @@ -397,6 +399,7 @@ const UnifiedTimelineComponent: React.FC = ({ columnIds={currentColumnIds} rowRenderers={rowRenderers} timelineId={timelineId} + isSortEnabled={isSortEnabled} itemsPerPage={itemsPerPage} itemsPerPageOptions={itemsPerPageOptions} sort={sortingColumns}