Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] integrate CellActions in Events/Alerts DataTables #149934

Merged
merged 31 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2c91688
cell actions type and fix add to timeline empty value
semd Jan 25, 2023
79820f5
fix types and test
semd Jan 25, 2023
a247620
async chunk size improvement
semd Jan 25, 2023
da88c20
fix type
semd Jan 26, 2023
1453d4e
Merge branch 'main' into 145666_cell_actions_datatable_integration
kibanamachine Jan 26, 2023
a5a082a
remove useMemo
semd Jan 26, 2023
fc8bfb3
Merge branch 'main' into 145666_cell_actions_datatable_integration
kibanamachine Jan 26, 2023
6f8f36c
Merge branch 'main' into 145666_cell_actions_datatable_integration
kibanamachine Jan 27, 2023
085b719
integrate cellActions in Security dataTables
semd Jan 31, 2023
fd20e2b
remove legacy cell actions
semd Jan 31, 2023
6302946
Merge branch '145666_cell_actions_datatable_integration' of https://g…
semd Jan 31, 2023
4f65169
Merge remote-tracking branch 'upstream/main' into 145666_cell_actions…
semd Jan 31, 2023
bb0845b
fix tests
semd Jan 31, 2023
b897806
fix cypress selector
semd Jan 31, 2023
3c9fc4f
fix cypress tests
semd Feb 1, 2023
b8ad09d
Merge remote-tracking branch 'upstream/main' into 145666_cell_actions…
semd Feb 6, 2023
465d1f8
link entities in dataTables
semd Feb 6, 2023
1596b8e
fix user link cypress test
semd Feb 6, 2023
c36868c
Merge branch 'main' into 145666_cell_actions_datatable_integration
kibanamachine Feb 6, 2023
cfa676f
Merge branch 'main' into 145666_cell_actions_datatable_integration
kibanamachine Feb 8, 2023
57667a8
Merge remote-tracking branch 'upstream/main' into 145666_cell_actions…
semd Feb 8, 2023
2e21c85
fix type
semd Feb 8, 2023
f737bf8
Merge branch 'main' into 145666_cell_actions_datatable_integration
kibanamachine Feb 9, 2023
0dafac6
fix functional test
semd Feb 9, 2023
134ccbc
Merge branch '145666_cell_actions_datatable_integration' of https://g…
semd Feb 9, 2023
78fd1ec
close popover on action clicked and cypress test
semd Feb 10, 2023
1dbcd45
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Feb 11, 2023
92dc22a
fix cypress
semd Feb 13, 2023
8dad378
solve conflicts
semd Feb 13, 2023
b3c1931
solve conflicts
semd Feb 13, 2023
e52bac6
fix aggregatable call
semd Feb 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export type ColumnHeaderOptions = Pick<
| 'isResizable'
> & {
aggregatable?: boolean;
dataTableCellActions?: DataTableCellAction[];
category?: string;
columnHeaderType: ColumnHeaderType;
description?: string | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
import type { Filter } from '@kbn/es-query';
import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
import type { ColumnHeaderOptions, RowRenderer } from '../..';
import type { BrowserFields, TimelineNonEcsData } from '../../../search_strategy';
Expand All @@ -18,7 +17,6 @@ export type CellValueElementProps = EuiDataGridCellValueElementProps & {
data: TimelineNonEcsData[];
ecsData?: Ecs;
eventId: string; // _id
globalFilters?: Filter[];
header: ColumnHeaderOptions;
isDraggable: boolean;
isTimeline?: boolean; // Default cell renderer is used for both the alert table and timeline. This allows us to cheaply separate concerns
Expand All @@ -30,5 +28,4 @@ export type CellValueElementProps = EuiDataGridCellValueElementProps & {
truncate?: boolean;
key?: string;
closeCellPopover?: () => void;
enableActions?: boolean;
};
3 changes: 2 additions & 1 deletion x-pack/plugins/security_solution/cypress/screens/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ export const ALERT_TABLE_FILE_NAME_HEADER = '[data-gridcell-column-id="file.name
export const ALERT_TABLE_FILE_NAME_VALUES =
'[data-gridcell-column-id="file.name"][data-test-subj="dataGridRowCell"]'; // empty column for the test data

export const ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE = '[data-test-subj="add-to-timeline"]';
export const ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE =
'[data-test-subj="dataGridColumnCellAction-security_addToTimeline]';

export const ACTIVE_TIMELINE_BOTTOM_BAR =
'[data-test-subj="flyoutBottomBar"] .active-timeline-button';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ describe('transformControlColumns', () => {
setEventsDeleted: jest.fn(),
columnHeaders: [],
controlColumns: [],
disabledCellActions: [],
selectedEventIds: {},
tabType: '',
isSelectAllChecked: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export interface TransformColumnsProps {
columnHeaders: ColumnHeaderOptions[];
controlColumns: ControlColumnProps[];
data: TimelineItem[];
disabledCellActions: string[];
fieldBrowserOptions?: FieldBrowserOptions;
loadingEventIds: string[];
onRowSelected: OnRowSelected;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,32 @@ import { REMOVE_COLUMN } from './column_headers/translations';
import { useMountAppended } from '../../utils/use_mount_appended';
import type { EuiDataGridColumn } from '@elastic/eui';
import { defaultHeaders, mockGlobalState, mockTimelineData, TestProviders } from '../../mock';
import { defaultColumnHeaderType } from '../../store/data_table/defaults';
import { mockBrowserFields } from '../../containers/source/mock';
import { getMappedNonEcsValue } from '../../../timelines/components/timeline/body/data_driven_columns';
import type { CellValueElementProps } from '../../../../common/types';
import { TableId } from '../../../../common/types';
import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../common/constants';

const mockDispatch = jest.fn();
jest.mock('react-redux', () => {
const original = jest.requireActual('react-redux');
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));

return {
...original,
useDispatch: () => mockDispatch,
};
});
const mockUseDataGridColumnsCellActions = jest.fn(
(_: object): Array<Array<() => JSX.Element>> => []
);
jest.mock('@kbn/cell-actions', () => ({
...jest.requireActual('@kbn/cell-actions'),
useDataGridColumnsCellActions: (params: object) => mockUseDataGridColumnsCellActions(params),
}));

const headersJustTimestamp = defaultHeaders.filter((h) => h.id === '@timestamp');
const mockGetColumnHeaders = jest.fn(() => headersJustTimestamp);
jest.mock('./column_headers/helpers', () => ({
...jest.requireActual('./column_headers/helpers'),
getColumnHeaders: () => mockGetColumnHeaders(),
}));

jest.mock('@kbn/kibana-react-plugin/public', () => {
const originalModule = jest.requireActual('@kbn/kibana-react-plugin/public');
Expand Down Expand Up @@ -80,8 +91,6 @@ describe('DataTable', () => {
const props: DataTableProps = {
browserFields: mockBrowserFields,
data: mockTimelineData,
defaultCellActions: [],
disabledCellActions: ['signal.rule.risk_score', 'signal.reason'],
id: TableId.test,
loadPage: jest.fn(),
renderCellValue: TestCellRenderer,
Expand Down Expand Up @@ -142,10 +151,8 @@ describe('DataTable', () => {
});

test('it renders cell value', () => {
const headersJustTimestamp = defaultHeaders.filter((h) => h.id === '@timestamp');
const testProps = {
...props,
columnHeaders: headersJustTimestamp,
data: mockTimelineData.slice(0, 1),
};
const wrapper = mount(
Expand All @@ -163,49 +170,43 @@ describe('DataTable', () => {
.text()
).toEqual(mockTimelineData[0].ecs.timestamp);
});
});

test('timestamp column renders cell actions', () => {
const headersJustTimestamp = defaultHeaders.filter((h) => h.id === '@timestamp');
const testProps = {
...props,
columnHeaders: headersJustTimestamp,
data: mockTimelineData.slice(0, 1),
};
describe('cellActions', () => {
test('calls useDataGridColumnsCellActions properly', () => {
const data = mockTimelineData.slice(0, 1);
const wrapper = mount(
<TestProviders>
<DataTableComponent {...testProps} />
<DataTableComponent {...props} data={data} />
</TestProviders>
);
wrapper.update();

expect(
wrapper
.find('[data-test-subj="body-data-grid"]')
.first()
.prop<EuiDataGridColumn[]>('columns')
.find((c) => c.id === '@timestamp')?.cellActions
).toBeDefined();
expect(mockUseDataGridColumnsCellActions).toHaveBeenCalledWith({
triggerId: CELL_ACTIONS_DEFAULT_TRIGGER,
fields: [{ name: '@timestamp', values: [data[0]?.data[0]?.value], type: 'date' }],
});
});

test("signal.rule.risk_score column doesn't render cell actions", () => {
const columnHeaders = [
{
category: 'signal',
columnHeaderType: defaultColumnHeaderType,
id: 'signal.rule.risk_score',
type: 'number',
aggregatable: true,
initialWidth: 105,
},
];
const testProps = {
...props,
columnHeaders,
data: mockTimelineData.slice(0, 1),
};
test('does not render cell actions if disableCellActions is true', () => {
const wrapper = mount(
<TestProviders>
<DataTableComponent {...testProps} />
<DataTableComponent {...props} data={mockTimelineData.slice(0, 1)} disableCellActions />
</TestProviders>
);
wrapper.update();

expect(mockUseDataGridColumnsCellActions).toHaveBeenCalledWith({
triggerId: CELL_ACTIONS_DEFAULT_TRIGGER,
fields: [],
});
});

test('does not render cell actions if empty actions returned', () => {
mockUseDataGridColumnsCellActions.mockReturnValueOnce([]);
const wrapper = mount(
<TestProviders>
<DataTableComponent {...props} data={mockTimelineData.slice(0, 1)} />
</TestProviders>
);
wrapper.update();
Expand All @@ -215,29 +216,15 @@ describe('DataTable', () => {
.find('[data-test-subj="body-data-grid"]')
.first()
.prop<EuiDataGridColumn[]>('columns')
.find((c) => c.id === 'signal.rule.risk_score')?.cellActions
).toBeUndefined();
.find((c) => c.id === '@timestamp')?.cellActions
).toHaveLength(0);
});

test("signal.reason column doesn't render cell actions", () => {
const columnHeaders = [
{
category: 'signal',
columnHeaderType: defaultColumnHeaderType,
id: 'signal.reason',
type: 'string',
aggregatable: true,
initialWidth: 450,
},
];
const testProps = {
...props,
columnHeaders,
data: mockTimelineData.slice(0, 1),
};
test('renders returned cell actions', () => {
mockUseDataGridColumnsCellActions.mockReturnValueOnce([[() => <div />]]);
const wrapper = mount(
<TestProviders>
<DataTableComponent {...testProps} />
<DataTableComponent {...props} data={mockTimelineData.slice(0, 1)} />
</TestProviders>
);
wrapper.update();
Expand All @@ -247,43 +234,11 @@ describe('DataTable', () => {
.find('[data-test-subj="body-data-grid"]')
.first()
.prop<EuiDataGridColumn[]>('columns')
.find((c) => c.id === 'signal.reason')?.cellActions
).toBeUndefined();
.find((c) => c.id === '@timestamp')?.cellActions
).toHaveLength(1);
});
});

test("signal.rule.risk_score column doesn't render cell actions", () => {
const columnHeaders = [
{
category: 'signal',
columnHeaderType: defaultColumnHeaderType,
id: 'signal.rule.risk_score',
type: 'number',
aggregatable: true,
initialWidth: 105,
},
];
const testProps = {
...props,
columnHeaders,
data: mockTimelineData.slice(0, 1),
};
const wrapper = mount(
<TestProviders>
<DataTableComponent {...testProps} />
</TestProviders>
);
wrapper.update();

expect(
wrapper
.find('[data-test-subj="body-data-grid"]')
.first()
.prop<EuiDataGridColumn[]>('columns')
.find((c) => c.id === 'signal.rule.risk_score')?.cellActions
).toBeUndefined();
});

test('it does NOT render switches for hiding columns in the `EuiDataGrid` `Columns` popover', async () => {
render(
<TestProviders>
Expand Down
Loading