mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
[RAM] Alert table all column fix 2 (#161054)
https://github.com/elastic/kibana/pull/160455 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
4fc4ded06b
commit
bed184b829
12 changed files with 256 additions and 169 deletions
|
@ -66,6 +66,7 @@ const uploadPipeline = (pipelineContent: string | object) => {
|
||||||
/^x-pack\/plugins\/security_solution/,
|
/^x-pack\/plugins\/security_solution/,
|
||||||
/^x-pack\/plugins\/timelines/,
|
/^x-pack\/plugins\/timelines/,
|
||||||
/^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/,
|
/^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/,
|
||||||
|
/^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/alerts_table/,
|
||||||
/^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/,
|
/^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/,
|
||||||
/^x-pack\/test\/defend_workflows_cypress/,
|
/^x-pack\/test\/defend_workflows_cypress/,
|
||||||
/^x-pack\/test\/security_solution_cypress/,
|
/^x-pack\/test\/security_solution_cypress/,
|
||||||
|
|
|
@ -140,6 +140,7 @@ def functionalXpack(Map params = [:]) {
|
||||||
'x-pack/test/security_solution_cypress/',
|
'x-pack/test/security_solution_cypress/',
|
||||||
'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/',
|
'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/',
|
||||||
'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx',
|
'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx',
|
||||||
|
'x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/',
|
||||||
]) {
|
]) {
|
||||||
if (githubPr.isPr()) {
|
if (githubPr.isPr()) {
|
||||||
task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressChrome', './test/scripts/jenkins_security_solution_cypress_chrome.sh'))
|
task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressChrome', './test/scripts/jenkins_security_solution_cypress_chrome.sh'))
|
||||||
|
|
|
@ -1261,66 +1261,17 @@ describe('SiemLocalStorage', () => {
|
||||||
],
|
],
|
||||||
sort: [{ '@timestamp': { order: 'desc' } }],
|
sort: [{ '@timestamp': { order: 'desc' } }],
|
||||||
visibleColumns: [
|
visibleColumns: [
|
||||||
{
|
'@timestamp',
|
||||||
columnHeaderType: 'not-filtered',
|
'kibana.alert.rule.name',
|
||||||
id: '@timestamp',
|
'kibana.alert.severity',
|
||||||
initialWidth: 180,
|
'kibana.alert.risk_score',
|
||||||
},
|
'kibana.alert.reason',
|
||||||
{
|
'host.name',
|
||||||
columnHeaderType: 'not-filtered',
|
'user.name',
|
||||||
displayAsText: 'Rule',
|
'process.name',
|
||||||
id: 'kibana.alert.rule.name',
|
'file.name',
|
||||||
initialWidth: 180,
|
'source.ip',
|
||||||
linkField: 'kibana.alert.rule.uuid',
|
'destination.ip',
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
displayAsText: 'Severity',
|
|
||||||
id: 'kibana.alert.severity',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
displayAsText: 'Risk Score',
|
|
||||||
id: 'kibana.alert.risk_score',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
displayAsText: 'Reason',
|
|
||||||
id: 'kibana.alert.reason',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'host.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'user.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'process.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'file.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'source.ip',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'destination.ip',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1393,66 +1344,17 @@ describe('SiemLocalStorage', () => {
|
||||||
{ 'kibana.alert.rule.name': { order: 'desc' } },
|
{ 'kibana.alert.rule.name': { order: 'desc' } },
|
||||||
],
|
],
|
||||||
visibleColumns: [
|
visibleColumns: [
|
||||||
{
|
'@timestamp',
|
||||||
columnHeaderType: 'not-filtered',
|
'kibana.alert.rule.name',
|
||||||
id: '@timestamp',
|
'kibana.alert.severity',
|
||||||
initialWidth: 180,
|
'kibana.alert.risk_score',
|
||||||
},
|
'kibana.alert.reason',
|
||||||
{
|
'host.name',
|
||||||
columnHeaderType: 'not-filtered',
|
'user.name',
|
||||||
displayAsText: 'Rule',
|
'process.name',
|
||||||
id: 'kibana.alert.rule.name',
|
'file.name',
|
||||||
initialWidth: 180,
|
'source.ip',
|
||||||
linkField: 'kibana.alert.rule.uuid',
|
'destination.ip',
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
displayAsText: 'Severity',
|
|
||||||
id: 'kibana.alert.severity',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
displayAsText: 'Risk Score',
|
|
||||||
id: 'kibana.alert.risk_score',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
displayAsText: 'Reason',
|
|
||||||
id: 'kibana.alert.reason',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'host.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'user.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'process.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'file.name',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'source.ip',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnHeaderType: 'not-filtered',
|
|
||||||
id: 'destination.ip',
|
|
||||||
initialWidth: 180,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,7 +100,7 @@ export const migrateAlertTableStateToTriggerActionsState = (
|
||||||
sort: legacyDataTableState[tableKey].sort.map((sortCandidate) => ({
|
sort: legacyDataTableState[tableKey].sort.map((sortCandidate) => ({
|
||||||
[sortCandidate.columnId]: { order: sortCandidate.sortDirection },
|
[sortCandidate.columnId]: { order: sortCandidate.sortDirection },
|
||||||
})),
|
})),
|
||||||
visibleColumns: legacyDataTableState[tableKey].columns,
|
visibleColumns: legacyDataTableState[tableKey].columns.map((c) => c.id),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -110,6 +110,7 @@ export const migrateAlertTableStateToTriggerActionsState = (
|
||||||
storage.set(key, stateObj[key]);
|
storage.set(key, stateObj[key]);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
return Object.assign(legacyDataTableState, triggersActionsState);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,7 +155,7 @@ export const getDataTablesInStorageByIds = (storage: Storage, tableIds: TableIdL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
migrateAlertTableStateToTriggerActionsState(storage, allDataTables);
|
allDataTables = migrateAlertTableStateToTriggerActionsState(storage, allDataTables);
|
||||||
|
|
||||||
return tableIds.reduce((acc, tableId) => {
|
return tableIds.reduce((acc, tableId) => {
|
||||||
const tableModel = allDataTables[tableId];
|
const tableModel = allDataTables[tableId];
|
||||||
|
|
|
@ -329,7 +329,6 @@ describe('AlertsTable', () => {
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
onToggleColumn: () => {},
|
onToggleColumn: () => {},
|
||||||
onResetColumns: () => {},
|
onResetColumns: () => {},
|
||||||
onColumnsChange: () => {},
|
|
||||||
onChangeVisibleColumns: () => {},
|
onChangeVisibleColumns: () => {},
|
||||||
browserFields,
|
browserFields,
|
||||||
query: {},
|
query: {},
|
||||||
|
|
|
@ -142,6 +142,7 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
|
||||||
updatedAt,
|
updatedAt,
|
||||||
browserFields,
|
browserFields,
|
||||||
onChangeVisibleColumns,
|
onChangeVisibleColumns,
|
||||||
|
onColumnResize,
|
||||||
showAlertStatusWithFlapping = false,
|
showAlertStatusWithFlapping = false,
|
||||||
showInspectButton = false,
|
showInspectButton = false,
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -486,6 +487,7 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
|
||||||
onChangePage: onChangePageIndex,
|
onChangePage: onChangePageIndex,
|
||||||
}}
|
}}
|
||||||
rowHeightsOptions={props.rowHeightsOptions}
|
rowHeightsOptions={props.rowHeightsOptions}
|
||||||
|
onColumnResize={onColumnResize}
|
||||||
ref={dataGridRef}
|
ref={dataGridRef}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -762,9 +762,15 @@ describe('AlertsTableState', () => {
|
||||||
storageMock.mockImplementation(() => ({
|
storageMock.mockImplementation(() => ({
|
||||||
get: () => {
|
get: () => {
|
||||||
return {
|
return {
|
||||||
columns: [{ displayAsText: 'Reason', id: 'kibana.alert.reason', schema: undefined }],
|
columns: [{ displayAsText: 'Reason', id: AlertsField.reason, schema: undefined }],
|
||||||
sort: [],
|
sort: [
|
||||||
visibleColumns: ['kibana.alert.reason'],
|
{
|
||||||
|
[AlertsField.reason]: {
|
||||||
|
order: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
visibleColumns: [AlertsField.reason],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
set: jest.fn(),
|
set: jest.fn(),
|
||||||
|
@ -780,11 +786,11 @@ describe('AlertsTableState', () => {
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(queryByTestId(`dataGridHeaderCell-${AlertsField.name}`)).not.toBe(null);
|
expect(queryByTestId(`dataGridHeaderCell-${AlertsField.name}`)).not.toBe(null);
|
||||||
expect(
|
const titles: string[] = [];
|
||||||
getByTestId('dataGridHeader')
|
getByTestId('dataGridHeader')
|
||||||
.querySelectorAll('.euiDataGridHeaderCell__content')[1]
|
.querySelectorAll('.euiDataGridHeaderCell__content')
|
||||||
.getAttribute('title')
|
.forEach((n) => titles.push(n?.getAttribute('title') ?? ''));
|
||||||
).toBe('Name');
|
expect(titles).toContain('Name');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -219,13 +219,13 @@ const AlertsTableStateWithQueryProvider = ({
|
||||||
|
|
||||||
const {
|
const {
|
||||||
columns,
|
columns,
|
||||||
onColumnsChange,
|
|
||||||
browserFields,
|
browserFields,
|
||||||
isBrowserFieldDataLoading,
|
isBrowserFieldDataLoading,
|
||||||
onToggleColumn,
|
onToggleColumn,
|
||||||
onResetColumns,
|
onResetColumns,
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
onChangeVisibleColumns,
|
onChangeVisibleColumns,
|
||||||
|
onColumnResize,
|
||||||
fields,
|
fields,
|
||||||
} = useColumns({
|
} = useColumns({
|
||||||
featureIds,
|
featureIds,
|
||||||
|
@ -393,8 +393,8 @@ const AlertsTableStateWithQueryProvider = ({
|
||||||
browserFields,
|
browserFields,
|
||||||
onToggleColumn,
|
onToggleColumn,
|
||||||
onResetColumns,
|
onResetColumns,
|
||||||
onColumnsChange,
|
|
||||||
onChangeVisibleColumns,
|
onChangeVisibleColumns,
|
||||||
|
onColumnResize,
|
||||||
query,
|
query,
|
||||||
rowHeightsOptions,
|
rowHeightsOptions,
|
||||||
renderCellValue,
|
renderCellValue,
|
||||||
|
@ -410,7 +410,7 @@ const AlertsTableStateWithQueryProvider = ({
|
||||||
memoizedMaintenanceWindows,
|
memoizedMaintenanceWindows,
|
||||||
columns,
|
columns,
|
||||||
flyoutSize,
|
flyoutSize,
|
||||||
pagination,
|
pagination.pageSize,
|
||||||
id,
|
id,
|
||||||
leadingControlColumns,
|
leadingControlColumns,
|
||||||
showExpandToDetails,
|
showExpandToDetails,
|
||||||
|
@ -421,8 +421,8 @@ const AlertsTableStateWithQueryProvider = ({
|
||||||
browserFields,
|
browserFields,
|
||||||
onToggleColumn,
|
onToggleColumn,
|
||||||
onResetColumns,
|
onResetColumns,
|
||||||
onColumnsChange,
|
|
||||||
onChangeVisibleColumns,
|
onChangeVisibleColumns,
|
||||||
|
onColumnResize,
|
||||||
query,
|
query,
|
||||||
rowHeightsOptions,
|
rowHeightsOptions,
|
||||||
renderCellValue,
|
renderCellValue,
|
||||||
|
|
|
@ -171,7 +171,6 @@ describe('AlertsTable.BulkActions', () => {
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
onToggleColumn: () => {},
|
onToggleColumn: () => {},
|
||||||
onResetColumns: () => {},
|
onResetColumns: () => {},
|
||||||
onColumnsChange: () => {},
|
|
||||||
onChangeVisibleColumns: () => {},
|
onChangeVisibleColumns: () => {},
|
||||||
browserFields: {},
|
browserFields: {},
|
||||||
query: {},
|
query: {},
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiDataGridColumn } from '@elastic/eui';
|
||||||
|
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||||
|
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||||
|
import { act, renderHook } from '@testing-library/react-hooks';
|
||||||
|
|
||||||
|
import { useColumns, UseColumnsArgs, UseColumnsResp } from './use_columns';
|
||||||
|
|
||||||
|
jest.mock('../../../../../common/lib/kibana');
|
||||||
|
|
||||||
|
const setItemStorageMock = jest.fn();
|
||||||
|
const mockStorage = {
|
||||||
|
getItem: jest.fn(),
|
||||||
|
setItem: setItemStorageMock,
|
||||||
|
removeItem: jest.fn(),
|
||||||
|
clear: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useColumn', () => {
|
||||||
|
const id = 'useColumnTest';
|
||||||
|
const featureIds: AlertConsumers[] = [AlertConsumers.LOGS, AlertConsumers.APM];
|
||||||
|
let storage = { current: new Storage(mockStorage) };
|
||||||
|
const storageAlertsTable = {
|
||||||
|
current: {
|
||||||
|
columns: [],
|
||||||
|
visibleColumns: [],
|
||||||
|
sort: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const defaultColumns: EuiDataGridColumn[] = [
|
||||||
|
{
|
||||||
|
id: 'event.action',
|
||||||
|
displayAsText: 'Alert status',
|
||||||
|
initialWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '@timestamp',
|
||||||
|
displayAsText: 'Last updated',
|
||||||
|
initialWidth: 250,
|
||||||
|
schema: 'datetime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'kibana.alert.duration.us',
|
||||||
|
displayAsText: 'Duration',
|
||||||
|
initialWidth: 150,
|
||||||
|
schema: 'numeric',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'kibana.alert.reason',
|
||||||
|
displayAsText: 'Reason',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setItemStorageMock.mockClear();
|
||||||
|
storage = { current: new Storage(mockStorage) };
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hide all columns with onChangeVisibleColumns', async () => {
|
||||||
|
const { result, waitForNextUpdate } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||||
|
useColumns({ defaultColumns, featureIds, id, storageAlertsTable, storage })
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onChangeVisibleColumns([]);
|
||||||
|
});
|
||||||
|
await waitForNextUpdate();
|
||||||
|
expect(result.current.visibleColumns).toEqual([]);
|
||||||
|
expect(result.current.columns).toEqual(defaultColumns);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('show all columns with onChangeVisibleColumns', async () => {
|
||||||
|
const { result, waitForNextUpdate } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||||
|
useColumns({ defaultColumns, featureIds, id, storageAlertsTable, storage })
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onChangeVisibleColumns([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onChangeVisibleColumns(defaultColumns.map((dc) => dc.id));
|
||||||
|
});
|
||||||
|
await waitForNextUpdate();
|
||||||
|
expect(result.current.visibleColumns).toEqual([
|
||||||
|
'event.action',
|
||||||
|
'@timestamp',
|
||||||
|
'kibana.alert.duration.us',
|
||||||
|
'kibana.alert.reason',
|
||||||
|
]);
|
||||||
|
expect(result.current.columns).toEqual(defaultColumns);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('onColumnResize', async () => {
|
||||||
|
const { result, waitForNextUpdate } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||||
|
useColumns({ defaultColumns, featureIds, id, storageAlertsTable, storage })
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onColumnResize({ columnId: '@timestamp', width: 100 });
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForNextUpdate();
|
||||||
|
expect(setItemStorageMock).toHaveBeenCalledWith(
|
||||||
|
'useColumnTest',
|
||||||
|
'{"columns":[{"id":"event.action","displayAsText":"Alert status","initialWidth":150},{"id":"@timestamp","displayAsText":"Last updated","initialWidth":100,"schema":"datetime"},{"id":"kibana.alert.duration.us","displayAsText":"Duration","initialWidth":150,"schema":"numeric"},{"id":"kibana.alert.reason","displayAsText":"Reason"}],"visibleColumns":["event.action","@timestamp","kibana.alert.duration.us","kibana.alert.reason"],"sort":[]}'
|
||||||
|
);
|
||||||
|
expect(result.current.columns.find((c) => c.id === '@timestamp')).toEqual({
|
||||||
|
displayAsText: 'Last updated',
|
||||||
|
id: '@timestamp',
|
||||||
|
initialWidth: 100,
|
||||||
|
schema: 'datetime',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,7 +5,7 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EuiDataGridColumn } from '@elastic/eui';
|
import { EuiDataGridColumn, EuiDataGridOnColumnResizeData } from '@elastic/eui';
|
||||||
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||||
import { BrowserField, BrowserFields } from '@kbn/rule-registry-plugin/common';
|
import { BrowserField, BrowserFields } from '@kbn/rule-registry-plugin/common';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
@ -15,7 +15,7 @@ import { AlertsTableStorage } from '../../alerts_table_state';
|
||||||
import { toggleColumn } from './toggle_column';
|
import { toggleColumn } from './toggle_column';
|
||||||
import { useFetchBrowserFieldCapabilities } from '../use_fetch_browser_fields_capabilities';
|
import { useFetchBrowserFieldCapabilities } from '../use_fetch_browser_fields_capabilities';
|
||||||
|
|
||||||
interface UseColumnsArgs {
|
export interface UseColumnsArgs {
|
||||||
featureIds: AlertConsumers[];
|
featureIds: AlertConsumers[];
|
||||||
storageAlertsTable: React.MutableRefObject<AlertsTableStorage>;
|
storageAlertsTable: React.MutableRefObject<AlertsTableStorage>;
|
||||||
storage: React.MutableRefObject<IStorageWrapper>;
|
storage: React.MutableRefObject<IStorageWrapper>;
|
||||||
|
@ -24,6 +24,21 @@ interface UseColumnsArgs {
|
||||||
initialBrowserFields?: BrowserFields;
|
initialBrowserFields?: BrowserFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UseColumnsResp {
|
||||||
|
columns: EuiDataGridColumn[];
|
||||||
|
visibleColumns: string[];
|
||||||
|
isBrowserFieldDataLoading: boolean | undefined;
|
||||||
|
browserFields: BrowserFields;
|
||||||
|
onToggleColumn: (columnId: string) => void;
|
||||||
|
onResetColumns: () => void;
|
||||||
|
onChangeVisibleColumns: (columnIds: string[]) => void;
|
||||||
|
onColumnResize: (args: EuiDataGridOnColumnResizeData) => void;
|
||||||
|
fields: Array<{
|
||||||
|
field: string;
|
||||||
|
include_unmapped: boolean;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
const EMPTY_FIELDS = [{ field: '*', include_unmapped: true }];
|
const EMPTY_FIELDS = [{ field: '*', include_unmapped: true }];
|
||||||
|
|
||||||
const fieldTypeToDataGridColumnTypeMapper = (fieldType: string | undefined) => {
|
const fieldTypeToDataGridColumnTypeMapper = (fieldType: string | undefined) => {
|
||||||
|
@ -114,26 +129,23 @@ const getColumnByColumnId = (columns: EuiDataGridColumn[], columnId: string) =>
|
||||||
return columns.find(({ id }: { id: string }) => id === columnId);
|
return columns.find(({ id }: { id: string }) => id === columnId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getColumnsByColumnIds = (columns: EuiDataGridColumn[], columnIds: string[]) => {
|
|
||||||
return columnIds
|
|
||||||
.map((columnId: string) => columns.find((column: EuiDataGridColumn) => column.id === columnId))
|
|
||||||
.filter(Boolean) as EuiDataGridColumn[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const persist = ({
|
const persist = ({
|
||||||
id,
|
id,
|
||||||
storageAlertsTable,
|
storageAlertsTable,
|
||||||
columns,
|
columns,
|
||||||
storage,
|
storage,
|
||||||
|
visibleColumns,
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
storageAlertsTable: React.MutableRefObject<AlertsTableStorage>;
|
storageAlertsTable: React.MutableRefObject<AlertsTableStorage>;
|
||||||
storage: React.MutableRefObject<IStorageWrapper>;
|
storage: React.MutableRefObject<IStorageWrapper>;
|
||||||
columns: EuiDataGridColumn[];
|
columns: EuiDataGridColumn[];
|
||||||
|
visibleColumns: string[];
|
||||||
}) => {
|
}) => {
|
||||||
storageAlertsTable.current = {
|
storageAlertsTable.current = {
|
||||||
...storageAlertsTable.current,
|
...storageAlertsTable.current,
|
||||||
columns,
|
columns,
|
||||||
|
visibleColumns,
|
||||||
};
|
};
|
||||||
storage.current.set(id, storageAlertsTable.current);
|
storage.current.set(id, storageAlertsTable.current);
|
||||||
};
|
};
|
||||||
|
@ -145,7 +157,7 @@ export const useColumns = ({
|
||||||
id,
|
id,
|
||||||
defaultColumns,
|
defaultColumns,
|
||||||
initialBrowserFields,
|
initialBrowserFields,
|
||||||
}: UseColumnsArgs) => {
|
}: UseColumnsArgs): UseColumnsResp => {
|
||||||
const [isBrowserFieldDataLoading, browserFields] = useFetchBrowserFieldCapabilities({
|
const [isBrowserFieldDataLoading, browserFields] = useFetchBrowserFieldCapabilities({
|
||||||
featureIds,
|
featureIds,
|
||||||
initialBrowserFields,
|
initialBrowserFields,
|
||||||
|
@ -156,10 +168,16 @@ export const useColumns = ({
|
||||||
// before restoring from storage, enrich the column data
|
// before restoring from storage, enrich the column data
|
||||||
if (initialBrowserFields && defaultColumns) {
|
if (initialBrowserFields && defaultColumns) {
|
||||||
cols = populateColumns(cols, initialBrowserFields, defaultColumns);
|
cols = populateColumns(cols, initialBrowserFields, defaultColumns);
|
||||||
|
} else if (cols && cols.length === 0) {
|
||||||
|
cols = defaultColumns;
|
||||||
}
|
}
|
||||||
return cols;
|
return cols;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [visibleColumns, setVisibleColumns] = useState(
|
||||||
|
storageAlertsTable.current.visibleColumns ?? getColumnIds(columns)
|
||||||
|
);
|
||||||
|
|
||||||
const [isColumnsPopulated, setColumnsPopulated] = useState<boolean>(false);
|
const [isColumnsPopulated, setColumnsPopulated] = useState<boolean>(false);
|
||||||
|
|
||||||
const defaultColumnsRef = useRef<typeof defaultColumns>(defaultColumns);
|
const defaultColumnsRef = useRef<typeof defaultColumns>(defaultColumns);
|
||||||
|
@ -190,13 +208,14 @@ export const useColumns = ({
|
||||||
}, [browserFields, defaultColumns, isBrowserFieldDataLoading, isColumnsPopulated, columns]);
|
}, [browserFields, defaultColumns, isBrowserFieldDataLoading, isColumnsPopulated, columns]);
|
||||||
|
|
||||||
const setColumnsAndSave = useCallback(
|
const setColumnsAndSave = useCallback(
|
||||||
(newColumns: EuiDataGridColumn[]) => {
|
(newColumns: EuiDataGridColumn[], newVisibleColumns: string[]) => {
|
||||||
setColumns(newColumns);
|
setColumns(newColumns);
|
||||||
persist({
|
persist({
|
||||||
id,
|
id,
|
||||||
storage,
|
storage,
|
||||||
storageAlertsTable,
|
storageAlertsTable,
|
||||||
columns: newColumns,
|
columns: newColumns,
|
||||||
|
visibleColumns: newVisibleColumns,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[id, storage, storageAlertsTable]
|
[id, storage, storageAlertsTable]
|
||||||
|
@ -204,10 +223,16 @@ export const useColumns = ({
|
||||||
|
|
||||||
const setColumnsByColumnIds = useCallback(
|
const setColumnsByColumnIds = useCallback(
|
||||||
(columnIds: string[]) => {
|
(columnIds: string[]) => {
|
||||||
const newColumns = getColumnsByColumnIds(columns, columnIds);
|
setVisibleColumns(columnIds);
|
||||||
setColumnsAndSave(newColumns);
|
persist({
|
||||||
|
id,
|
||||||
|
storage,
|
||||||
|
storageAlertsTable,
|
||||||
|
columns,
|
||||||
|
visibleColumns: columnIds,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setColumnsAndSave, columns]
|
[columns, id, storage, storageAlertsTable]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onToggleColumn = useCallback(
|
const onToggleColumn = useCallback(
|
||||||
|
@ -219,17 +244,37 @@ export const useColumns = ({
|
||||||
columns,
|
columns,
|
||||||
defaultColumns,
|
defaultColumns,
|
||||||
});
|
});
|
||||||
|
let newVisibleColumns = visibleColumns;
|
||||||
setColumnsAndSave(newColumns);
|
if (visibleColumns.includes(columnId)) {
|
||||||
|
newVisibleColumns = visibleColumns.filter((vc) => vc !== columnId);
|
||||||
|
} else {
|
||||||
|
newVisibleColumns = [visibleColumns[0], columnId, ...visibleColumns.slice(1)];
|
||||||
|
}
|
||||||
|
setVisibleColumns(newVisibleColumns);
|
||||||
|
setColumnsAndSave(newColumns, newVisibleColumns);
|
||||||
},
|
},
|
||||||
[browserFields, columns, defaultColumns, setColumnsAndSave]
|
[browserFields, columns, defaultColumns, setColumnsAndSave, visibleColumns]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onResetColumns = useCallback(() => {
|
const onResetColumns = useCallback(() => {
|
||||||
const populatedDefaultColumns = populateColumns(defaultColumns, browserFields, defaultColumns);
|
const populatedDefaultColumns = populateColumns(defaultColumns, browserFields, defaultColumns);
|
||||||
setColumnsAndSave(populatedDefaultColumns);
|
setColumnsAndSave(
|
||||||
|
populatedDefaultColumns,
|
||||||
|
populatedDefaultColumns.map((pdc) => pdc.id)
|
||||||
|
);
|
||||||
}, [browserFields, defaultColumns, setColumnsAndSave]);
|
}, [browserFields, defaultColumns, setColumnsAndSave]);
|
||||||
|
|
||||||
|
const onColumnResize = useCallback(
|
||||||
|
({ columnId, width }: EuiDataGridOnColumnResizeData) => {
|
||||||
|
const colIndex = columns.findIndex((c) => c.id === columnId);
|
||||||
|
if (colIndex > -1) {
|
||||||
|
columns.splice(colIndex, 1, { ...columns[colIndex], initialWidth: width });
|
||||||
|
setColumnsAndSave(columns, visibleColumns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[columns, setColumnsAndSave, visibleColumns]
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In some case such security, we need some special fields such as threat.enrichments which are
|
* In some case such security, we need some special fields such as threat.enrichments which are
|
||||||
* not fetched when passing only EMPTY_FIELDS. Hence, we will fetch all the fields that user has added to the table.
|
* not fetched when passing only EMPTY_FIELDS. Hence, we will fetch all the fields that user has added to the table.
|
||||||
|
@ -243,19 +288,28 @@ export const useColumns = ({
|
||||||
[columns]
|
[columns]
|
||||||
);
|
);
|
||||||
|
|
||||||
const visibleColumns = useMemo(() => {
|
return useMemo(
|
||||||
return getColumnIds(columns);
|
() => ({
|
||||||
}, [columns]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
columns,
|
columns,
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
isBrowserFieldDataLoading,
|
isBrowserFieldDataLoading,
|
||||||
browserFields,
|
browserFields,
|
||||||
onColumnsChange: setColumnsAndSave,
|
|
||||||
onToggleColumn,
|
onToggleColumn,
|
||||||
onResetColumns,
|
onResetColumns,
|
||||||
onChangeVisibleColumns: setColumnsByColumnIds,
|
onChangeVisibleColumns: setColumnsByColumnIds,
|
||||||
|
onColumnResize,
|
||||||
fields: fieldsToFetch,
|
fields: fieldsToFetch,
|
||||||
};
|
}),
|
||||||
|
[
|
||||||
|
browserFields,
|
||||||
|
columns,
|
||||||
|
fieldsToFetch,
|
||||||
|
isBrowserFieldDataLoading,
|
||||||
|
onColumnResize,
|
||||||
|
onResetColumns,
|
||||||
|
onToggleColumn,
|
||||||
|
setColumnsByColumnIds,
|
||||||
|
visibleColumns,
|
||||||
|
]
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,7 @@ import type {
|
||||||
EuiDataGridColumnCellAction,
|
EuiDataGridColumnCellAction,
|
||||||
EuiDataGridToolBarVisibilityOptions,
|
EuiDataGridToolBarVisibilityOptions,
|
||||||
EuiSuperSelectOption,
|
EuiSuperSelectOption,
|
||||||
|
EuiDataGridOnColumnResizeHandler,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { EuiDataGridColumn, EuiDataGridControlColumn, EuiDataGridSorting } from '@elastic/eui';
|
import { EuiDataGridColumn, EuiDataGridControlColumn, EuiDataGridSorting } from '@elastic/eui';
|
||||||
import { HttpSetup } from '@kbn/core/public';
|
import { HttpSetup } from '@kbn/core/public';
|
||||||
|
@ -545,8 +546,8 @@ export type AlertsTableProps = {
|
||||||
browserFields: any;
|
browserFields: any;
|
||||||
onToggleColumn: (columnId: string) => void;
|
onToggleColumn: (columnId: string) => void;
|
||||||
onResetColumns: () => void;
|
onResetColumns: () => void;
|
||||||
onColumnsChange: (columns: EuiDataGridColumn[], visibleColumns: string[]) => void;
|
|
||||||
onChangeVisibleColumns: (newColumns: string[]) => void;
|
onChangeVisibleColumns: (newColumns: string[]) => void;
|
||||||
|
onColumnResize?: EuiDataGridOnColumnResizeHandler;
|
||||||
query: Pick<QueryDslQueryContainer, 'bool' | 'ids'>;
|
query: Pick<QueryDslQueryContainer, 'bool' | 'ids'>;
|
||||||
controls?: EuiDataGridToolBarAdditionalControlsOptions;
|
controls?: EuiDataGridToolBarAdditionalControlsOptions;
|
||||||
showInspectButton?: boolean;
|
showInspectButton?: boolean;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue