[RAC][Alert Triage][TGrid] Update the Alerts Table (TGrid) API to implement renderCellValue (#96098)

### [RAC][Alert Triage][TGrid] Update the Alerts Table (TGrid) API to implement `renderCellValue`

- This PR implements a superset of the `renderCellValue` API from [EuiDataGrid](https://elastic.github.io/eui/#/tabular-content/data-grid) in the `TGrid` (Timeline grid) API

- The TGrid API was also updated to accept a collection of `RowRenderer`s as a prop

The API changes are summarized by the following screenshot:

<img width="1239" alt="render-cell-value" src="https://user-images.githubusercontent.com/4459398/113345484-c121f800-92ef-11eb-8a21-2b6dd8ef499b.png">

The following screenshot shows the `signal.rule.risk_score` column in the Alerts table being rendered with a green background color, using the same technique illustrated by `EuiDataGrid`'s [codesandbox example](https://codesandbox.io/s/nsmzs):

<img width="1231" alt="alerts" src="https://user-images.githubusercontent.com/4459398/113349015-a30ac680-92f4-11eb-8518-5c1b7465e76e.png">

Note: In the screenshot above, the values in the Alerts table are also _not_ rendered as draggables.

Related (RAC) issue: https://github.com/elastic/kibana/issues/94520

### Details

The `StatefulEventsViewer` has been updated to accept `renderCellValue` as a (required) prop:

```
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
```

The type definition of `CellValueElementProps` is:

```
export type CellValueElementProps = EuiDataGridCellValueElementProps & {
  data: TimelineNonEcsData[];
  eventId: string; // _id
  header: ColumnHeaderOptions;
  linkValues: string[] | undefined;
  timelineId: string;
};
```

The `CellValueElementProps` type above is a _superset_ of `EuiDataGridCellValueElementProps`. The additional properties above include the `data` returned by the TGrid when it performs IO to retrieve alerts and events.

### Using `renderCellValue` to control rendering

The internal implementation of TGrid's cell rendering didn't change with this PR; it moved to

`x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx` as shown below:

```
export const DefaultCellRenderer: React.FC<CellValueElementProps> = ({
  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,
      }),
    })}
  </>
);
```

Any usages of TGrid were updated to pass `DefaultCellRenderer` as the value of the `renderCellValue` prop, as shown in the screenshot below:

<img width="1239" alt="render-cell-value" src="https://user-images.githubusercontent.com/4459398/113345484-c121f800-92ef-11eb-8a21-2b6dd8ef499b.png">

The `EuiDataGrid` [codesandbox example](https://codesandbox.io/s/nsmzs) provides the following example `renderCellValue` implementation, which highlights a cell green based on it's numeric value:

```
  const renderCellValue = useMemo(() => {
    return ({ rowIndex, columnId, setCellProps }) => {
      const data = useContext(DataContext);
      useEffect(() => {
        if (columnId === 'amount') {
          if (data.hasOwnProperty(rowIndex)) {
            const numeric = parseFloat(
              data[rowIndex][columnId].match(/\d+\.\d+/)[0],
              10
            );
            setCellProps({
              style: {
                backgroundColor: `rgba(0, 255, 0, ${numeric * 0.0002})`,
              },
            });
          }
        }
      }, [rowIndex, columnId, setCellProps, data]);

      function getFormatted() {
        return data[rowIndex][columnId].formatted
          ? data[rowIndex][columnId].formatted
          : data[rowIndex][columnId];
      }

      return data.hasOwnProperty(rowIndex)
        ? getFormatted(rowIndex, columnId)
        : null;
    };
  }, []);
```

The sample code above formats the `amount` column in the example `EuiDataGrid` with a green `backgroundColor` based on the value of the data, as shown in the screenshot below:

<img width="956" alt="datagrid-cell-formatting" src="https://user-images.githubusercontent.com/4459398/113348300-a782af80-92f3-11eb-896a-3d92cf4b9b53.png">

To demonstrate that similar styling can be applied to TGrid using the same technique illustrated by `EuiDataGrid`'s [codesandbox example](https://codesandbox.io/s/nsmzs), we can update the `DefaultCellRenderer` in `x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx` to apply a similar technique:

```
export const DefaultCellRenderer: React.FC<CellValueElementProps> = ({
  columnId,
  data,
  eventId,
  header,
  linkValues,
  setCellProps,
  timelineId,
}) => {
  useEffect(() => {
    if (columnId === 'signal.rule.risk_score') {
      const value = getMappedNonEcsValue({
        data,
        fieldName: columnId,
      });
      if (Array.isArray(value) && value.length > 0) {
        const numeric = parseFloat(value[0]);
        setCellProps({
          style: {
            backgroundColor: `rgba(0, 255, 0, ${numeric * 0.002})`,
          },
        });
      }
    }
  }, [columnId, data, setCellProps]);

  return (
    <>
      {getMappedNonEcsValue({
        data,
        fieldName: columnId,
      })}
    </>
  );
};
```

The example code above renders the  `signal.rule.risk_score` column in the Alerts table with a green `backgroundColor` based on the value of the data, as shown in the screenshot below:

<img width="1231" alt="alerts" src="https://user-images.githubusercontent.com/4459398/113349015-a30ac680-92f4-11eb-8518-5c1b7465e76e.png">

Note: In the screenshot above, the values in the Alerts table are not rendered as draggables.
This commit is contained in:
Andrew Goldstein 2021-04-05 11:39:09 -06:00 committed by GitHub
parent 8e11e2598e
commit bcb72c596a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 4047 additions and 128 deletions

View file

@ -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<Props> = ({
defaultModel={alertsDefaultModel}
end={endDate}
id={timelineId}
renderCellValue={DefaultCellRenderer}
rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.default}
start={startDate}
/>

View file

@ -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,
};

View file

@ -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<Props> = ({
itemsPerPage,
itemsPerPageOptions,
kqlMode,
query,
onRuleChange,
query,
renderCellValue,
rowRenderers,
start,
sort,
utilityBar,
@ -310,6 +315,8 @@ const EventsViewerComponent: React.FC<Props> = ({
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<Props> = ({
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 &&

View file

@ -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,
};

View file

@ -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<Props> = ({
itemsPerPageOptions,
kqlMode,
pageFilters,
query,
onRuleChange,
query,
renderCellValue,
rowRenderers,
start,
scopeId,
showCheckboxes,
@ -129,6 +135,8 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
kqlMode={kqlMode}
query={query}
onRuleChange={onRuleChange}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
start={start}
sort={sort}
utilityBar={utilityBar}
@ -201,6 +209,7 @@ type PropsFromRedux = ConnectedProps<typeof connector>;
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) &&

View file

@ -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<AlertsTableComponentProps> = ({
headerFilterGroup={headerFilterGroup}
id={timelineId}
onRuleChange={onRuleChange}
renderCellValue={DefaultCellRenderer}
rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.detections}
start={from}
utilityBar={utilityBarCallback}

View file

@ -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<HostsComponentsQueryProps> = ({
defaultModel={eventsDefaultModel}
end={endDate}
id={TimelineId.hostsPageEvents}
renderCellValue={DefaultCellRenderer}
rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.default}
start={startDate}
pageFilters={pageFilters}

View file

@ -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<FlyoutPaneComponentProps> = ({ timelineId })
onClose={handleClose}
size="l"
>
<StatefulTimeline timelineId={timelineId} />
<StatefulTimeline
renderCellValue={DefaultCellRenderer}
rowRenderers={defaultRowRenderers}
timelineId={timelineId}
/>
</EuiFlyout>
</EuiFlyoutContainer>
);

View file

@ -22,26 +22,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 2
</p>
</EuiScreenReaderOnly>
<DraggableWrapper
dataProvider={
Object {
"and": Array [],
"enabled": true,
"excluded": true,
"id": "empty-column-renderer-draggable-wrapper-test-message-1-message",
"kqlQuery": "",
"name": "message: ",
"queryMatch": Object {
"displayValue": "—",
"field": "message",
"operator": ":*",
"value": "",
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "message",
"width": 180,
}
}
key="empty-column-renderer-draggable-wrapper-test-message-1-message"
render={[Function]}
truncate={true}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>
@ -63,15 +114,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 3
</p>
</EuiScreenReaderOnly>
<Memo(FormattedFieldValueComponent)
contextId="plain-column-renderer-formatted-field-value-test"
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
fieldFormat=""
fieldName="event.category"
fieldType=""
key="plain-column-renderer-formatted-field-value-test-event.category-1-event.category-Access-0"
truncate={true}
value="Access"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "event.category",
"width": 180,
}
}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>
@ -93,15 +206,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 4
</p>
</EuiScreenReaderOnly>
<Memo(FormattedFieldValueComponent)
contextId="plain-column-renderer-formatted-field-value-test"
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
fieldFormat=""
fieldName="event.action"
fieldType=""
key="plain-column-renderer-formatted-field-value-test-event.action-1-event.action-Action-0"
truncate={true}
value="Action"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "event.action",
"width": 180,
}
}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>
@ -123,15 +298,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 5
</p>
</EuiScreenReaderOnly>
<Memo(FormattedFieldValueComponent)
contextId="plain-column-renderer-formatted-field-value-test"
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
fieldFormat=""
fieldName="host.name"
fieldType=""
key="plain-column-renderer-formatted-field-value-test-host.name-1-host.name-apache-0"
truncate={true}
value="apache"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "host.name",
"width": 180,
}
}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>
@ -153,15 +390,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 6
</p>
</EuiScreenReaderOnly>
<Memo(FormattedFieldValueComponent)
contextId="plain-column-renderer-formatted-field-value-test"
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
fieldFormat=""
fieldName="source.ip"
fieldType=""
key="plain-column-renderer-formatted-field-value-test-source.ip-1-source.ip-192.168.0.1-0"
truncate={true}
value="192.168.0.1"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "source.ip",
"width": 180,
}
}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>
@ -183,15 +482,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 7
</p>
</EuiScreenReaderOnly>
<Memo(FormattedFieldValueComponent)
contextId="plain-column-renderer-formatted-field-value-test"
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
fieldFormat=""
fieldName="destination.ip"
fieldType=""
key="plain-column-renderer-formatted-field-value-test-destination.ip-1-destination.ip-192.168.0.3-0"
truncate={true}
value="192.168.0.3"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "destination.ip",
"width": 180,
}
}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>
@ -213,15 +574,77 @@ exports[`Columns it renders the expected columns 1`] = `
You are in a table cell. row: 2, column: 8
</p>
</EuiScreenReaderOnly>
<Memo(FormattedFieldValueComponent)
contextId="plain-column-renderer-formatted-field-value-test"
<Memo(StatefulCellComponent)
ariaRowindex={2}
data={
Array [
Object {
"field": "@timestamp",
"value": Array [
"2018-11-05T19:03:25.937Z",
],
},
Object {
"field": "event.severity",
"value": Array [
"3",
],
},
Object {
"field": "event.category",
"value": Array [
"Access",
],
},
Object {
"field": "event.action",
"value": Array [
"Action",
],
},
Object {
"field": "host.name",
"value": Array [
"apache",
],
},
Object {
"field": "source.ip",
"value": Array [
"192.168.0.1",
],
},
Object {
"field": "destination.ip",
"value": Array [
"192.168.0.3",
],
},
Object {
"field": "destination.bytes",
"value": Array [
"123456",
],
},
Object {
"field": "user.name",
"value": Array [
"john.dee",
],
},
]
}
eventId="1"
fieldFormat=""
fieldName="user.name"
fieldType=""
key="plain-column-renderer-formatted-field-value-test-user.name-1-user.name-john.dee-0"
truncate={true}
value="john.dee"
header={
Object {
"columnHeaderType": "not-filtered",
"id": "user.name",
"width": 180,
}
}
linkValues={Array []}
renderCellValue={[Function]}
timelineId="test"
/>
</styled.div>
</styled.div>

View file

@ -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"
/>
);

View file

@ -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<Props>(
_id,
ariaRowindex,
columnHeaders,
columnRenderers,
data,
ecsData,
hasRowRenderers,
notesCount,
renderCellValue,
tabType,
timelineId,
}) => (
@ -105,18 +105,16 @@ export const DataDrivenColumns = React.memo<Props>(
<EuiScreenReaderOnly data-test-subj="screenReaderOnly">
<p>{i18n.YOU_ARE_IN_A_TABLE_CELL({ row: ariaRowindex, column: i + 2 })}</p>
</EuiScreenReaderOnly>
{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,
}),
})}
<StatefulCell
ariaRowindex={ariaRowindex}
data={data}
header={header}
eventId={_id}
linkValues={getOr([], header.linkField ?? '', ecsData)}
renderCellValue={renderCellValue}
tabType={tabType}
timelineId={timelineId}
/>
</>
</EventsTdContent>
{hasRowRenderers ? (

View file

@ -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<CellValueElementProps> = ({ 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 (
<div data-test-subj="renderCellValue">
{getMappedNonEcsValue({
data,
fieldName: columnId,
})}
</div>
);
};
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(
<StatefulCell
ariaRowindex={ariaRowindex}
data={data}
header={header}
eventId={eventId}
linkValues={linkValues}
renderCellValue={renderCellValue}
tabType={TimelineTabs.query}
timelineId={timelineId}
/>
);
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(
<StatefulCell
ariaRowindex={ariaRowindex}
data={data}
header={header}
eventId={eventId}
linkValues={linkValues}
renderCellValue={renderCellValue}
timelineId={timelineId}
/>
);
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 = () => <div data-test-subj="renderCellValue" />;
const wrapper = mount(
<StatefulCell
ariaRowindex={ariaRowindex}
data={data}
header={header}
eventId={eventId}
linkValues={linkValues}
renderCellValue={renderCellValue}
timelineId={timelineId}
/>
);
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(
<StatefulCell
ariaRowindex={ariaRowindex}
data={data}
header={header}
eventId={eventId}
linkValues={linkValues}
renderCellValue={RenderCellValue}
timelineId={timelineId}
/>
);
expect(
wrapper.find('[data-test-subj="statefulCell"]').getDOMNode().getAttribute('style')
).toEqual('background-color: green;');
});
});

View file

@ -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<CommonProps & HTMLAttributes<HTMLDivElement>>({});
return (
<div data-test-subj="statefulCell" {...cellProps}>
{renderCellValue({
columnId: header.id,
eventId,
data,
header,
isExpandable: true,
isExpanded: false,
isDetails: false,
linkValues,
rowIndex: ariaRowindex - 1,
setCellProps,
timelineId: tabType != null ? `${timelineId}-${tabType}` : timelineId,
})}
</div>
);
};
StatefulCellComponent.displayName = 'StatefulCellComponent';
export const StatefulCell = React.memo(StatefulCellComponent);

View file

@ -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,

View file

@ -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<Record<string, string[]>>;
@ -51,6 +50,7 @@ interface Props {
onRowSelected: OnRowSelected;
onUnPinEvent: OnUnPinEvent;
refetch: inputsModel.Refetch;
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
onRuleChange?: () => void;
hasRowRenderers: boolean;
selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
@ -69,7 +69,6 @@ export const EventColumnView = React.memo<Props>(
actionsColumnWidth,
ariaRowindex,
columnHeaders,
columnRenderers,
data,
ecsData,
eventIdToNoteIds,
@ -84,6 +83,7 @@ export const EventColumnView = React.memo<Props>(
refetch,
hasRowRenderers,
onRuleChange,
renderCellValue,
selectedEventIds,
showCheckboxes,
showNotes,
@ -227,11 +227,11 @@ export const EventColumnView = React.memo<Props>(
_id={id}
ariaRowindex={ariaRowindex}
columnHeaders={columnHeaders}
columnRenderers={columnRenderers}
data={data}
ecsData={ecsData}
hasRowRenderers={hasRowRenderers}
notesCount={notesCount}
renderCellValue={renderCellValue}
tabType={tabType}
timelineId={timelineId}
/>

View file

@ -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<HTMLDivElement | null>;
data: TimelineItem[];
eventIdToNoteIds: Readonly<Record<string, string[]>>;
@ -41,6 +40,7 @@ interface Props {
onRowSelected: OnRowSelected;
pinnedEventIds: Readonly<Record<string, boolean>>;
refetch: inputsModel.Refetch;
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
onRuleChange?: () => void;
rowRenderers: RowRenderer[];
selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
@ -52,7 +52,6 @@ const EventsComponent: React.FC<Props> = ({
actionsColumnWidth,
browserFields,
columnHeaders,
columnRenderers,
containerRef,
data,
eventIdToNoteIds,
@ -64,6 +63,7 @@ const EventsComponent: React.FC<Props> = ({
pinnedEventIds,
refetch,
onRuleChange,
renderCellValue,
rowRenderers,
selectedEventIds,
showCheckboxes,
@ -76,7 +76,6 @@ const EventsComponent: React.FC<Props> = ({
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<Props> = ({
lastFocusedAriaColindex={lastFocusedAriaColindex}
loadingEventIds={loadingEventIds}
onRowSelected={onRowSelected}
renderCellValue={renderCellValue}
refetch={refetch}
rowRenderers={rowRenderers}
onRuleChange={onRuleChange}

View file

@ -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<HTMLDivElement | null>;
browserFields: BrowserFields;
columnHeaders: ColumnHeaderOptions[];
columnRenderers: ColumnRenderer[];
event: TimelineItem;
eventIdToNoteIds: Readonly<Record<string, string[]>>;
isEventViewer?: boolean;
@ -56,6 +55,7 @@ interface Props {
refetch: inputsModel.Refetch;
ariaRowindex: number;
onRuleChange?: () => void;
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
rowRenderers: RowRenderer[];
selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
showCheckboxes: boolean;
@ -77,7 +77,6 @@ const StatefulEventComponent: React.FC<Props> = ({
browserFields,
containerRef,
columnHeaders,
columnRenderers,
event,
eventIdToNoteIds,
isEventViewer = false,
@ -86,8 +85,9 @@ const StatefulEventComponent: React.FC<Props> = ({
loadingEventIds,
onRowSelected,
refetch,
onRuleChange,
renderCellValue,
rowRenderers,
onRuleChange,
ariaRowindex,
selectedEventIds,
showCheckboxes,
@ -259,7 +259,6 @@ const StatefulEventComponent: React.FC<Props> = ({
actionsColumnWidth={actionsColumnWidth}
ariaRowindex={ariaRowindex}
columnHeaders={columnHeaders}
columnRenderers={columnRenderers}
data={event.data}
ecsData={event.ecs}
eventIdToNoteIds={eventIdToNoteIds}
@ -273,6 +272,7 @@ const StatefulEventComponent: React.FC<Props> = ({
onRowSelected={onRowSelected}
onUnPinEvent={onUnPinEvent}
refetch={refetch}
renderCellValue={renderCellValue}
onRuleChange={onRuleChange}
selectedEventIds={selectedEventIds}
showCheckboxes={showCheckboxes}

View file

@ -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,

View file

@ -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<StatefulBodyProps>(
onRuleChange,
showCheckboxes,
refetch,
renderCellValue,
rowRenderers,
sort,
tabType,
totalPages,
@ -141,7 +146,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
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<StatefulBodyProps>(
actionsColumnWidth={actionsColumnWidth}
browserFields={browserFields}
columnHeaders={columnHeaders}
columnRenderers={columnRenderers}
data={data}
eventIdToNoteIds={eventIdToNoteIds}
id={id}
@ -219,6 +223,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
onRowSelected={onRowSelected}
pinnedEventIds={pinnedEventIds}
refetch={refetch}
renderCellValue={renderCellValue}
rowRenderers={enabledRowRenderers}
onRuleChange={onRuleChange}
selectedEventIds={selectedEventIds}
@ -244,6 +249,8 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
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
);

View file

@ -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,

View file

@ -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,

View file

@ -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(
<TestProviders>
<DragDropContextWrapper browserFields={mockBrowserFields}>
<DroppableWrapper droppableId="testing">
<DefaultCellRenderer
columnId={columnId}
data={data}
eventId={eventId}
header={header}
isDetails={isDetails}
isExpandable={isExpandable}
isExpanded={isExpanded}
linkValues={linkValues}
rowIndex={rowIndex}
setCellProps={setCellProps}
timelineId={timelineId}
/>
</DroppableWrapper>
</DragDropContextWrapper>
</TestProviders>
);
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(
<TestProviders>
<DragDropContextWrapper browserFields={mockBrowserFields}>
<DroppableWrapper droppableId="testing">
<DefaultCellRenderer
columnId={columnId}
data={data}
eventId={eventId}
header={header}
isDetails={isDetails}
isExpandable={isExpandable}
isExpanded={isExpanded}
linkValues={linkValues}
rowIndex={rowIndex}
setCellProps={setCellProps}
timelineId={timelineId}
/>
</DroppableWrapper>
</DragDropContextWrapper>
</TestProviders>
);
expect(mockImplementation.renderColumn).toBeCalledWith({
columnName: header.id,
eventId,
field: header,
linkValues,
timelineId,
truncate: true,
values: ['2018-11-05T19:03:25.937Z'],
});
});
});

View file

@ -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<CellValueElementProps> = ({
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,
}),
})}
</>
);

View file

@ -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;
};

View file

@ -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"

View file

@ -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',

View file

@ -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<Props> = ({
itemsPerPage,
itemsPerPageOptions,
onEventClosed,
renderCellValue,
rowRenderers,
showExpandedDetails,
start,
timerangeKind,
@ -284,6 +290,8 @@ export const EqlTabContentComponent: React.FC<Props> = ({
data={isBlankTimeline ? EMPTY_EVENTS : events}
id={timelineId}
refetch={refetch}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
sort={NO_SORTING}
tabType={TimelineTabs.eql}
totalPages={calculateTotalPages({

View file

@ -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,
};

View file

@ -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<Props> = ({ 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<Props> = ({ timelineId }) => {
const TimelineSavingProgress = React.memo(TimelineSavingProgressComponent);
const StatefulTimelineComponent: React.FC<Props> = ({ timelineId }) => {
const StatefulTimelineComponent: React.FC<Props> = ({
renderCellValue,
rowRenderers,
timelineId,
}) => {
const dispatch = useDispatch();
const containerElement = useRef<HTMLDivElement | null>(null);
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
@ -131,6 +139,8 @@ const StatefulTimelineComponent: React.FC<Props> = ({ timelineId }) => {
<TabsContent
graphEventId={graphEventId}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
setTimelineFullScreen={setTimelineFullScreen}
timelineId={timelineId}
timelineType={timelineType}

View file

@ -135,6 +135,986 @@ In other use cases the message field can be used to concatenate different values
}
onEventClosed={[MockFunction]}
pinnedEventIds={Object {}}
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}
sort={
Array [

View file

@ -10,10 +10,11 @@ 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 { defaultRowRenderers } from '../body/renderers';
import { Sort } from '../body/sort';
import { useMountAppended } from '../../../../common/utils/use_mount_appended';
import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline';
@ -94,6 +95,8 @@ describe('PinnedTabContent', () => {
timelineId: TimelineId.test,
itemsPerPage: 5,
itemsPerPageOptions: [5, 10, 20],
renderCellValue: DefaultCellRenderer,
rowRenderers: defaultRowRenderers,
sort,
pinnedEventIds: {},
showExpandedDetails: false,

View file

@ -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<Props> = ({
itemsPerPageOptions,
pinnedEventIds,
onEventClosed,
renderCellValue,
rowRenderers,
showExpandedDetails,
sort,
}) => {
@ -217,6 +223,8 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
data={events}
id={timelineId}
refetch={refetch}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
sort={sort}
tabType={TimelineTabs.pinned}
totalPages={calculateTotalPages({

View file

@ -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}

View file

@ -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,

View file

@ -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<Props> = ({
kqlMode,
kqlQueryExpression,
onEventClosed,
renderCellValue,
rowRenderers,
show,
showCallOutUnauthorizedMsg,
showExpandedDetails,
@ -330,6 +336,8 @@ export const QueryTabContentComponent: React.FC<Props> = ({
data={isBlankTimeline ? EMPTY_EVENTS : events}
id={timelineId}
refetch={refetch}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
sort={sort}
tabType={TimelineTabs.query}
totalPages={calculateTotalPages({

View file

@ -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 }) => (
<Suspense fallback={<EuiLoadingContent lines={10} />}>
<QueryTabContent timelineId={timelineId} />
<QueryTabContent
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
/>
</Suspense>
));
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 }) => (
<Suspense fallback={<EuiLoadingContent lines={10} />}>
<EqlTabContent timelineId={timelineId} />
<EqlTabContent
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
/>
</Suspense>
));
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 }) => (
<Suspense fallback={<EuiLoadingContent lines={10} />}>
<PinnedTabContent timelineId={timelineId} />
<PinnedTabContent
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
/>
</Suspense>
));
PinnedTab.displayName = 'PinnedTab';
@ -91,7 +119,7 @@ PinnedTab.displayName = 'PinnedTab';
type ActiveTimelineTabProps = BasicTimelineTab & { activeTimelineTab: TimelineTabs };
const ActiveTimelineTab = memo<ActiveTimelineTabProps>(
({ activeTimelineTab, timelineId, timelineType }) => {
({ activeTimelineTab, renderCellValue, rowRenderers, timelineId, timelineType }) => {
const getTab = useCallback(
(tab: TimelineTabs) => {
switch (tab) {
@ -119,14 +147,26 @@ const ActiveTimelineTab = memo<ActiveTimelineTabProps>(
return (
<>
<HideShowContainer $isVisible={TimelineTabs.query === activeTimelineTab}>
<QueryTab timelineId={timelineId} />
<QueryTab
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
/>
</HideShowContainer>
<HideShowContainer $isVisible={TimelineTabs.pinned === activeTimelineTab}>
<PinnedTab timelineId={timelineId} />
<PinnedTab
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
/>
</HideShowContainer>
{timelineType === TimelineType.default && (
<HideShowContainer $isVisible={TimelineTabs.eql === activeTimelineTab}>
<EqlTab timelineId={timelineId} />
<EqlTab
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
/>
</HideShowContainer>
)}
<HideShowContainer $isVisible={isGraphOrNotesTabs}>
@ -160,6 +200,8 @@ const StyledEuiTab = styled(EuiTab)`
`;
const TabsContentComponent: React.FC<BasicTimelineTab> = ({
renderCellValue,
rowRenderers,
timelineId,
timelineFullScreen,
timelineType,
@ -300,6 +342,8 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
<ActiveTimelineTab
activeTimelineTab={activeTab}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}
timelineType={timelineType}
/>

View file

@ -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,