diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx
index af90d17fe62b8..43d5c66655808 100644
--- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx
@@ -12,6 +12,8 @@ import { TimelineIdLiteral } from '../../../../common/types/timeline';
import { StatefulEventsViewer } from '../events_viewer';
import { alertsDefaultModel } from './default_headers';
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
+import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
+import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
import * as i18n from './translations';
import { useKibana } from '../../lib/kibana';
import { SourcererScopeName } from '../../store/sourcerer/model';
@@ -91,6 +93,8 @@ const AlertsTableComponent: React.FC = ({
defaultModel={alertsDefaultModel}
end={endDate}
id={timelineId}
+ renderCellValue={DefaultCellRenderer}
+ rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.default}
start={startDate}
/>
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx
index 3ecc17589fe08..8962f5e6c5146 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx
@@ -26,6 +26,8 @@ import { KqlMode } from '../../../timelines/store/timeline/model';
import { SortDirection } from '../../../timelines/components/timeline/body/sort';
import { AlertsTableFilterGroup } from '../../../detections/components/alerts_table/alerts_filter_group';
import { SourcererScopeName } from '../../store/sourcerer/model';
+import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
+import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
import { useTimelineEvents } from '../../../timelines/containers';
jest.mock('../../../timelines/components/graph_overlay', () => ({
@@ -99,6 +101,8 @@ const eventsViewerDefaultProps = {
query: '',
language: 'kql',
},
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
start: from,
sort: [
{
@@ -118,6 +122,8 @@ describe('EventsViewer', () => {
defaultModel: eventsDefaultModel,
end: to,
id: TimelineId.test,
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
start: from,
scopeId: SourcererScopeName.timeline,
};
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
index 050cd92b0556e..e6e868f1a7365 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
@@ -4,7 +4,6 @@
* 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 { isEmpty } from 'lodash/fp';
import React, { useEffect, useMemo, useState } from 'react';
@@ -41,7 +40,9 @@ import { useManageTimeline } from '../../../timelines/components/manage_timeline
import { ExitFullScreen } from '../exit_full_screen';
import { useGlobalFullScreen } from '../../containers/use_full_screen';
import { TimelineId, TimelineTabs } from '../../../../common/types/timeline';
+import { RowRenderer } from '../../../timelines/components/timeline/body/renderers/row_renderer';
import { GraphOverlay } from '../../../timelines/components/graph_overlay';
+import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering';
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles';
export const EVENTS_VIEWER_HEADER_HEIGHT = 90; // px
@@ -122,6 +123,8 @@ interface Props {
kqlMode: KqlMode;
query: Query;
onRuleChange?: () => void;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
start: string;
sort: Sort[];
utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
@@ -146,8 +149,10 @@ const EventsViewerComponent: React.FC = ({
itemsPerPage,
itemsPerPageOptions,
kqlMode,
- query,
onRuleChange,
+ query,
+ renderCellValue,
+ rowRenderers,
start,
sort,
utilityBar,
@@ -310,6 +315,8 @@ const EventsViewerComponent: React.FC = ({
isEventViewer={true}
onRuleChange={onRuleChange}
refetch={refetch}
+ renderCellValue={renderCellValue}
+ rowRenderers={rowRenderers}
sort={sort}
tabType={TimelineTabs.query}
totalPages={calculateTotalPages({
@@ -343,6 +350,7 @@ const EventsViewerComponent: React.FC = ({
export const EventsViewer = React.memo(
EventsViewerComponent,
+ // eslint-disable-next-line complexity
(prevProps, nextProps) =>
deepEqual(prevProps.browserFields, nextProps.browserFields) &&
prevProps.columns === nextProps.columns &&
@@ -359,6 +367,8 @@ export const EventsViewer = React.memo(
prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions &&
prevProps.kqlMode === nextProps.kqlMode &&
deepEqual(prevProps.query, nextProps.query) &&
+ prevProps.renderCellValue === nextProps.renderCellValue &&
+ prevProps.rowRenderers === nextProps.rowRenderers &&
prevProps.start === nextProps.start &&
deepEqual(prevProps.sort, nextProps.sort) &&
prevProps.utilityBar === nextProps.utilityBar &&
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx
index 5004c23f9111c..cd27177643b44 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx
@@ -18,7 +18,9 @@ import { StatefulEventsViewer } from '.';
import { eventsDefaultModel } from './default_model';
import { TimelineId } from '../../../../common/types/timeline';
import { SourcererScopeName } from '../../store/sourcerer/model';
+import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
import { useTimelineEvents } from '../../../timelines/containers';
+import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
jest.mock('../../../timelines/containers', () => ({
useTimelineEvents: jest.fn(),
@@ -38,6 +40,8 @@ const testProps = {
end: to,
indexNames: [],
id: TimelineId.test,
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
scopeId: SourcererScopeName.default,
start: from,
};
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx
index 59dc756bb2b3e..b58aa2236d292 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx
@@ -22,6 +22,8 @@ import { useGlobalFullScreen } from '../../containers/use_full_screen';
import { SourcererScopeName } from '../../store/sourcerer/model';
import { useSourcererScope } from '../../containers/sourcerer';
import { DetailsPanel } from '../../../timelines/components/side_panel';
+import { RowRenderer } from '../../../timelines/components/timeline/body/renderers/row_renderer';
+import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering';
const DEFAULT_EVENTS_VIEWER_HEIGHT = 652;
@@ -41,6 +43,8 @@ export interface OwnProps {
headerFilterGroup?: React.ReactNode;
pageFilters?: Filter[];
onRuleChange?: () => void;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
}
@@ -67,8 +71,10 @@ const StatefulEventsViewerComponent: React.FC = ({
itemsPerPageOptions,
kqlMode,
pageFilters,
- query,
onRuleChange,
+ query,
+ renderCellValue,
+ rowRenderers,
start,
scopeId,
showCheckboxes,
@@ -129,6 +135,8 @@ const StatefulEventsViewerComponent: React.FC = ({
kqlMode={kqlMode}
query={query}
onRuleChange={onRuleChange}
+ renderCellValue={renderCellValue}
+ rowRenderers={rowRenderers}
start={start}
sort={sort}
utilityBar={utilityBar}
@@ -201,6 +209,7 @@ type PropsFromRedux = ConnectedProps;
export const StatefulEventsViewer = connector(
React.memo(
StatefulEventsViewerComponent,
+ // eslint-disable-next-line complexity
(prevProps, nextProps) =>
prevProps.id === nextProps.id &&
prevProps.scopeId === nextProps.scopeId &&
@@ -215,6 +224,8 @@ export const StatefulEventsViewer = connector(
deepEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) &&
prevProps.kqlMode === nextProps.kqlMode &&
deepEqual(prevProps.query, nextProps.query) &&
+ prevProps.renderCellValue === nextProps.renderCellValue &&
+ prevProps.rowRenderers === nextProps.rowRenderers &&
deepEqual(prevProps.sort, nextProps.sort) &&
prevProps.start === nextProps.start &&
deepEqual(prevProps.pageFilters, nextProps.pageFilters) &&
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx
index 6c88b8e29800b..cf6db52d0cece 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx
@@ -48,6 +48,8 @@ import {
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
import { useSourcererScope } from '../../../common/containers/sourcerer';
import { buildTimeRangeFilter } from './helpers';
+import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
+import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
interface OwnProps {
timelineId: TimelineIdLiteral;
@@ -336,6 +338,8 @@ export const AlertsTableComponent: React.FC = ({
headerFilterGroup={headerFilterGroup}
id={timelineId}
onRuleChange={onRuleChange}
+ renderCellValue={DefaultCellRenderer}
+ rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.detections}
start={from}
utilityBar={utilityBarCallback}
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx
index 922d52b6cfe5a..f88709e6e95ac 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx
@@ -21,6 +21,8 @@ import { useGlobalFullScreen } from '../../../common/containers/use_full_screen'
import * as i18n from '../translations';
import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
+import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
+import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
const EVENTS_HISTOGRAM_ID = 'eventsHistogramQuery';
@@ -96,6 +98,8 @@ const EventsQueryTabBodyComponent: React.FC = ({
defaultModel={eventsDefaultModel}
end={endDate}
id={TimelineId.hostsPageEvents}
+ renderCellValue={DefaultCellRenderer}
+ rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.default}
start={startDate}
pageFilters={pageFilters}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx
index e63ffedf3da7c..459706de36569 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx
@@ -14,6 +14,8 @@ import { StatefulTimeline } from '../../timeline';
import { TimelineId } from '../../../../../common/types/timeline';
import * as i18n from './translations';
import { timelineActions } from '../../../store/timeline';
+import { defaultRowRenderers } from '../../timeline/body/renderers';
+import { DefaultCellRenderer } from '../../timeline/cell_rendering/default_cell_renderer';
import { focusActiveTimelineButton } from '../../timeline/helpers';
interface FlyoutPaneComponentProps {
@@ -46,7 +48,11 @@ const FlyoutPaneComponent: React.FC = ({ timelineId })
onClose={handleClose}
size="l"
>
-
+
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap
index 72d2956bd4086..91d039a19495c 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap
@@ -22,26 +22,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 2
-
@@ -63,15 +114,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 3
-
@@ -93,15 +206,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 4
-
@@ -123,15 +298,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 5
-
@@ -153,15 +390,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 6
-
@@ -183,15 +482,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 7
-
@@ -213,15 +574,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 8
-
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.test.tsx
index f20978c6ba726..234e28e6231c5 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.test.tsx
@@ -9,10 +9,10 @@ import { shallow } from 'enzyme';
import React from 'react';
+import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
import '../../../../../common/mock/match_media';
import { mockTimelineData } from '../../../../../common/mock';
import { defaultHeaders } from '../column_headers/default_headers';
-import { columnRenderers } from '../renderers';
import { DataDrivenColumns } from '.';
@@ -25,11 +25,11 @@ describe('Columns', () => {
ariaRowindex={2}
_id={mockTimelineData[0]._id}
columnHeaders={headersSansTimestamp}
- columnRenderers={columnRenderers}
data={mockTimelineData[0].data}
ecsData={mockTimelineData[0].ecs}
hasRowRenderers={false}
notesCount={0}
+ renderCellValue={DefaultCellRenderer}
timelineId="test"
/>
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx
index 5aba562749f01..aeb9af46ea2ec 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx
@@ -9,6 +9,7 @@ import { EuiScreenReaderOnly } from '@elastic/eui';
import React from 'react';
import { getOr } from 'lodash/fp';
+import { CellValueElementProps } from '../../cell_rendering';
import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '../../../../../common/components/drag_and_drop/helpers';
import { Ecs } from '../../../../../../common/ecs';
import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline';
@@ -16,20 +17,19 @@ import { TimelineTabs } from '../../../../../../common/types/timeline';
import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model';
import { ARIA_COLUMN_INDEX_OFFSET } from '../../helpers';
import { EventsTd, EVENTS_TD_CLASS_NAME, EventsTdContent, EventsTdGroupData } from '../../styles';
-import { ColumnRenderer } from '../renderers/column_renderer';
-import { getColumnRenderer } from '../renderers/get_column_renderer';
+import { StatefulCell } from './stateful_cell';
import * as i18n from './translations';
interface Props {
_id: string;
ariaRowindex: number;
columnHeaders: ColumnHeaderOptions[];
- columnRenderers: ColumnRenderer[];
data: TimelineNonEcsData[];
ecsData: Ecs;
hasRowRenderers: boolean;
notesCount: number;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
tabType?: TimelineTabs;
timelineId: string;
}
@@ -82,11 +82,11 @@ export const DataDrivenColumns = React.memo(
_id,
ariaRowindex,
columnHeaders,
- columnRenderers,
data,
ecsData,
hasRowRenderers,
notesCount,
+ renderCellValue,
tabType,
timelineId,
}) => (
@@ -105,18 +105,16 @@ export const DataDrivenColumns = React.memo(
{i18n.YOU_ARE_IN_A_TABLE_CELL({ row: ariaRowindex, column: i + 2 })}
- {getColumnRenderer(header.id, columnRenderers, data).renderColumn({
- columnName: header.id,
- eventId: _id,
- field: header,
- linkValues: getOr([], header.linkField ?? '', ecsData),
- timelineId: tabType != null ? `${timelineId}-${tabType}` : timelineId,
- truncate: true,
- values: getMappedNonEcsValue({
- data,
- fieldName: header.id,
- }),
- })}
+
>
{hasRowRenderers ? (
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/stateful_cell.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/stateful_cell.test.tsx
new file mode 100644
index 0000000000000..3c75bc7fb2649
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/stateful_cell.test.tsx
@@ -0,0 +1,171 @@
+/*
+ * 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 { mount } from 'enzyme';
+import { cloneDeep } from 'lodash/fp';
+import React, { useEffect } from 'react';
+
+import { CellValueElementProps } from '../../cell_rendering';
+import { defaultHeaders, mockTimelineData } from '../../../../../common/mock';
+import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline';
+import { TimelineTabs } from '../../../../../../common/types/timeline';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
+
+import { StatefulCell } from './stateful_cell';
+import { getMappedNonEcsValue } from '.';
+
+/**
+ * This (test) component implement's `EuiDataGrid`'s `renderCellValue` interface,
+ * as documented here: https://elastic.github.io/eui/#/tabular-content/data-grid
+ *
+ * Its `CellValueElementProps` props are a superset of `EuiDataGridCellValueElementProps`.
+ * The `setCellProps` function, defined by the `EuiDataGridCellValueElementProps` interface,
+ * is typically called in a `useEffect`, as illustrated by `EuiDataGrid`'s code sandbox example:
+ * https://codesandbox.io/s/zhxmo
+ */
+const RenderCellValue: React.FC = ({ columnId, data, setCellProps }) => {
+ useEffect(() => {
+ // branching logic that conditionally renders a specific cell green:
+ if (columnId === defaultHeaders[0].id) {
+ const value = getMappedNonEcsValue({
+ data,
+ fieldName: columnId,
+ });
+
+ if (value?.length) {
+ setCellProps({
+ style: {
+ backgroundColor: 'green',
+ },
+ });
+ }
+ }
+ }, [columnId, data, setCellProps]);
+
+ return (
+
+ {getMappedNonEcsValue({
+ data,
+ fieldName: columnId,
+ })}
+
+ );
+};
+
+describe('StatefulCell', () => {
+ const ariaRowindex = 123;
+ const eventId = '_id-123';
+ const linkValues = ['foo', 'bar', '@baz'];
+ const tabType = TimelineTabs.query;
+ const timelineId = 'test';
+
+ let header: ColumnHeaderOptions;
+ let data: TimelineNonEcsData[];
+ beforeEach(() => {
+ data = cloneDeep(mockTimelineData[0].data);
+ header = cloneDeep(defaultHeaders[0]);
+ });
+
+ test('it invokes renderCellValue with the expected arguments when tabType is specified', () => {
+ const renderCellValue = jest.fn();
+
+ mount(
+
+ );
+
+ expect(renderCellValue).toBeCalledWith(
+ expect.objectContaining({
+ columnId: header.id,
+ eventId,
+ data,
+ header,
+ isExpandable: true,
+ isExpanded: false,
+ isDetails: false,
+ linkValues,
+ rowIndex: ariaRowindex - 1,
+ timelineId: `${timelineId}-${tabType}`,
+ })
+ );
+ });
+
+ test('it invokes renderCellValue with the expected arguments when tabType is NOT specified', () => {
+ const renderCellValue = jest.fn();
+
+ mount(
+
+ );
+
+ expect(renderCellValue).toBeCalledWith(
+ expect.objectContaining({
+ columnId: header.id,
+ eventId,
+ data,
+ header,
+ isExpandable: true,
+ isExpanded: false,
+ isDetails: false,
+ linkValues,
+ rowIndex: ariaRowindex - 1,
+ timelineId,
+ })
+ );
+ });
+
+ test('it renders the React.Node returned by renderCellValue', () => {
+ const renderCellValue = () => ;
+
+ const wrapper = mount(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="renderCellValue"]').exists()).toBe(true);
+ });
+
+ test("it renders a div with the styles set by `renderCellValue`'s `setCellProps` argument", () => {
+ const wrapper = mount(
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="statefulCell"]').getDOMNode().getAttribute('style')
+ ).toEqual('background-color: green;');
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/stateful_cell.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/stateful_cell.tsx
new file mode 100644
index 0000000000000..83f603364ba8c
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/stateful_cell.tsx
@@ -0,0 +1,63 @@
+/*
+ * 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, { HTMLAttributes, useState } from 'react';
+
+import { CellValueElementProps } from '../../cell_rendering';
+import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline';
+import { TimelineTabs } from '../../../../../../common/types/timeline';
+import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model';
+
+export interface CommonProps {
+ className?: string;
+ 'aria-label'?: string;
+ 'data-test-subj'?: string;
+}
+
+const StatefulCellComponent = ({
+ ariaRowindex,
+ data,
+ header,
+ eventId,
+ linkValues,
+ renderCellValue,
+ tabType,
+ timelineId,
+}: {
+ ariaRowindex: number;
+ data: TimelineNonEcsData[];
+ header: ColumnHeaderOptions;
+ eventId: string;
+ linkValues: string[] | undefined;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ tabType?: TimelineTabs;
+ timelineId: string;
+}) => {
+ const [cellProps, setCellProps] = useState>({});
+
+ return (
+
+ {renderCellValue({
+ columnId: header.id,
+ eventId,
+ data,
+ header,
+ isExpandable: true,
+ isExpanded: false,
+ isDetails: false,
+ linkValues,
+ rowIndex: ariaRowindex - 1,
+ setCellProps,
+ timelineId: tabType != null ? `${timelineId}-${tabType}` : timelineId,
+ })}
+
+ );
+};
+
+StatefulCellComponent.displayName = 'StatefulCellComponent';
+
+export const StatefulCell = React.memo(StatefulCellComponent);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx
index abdfda3272d6a..74724dedf4d11 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx
@@ -14,6 +14,7 @@ import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../constants';
import * as i18n from '../translations';
import { EventColumnView } from './event_column_view';
+import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
import { TimelineTabs, TimelineType, TimelineId } from '../../../../../../common/types/timeline';
import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector';
@@ -56,6 +57,7 @@ describe('EventColumnView', () => {
onRowSelected: jest.fn(),
onUnPinEvent: jest.fn(),
refetch: jest.fn(),
+ renderCellValue: DefaultCellRenderer,
selectedEventIds: {},
showCheckboxes: false,
showNotes: false,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
index c6caf0a7b5b15..a0a0aeb23e8f7 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
@@ -7,6 +7,7 @@
import React, { useCallback, useMemo } from 'react';
+import { CellValueElementProps } from '../../cell_rendering';
import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector';
import { Ecs } from '../../../../../../common/ecs';
import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline';
@@ -21,7 +22,6 @@ import {
getPinOnClick,
InvestigateInResolverAction,
} from '../helpers';
-import { ColumnRenderer } from '../renderers/column_renderer';
import { AlertContextMenu } from '../../../../../detections/components/alerts_table/timeline_actions/alert_context_menu';
import { InvestigateInTimelineAction } from '../../../../../detections/components/alerts_table/timeline_actions/investigate_in_timeline_action';
import { AddEventNoteAction } from '../actions/add_note_icon_item';
@@ -38,7 +38,6 @@ interface Props {
actionsColumnWidth: number;
ariaRowindex: number;
columnHeaders: ColumnHeaderOptions[];
- columnRenderers: ColumnRenderer[];
data: TimelineNonEcsData[];
ecsData: Ecs;
eventIdToNoteIds: Readonly>;
@@ -51,6 +50,7 @@ interface Props {
onRowSelected: OnRowSelected;
onUnPinEvent: OnUnPinEvent;
refetch: inputsModel.Refetch;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
onRuleChange?: () => void;
hasRowRenderers: boolean;
selectedEventIds: Readonly>;
@@ -69,7 +69,6 @@ export const EventColumnView = React.memo(
actionsColumnWidth,
ariaRowindex,
columnHeaders,
- columnRenderers,
data,
ecsData,
eventIdToNoteIds,
@@ -84,6 +83,7 @@ export const EventColumnView = React.memo(
refetch,
hasRowRenderers,
onRuleChange,
+ renderCellValue,
selectedEventIds,
showCheckboxes,
showNotes,
@@ -227,11 +227,11 @@ export const EventColumnView = React.memo(
_id={id}
ariaRowindex={ariaRowindex}
columnHeaders={columnHeaders}
- columnRenderers={columnRenderers}
data={data}
ecsData={ecsData}
hasRowRenderers={hasRowRenderers}
notesCount={notesCount}
+ renderCellValue={renderCellValue}
tabType={tabType}
timelineId={timelineId}
/>
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx
index d76b5834c233e..7f8a3a92fb5ba 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx
@@ -8,6 +8,7 @@
import React from 'react';
import { isEmpty } from 'lodash';
+import { CellValueElementProps } from '../../cell_rendering';
import { inputsModel } from '../../../../../common/store';
import { BrowserFields } from '../../../../../common/containers/source';
import {
@@ -18,7 +19,6 @@ import { TimelineTabs } from '../../../../../../common/types/timeline';
import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model';
import { OnRowSelected } from '../../events';
import { EventsTbody } from '../../styles';
-import { ColumnRenderer } from '../renderers/column_renderer';
import { RowRenderer } from '../renderers/row_renderer';
import { StatefulEvent } from './stateful_event';
import { eventIsPinned } from '../helpers';
@@ -30,7 +30,6 @@ interface Props {
actionsColumnWidth: number;
browserFields: BrowserFields;
columnHeaders: ColumnHeaderOptions[];
- columnRenderers: ColumnRenderer[];
containerRef: React.MutableRefObject;
data: TimelineItem[];
eventIdToNoteIds: Readonly>;
@@ -41,6 +40,7 @@ interface Props {
onRowSelected: OnRowSelected;
pinnedEventIds: Readonly>;
refetch: inputsModel.Refetch;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
onRuleChange?: () => void;
rowRenderers: RowRenderer[];
selectedEventIds: Readonly>;
@@ -52,7 +52,6 @@ const EventsComponent: React.FC = ({
actionsColumnWidth,
browserFields,
columnHeaders,
- columnRenderers,
containerRef,
data,
eventIdToNoteIds,
@@ -64,6 +63,7 @@ const EventsComponent: React.FC = ({
pinnedEventIds,
refetch,
onRuleChange,
+ renderCellValue,
rowRenderers,
selectedEventIds,
showCheckboxes,
@@ -76,7 +76,6 @@ const EventsComponent: React.FC = ({
ariaRowindex={i + ARIA_ROW_INDEX_OFFSET}
browserFields={browserFields}
columnHeaders={columnHeaders}
- columnRenderers={columnRenderers}
containerRef={containerRef}
event={event}
eventIdToNoteIds={eventIdToNoteIds}
@@ -88,6 +87,7 @@ const EventsComponent: React.FC = ({
lastFocusedAriaColindex={lastFocusedAriaColindex}
loadingEventIds={loadingEventIds}
onRowSelected={onRowSelected}
+ renderCellValue={renderCellValue}
refetch={refetch}
rowRenderers={rowRenderers}
onRuleChange={onRuleChange}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
index 4191badd6b03f..97ab088b61583 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
@@ -8,6 +8,7 @@
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
+import { CellValueElementProps } from '../../cell_rendering';
import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector';
import {
TimelineExpandedDetailType,
@@ -23,7 +24,6 @@ import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/mod
import { OnPinEvent, OnRowSelected } from '../../events';
import { STATEFUL_EVENT_CSS_CLASS_NAME } from '../../helpers';
import { EventsTrGroup, EventsTrSupplement, EventsTrSupplementContainer } from '../../styles';
-import { ColumnRenderer } from '../renderers/column_renderer';
import { RowRenderer } from '../renderers/row_renderer';
import { isEventBuildingBlockType, getEventType, isEvenEqlSequence } from '../helpers';
import { NoteCards } from '../../../notes/note_cards';
@@ -45,7 +45,6 @@ interface Props {
containerRef: React.MutableRefObject;
browserFields: BrowserFields;
columnHeaders: ColumnHeaderOptions[];
- columnRenderers: ColumnRenderer[];
event: TimelineItem;
eventIdToNoteIds: Readonly>;
isEventViewer?: boolean;
@@ -56,6 +55,7 @@ interface Props {
refetch: inputsModel.Refetch;
ariaRowindex: number;
onRuleChange?: () => void;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
rowRenderers: RowRenderer[];
selectedEventIds: Readonly>;
showCheckboxes: boolean;
@@ -77,7 +77,6 @@ const StatefulEventComponent: React.FC = ({
browserFields,
containerRef,
columnHeaders,
- columnRenderers,
event,
eventIdToNoteIds,
isEventViewer = false,
@@ -86,8 +85,9 @@ const StatefulEventComponent: React.FC = ({
loadingEventIds,
onRowSelected,
refetch,
- onRuleChange,
+ renderCellValue,
rowRenderers,
+ onRuleChange,
ariaRowindex,
selectedEventIds,
showCheckboxes,
@@ -259,7 +259,6 @@ const StatefulEventComponent: React.FC = ({
actionsColumnWidth={actionsColumnWidth}
ariaRowindex={ariaRowindex}
columnHeaders={columnHeaders}
- columnRenderers={columnRenderers}
data={event.data}
ecsData={event.ecs}
eventIdToNoteIds={eventIdToNoteIds}
@@ -273,6 +272,7 @@ const StatefulEventComponent: React.FC = ({
onRowSelected={onRowSelected}
onUnPinEvent={onUnPinEvent}
refetch={refetch}
+ renderCellValue={renderCellValue}
onRuleChange={onRuleChange}
selectedEventIds={selectedEventIds}
showCheckboxes={showCheckboxes}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx
index 723e4c3de5c27..76dbfc553d228 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx
@@ -8,6 +8,7 @@
import React from 'react';
import { waitFor } from '@testing-library/react';
+import { DefaultCellRenderer } from '../cell_rendering/default_cell_renderer';
import '../../../../common/mock/match_media';
import { mockBrowserFields } from '../../../../common/containers/source/mock';
import { Direction } from '../../../../../common/search_strategy';
@@ -19,6 +20,7 @@ import { Sort } from './sort';
import { useMountAppended } from '../../../../common/utils/use_mount_appended';
import { timelineActions } from '../../../store/timeline';
import { TimelineTabs } from '../../../../../common/types/timeline';
+import { defaultRowRenderers } from './renderers';
const mockSort: Sort[] = [
{
@@ -39,8 +41,8 @@ jest.mock('react-redux', () => {
});
jest.mock('../../../../common/hooks/use_selector', () => ({
- useShallowEqualSelector: jest.fn().mockReturnValue(mockTimelineModel),
- useDeepEqualSelector: jest.fn().mockReturnValue(mockTimelineModel),
+ useShallowEqualSelector: () => mockTimelineModel,
+ useDeepEqualSelector: () => mockTimelineModel,
}));
jest.mock('../../../../common/components/link_to');
@@ -76,6 +78,8 @@ describe('Body', () => {
loadingEventIds: [],
pinnedEventIds: {},
refetch: jest.fn(),
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
selectedEventIds: {},
setSelected: (jest.fn() as unknown) as StatefulBodyProps['setSelected'],
sort: mockSort,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx
index 4df6eb16ccb62..59c0610c544e9 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx
@@ -11,6 +11,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { connect, ConnectedProps } from 'react-redux';
import deepEqual from 'fast-deep-equal';
+import { CellValueElementProps } from '../cell_rendering';
import { RowRendererId, TimelineId, TimelineTabs } from '../../../../../common/types/timeline';
import {
FIRST_ARIA_INDEX,
@@ -28,9 +29,9 @@ import { timelineActions, timelineSelectors } from '../../../store/timeline';
import { OnRowSelected, OnSelectAll } from '../events';
import { getActionsColumnWidth, getColumnHeaders } from './column_headers/helpers';
import { getEventIdToDataMapping } from './helpers';
-import { columnRenderers, rowRenderers } from './renderers';
import { Sort } from './sort';
import { plainRowRenderer } from './renderers/plain_row_renderer';
+import { RowRenderer } from './renderers/row_renderer';
import { EventsTable, TimelineBody, TimelineBodyGlobalStyle } from '../styles';
import { ColumnHeaders } from './column_headers';
import { Events } from './events';
@@ -44,6 +45,8 @@ interface OwnProps {
isEventViewer?: boolean;
sort: Sort[];
refetch: inputsModel.Refetch;
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
tabType: TimelineTabs;
totalPages: number;
onRuleChange?: () => void;
@@ -83,6 +86,8 @@ export const BodyComponent = React.memo(
onRuleChange,
showCheckboxes,
refetch,
+ renderCellValue,
+ rowRenderers,
sort,
tabType,
totalPages,
@@ -141,7 +146,7 @@ export const BodyComponent = React.memo(
if (!excludedRowRendererIds) return rowRenderers;
return rowRenderers.filter((rowRenderer) => !excludedRowRendererIds.includes(rowRenderer.id));
- }, [excludedRowRendererIds]);
+ }, [excludedRowRendererIds, rowRenderers]);
const actionsColumnWidth = useMemo(
() =>
@@ -209,7 +214,6 @@ export const BodyComponent = React.memo(
actionsColumnWidth={actionsColumnWidth}
browserFields={browserFields}
columnHeaders={columnHeaders}
- columnRenderers={columnRenderers}
data={data}
eventIdToNoteIds={eventIdToNoteIds}
id={id}
@@ -219,6 +223,7 @@ export const BodyComponent = React.memo(
onRowSelected={onRowSelected}
pinnedEventIds={pinnedEventIds}
refetch={refetch}
+ renderCellValue={renderCellValue}
rowRenderers={enabledRowRenderers}
onRuleChange={onRuleChange}
selectedEventIds={selectedEventIds}
@@ -244,6 +249,8 @@ export const BodyComponent = React.memo(
prevProps.id === nextProps.id &&
prevProps.isEventViewer === nextProps.isEventViewer &&
prevProps.isSelectAllChecked === nextProps.isSelectAllChecked &&
+ prevProps.renderCellValue === nextProps.renderCellValue &&
+ prevProps.rowRenderers === nextProps.rowRenderers &&
prevProps.showCheckboxes === nextProps.showCheckboxes &&
prevProps.tabType === nextProps.tabType
);
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 6e36102da2de9..b92a4381d837b 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
@@ -17,7 +17,7 @@ import { mockTimelineData } from '../../../../../common/mock';
import { TestProviders } from '../../../../../common/mock/test_providers';
import { useMountAppended } from '../../../../../common/utils/use_mount_appended';
-import { rowRenderers } from '.';
+import { defaultRowRenderers } from '.';
import { getRowRenderer } from './get_row_renderer';
jest.mock('@elastic/eui', () => {
@@ -48,7 +48,7 @@ describe('get_column_renderer', () => {
});
test('renders correctly against snapshot', () => {
- const rowRenderer = getRowRenderer(nonSuricata, rowRenderers);
+ const rowRenderer = getRowRenderer(nonSuricata, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: nonSuricata,
@@ -60,7 +60,7 @@ describe('get_column_renderer', () => {
});
test('should render plain row data when it is a non suricata row', () => {
- const rowRenderer = getRowRenderer(nonSuricata, rowRenderers);
+ const rowRenderer = getRowRenderer(nonSuricata, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: nonSuricata,
@@ -75,7 +75,7 @@ describe('get_column_renderer', () => {
});
test('should render a suricata row data when it is a suricata row', () => {
- const rowRenderer = getRowRenderer(suricata, rowRenderers);
+ const rowRenderer = getRowRenderer(suricata, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: suricata,
@@ -93,7 +93,7 @@ describe('get_column_renderer', () => {
test('should render a suricata row data if event.category is network_traffic', () => {
suricata.event = { ...suricata.event, ...{ category: ['network_traffic'] } };
- const rowRenderer = getRowRenderer(suricata, rowRenderers);
+ const rowRenderer = getRowRenderer(suricata, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: suricata,
@@ -111,7 +111,7 @@ describe('get_column_renderer', () => {
test('should render a zeek row data if event.category is network_traffic', () => {
zeek.event = { ...zeek.event, ...{ category: ['network_traffic'] } };
- const rowRenderer = getRowRenderer(zeek, rowRenderers);
+ const rowRenderer = getRowRenderer(zeek, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: zeek,
@@ -129,7 +129,7 @@ describe('get_column_renderer', () => {
test('should render a system row data if event.category is network_traffic', () => {
system.event = { ...system.event, ...{ category: ['network_traffic'] } };
- const rowRenderer = getRowRenderer(system, rowRenderers);
+ const rowRenderer = getRowRenderer(system, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: system,
@@ -147,7 +147,7 @@ describe('get_column_renderer', () => {
test('should render a auditd row data if event.category is network_traffic', () => {
auditd.event = { ...auditd.event, ...{ category: ['network_traffic'] } };
- const rowRenderer = getRowRenderer(auditd, rowRenderers);
+ const rowRenderer = getRowRenderer(auditd, defaultRowRenderers);
const row = rowRenderer?.renderRow({
browserFields: mockBrowserFields,
data: auditd,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts
index 671d183c62e6d..209a9414f62f1 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/index.ts
@@ -23,7 +23,7 @@ import { systemRowRenderers } from './system/generic_row_renderer';
// Suricata and Zeek which is why Suricata and Zeek are above it. The
// plainRowRenderer always returns true to everything which is why it always
// should be last.
-export const rowRenderers: RowRenderer[] = [
+export const defaultRowRenderers: RowRenderer[] = [
...auditdRowRenderers,
...systemRowRenderers,
suricataRowRenderer,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.test.tsx
new file mode 100644
index 0000000000000..5ac1dcf8805cf
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.test.tsx
@@ -0,0 +1,107 @@
+/*
+ * 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 { mount } from 'enzyme';
+import { cloneDeep } from 'lodash/fp';
+import React from 'react';
+
+import { columnRenderers } from '../body/renderers';
+import { getColumnRenderer } from '../body/renderers/get_column_renderer';
+import { DragDropContextWrapper } from '../../../../common/components/drag_and_drop/drag_drop_context_wrapper';
+import { DroppableWrapper } from '../../../../common/components/drag_and_drop/droppable_wrapper';
+import { mockBrowserFields } from '../../../../common/containers/source/mock';
+import { defaultHeaders, mockTimelineData, TestProviders } from '../../../../common/mock';
+import { DefaultCellRenderer } from './default_cell_renderer';
+
+jest.mock('../body/renderers/get_column_renderer');
+const getColumnRendererMock = getColumnRenderer as jest.Mock;
+const mockImplementation = {
+ renderColumn: jest.fn(),
+};
+
+describe('DefaultCellRenderer', () => {
+ const columnId = 'signal.rule.risk_score';
+ const eventId = '_id-123';
+ const isDetails = true;
+ const isExpandable = true;
+ const isExpanded = true;
+ const linkValues = ['foo', 'bar', '@baz'];
+ const rowIndex = 3;
+ const setCellProps = jest.fn();
+ const timelineId = 'test';
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ getColumnRendererMock.mockImplementation(() => mockImplementation);
+ });
+
+ test('it invokes `getColumnRenderer` with the expected arguments', () => {
+ const data = cloneDeep(mockTimelineData[0].data);
+ const header = cloneDeep(defaultHeaders[0]);
+
+ mount(
+
+
+
+
+
+
+
+ );
+
+ expect(getColumnRenderer).toBeCalledWith(header.id, columnRenderers, data);
+ });
+
+ test('it invokes `renderColumn` with the expected arguments', () => {
+ const data = cloneDeep(mockTimelineData[0].data);
+ const header = cloneDeep(defaultHeaders[0]);
+
+ mount(
+
+
+
+
+
+
+
+ );
+
+ expect(mockImplementation.renderColumn).toBeCalledWith({
+ columnName: header.id,
+ eventId,
+ field: header,
+ linkValues,
+ timelineId,
+ truncate: true,
+ values: ['2018-11-05T19:03:25.937Z'],
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx
new file mode 100644
index 0000000000000..8d8f821107e7b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx
@@ -0,0 +1,39 @@
+/*
+ * 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 { getMappedNonEcsValue } from '../body/data_driven_columns';
+import { columnRenderers } from '../body/renderers';
+import { getColumnRenderer } from '../body/renderers/get_column_renderer';
+
+import { CellValueElementProps } from '.';
+
+export const DefaultCellRenderer: React.FC = ({
+ columnId,
+ data,
+ eventId,
+ header,
+ linkValues,
+ setCellProps,
+ timelineId,
+}) => (
+ <>
+ {getColumnRenderer(header.id, columnRenderers, data).renderColumn({
+ columnName: header.id,
+ eventId,
+ field: header,
+ linkValues,
+ timelineId,
+ truncate: true,
+ values: getMappedNonEcsValue({
+ data,
+ fieldName: header.id,
+ }),
+ })}
+ >
+);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/index.tsx
new file mode 100644
index 0000000000000..03e444e3a9afd
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/index.tsx
@@ -0,0 +1,20 @@
+/*
+ * 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 { EuiDataGridCellValueElementProps } from '@elastic/eui';
+
+import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline';
+import { ColumnHeaderOptions } from '../../../store/timeline/model';
+
+/** The following props are provided to the function called by `renderCellValue` */
+export type CellValueElementProps = EuiDataGridCellValueElementProps & {
+ data: TimelineNonEcsData[];
+ eventId: string; // _id
+ header: ColumnHeaderOptions;
+ linkValues: string[] | undefined;
+ timelineId: string;
+};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap
index 2595f29144b80..7d237ecaf92df 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap
@@ -140,6 +140,986 @@ In other use cases the message field can be used to concatenate different values
]
}
onEventClosed={[MockFunction]}
+ renderCellValue={[Function]}
+ rowRenderers={
+ Array [
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_dns",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "library",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "registry",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "suricata",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "zeek",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "netflow",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ ]
+ }
showExpandedDetails={false}
start="2018-03-23T18:49:23.132Z"
timelineId="test"
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx
index 7b77a915f2f05..e13bed1e2eff6 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx
@@ -9,6 +9,8 @@ import { shallow } from 'enzyme';
import React from 'react';
import useResizeObserver from 'use-resize-observer/polyfilled';
+import { defaultRowRenderers } from '../body/renderers';
+import { DefaultCellRenderer } from '../cell_rendering/default_cell_renderer';
import { defaultHeaders, mockTimelineData } from '../../../../common/mock';
import '../../../../common/mock/match_media';
import { TestProviders } from '../../../../common/mock/test_providers';
@@ -94,6 +96,8 @@ describe('Timeline', () => {
itemsPerPage: 5,
itemsPerPageOptions: [5, 10, 20],
onEventClosed: jest.fn(),
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
showExpandedDetails: false,
start: startDate,
timerangeKind: 'absolute',
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx
index 51f8db4e796e5..6bb19ce5a6852 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx
@@ -22,10 +22,12 @@ import deepEqual from 'fast-deep-equal';
import { InPortal } from 'react-reverse-portal';
import { timelineActions, timelineSelectors } from '../../../store/timeline';
+import { CellValueElementProps } from '../cell_rendering';
import { TimelineItem } from '../../../../../common/search_strategy';
import { useTimelineEvents } from '../../../containers/index';
import { defaultHeaders } from '../body/column_headers/default_headers';
import { StatefulBody } from '../body';
+import { RowRenderer } from '../body/renderers/row_renderer';
import { Footer, footerHeight } from '../footer';
import { calculateTotalPages } from '../helpers';
import { TimelineRefetch } from '../refetch_timeline';
@@ -133,6 +135,8 @@ const isTimerangeSame = (prevProps: Props, nextProps: Props) =>
prevProps.timerangeKind === nextProps.timerangeKind;
interface OwnProps {
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
timelineId: string;
}
@@ -154,6 +158,8 @@ export const EqlTabContentComponent: React.FC = ({
itemsPerPage,
itemsPerPageOptions,
onEventClosed,
+ renderCellValue,
+ rowRenderers,
showExpandedDetails,
start,
timerangeKind,
@@ -284,6 +290,8 @@ export const EqlTabContentComponent: React.FC = ({
data={isBlankTimeline ? EMPTY_EVENTS : events}
id={timelineId}
refetch={refetch}
+ renderCellValue={renderCellValue}
+ rowRenderers={rowRenderers}
sort={NO_SORTING}
tabType={TimelineTabs.eql}
totalPages={calculateTotalPages({
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx
index ee2ce8cf8103b..db7a3cc3c9900 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx
@@ -17,7 +17,9 @@ import { mockIndexNames, mockIndexPattern, TestProviders } from '../../../common
import { StatefulTimeline, Props as StatefulTimelineOwnProps } from './index';
import { useTimelineEvents } from '../../containers/index';
+import { DefaultCellRenderer } from './cell_rendering/default_cell_renderer';
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from './styles';
+import { defaultRowRenderers } from './body/renderers';
jest.mock('../../containers/index', () => ({
useTimelineEvents: jest.fn(),
@@ -63,6 +65,8 @@ jest.mock('../../../common/containers/sourcerer', () => {
});
describe('StatefulTimeline', () => {
const props: StatefulTimelineOwnProps = {
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
timelineId: TimelineId.test,
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx
index 6d2374dd8eef7..367357511c9c8 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx
@@ -14,6 +14,8 @@ import styled from 'styled-components';
import { timelineActions, timelineSelectors } from '../../store/timeline';
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
import { defaultHeaders } from './body/column_headers/default_headers';
+import { RowRenderer } from './body/renderers/row_renderer';
+import { CellValueElementProps } from './cell_rendering';
import { isTab } from '../../../common/components/accessibility/helpers';
import { useSourcererScope } from '../../../common/containers/sourcerer';
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
@@ -36,10 +38,12 @@ const TimelineTemplateBadge = styled.div`
`;
export interface Props {
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
timelineId: TimelineId;
}
-const TimelineSavingProgressComponent: React.FC = ({ timelineId }) => {
+const TimelineSavingProgressComponent: React.FC<{ timelineId: TimelineId }> = ({ timelineId }) => {
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const isSaving = useShallowEqualSelector(
(state) => (getTimeline(state, timelineId) ?? timelineDefaults).isSaving
@@ -50,7 +54,11 @@ const TimelineSavingProgressComponent: React.FC = ({ timelineId }) => {
const TimelineSavingProgress = React.memo(TimelineSavingProgressComponent);
-const StatefulTimelineComponent: React.FC = ({ timelineId }) => {
+const StatefulTimelineComponent: React.FC = ({
+ renderCellValue,
+ rowRenderers,
+ timelineId,
+}) => {
const dispatch = useDispatch();
const containerElement = useRef(null);
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
@@ -131,6 +139,8 @@ const StatefulTimelineComponent: React.FC = ({ timelineId }) => {
{
timelineId: TimelineId.test,
itemsPerPage: 5,
itemsPerPageOptions: [5, 10, 20],
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
sort,
pinnedEventIds: {},
showExpandedDetails: false,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx
index a19a61d8268ff..dfc14747dacf3 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx
@@ -14,10 +14,12 @@ import { connect, ConnectedProps } from 'react-redux';
import deepEqual from 'fast-deep-equal';
import { timelineActions, timelineSelectors } from '../../../store/timeline';
+import { CellValueElementProps } from '../cell_rendering';
import { Direction } from '../../../../../common/search_strategy';
import { useTimelineEvents } from '../../../containers/index';
import { defaultHeaders } from '../body/column_headers/default_headers';
import { StatefulBody } from '../body';
+import { RowRenderer } from '../body/renderers/row_renderer';
import { Footer, footerHeight } from '../footer';
import { requiredFieldsForActions } from '../../../../detections/components/alerts_table/default_config';
import { EventDetailsWidthProvider } from '../../../../common/components/events_viewer/event_details_width_context';
@@ -87,6 +89,8 @@ const VerticalRule = styled.div`
VerticalRule.displayName = 'VerticalRule';
interface OwnProps {
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
timelineId: string;
}
@@ -106,6 +110,8 @@ export const PinnedTabContentComponent: React.FC = ({
itemsPerPageOptions,
pinnedEventIds,
onEventClosed,
+ renderCellValue,
+ rowRenderers,
showExpandedDetails,
sort,
}) => {
@@ -217,6 +223,8 @@ export const PinnedTabContentComponent: React.FC = ({
data={events}
id={timelineId}
refetch={refetch}
+ renderCellValue={renderCellValue}
+ rowRenderers={rowRenderers}
sort={sort}
tabType={TimelineTabs.pinned}
totalPages={calculateTotalPages({
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap
index 0688a10b31eef..46c85f634ff6b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap
@@ -276,6 +276,986 @@ In other use cases the message field can be used to concatenate different values
kqlMode="search"
kqlQueryExpression=""
onEventClosed={[MockFunction]}
+ renderCellValue={[Function]}
+ rowRenderers={
+ Array [
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "auditd",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_dns",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "alerts",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "library",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "registry",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_fim",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_security_event",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_file",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system_socket",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "system",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "suricata",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "zeek",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ Object {
+ "id": "netflow",
+ "isInstance": [Function],
+ "renderRow": [Function],
+ },
+ ]
+ }
show={true}
showCallOutUnauthorizedMsg={false}
showExpandedDetails={false}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx
index c7d27da64c650..ede473acbfb2a 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx
@@ -10,11 +10,13 @@ import React from 'react';
import useResizeObserver from 'use-resize-observer/polyfilled';
import { Direction } from '../../../../graphql/types';
+import { DefaultCellRenderer } from '../cell_rendering/default_cell_renderer';
import { defaultHeaders, mockTimelineData } from '../../../../common/mock';
import '../../../../common/mock/match_media';
import { TestProviders } from '../../../../common/mock/test_providers';
import { QueryTabContentComponent, Props as QueryTabContentComponentProps } from './index';
+import { defaultRowRenderers } from '../body/renderers';
import { Sort } from '../body/sort';
import { mockDataProviders } from '../data_providers/mock/mock_data_providers';
import { useMountAppended } from '../../../../common/utils/use_mount_appended';
@@ -106,6 +108,8 @@ describe('Timeline', () => {
kqlMode: 'search' as QueryTabContentComponentProps['kqlMode'],
kqlQueryExpression: '',
onEventClosed: jest.fn(),
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
showCallOutUnauthorizedMsg: false,
showExpandedDetails: false,
sort,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx
index 28fec7ded9ca2..74a0f02354219 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx
@@ -22,6 +22,8 @@ import deepEqual from 'fast-deep-equal';
import { InPortal } from 'react-reverse-portal';
import { timelineActions, timelineSelectors } from '../../../store/timeline';
+import { RowRenderer } from '../body/renderers/row_renderer';
+import { CellValueElementProps } from '../cell_rendering';
import { Direction, TimelineItem } from '../../../../../common/search_strategy';
import { useTimelineEvents } from '../../../containers/index';
import { useKibana } from '../../../../common/lib/kibana';
@@ -142,6 +144,8 @@ const compareQueryProps = (prevProps: Props, nextProps: Props) =>
deepEqual(prevProps.filters, nextProps.filters);
interface OwnProps {
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
timelineId: string;
}
@@ -164,6 +168,8 @@ export const QueryTabContentComponent: React.FC = ({
kqlMode,
kqlQueryExpression,
onEventClosed,
+ renderCellValue,
+ rowRenderers,
show,
showCallOutUnauthorizedMsg,
showExpandedDetails,
@@ -330,6 +336,8 @@ export const QueryTabContentComponent: React.FC = ({
data={isBlankTimeline ? EMPTY_EVENTS : events}
id={timelineId}
refetch={refetch}
+ renderCellValue={renderCellValue}
+ rowRenderers={rowRenderers}
sort={sort}
tabType={TimelineTabs.query}
totalPages={calculateTotalPages({
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
index f29211d519841..76a2ad0960322 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
@@ -20,6 +20,8 @@ import {
TimelineEventsCountBadge,
} from '../../../../common/hooks/use_timeline_events_count';
import { timelineActions } from '../../../store/timeline';
+import { RowRenderer } from '../body/renderers/row_renderer';
+import { CellValueElementProps } from '../cell_rendering';
import {
getActiveTabSelector,
getNoteIdsSelector,
@@ -46,6 +48,8 @@ const NotesTabContent = lazy(() => import('../notes_tab_content'));
const PinnedTabContent = lazy(() => import('../pinned_tab_content'));
interface BasicTimelineTab {
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
setTimelineFullScreen?: (fullScreen: boolean) => void;
timelineFullScreen?: boolean;
timelineId: TimelineId;
@@ -53,16 +57,32 @@ interface BasicTimelineTab {
graphEventId?: string;
}
-const QueryTab: React.FC<{ timelineId: TimelineId }> = memo(({ timelineId }) => (
+const QueryTab: React.FC<{
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
+ timelineId: TimelineId;
+}> = memo(({ renderCellValue, rowRenderers, timelineId }) => (
}>
-
+
));
QueryTab.displayName = 'QueryTab';
-const EqlTab: React.FC<{ timelineId: TimelineId }> = memo(({ timelineId }) => (
+const EqlTab: React.FC<{
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
+ timelineId: TimelineId;
+}> = memo(({ renderCellValue, rowRenderers, timelineId }) => (
}>
-
+
));
EqlTab.displayName = 'EqlTab';
@@ -81,9 +101,17 @@ const NotesTab: React.FC<{ timelineId: TimelineId }> = memo(({ timelineId }) =>
));
NotesTab.displayName = 'NotesTab';
-const PinnedTab: React.FC<{ timelineId: TimelineId }> = memo(({ timelineId }) => (
+const PinnedTab: React.FC<{
+ renderCellValue: (props: CellValueElementProps) => React.ReactNode;
+ rowRenderers: RowRenderer[];
+ timelineId: TimelineId;
+}> = memo(({ renderCellValue, rowRenderers, timelineId }) => (
}>
-
+
));
PinnedTab.displayName = 'PinnedTab';
@@ -91,7 +119,7 @@ PinnedTab.displayName = 'PinnedTab';
type ActiveTimelineTabProps = BasicTimelineTab & { activeTimelineTab: TimelineTabs };
const ActiveTimelineTab = memo(
- ({ activeTimelineTab, timelineId, timelineType }) => {
+ ({ activeTimelineTab, renderCellValue, rowRenderers, timelineId, timelineType }) => {
const getTab = useCallback(
(tab: TimelineTabs) => {
switch (tab) {
@@ -119,14 +147,26 @@ const ActiveTimelineTab = memo(
return (
<>
-
+
-
+
{timelineType === TimelineType.default && (
-
+
)}
@@ -160,6 +200,8 @@ const StyledEuiTab = styled(EuiTab)`
`;
const TabsContentComponent: React.FC = ({
+ renderCellValue,
+ rowRenderers,
timelineId,
timelineFullScreen,
timelineType,
@@ -300,6 +342,8 @@ const TabsContentComponent: React.FC = ({
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
index 3d92397f4ab50..0b70ba8991686 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
@@ -30,11 +30,12 @@ import {
updateItemsPerPage,
updateSort,
} from './actions';
-
+import { DefaultCellRenderer } from '../../components/timeline/cell_rendering/default_cell_renderer';
import {
QueryTabContentComponent,
Props as QueryTabContentComponentProps,
} from '../../components/timeline/query_tab_content';
+import { defaultRowRenderers } from '../../components/timeline/body/renderers';
import { mockDataProviders } from '../../components/timeline/data_providers/mock/mock_data_providers';
import { Sort } from '../../components/timeline/body/sort';
import { Direction } from '../../../graphql/types';
@@ -90,6 +91,8 @@ describe('epicLocalStorage', () => {
kqlMode: 'search' as QueryTabContentComponentProps['kqlMode'],
kqlQueryExpression: '',
onEventClosed: jest.fn(),
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
showCallOutUnauthorizedMsg: false,
showExpandedDetails: false,
start: startDate,