mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security solution] [RAC] Add row renderer popover to alert table "reason" field (#108054)
* Add row renderer popover to alert table reason field * Add a title to row renderer popover on alert table * Fix issues found during code review
This commit is contained in:
parent
3950c436a7
commit
79fefe0311
11 changed files with 385 additions and 19 deletions
|
@ -31,6 +31,9 @@ export const RenderCellValue: React.FC<
|
|||
rowIndex,
|
||||
setCellProps,
|
||||
timelineId,
|
||||
ecsData,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
}) => (
|
||||
<DefaultCellRenderer
|
||||
columnId={columnId}
|
||||
|
@ -45,5 +48,8 @@ export const RenderCellValue: React.FC<
|
|||
rowIndex={rowIndex}
|
||||
setCellProps={setCellProps}
|
||||
timelineId={timelineId}
|
||||
ecsData={ecsData}
|
||||
rowRenderers={rowRenderers}
|
||||
browserFields={browserFields}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -40,6 +40,26 @@ const Link = ({ children, url }: { children: React.ReactNode; url: string }) =>
|
|||
</EuiLink>
|
||||
);
|
||||
|
||||
export const eventRendererNames: { [key in RowRendererId]: string } = {
|
||||
[RowRendererId.alerts]: i18n.ALERTS_NAME,
|
||||
[RowRendererId.auditd]: i18n.AUDITD_NAME,
|
||||
[RowRendererId.auditd_file]: i18n.AUDITD_FILE_NAME,
|
||||
[RowRendererId.library]: i18n.LIBRARY_NAME,
|
||||
[RowRendererId.system_security_event]: i18n.AUTHENTICATION_NAME,
|
||||
[RowRendererId.system_dns]: i18n.DNS_NAME,
|
||||
[RowRendererId.netflow]: i18n.FLOW_NAME,
|
||||
[RowRendererId.system]: i18n.SYSTEM_NAME,
|
||||
[RowRendererId.system_endgame_process]: i18n.PROCESS,
|
||||
[RowRendererId.registry]: i18n.REGISTRY_NAME,
|
||||
[RowRendererId.system_fim]: i18n.FIM_NAME,
|
||||
[RowRendererId.system_file]: i18n.FILE_NAME,
|
||||
[RowRendererId.system_socket]: i18n.SOCKET_NAME,
|
||||
[RowRendererId.suricata]: 'Suricata',
|
||||
[RowRendererId.threat_match]: i18n.THREAT_MATCH_NAME,
|
||||
[RowRendererId.zeek]: i18n.ZEEK_NAME,
|
||||
[RowRendererId.plain]: '',
|
||||
};
|
||||
|
||||
export interface RowRendererOption {
|
||||
id: RowRendererId;
|
||||
name: string;
|
||||
|
@ -51,14 +71,14 @@ export interface RowRendererOption {
|
|||
export const renderers: RowRendererOption[] = [
|
||||
{
|
||||
id: RowRendererId.alerts,
|
||||
name: i18n.ALERTS_NAME,
|
||||
name: eventRendererNames[RowRendererId.alerts],
|
||||
description: i18n.ALERTS_DESCRIPTION,
|
||||
example: AlertsExample,
|
||||
searchableDescription: i18n.ALERTS_DESCRIPTION,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.auditd,
|
||||
name: i18n.AUDITD_NAME,
|
||||
name: eventRendererNames[RowRendererId.auditd],
|
||||
description: (
|
||||
<span>
|
||||
<Link url="https://www.elastic.co/guide/en/beats/auditbeat/current/auditbeat-module-auditd.html">
|
||||
|
@ -72,7 +92,7 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.auditd_file,
|
||||
name: i18n.AUDITD_FILE_NAME,
|
||||
name: eventRendererNames[RowRendererId.auditd_file],
|
||||
description: (
|
||||
<span>
|
||||
<Link url="https://www.elastic.co/guide/en/beats/auditbeat/current/auditbeat-module-auditd.html">
|
||||
|
@ -86,14 +106,14 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.library,
|
||||
name: i18n.LIBRARY_NAME,
|
||||
name: eventRendererNames[RowRendererId.library],
|
||||
description: i18n.LIBRARY_DESCRIPTION,
|
||||
example: LibraryExample,
|
||||
searchableDescription: i18n.LIBRARY_DESCRIPTION,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.system_security_event,
|
||||
name: i18n.AUTHENTICATION_NAME,
|
||||
name: eventRendererNames[RowRendererId.system_security_event],
|
||||
description: (
|
||||
<div>
|
||||
<p>{i18n.AUTHENTICATION_DESCRIPTION_PART1}</p>
|
||||
|
@ -106,14 +126,14 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.system_dns,
|
||||
name: i18n.DNS_NAME,
|
||||
name: eventRendererNames[RowRendererId.system_dns],
|
||||
description: i18n.DNS_DESCRIPTION_PART1,
|
||||
example: SystemDnsExample,
|
||||
searchableDescription: i18n.DNS_DESCRIPTION_PART1,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.netflow,
|
||||
name: i18n.FLOW_NAME,
|
||||
name: eventRendererNames[RowRendererId.netflow],
|
||||
description: (
|
||||
<div>
|
||||
<p>{i18n.FLOW_DESCRIPTION_PART1}</p>
|
||||
|
@ -126,7 +146,7 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.system,
|
||||
name: i18n.SYSTEM_NAME,
|
||||
name: eventRendererNames[RowRendererId.system],
|
||||
description: (
|
||||
<div>
|
||||
<p>
|
||||
|
@ -145,7 +165,7 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.system_endgame_process,
|
||||
name: i18n.PROCESS,
|
||||
name: eventRendererNames[RowRendererId.system_endgame_process],
|
||||
description: (
|
||||
<div>
|
||||
<p>{i18n.PROCESS_DESCRIPTION_PART1}</p>
|
||||
|
@ -158,28 +178,28 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.registry,
|
||||
name: i18n.REGISTRY_NAME,
|
||||
name: eventRendererNames[RowRendererId.registry],
|
||||
description: i18n.REGISTRY_DESCRIPTION,
|
||||
example: RegistryExample,
|
||||
searchableDescription: i18n.REGISTRY_DESCRIPTION,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.system_fim,
|
||||
name: i18n.FIM_NAME,
|
||||
name: eventRendererNames[RowRendererId.system_fim],
|
||||
description: i18n.FIM_DESCRIPTION_PART1,
|
||||
example: SystemFimExample,
|
||||
searchableDescription: i18n.FIM_DESCRIPTION_PART1,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.system_file,
|
||||
name: i18n.FILE_NAME,
|
||||
name: eventRendererNames[RowRendererId.system_file],
|
||||
description: i18n.FILE_DESCRIPTION_PART1,
|
||||
example: SystemFileExample,
|
||||
searchableDescription: i18n.FILE_DESCRIPTION_PART1,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.system_socket,
|
||||
name: i18n.SOCKET_NAME,
|
||||
name: eventRendererNames[RowRendererId.system_socket],
|
||||
description: (
|
||||
<div>
|
||||
<p>{i18n.SOCKET_DESCRIPTION_PART1}</p>
|
||||
|
@ -192,7 +212,7 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.suricata,
|
||||
name: 'Suricata',
|
||||
name: eventRendererNames[RowRendererId.suricata],
|
||||
description: (
|
||||
<p>
|
||||
{i18n.SURICATA_DESCRIPTION_PART1}{' '}
|
||||
|
@ -207,14 +227,14 @@ export const renderers: RowRendererOption[] = [
|
|||
},
|
||||
{
|
||||
id: RowRendererId.threat_match,
|
||||
name: i18n.THREAT_MATCH_NAME,
|
||||
name: eventRendererNames[RowRendererId.threat_match],
|
||||
description: i18n.THREAT_MATCH_DESCRIPTION,
|
||||
example: ThreatMatchExample,
|
||||
searchableDescription: `${i18n.THREAT_MATCH_NAME} ${i18n.THREAT_MATCH_DESCRIPTION}`,
|
||||
},
|
||||
{
|
||||
id: RowRendererId.zeek,
|
||||
name: i18n.ZEEK_NAME,
|
||||
name: eventRendererNames[RowRendererId.zeek],
|
||||
description: (
|
||||
<p>
|
||||
{i18n.ZEEK_DESCRIPTION_PART1}{' '}
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import { ColumnHeaderOptions } from '../../../../../../common';
|
||||
|
||||
import { BrowserFields, ColumnHeaderOptions, RowRenderer } from '../../../../../../common';
|
||||
import { Ecs } from '../../../../../../common/ecs';
|
||||
import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline';
|
||||
|
||||
export interface ColumnRenderer {
|
||||
|
@ -29,5 +31,8 @@ export interface ColumnRenderer {
|
|||
truncate?: boolean;
|
||||
values: string[] | null | undefined;
|
||||
linkValues?: string[] | null | undefined;
|
||||
ecsData?: Ecs;
|
||||
rowRenderers?: RowRenderer[];
|
||||
browserFields?: BrowserFields;
|
||||
}) => React.ReactNode;
|
||||
}
|
||||
|
|
|
@ -17,3 +17,4 @@ export const EVENT_URL_FIELD_NAME = 'event.url';
|
|||
export const SIGNAL_RULE_NAME_FIELD_NAME = 'signal.rule.name';
|
||||
export const SIGNAL_STATUS_FIELD_NAME = 'signal.status';
|
||||
export const AGENT_STATUS_FIELD_NAME = 'agent.status';
|
||||
export const REASON_FIELD_NAME = 'signal.reason';
|
||||
|
|
|
@ -16,6 +16,7 @@ import { unknownColumnRenderer } from './unknown_column_renderer';
|
|||
import { zeekRowRenderer } from './zeek/zeek_row_renderer';
|
||||
import { systemRowRenderers } from './system/generic_row_renderer';
|
||||
import { threatMatchRowRenderer } from './cti/threat_match_row_renderer';
|
||||
import { reasonColumnRenderer } from './reason_column_renderer';
|
||||
|
||||
// The row renderers are order dependent and will return the first renderer
|
||||
// which returns true from its isInstance call. The bottom renderers which
|
||||
|
@ -34,6 +35,7 @@ export const defaultRowRenderers: RowRenderer[] = [
|
|||
];
|
||||
|
||||
export const columnRenderers: ColumnRenderer[] = [
|
||||
reasonColumnRenderer,
|
||||
plainColumnRenderer,
|
||||
emptyColumnRenderer,
|
||||
unknownColumnRenderer,
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* 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 { mockTimelineData } from '../../../../../common/mock';
|
||||
import { defaultColumnHeaderType } from '../column_headers/default_headers';
|
||||
import { REASON_FIELD_NAME } from './constants';
|
||||
import { reasonColumnRenderer } from './reason_column_renderer';
|
||||
import { plainColumnRenderer } from './plain_column_renderer';
|
||||
|
||||
import {
|
||||
BrowserFields,
|
||||
ColumnHeaderOptions,
|
||||
RowRenderer,
|
||||
RowRendererId,
|
||||
} from '../../../../../../common';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { TestProviders } from '../../../../../../../timelines/public/mock';
|
||||
import { useDraggableKeyboardWrapper as mockUseDraggableKeyboardWrapper } from '../../../../../../../timelines/public/components';
|
||||
import { cloneDeep } from 'lodash';
|
||||
jest.mock('./plain_column_renderer');
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana', () => {
|
||||
const originalModule = jest.requireActual('../../../../../common/lib/kibana');
|
||||
return {
|
||||
...originalModule,
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
timelines: {
|
||||
getUseDraggableKeyboardWrapper: () => mockUseDraggableKeyboardWrapper,
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../common/components/link_to', () => {
|
||||
const original = jest.requireActual('../../../../../common/components/link_to');
|
||||
return {
|
||||
...original,
|
||||
useFormatUrl: () => ({
|
||||
formatUrl: () => '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const invalidEcs = cloneDeep(mockTimelineData[0].ecs);
|
||||
const validEcs = cloneDeep(mockTimelineData[28].ecs);
|
||||
|
||||
const field: ColumnHeaderOptions = {
|
||||
id: 'test-field-id',
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
};
|
||||
|
||||
const rowRenderers: RowRenderer[] = [
|
||||
{
|
||||
id: RowRendererId.alerts,
|
||||
isInstance: (ecs) => ecs === validEcs,
|
||||
// eslint-disable-next-line react/display-name
|
||||
renderRow: () => <span data-test-subj="test-row-render" />,
|
||||
},
|
||||
];
|
||||
const browserFields: BrowserFields = {};
|
||||
|
||||
const defaultProps = {
|
||||
columnName: REASON_FIELD_NAME,
|
||||
eventId: 'test-event-id',
|
||||
field,
|
||||
timelineId: 'test-timeline-id',
|
||||
values: ['test-value'],
|
||||
};
|
||||
|
||||
describe('reasonColumnRenderer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('isIntance', () => {
|
||||
it('returns true when columnName is `signal.reason`', () => {
|
||||
expect(reasonColumnRenderer.isInstance(REASON_FIELD_NAME, [])).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderColumn', () => {
|
||||
it('calls `plainColumnRenderer.renderColumn` when ecsData, rowRenderers or browserFields is empty', () => {
|
||||
reasonColumnRenderer.renderColumn(defaultProps);
|
||||
|
||||
expect(plainColumnRenderer.renderColumn).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("doesn't call `plainColumnRenderer.renderColumn` when ecsData, rowRenderers or browserFields fields are not empty", () => {
|
||||
reasonColumnRenderer.renderColumn({
|
||||
...defaultProps,
|
||||
ecsData: invalidEcs,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
});
|
||||
|
||||
expect(plainColumnRenderer.renderColumn).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("doesn't render popover button when getRowRenderer doesn't find a rowRenderer", () => {
|
||||
const renderedColumn = reasonColumnRenderer.renderColumn({
|
||||
...defaultProps,
|
||||
ecsData: invalidEcs,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
});
|
||||
|
||||
const wrapper = render(<TestProviders>{renderedColumn}</TestProviders>);
|
||||
|
||||
expect(wrapper.queryByTestId('reason-cell-button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('render popover button when getRowRenderer finds a rowRenderer', () => {
|
||||
const renderedColumn = reasonColumnRenderer.renderColumn({
|
||||
...defaultProps,
|
||||
ecsData: validEcs,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
});
|
||||
|
||||
const wrapper = render(<TestProviders>{renderedColumn}</TestProviders>);
|
||||
|
||||
expect(wrapper.queryByTestId('reason-cell-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('render rowRender inside a popover when reson field button is clicked', () => {
|
||||
const renderedColumn = reasonColumnRenderer.renderColumn({
|
||||
...defaultProps,
|
||||
ecsData: validEcs,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
});
|
||||
|
||||
const wrapper = render(<TestProviders>{renderedColumn}</TestProviders>);
|
||||
|
||||
fireEvent.click(wrapper.getByTestId('reason-cell-button'));
|
||||
|
||||
expect(wrapper.queryByTestId('test-row-render')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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 { EuiButtonEmpty, EuiPopover, EuiPopoverTitle } from '@elastic/eui';
|
||||
import { isEqual } from 'lodash/fp';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { BrowserFields, ColumnHeaderOptions, RowRenderer } from '../../../../../../common';
|
||||
import { Ecs } from '../../../../../../common/ecs';
|
||||
import { DefaultDraggable } from '../../../../../common/components/draggables';
|
||||
import { eventRendererNames } from '../../../row_renderers_browser/catalog';
|
||||
import { ColumnRenderer } from './column_renderer';
|
||||
import { REASON_FIELD_NAME } from './constants';
|
||||
import { getRowRenderer } from './get_row_renderer';
|
||||
import { plainColumnRenderer } from './plain_column_renderer';
|
||||
import * as i18n from './translations';
|
||||
|
||||
export const reasonColumnRenderer: ColumnRenderer = {
|
||||
isInstance: isEqual(REASON_FIELD_NAME),
|
||||
|
||||
renderColumn: ({
|
||||
columnName,
|
||||
eventId,
|
||||
field,
|
||||
isDraggable = true,
|
||||
timelineId,
|
||||
truncate,
|
||||
values,
|
||||
linkValues,
|
||||
ecsData,
|
||||
rowRenderers = [],
|
||||
browserFields,
|
||||
}: {
|
||||
columnName: string;
|
||||
eventId: string;
|
||||
field: ColumnHeaderOptions;
|
||||
isDraggable?: boolean;
|
||||
timelineId: string;
|
||||
truncate?: boolean;
|
||||
values: string[] | undefined | null;
|
||||
linkValues?: string[] | null | undefined;
|
||||
|
||||
ecsData?: Ecs;
|
||||
rowRenderers?: RowRenderer[];
|
||||
browserFields?: BrowserFields;
|
||||
}) =>
|
||||
values != null && ecsData && rowRenderers?.length > 0 && browserFields
|
||||
? values.map((value, i) => (
|
||||
<ReasonCell
|
||||
key={`reason-column-renderer-value-${timelineId}-${columnName}-${eventId}-${field.id}-${value}-${i}`}
|
||||
contextId={`reason-column-renderer-${timelineId}`}
|
||||
timelineId={timelineId}
|
||||
eventId={eventId}
|
||||
value={value}
|
||||
isDraggable={isDraggable}
|
||||
fieldName={columnName}
|
||||
ecsData={ecsData}
|
||||
rowRenderers={rowRenderers}
|
||||
browserFields={browserFields}
|
||||
/>
|
||||
))
|
||||
: plainColumnRenderer.renderColumn({
|
||||
columnName,
|
||||
eventId,
|
||||
field,
|
||||
isDraggable,
|
||||
timelineId,
|
||||
truncate,
|
||||
values,
|
||||
linkValues,
|
||||
}),
|
||||
};
|
||||
|
||||
const StyledEuiButtonEmpty = styled(EuiButtonEmpty)`
|
||||
font-weight: ${(props) => props.theme.eui.euiFontWeightRegular};
|
||||
`;
|
||||
|
||||
const ReasonCell: React.FC<{
|
||||
contextId: string;
|
||||
eventId: string;
|
||||
fieldName: string;
|
||||
isDraggable?: boolean;
|
||||
value: string | number | undefined | null;
|
||||
timelineId: string;
|
||||
ecsData: Ecs;
|
||||
rowRenderers: RowRenderer[];
|
||||
browserFields: BrowserFields;
|
||||
}> = ({
|
||||
ecsData,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
timelineId,
|
||||
value,
|
||||
fieldName,
|
||||
isDraggable,
|
||||
contextId,
|
||||
eventId,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const rowRenderer = useMemo(() => getRowRenderer(ecsData, rowRenderers), [ecsData, rowRenderers]);
|
||||
|
||||
const rowRender = useMemo(() => {
|
||||
return (
|
||||
rowRenderer &&
|
||||
rowRenderer.renderRow({
|
||||
browserFields,
|
||||
data: ecsData,
|
||||
isDraggable: true,
|
||||
timelineId,
|
||||
})
|
||||
);
|
||||
}, [rowRenderer, browserFields, ecsData, timelineId]);
|
||||
|
||||
const handleTogglePopOver = useCallback(() => setIsOpen(!isOpen), [setIsOpen, isOpen]);
|
||||
const handleClosePopOver = useCallback(() => setIsOpen(false), [setIsOpen]);
|
||||
|
||||
const button = useMemo(
|
||||
() => (
|
||||
<StyledEuiButtonEmpty
|
||||
data-test-subj="reason-cell-button"
|
||||
size="xs"
|
||||
flush="left"
|
||||
onClick={handleTogglePopOver}
|
||||
>
|
||||
{value}
|
||||
</StyledEuiButtonEmpty>
|
||||
),
|
||||
[value, handleTogglePopOver]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DefaultDraggable
|
||||
field={fieldName}
|
||||
id={`reason-column-draggable-${contextId}-${eventId}-${fieldName}-${value}`}
|
||||
isDraggable={isDraggable}
|
||||
value={`${value}`}
|
||||
tooltipContent={value}
|
||||
>
|
||||
{rowRenderer && rowRender ? (
|
||||
<EuiPopover
|
||||
isOpen={isOpen}
|
||||
anchorPosition="rightCenter"
|
||||
closePopover={handleClosePopOver}
|
||||
button={button}
|
||||
>
|
||||
<EuiPopoverTitle paddingSize="s">
|
||||
{i18n.EVENT_RENDERER_POPOVER_TITLE(eventRendererNames[rowRenderer.id] ?? '')}
|
||||
</EuiPopoverTitle>
|
||||
{rowRender}
|
||||
</EuiPopover>
|
||||
) : (
|
||||
value
|
||||
)}
|
||||
</DefaultDraggable>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -51,3 +51,9 @@ export const EMPTY_STATUS = i18n.translate(
|
|||
defaultMessage: '-',
|
||||
}
|
||||
);
|
||||
|
||||
export const EVENT_RENDERER_POPOVER_TITLE = (eventRendererName: string) =>
|
||||
i18n.translate('xpack.securitySolution.event.reason.eventRenderPopoverTitle', {
|
||||
values: { eventRendererName },
|
||||
defaultMessage: 'Event renderer: {eventRendererName} ',
|
||||
});
|
||||
|
|
|
@ -22,6 +22,9 @@ export const DefaultCellRenderer: React.FC<CellValueElementProps> = ({
|
|||
linkValues,
|
||||
setCellProps,
|
||||
timelineId,
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
ecsData,
|
||||
}) => (
|
||||
<>
|
||||
{getColumnRenderer(header.id, columnRenderers, data).renderColumn({
|
||||
|
@ -36,6 +39,9 @@ export const DefaultCellRenderer: React.FC<CellValueElementProps> = ({
|
|||
data,
|
||||
fieldName: header.id,
|
||||
}),
|
||||
rowRenderers,
|
||||
browserFields,
|
||||
ecsData,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
*/
|
||||
|
||||
import { EuiDataGridCellValueElementProps } from '@elastic/eui';
|
||||
import { TimelineNonEcsData } from '../../../search_strategy';
|
||||
import { RowRenderer } from '../../..';
|
||||
import { Ecs } from '../../../ecs';
|
||||
import { BrowserFields, TimelineNonEcsData } from '../../../search_strategy';
|
||||
import { ColumnHeaderOptions } from '../columns';
|
||||
|
||||
/** The following props are provided to the function called by `renderCellValue` */
|
||||
|
@ -19,4 +21,7 @@ export type CellValueElementProps = EuiDataGridCellValueElementProps & {
|
|||
timelineId: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
setFlyoutAlert?: (data: any) => void;
|
||||
ecsData?: Ecs;
|
||||
rowRenderers?: RowRenderer[];
|
||||
browserFields?: BrowserFields;
|
||||
};
|
||||
|
|
|
@ -526,9 +526,12 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
rowIndex,
|
||||
setCellProps,
|
||||
timelineId: tabType != null ? `${id}-${tabType}` : id,
|
||||
ecsData: data[rowIndex].ecs,
|
||||
browserFields,
|
||||
rowRenderers,
|
||||
});
|
||||
},
|
||||
[columnHeaders, data, id, renderCellValue, tabType, theme]
|
||||
[columnHeaders, data, id, renderCellValue, tabType, theme, browserFields, rowRenderers]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue