[RAC] [Alerts] Add highlight to building block alerts (#107727)

* [rac] [Alerts] Add highlight to building block alerts

* Pull back 'additional filters' button to t-grid
This commit is contained in:
Pablo Machado 2021-08-10 12:31:05 +02:00 committed by GitHub
parent 78e2bd2788
commit eca7146a9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 223 additions and 117 deletions

View file

@ -14,8 +14,12 @@ import {
mapSortDirectionToDirection,
mapSortingColumns,
stringifyEvent,
addBuildingBlockStyle,
} from './helpers';
import { euiThemeVars } from '@kbn/ui-shared-deps/theme';
import { mockDnsEvent } from '../../../mock';
describe('helpers', () => {
describe('stringifyEvent', () => {
test('it omits __typename when it appears at arbitrary levels', () => {
@ -388,4 +392,32 @@ describe('helpers', () => {
).toBe(false);
});
});
describe('addBuildingBlockStyle', () => {
const THEME = { eui: euiThemeVars, darkMode: false };
test('it calls `setCellProps` with background color when event is a building block', () => {
const mockedSetCellProps = jest.fn();
const ecs = {
...mockDnsEvent,
...{ signal: { rule: { building_block_type: ['default'] } } },
};
addBuildingBlockStyle(ecs, THEME, mockedSetCellProps);
expect(mockedSetCellProps).toBeCalledWith({
style: {
backgroundColor: euiThemeVars.euiColorHighlight,
},
});
});
test('it call `setCellProps` reseting the background color when event is not a building block', () => {
const mockedSetCellProps = jest.fn();
addBuildingBlockStyle(mockDnsEvent, THEME, mockedSetCellProps);
expect(mockedSetCellProps).toBeCalledWith({ style: { backgroundColor: 'inherit' } });
});
});
});

View file

@ -7,6 +7,7 @@
import { isEmpty } from 'lodash/fp';
import { EuiDataGridCellValueElementProps } from '@elastic/eui';
import type { Ecs } from '../../../../common/ecs';
import type {
BrowserField,
@ -20,6 +21,8 @@ import type {
TimelineEventsType,
} from '../../../../common/types/timeline';
import type { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const omitTypenameAndEmpty = (k: string, v: any): any | undefined =>
k !== '__typename' && v != null ? v : undefined;
@ -185,3 +188,23 @@ export const allowSorting = ({
return isAllowlistedNonBrowserField || isAggregatable;
};
export const addBuildingBlockStyle = (
ecs: Ecs,
theme: EuiTheme,
setCellProps: EuiDataGridCellValueElementProps['setCellProps']
) => {
if (isEventBuildingBlockType(ecs)) {
setCellProps({
style: {
backgroundColor: `${theme.eui.euiColorHighlight}`,
},
});
} else {
// reset cell style
setCellProps({
style: {
backgroundColor: 'inherit',
},
});
}
};

View file

@ -24,28 +24,34 @@ import React, {
useEffect,
useMemo,
useState,
useContext,
} from 'react';
import { connect, ConnectedProps, useDispatch } from 'react-redux';
import { ThemeContext } from 'styled-components';
import {
TGridCellAction,
TimelineId,
TimelineTabs,
BulkActionsProp,
SortColumnTimeline,
} from '../../../../common/types/timeline';
import type {
CellValueElementProps,
ColumnHeaderOptions,
ControlColumnProps,
RowRenderer,
AlertStatus,
SortColumnTimeline,
TimelineId,
TimelineTabs,
} from '../../../../common/types/timeline';
import type { TimelineItem, TimelineNonEcsData } from '../../../../common/search_strategy/timeline';
import { getActionsColumnWidth, getColumnHeaders } from './column_headers/helpers';
import { getEventIdToDataMapping, mapSortDirectionToDirection, mapSortingColumns } from './helpers';
import {
addBuildingBlockStyle,
getEventIdToDataMapping,
mapSortDirectionToDirection,
mapSortingColumns,
} from './helpers';
import { DEFAULT_ICON_BUTTON_WIDTH } from '../helpers';
import type { BrowserFields } from '../../../../common/search_strategy/index_fields';
@ -58,6 +64,7 @@ import { RowAction } from './row_action';
import * as i18n from './translations';
import { AlertCount } from '../styles';
import { checkBoxControlColumn } from './control_columns';
import type { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common';
const StatefulAlertStatusBulkActions = lazy(
() => import('../toolbar/bulk_actions/alert_status_bulk_actions')
@ -121,6 +128,7 @@ const transformControlColumns = ({
onSelectPage,
browserFields,
sort,
theme,
}: {
actionColumnsWidth: number;
columnHeaders: ColumnHeaderOptions[];
@ -138,6 +146,7 @@ const transformControlColumns = ({
browserFields: BrowserFields;
onSelectPage: OnSelectAll;
sort: SortColumnTimeline[];
theme: EuiTheme;
}): EuiDataGridControlColumn[] =>
controlColumns.map(
({ id: columnId, headerCellRender = EmptyHeaderCellRender, rowCellRender, width }, i) => ({
@ -173,29 +182,33 @@ const transformControlColumns = ({
isExpanded,
rowIndex,
setCellProps,
}: EuiDataGridCellValueElementProps) => (
<RowAction
columnId={columnId ?? ''}
columnHeaders={columnHeaders}
controlColumn={controlColumns[i]}
data={data}
index={i}
isDetails={isDetails}
isExpanded={isExpanded}
isEventViewer={isEventViewer}
isExpandable={isExpandable}
loadingEventIds={loadingEventIds}
onRowSelected={onRowSelected}
onRuleChange={onRuleChange}
rowIndex={rowIndex}
selectedEventIds={selectedEventIds}
setCellProps={setCellProps}
showCheckboxes={showCheckboxes}
tabType={tabType}
timelineId={timelineId}
width={width ?? MIN_ACTION_COLUMN_WIDTH}
/>
),
}: EuiDataGridCellValueElementProps) => {
addBuildingBlockStyle(data[rowIndex].ecs, theme, setCellProps);
return (
<RowAction
columnId={columnId ?? ''}
columnHeaders={columnHeaders}
controlColumn={controlColumns[i]}
data={data}
index={i}
isDetails={isDetails}
isExpanded={isExpanded}
isEventViewer={isEventViewer}
isExpandable={isExpandable}
loadingEventIds={loadingEventIds}
onRowSelected={onRowSelected}
onRuleChange={onRuleChange}
rowIndex={rowIndex}
selectedEventIds={selectedEventIds}
setCellProps={setCellProps}
showCheckboxes={showCheckboxes}
tabType={tabType}
timelineId={timelineId}
width={width ?? MIN_ACTION_COLUMN_WIDTH}
/>
);
},
width: width ?? actionColumnsWidth,
})
);
@ -252,6 +265,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
const selectedCount = useMemo(() => Object.keys(selectedEventIds).length, [selectedEventIds]);
const theme: EuiTheme = useContext(ThemeContext);
const onRowSelected: OnRowSelected = useCallback(
({ eventIds, isSelected }: { eventIds: string[]; isSelected: boolean }) => {
setSelected({
@ -444,6 +458,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
sort,
browserFields,
onSelectPage,
theme,
})
);
}, [
@ -463,6 +478,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
browserFields,
onSelectPage,
sort,
theme,
]);
const columnsWithCellActions: EuiDataGridColumn[] = useMemo(
@ -483,34 +499,37 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
[browserFields, columnHeaders, data, defaultCellActions]
);
const renderTGridCellValue: (x: EuiDataGridCellValueElementProps) => React.ReactNode = ({
columnId,
rowIndex,
setCellProps,
}) => {
const rowData = rowIndex < data.length ? data[rowIndex].data : null;
const header = columnHeaders.find((h) => h.id === columnId);
const eventId = rowIndex < data.length ? data[rowIndex]._id : null;
const renderTGridCellValue: (
x: EuiDataGridCellValueElementProps
) => React.ReactNode = useCallback(
({ columnId, rowIndex, setCellProps }) => {
const rowData = rowIndex < data.length ? data[rowIndex].data : null;
const header = columnHeaders.find((h) => h.id === columnId);
const eventId = rowIndex < data.length ? data[rowIndex]._id : null;
if (rowData == null || header == null || eventId == null) {
return null;
}
addBuildingBlockStyle(data[rowIndex].ecs, theme, setCellProps);
return renderCellValue({
columnId: header.id,
eventId,
data: rowData,
header,
isDraggable: false,
isExpandable: true,
isExpanded: false,
isDetails: false,
linkValues: getOr([], header.linkField ?? '', data[rowIndex].ecs),
rowIndex,
setCellProps,
timelineId: tabType != null ? `${id}-${tabType}` : id,
});
};
if (rowData == null || header == null || eventId == null) {
return null;
}
return renderCellValue({
columnId: header.id,
eventId,
data: rowData,
header,
isDraggable: false,
isExpandable: true,
isExpanded: false,
isDetails: false,
linkValues: getOr([], header.linkField ?? '', data[rowIndex].ecs),
rowIndex,
setCellProps,
timelineId: tabType != null ? `${id}-${tabType}` : id,
});
},
[columnHeaders, data, id, renderCellValue, tabType, theme]
);
return (
<EuiDataGrid

View file

@ -34,7 +34,6 @@ import {
DataPublicPluginStart,
} from '../../../../../../../src/plugins/data/public';
import { useDeepEqualSelector } from '../../../hooks/use_selector';
import { Refetch } from '../../../store/t_grid/inputs';
import { defaultHeaders } from '../body/column_headers/default_headers';
import { calculateTotalPages, combineQueries, resolverIsShowing } from '../helpers';
import { tGridActions, tGridSelectors } from '../../../store/t_grid';
@ -42,7 +41,6 @@ import { useTimelineEvents } from '../../../container';
import { HeaderSection } from '../header_section';
import { StatefulBody } from '../body';
import { Footer, footerHeight } from '../footer';
import { LastUpdatedAt } from '../..';
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER, UpdatedFlexItem } from '../styles';
import * as i18n from '../translations';
import { ExitFullScreen } from '../../exit_full_screen';
@ -132,7 +130,7 @@ export interface TGridIntegratedProps {
setGlobalFullScreen: (fullscreen: boolean) => void;
start: string;
sort: Sort[];
utilityBar?: (refetch: Refetch, totalCount: number) => React.ReactNode;
additionalFilters: React.ReactNode;
// If truthy, the graph viewer (Resolver) is showing
graphEventId: string | undefined;
leadingControlColumns?: ControlColumnProps[];
@ -167,7 +165,7 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
setGlobalFullScreen,
start,
sort,
utilityBar,
additionalFilters,
graphEventId,
leadingControlColumns,
trailingControlColumns,
@ -239,7 +237,7 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
const [
loading,
{ events, updatedAt, loadPage, pageInfo, refetch, totalCount = 0, inspect },
{ events, loadPage, pageInfo, refetch, totalCount = 0, inspect },
] = useTimelineEvents({
alertConsumers: SECURITY_ALERTS_CONSUMERS,
docValueFields,
@ -294,19 +292,17 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
height={
headerFilterGroup == null ? COMPACT_HEADER_HEIGHT : EVENTS_VIEWER_HEADER_HEIGHT
}
subtitle={utilityBar}
title={globalFullScreen ? titleWithExitFullScreen : justTitle}
>
{HeaderSectionContent}
</HeaderSection>
<EventsContainerLoading
data-timeline-id={id}
data-test-subj={`events-container-loading-${loading}`}
>
<EuiFlexGroup gutterSize="none" justifyContent="flexEnd">
<UpdatedFlexItem grow={false} show={!loading}>
<LastUpdatedAt updatedAt={updatedAt} />
{!resolverIsShowing(graphEventId) && additionalFilters}
</UpdatedFlexItem>
</EuiFlexGroup>