mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[8.9] [Security Solution][Fix] Event Summary columns is not visible in Event Rendered View of Alert Table (#162635) (#163561)
# Backport This will backport the following commits from `main` to `8.9`: - [[Security Solution][Fix] Event Summary columns is not visible in Event Rendered View of Alert Table (#162635)](https://github.com/elastic/kibana/pull/162635) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Jatin Kathuria","email":"jatin.kathuria@elastic.co"},"sourceCommit":{"committedDate":"2023-08-03T10:56:25Z","message":"[Security Solution][Fix] Event Summary columns is not visible in Event Rendered View of Alert Table (#162635)\n\n## Summary\r\n\r\nHandles. #162471\r\n\r\nThis PR fixes the stability of visibleColumns in the alert Table. Below\r\nvideos shows the difference between before and after the change.\r\n\r\n| Before | After |\r\n|--|--|\r\n|<video\r\nsrc=\"06d8616b
-9708-40ed-814f-5899d6158551\"\r\n/> | <video\r\nsrc=\"12c86e11
-fccb-4b6f-88cf-1ba2bd96ea52\"\r\n/> |\r\n\r\n\r\n## Existing issues.\r\n\r\n1. If you noticed in the after video, there remains an existing issue\r\nwhich is the event rendered view is coming without alternating color\r\nrows as shown in screenshot below. ( ✅ Fixed in this PR )\r\n\r\n2. Additionally, this bug was also found which also being separately\r\ntracked : https://github.com/elastic/kibana/issues/162684\r\n\r\n\r\n\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"fcfabcdcd10f1cba284dd6876edf8bc583dea1bc","branchLabelMapping":{"^v8.10.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:ResponseOps","Team:Threat Hunting:Investigations","v8.9.0","v8.10.0"],"number":162635,"url":"https://github.com/elastic/kibana/pull/162635","mergeCommit":{"message":"[Security Solution][Fix] Event Summary columns is not visible in Event Rendered View of Alert Table (#162635)\n\n## Summary\r\n\r\nHandles. #162471\r\n\r\nThis PR fixes the stability of visibleColumns in the alert Table. Below\r\nvideos shows the difference between before and after the change.\r\n\r\n| Before | After |\r\n|--|--|\r\n|<video\r\nsrc=\"06d8616b
-9708-40ed-814f-5899d6158551\"\r\n/> | <video\r\nsrc=\"12c86e11
-fccb-4b6f-88cf-1ba2bd96ea52\"\r\n/> |\r\n\r\n\r\n## Existing issues.\r\n\r\n1. If you noticed in the after video, there remains an existing issue\r\nwhich is the event rendered view is coming without alternating color\r\nrows as shown in screenshot below. ( ✅ Fixed in this PR )\r\n\r\n2. Additionally, this bug was also found which also being separately\r\ntracked : https://github.com/elastic/kibana/issues/162684\r\n\r\n\r\n\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"fcfabcdcd10f1cba284dd6876edf8bc583dea1bc"}},"sourceBranch":"main","suggestedTargetBranches":["8.9"],"targetPullRequestStates":[{"branch":"8.9","label":"v8.9.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.10.0","labelRegex":"^v8.10.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/162635","number":162635,"mergeCommit":{"message":"[Security Solution][Fix] Event Summary columns is not visible in Event Rendered View of Alert Table (#162635)\n\n## Summary\r\n\r\nHandles. #162471\r\n\r\nThis PR fixes the stability of visibleColumns in the alert Table. Below\r\nvideos shows the difference between before and after the change.\r\n\r\n| Before | After |\r\n|--|--|\r\n|<video\r\nsrc=\"06d8616b
-9708-40ed-814f-5899d6158551\"\r\n/> | <video\r\nsrc=\"12c86e11
-fccb-4b6f-88cf-1ba2bd96ea52\"\r\n/> |\r\n\r\n\r\n## Existing issues.\r\n\r\n1. If you noticed in the after video, there remains an existing issue\r\nwhich is the event rendered view is coming without alternating color\r\nrows as shown in screenshot below. ( ✅ Fixed in this PR )\r\n\r\n2. Additionally, this bug was also found which also being separately\r\ntracked : https://github.com/elastic/kibana/issues/162684\r\n\r\n\r\n\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"fcfabcdcd10f1cba284dd6876edf8bc583dea1bc"}}]}] BACKPORT-->
This commit is contained in:
parent
6f149e8764
commit
bea10503e9
18 changed files with 615 additions and 98 deletions
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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 {
|
||||
switchAlertTableToEventRenderedView,
|
||||
switchAlertTableToGridView,
|
||||
waitForAlerts,
|
||||
} from '../../../tasks/alerts';
|
||||
import { navigateFromHeaderTo } from '../../../tasks/security_header';
|
||||
import { FIELDS_BROWSER_BTN } from '../../../screens/rule_details';
|
||||
import {
|
||||
addsFields,
|
||||
closeFieldsBrowser,
|
||||
filterFieldsBrowser,
|
||||
removeField,
|
||||
} from '../../../tasks/fields_browser';
|
||||
import { FIELDS_BROWSER_CONTAINER } from '../../../screens/fields_browser';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import {
|
||||
DATA_GRID_COLUMN_ORDER_BTN,
|
||||
DATA_GRID_FIELDS,
|
||||
DATA_GRID_FULL_SCREEN,
|
||||
GET_DATA_GRID_HEADER,
|
||||
GET_DATA_GRID_HEADER_CELL_ACTION_GROUP,
|
||||
} from '../../../screens/common/data_grid';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { cleanKibana } from '../../../tasks/common';
|
||||
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
|
||||
import { login, visit } from '../../../tasks/login';
|
||||
import { ALERTS_URL } from '../../../urls/navigation';
|
||||
import { DATAGRID_HEADER } from '../../../screens/timeline';
|
||||
import { TIMELINES, ALERTS } from '../../../screens/security_header';
|
||||
|
||||
/*
|
||||
*
|
||||
* Alert table is third party component which cannot be easily tested by jest.
|
||||
* This test main checks if Alert Table controls are rendered properly.
|
||||
*
|
||||
* */
|
||||
|
||||
describe(`Alert Table Controls`, () => {
|
||||
before(() => {
|
||||
cleanKibana();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
createRule(getNewRule());
|
||||
visit(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
it('full screen, column sorting', () => {
|
||||
cy.get(DATA_GRID_FULL_SCREEN)
|
||||
.should('have.attr', 'aria-label', 'Enter fullscreen')
|
||||
.trigger('click');
|
||||
cy.get(DATA_GRID_FULL_SCREEN)
|
||||
.should('have.attr', 'aria-label', 'Exit fullscreen')
|
||||
.trigger('click');
|
||||
cy.get(DATA_GRID_COLUMN_ORDER_BTN).should('be.visible');
|
||||
});
|
||||
|
||||
context('Sorting', () => {
|
||||
it('Date Column', () => {
|
||||
const timestampField = DATA_GRID_FIELDS.TIMESTAMP.fieldName;
|
||||
cy.get(GET_DATA_GRID_HEADER(timestampField)).trigger('click');
|
||||
cy.get(GET_DATA_GRID_HEADER_CELL_ACTION_GROUP(timestampField))
|
||||
.should('be.visible')
|
||||
.should('contain.text', 'Sort Old-New');
|
||||
});
|
||||
|
||||
it('Number column', () => {
|
||||
const riskScoreField = DATA_GRID_FIELDS.RISK_SCORE.fieldName;
|
||||
cy.get(GET_DATA_GRID_HEADER(riskScoreField)).trigger('click');
|
||||
cy.get(GET_DATA_GRID_HEADER_CELL_ACTION_GROUP(riskScoreField))
|
||||
.should('be.visible')
|
||||
.should('contain.text', 'Sort Low-High');
|
||||
});
|
||||
|
||||
it('Text Column', () => {
|
||||
const ruleField = DATA_GRID_FIELDS.RULE.fieldName;
|
||||
cy.get(GET_DATA_GRID_HEADER(ruleField)).trigger('click');
|
||||
cy.get(GET_DATA_GRID_HEADER_CELL_ACTION_GROUP(ruleField))
|
||||
.should('be.visible')
|
||||
.should('contain.text', 'Sort A-Z');
|
||||
});
|
||||
});
|
||||
|
||||
context('Columns Configuration', () => {
|
||||
it('should retain column configuration when a column is removed when coming back to alert page', () => {
|
||||
const fieldName = 'kibana.alert.severity';
|
||||
cy.get(FIELDS_BROWSER_BTN).click();
|
||||
cy.get(FIELDS_BROWSER_CONTAINER).should('be.visible');
|
||||
|
||||
filterFieldsBrowser(fieldName);
|
||||
removeField(fieldName);
|
||||
closeFieldsBrowser();
|
||||
cy.get(DATAGRID_HEADER(fieldName)).should('not.exist');
|
||||
|
||||
navigateFromHeaderTo(TIMELINES);
|
||||
navigateFromHeaderTo(ALERTS);
|
||||
waitForAlerts();
|
||||
cy.get(DATAGRID_HEADER('_id')).should('not.exist');
|
||||
});
|
||||
it('should retain column configuration when a column is added when coming back to alert page', () => {
|
||||
cy.get(FIELDS_BROWSER_BTN).click();
|
||||
cy.get(FIELDS_BROWSER_CONTAINER).should('be.visible');
|
||||
|
||||
addsFields(['_id']);
|
||||
closeFieldsBrowser();
|
||||
cy.get(DATAGRID_HEADER('_id')).should('be.visible');
|
||||
|
||||
navigateFromHeaderTo(TIMELINES);
|
||||
navigateFromHeaderTo(ALERTS);
|
||||
waitForAlerts();
|
||||
cy.get(DATAGRID_HEADER('_id')).should('be.visible');
|
||||
});
|
||||
it('should retain columns configuration when switching between eventrenderedView and gridView', () => {
|
||||
const fieldName = '_id';
|
||||
cy.get(FIELDS_BROWSER_BTN).click();
|
||||
cy.get(FIELDS_BROWSER_CONTAINER).should('be.visible');
|
||||
|
||||
addsFields([fieldName]);
|
||||
closeFieldsBrowser();
|
||||
cy.get(DATAGRID_HEADER(fieldName)).should('be.visible');
|
||||
|
||||
switchAlertTableToEventRenderedView();
|
||||
cy.get(DATAGRID_HEADER(fieldName)).should('not.exist');
|
||||
|
||||
switchAlertTableToGridView();
|
||||
cy.get(DATAGRID_HEADER(fieldName)).should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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 { recurse } from 'cypress-recurse';
|
||||
import { TOP_N_CONTAINER } from '../../../screens/network/flows';
|
||||
import { FIELDS_BROWSER_BTN } from '../../../screens/rule_details';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import {
|
||||
EVENT_SUMMARY_ALERT_RENDERER_CONTENT,
|
||||
EVENT_SUMMARY_COLUMN,
|
||||
ALERT_RENDERER_HOST_NAME,
|
||||
SHOW_TOP_N_HEADER,
|
||||
} from '../../../screens/alerts';
|
||||
import {
|
||||
DATA_GRID_COLUMN_ORDER_BTN,
|
||||
DATA_GRID_FIELD_SORT_BTN,
|
||||
} from '../../../screens/common/data_grid';
|
||||
import { HOVER_ACTIONS } from '../../../screens/timeline';
|
||||
import {
|
||||
showHoverActionsEventRenderedView,
|
||||
switchAlertTableToEventRenderedView,
|
||||
waitForAlerts,
|
||||
} from '../../../tasks/alerts';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { cleanKibana } from '../../../tasks/common';
|
||||
import { login, visit } from '../../../tasks/login';
|
||||
import { ALERTS_URL } from '../../../urls/navigation';
|
||||
import {
|
||||
TOP_N_ALERT_HISTOGRAM,
|
||||
TOP_N_CONTAINER_CLOSE_BTN,
|
||||
XY_CHART,
|
||||
} from '../../../screens/shared';
|
||||
|
||||
describe(`Event Rendered View`, () => {
|
||||
before(() => {
|
||||
cleanKibana();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
createRule(getNewRule());
|
||||
visit(ALERTS_URL);
|
||||
waitForAlerts();
|
||||
switchAlertTableToEventRenderedView();
|
||||
waitForAlerts();
|
||||
});
|
||||
|
||||
it('Event Summary Column', () => {
|
||||
cy.get(EVENT_SUMMARY_COLUMN).should('be.visible');
|
||||
cy.get(EVENT_SUMMARY_ALERT_RENDERER_CONTENT).should('be.visible');
|
||||
});
|
||||
|
||||
it('Hover Action TopN in event summary column', () => {
|
||||
showHoverActionsEventRenderedView(ALERT_RENDERER_HOST_NAME);
|
||||
recurse(
|
||||
() => {
|
||||
// some times clicking once is not enough
|
||||
// so this block clicks TopN hover actions till TopN container is visible
|
||||
cy.get(HOVER_ACTIONS.SHOW_TOP).trigger('click');
|
||||
return cy.root();
|
||||
},
|
||||
($root) => $root.find(TOP_N_CONTAINER).is(':visible')
|
||||
);
|
||||
cy.get(TOP_N_ALERT_HISTOGRAM).should('be.visible');
|
||||
cy.get(SHOW_TOP_N_HEADER).first().should('have.text', 'Top host.name');
|
||||
cy.get(XY_CHART).should('be.visible');
|
||||
cy.get(TOP_N_CONTAINER_CLOSE_BTN).trigger('click');
|
||||
cy.get(XY_CHART).should('not.exist');
|
||||
});
|
||||
|
||||
/*
|
||||
*
|
||||
* Alert table is third party component which cannot be easily tested by jest.
|
||||
* This test main checks if Alert Table controls are rendered properly.
|
||||
*
|
||||
* */
|
||||
it('Field Browser is not visible', () => {
|
||||
cy.get(FIELDS_BROWSER_BTN).should('not.exist');
|
||||
});
|
||||
|
||||
it('Sorting control is not visible', () => {
|
||||
cy.get(DATA_GRID_FIELD_SORT_BTN).should('not.be.visible');
|
||||
});
|
||||
|
||||
it('Column Order button is not visible', () => {
|
||||
cy.get(DATA_GRID_COLUMN_ORDER_BTN).should('not.exist');
|
||||
});
|
||||
});
|
|
@ -201,3 +201,22 @@ export const MIXED_ALERT_TAG = '[data-test-subj="mixed-alert-tag"]';
|
|||
export const UNSELECTED_ALERT_TAG = '[data-test-subj="unselected-alert-tag"]';
|
||||
|
||||
export const ALERTS_TABLE_ROW_LOADER = '[data-test-subj="row-loader"]';
|
||||
|
||||
export const ALERT_TABLE_SUMMARY_VIEW_SELECTABLE = '[data-test-subj="summary-view-selector"]';
|
||||
|
||||
export const ALERT_TABLE_GRID_VIEW_OPTION = '[data-test-subj="gridView"]';
|
||||
|
||||
export const EVENT_SUMMARY_COLUMN = '[data-gridcell-column-id="eventSummary"]';
|
||||
|
||||
export const EVENT_SUMMARY_ALERT_RENDERER_CONTENT = '[data-test-subj="alertRenderer"]';
|
||||
|
||||
export const ALERT_TABLE_EVENT_RENDERED_VIEW_OPTION = '[data-test-subj="eventRenderedView"]';
|
||||
|
||||
export const ALERT_TABLE_ADDITIONAL_CONTROLS = '[data-test-subj="additionalFilters-popover"]';
|
||||
|
||||
export const ALERT_RENDERER_CONTENT = '[data-test-subj="alertRenderer"]';
|
||||
|
||||
export const ALERT_RENDERER_HOST_NAME =
|
||||
'[data-test-subj="alertFieldBadge"] [data-test-subj="render-content-host.name"]';
|
||||
|
||||
export const HOVER_ACTIONS_CONTAINER = getDataTestSubjectSelector('hover-actions-container');
|
||||
|
|
|
@ -8,3 +8,41 @@
|
|||
export const GET_DATA_GRID_HEADER = (fieldName: string) => {
|
||||
return `[data-test-subj="dataGridHeaderCell-${fieldName}"]`;
|
||||
};
|
||||
|
||||
export const DATA_GRID_FIELDS = {
|
||||
TIMESTAMP: {
|
||||
fieldName: '@timestamp',
|
||||
label: '@timestamp',
|
||||
},
|
||||
ID: {
|
||||
fieldName: '_id',
|
||||
label: '_id',
|
||||
},
|
||||
RISK_SCORE: {
|
||||
fieldName: 'kibana.alert.risk_score',
|
||||
label: 'Risk Score',
|
||||
},
|
||||
|
||||
RULE: {
|
||||
fieldName: 'kibana.alert.rule.name',
|
||||
label: 'Rule',
|
||||
},
|
||||
};
|
||||
|
||||
export const GET_DATA_GRID_HEADER_CELL_ACTION_GROUP = (fieldName: string) => {
|
||||
return `[data-test-subj="dataGridHeaderCellActionGroup-${fieldName}"]`;
|
||||
};
|
||||
|
||||
export const DATA_GRID_FULL_SCREEN =
|
||||
'[data-test-subj="alertsTable"] [data-test-subj="dataGridFullScreenButton"]';
|
||||
|
||||
export const DATA_GRID_FIELD_SORT_BTN = '[data-test-subj="dataGridColumnSortingButton"]';
|
||||
|
||||
export const DATA_GRID_COLUMN_ORDER_BTN = '[data-test-subj="dataGridColumnSelectorButton"]';
|
||||
|
||||
export const DATA_GRID_COLUMNS = '.euiDataGridHeaderCell__content';
|
||||
|
||||
export const COLUMN_ORDER_POPUP = {
|
||||
TIMESTAMP: '[data-test-subj="dataGridColumnSelectorColumnItem-@timestamp"]',
|
||||
REASON: '[data-test-subj="dataGridColumnSelectorColumnItem-kibana.alert.reason"]',
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const CLOSE_BTN = '[data-test-subj="close"]';
|
||||
export const FIELD_BROWSER_CLOSE_BTN = '[data-test-subj="close"]';
|
||||
|
||||
export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
|
||||
|
||||
|
@ -34,6 +34,9 @@ export const FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER =
|
|||
|
||||
export const FIELDS_BROWSER_MESSAGE_CHECKBOX = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="field-message-checkbox"]`;
|
||||
|
||||
export const GET_FIELD_CHECKBOX = (fieldName: string) =>
|
||||
`${FIELDS_BROWSER_CONTAINER} [data-test-subj="field-${fieldName}-checkbox"]`;
|
||||
|
||||
export const FIELDS_BROWSER_MESSAGE_HEADER =
|
||||
'[data-test-subj="timeline"] [data-test-subj="header-text-message"]';
|
||||
|
||||
|
|
|
@ -8,3 +8,13 @@
|
|||
export const TOAST_ERROR = '.euiToast--danger';
|
||||
|
||||
export const SELECT_ALL_CHECKBOX = '[data-test-subj="checkboxSelectAll"]';
|
||||
|
||||
export const TOP_N_ALERT_HISTOGRAM =
|
||||
'[data-test-subj="topN-container"] [data-test-subj="alerts-histogram-panel"]';
|
||||
|
||||
export const TOP_N_CONTAINER = '[data-test-subj="topN-container"]';
|
||||
|
||||
export const TOP_N_CONTAINER_CLOSE_BTN =
|
||||
'[data-test-subj="topN-container"] [data-test-subj="close"]';
|
||||
|
||||
export const XY_CHART = '[data-test-subj="xyVisChart"]';
|
||||
|
|
|
@ -332,6 +332,7 @@ export const HOVER_ACTIONS = {
|
|||
FILTER_FOR: '[data-test-subj="hover-actions-filter-for"]',
|
||||
FILTER_OUT: '[data-test-subj="hovhover-actions-filter-out"]',
|
||||
COPY: '[data-test-subj="hover-actions-copy-button"]',
|
||||
SHOW_TOP: '[data-test-subj=show-top-field]',
|
||||
};
|
||||
|
||||
export const GET_TIMELINE_HEADER = (fieldName: string) => {
|
||||
|
|
|
@ -49,6 +49,11 @@ import {
|
|||
ALERT_TAGGING_CONTEXT_MENU_ITEM,
|
||||
ALERT_TAGGING_CONTEXT_MENU,
|
||||
ALERT_TAGGING_UPDATE_BUTTON,
|
||||
ALERTS_HISTOGRAM_PANEL_LOADER,
|
||||
ALERT_TABLE_SUMMARY_VIEW_SELECTABLE,
|
||||
ALERT_TABLE_EVENT_RENDERED_VIEW_OPTION,
|
||||
HOVER_ACTIONS_CONTAINER,
|
||||
ALERT_TABLE_GRID_VIEW_OPTION,
|
||||
} from '../screens/alerts';
|
||||
import { LOADING_INDICATOR, REFRESH_BUTTON } from '../screens/security_header';
|
||||
import { TIMELINE_COLUMN_SPINNER } from '../screens/timeline';
|
||||
|
@ -471,3 +476,23 @@ export const clickAlertTag = (tag: string) => {
|
|||
export const updateAlertTags = () => {
|
||||
cy.get(ALERT_TAGGING_UPDATE_BUTTON).click();
|
||||
};
|
||||
|
||||
export const showHoverActionsEventRenderedView = (fieldSelector: string) => {
|
||||
cy.get(fieldSelector).first().trigger('mouseover');
|
||||
cy.get(HOVER_ACTIONS_CONTAINER).should('be.visible');
|
||||
};
|
||||
|
||||
export const waitForTopNHistogramToLoad = () => {
|
||||
cy.get(ALERTS_HISTOGRAM_PANEL_LOADER).should('exist');
|
||||
cy.get(ALERTS_HISTOGRAM_PANEL_LOADER).should('not.exist');
|
||||
};
|
||||
|
||||
export const switchAlertTableToEventRenderedView = () => {
|
||||
cy.get(ALERT_TABLE_SUMMARY_VIEW_SELECTABLE).should('be.visible').trigger('click');
|
||||
cy.get(ALERT_TABLE_EVENT_RENDERED_VIEW_OPTION).should('be.visible').trigger('click');
|
||||
};
|
||||
|
||||
export const switchAlertTableToGridView = () => {
|
||||
cy.get(ALERT_TABLE_SUMMARY_VIEW_SELECTABLE).should('be.visible').trigger('click');
|
||||
cy.get(ALERT_TABLE_GRID_VIEW_OPTION).should('be.visible').trigger('click');
|
||||
};
|
||||
|
|
|
@ -12,13 +12,14 @@ import {
|
|||
FIELDS_BROWSER_MESSAGE_CHECKBOX,
|
||||
FIELDS_BROWSER_RESET_FIELDS,
|
||||
FIELDS_BROWSER_CHECKBOX,
|
||||
CLOSE_BTN,
|
||||
FIELD_BROWSER_CLOSE_BTN,
|
||||
FIELDS_BROWSER_CATEGORIES_FILTER_BUTTON,
|
||||
FIELDS_BROWSER_CATEGORY_FILTER_OPTION,
|
||||
FIELDS_BROWSER_CATEGORIES_FILTER_SEARCH,
|
||||
FIELDS_BROWSER_VIEW_ALL,
|
||||
FIELDS_BROWSER_VIEW_BUTTON,
|
||||
FIELDS_BROWSER_VIEW_SELECTED,
|
||||
GET_FIELD_CHECKBOX,
|
||||
} from '../screens/fields_browser';
|
||||
|
||||
export const addsFields = (fields: string[]) => {
|
||||
|
@ -50,7 +51,7 @@ export const clearFieldsBrowser = () => {
|
|||
};
|
||||
|
||||
export const closeFieldsBrowser = () => {
|
||||
cy.get(CLOSE_BTN).click({ force: true });
|
||||
cy.get(FIELD_BROWSER_CLOSE_BTN).click({ force: true });
|
||||
cy.get(FIELDS_BROWSER_FILTER_INPUT).should('not.exist');
|
||||
};
|
||||
|
||||
|
@ -83,6 +84,10 @@ export const removesMessageField = () => {
|
|||
});
|
||||
};
|
||||
|
||||
export const removeField = (fieldName: string) => {
|
||||
cy.get(GET_FIELD_CHECKBOX(fieldName)).uncheck({ force: true });
|
||||
};
|
||||
|
||||
export const resetFields = () => {
|
||||
cy.get(FIELDS_BROWSER_RESET_FIELDS).click({ force: true });
|
||||
};
|
||||
|
|
|
@ -84,7 +84,7 @@ export const RightTopMenu = ({
|
|||
</UpdatedFlexItem>
|
||||
{tGridEventRenderedViewEnabled &&
|
||||
[TableId.alertsOnRuleDetailsPage, TableId.alertsOnAlertsPage].includes(tableId) && (
|
||||
<UpdatedFlexItem grow={false} $show={!loading}>
|
||||
<UpdatedFlexItem grow={false} $show={!loading} data-test-subj="summary-view-selector">
|
||||
<SummaryViewSelector viewSelected={tableView} onViewChange={onViewChange} />
|
||||
</UpdatedFlexItem>
|
||||
)}
|
||||
|
|
|
@ -79,6 +79,7 @@ const SummaryViewSelectorComponent = ({ viewSelected, onViewChange }: SummaryVie
|
|||
() => [
|
||||
{
|
||||
label: gridView,
|
||||
'data-test-subj': 'gridView',
|
||||
key: 'gridView',
|
||||
checked: (viewSelected === 'gridView' ? 'on' : undefined) as EuiSelectableOption['checked'],
|
||||
meta: [
|
||||
|
@ -95,6 +96,7 @@ const SummaryViewSelectorComponent = ({ viewSelected, onViewChange }: SummaryVie
|
|||
},
|
||||
{
|
||||
label: eventRenderedView,
|
||||
'data-test-subj': 'eventRenderedView',
|
||||
key: 'eventRenderedView',
|
||||
checked: (viewSelected === 'eventRenderedView'
|
||||
? 'on'
|
||||
|
|
|
@ -272,6 +272,7 @@ export const HoverActions: React.FC<Props> = React.memo(
|
|||
})}
|
||||
>
|
||||
<Container
|
||||
data-test-subj="hover-actions-container"
|
||||
onKeyDown={onKeyDown}
|
||||
$showTopN={showTopN}
|
||||
$showOwnFocus={showOwnFocus}
|
||||
|
|
|
@ -114,13 +114,6 @@ const TopNComponent: React.FC<Props> = ({
|
|||
|
||||
return (
|
||||
<TopNContainer data-test-subj="topN-container">
|
||||
<CloseButton
|
||||
aria-label={i18n.CLOSE}
|
||||
data-test-subj="close"
|
||||
iconType="cross"
|
||||
onClick={toggleTopN}
|
||||
/>
|
||||
|
||||
<TopNContent>
|
||||
{view === 'raw' || view === 'all' ? (
|
||||
<EventsByDataset
|
||||
|
@ -160,6 +153,13 @@ const TopNComponent: React.FC<Props> = ({
|
|||
/>
|
||||
)}
|
||||
</TopNContent>
|
||||
|
||||
<CloseButton
|
||||
aria-label={i18n.CLOSE}
|
||||
data-test-subj="close"
|
||||
iconType="cross"
|
||||
onClick={toggleTopN}
|
||||
/>
|
||||
</TopNContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -10,7 +10,6 @@ import { EuiFlexGroup } from '@elastic/eui';
|
|||
import type { Filter } from '@kbn/es-query';
|
||||
import type { FC } from 'react';
|
||||
import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/alerts_table_state';
|
||||
import type { Alert } from '@kbn/triggers-actions-ui-plugin/public/types';
|
||||
import { ALERT_BUILDING_BLOCK_TYPE } from '@kbn/rule-data-utils';
|
||||
|
@ -37,7 +36,6 @@ import { inputsSelectors } from '../../../common/store';
|
|||
import { combineQueries } from '../../../common/lib/kuery';
|
||||
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
|
||||
import { StatefulEventContext } from '../../../common/components/events_viewer/stateful_event_context';
|
||||
import { getDataTablesInStorageByIds } from '../../../timelines/containers/local_storage';
|
||||
import { useSourcererDataView } from '../../../common/containers/sourcerer';
|
||||
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
|
@ -48,14 +46,13 @@ import { eventsViewerSelector } from '../../../common/components/events_viewer/s
|
|||
import type { State } from '../../../common/store';
|
||||
import * as i18n from './translations';
|
||||
import { eventRenderedViewColumns } from '../../configurations/security_solution_detections/columns';
|
||||
import { getAlertsDefaultModel } from './default_config';
|
||||
|
||||
const { updateIsLoading, updateTotalCount } = dataTableActions;
|
||||
|
||||
// Highlight rows with building block alerts
|
||||
const shouldHighlightRow = (alert: Alert) => !!alert[ALERT_BUILDING_BLOCK_TYPE];
|
||||
|
||||
const storage = new Storage(localStorage);
|
||||
|
||||
interface GridContainerProps {
|
||||
hideLastPage: boolean;
|
||||
}
|
||||
|
@ -154,7 +151,8 @@ export const AlertsTableComponent: FC<DetectionEngineAlertTableProps> = ({
|
|||
graphEventId, // If truthy, the graph viewer (Resolver) is showing
|
||||
sessionViewConfig,
|
||||
viewMode: tableView = eventsDefaultModel.viewMode,
|
||||
} = eventsDefaultModel,
|
||||
columns,
|
||||
} = getAlertsDefaultModel(license),
|
||||
} = useShallowEqualSelector((state: State) => eventsViewerSelector(state, tableId));
|
||||
|
||||
const combinedQuery = useMemo(() => {
|
||||
|
@ -210,9 +208,10 @@ export const AlertsTableComponent: FC<DetectionEngineAlertTableProps> = ({
|
|||
return undefined;
|
||||
}, [isEventRenderedView]);
|
||||
|
||||
const dataTableStorage = getDataTablesInStorageByIds(storage, [TableId.alertsOnAlertsPage]);
|
||||
const columnsFormStorage = dataTableStorage?.[TableId.alertsOnAlertsPage]?.columns ?? [];
|
||||
const alertColumns = columnsFormStorage.length ? columnsFormStorage : getColumns(license);
|
||||
const alertColumns = useMemo(
|
||||
() => (columns.length ? columns : getColumns(license)),
|
||||
[columns, license]
|
||||
);
|
||||
|
||||
const finalBrowserFields = useMemo(
|
||||
() => (isEventRenderedView ? {} : browserFields),
|
||||
|
|
|
@ -308,17 +308,32 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
|
|||
if (shouldHighlightRowCheck) {
|
||||
mappedRowClasses = alerts.reduce<NonNullable<EuiDataGridStyle['rowClasses']>>(
|
||||
(rowClasses, alert, index) => {
|
||||
if (props.gridStyle?.stripes && index % 2 !== 0) {
|
||||
// manually add stripes if props.gridStyle.stripes is true because presence of rowClasses
|
||||
// overrides the props.gridStyle.stripes option. And rowClasses will always be there.
|
||||
// Adding strips only on even rows. It will be replace by alertsTableHighlightedRow if
|
||||
// shouldHighlightRow is correct
|
||||
rowClasses[index + pagination.pageIndex * pagination.pageSize] =
|
||||
'euiDataGridRow--striped';
|
||||
}
|
||||
if (shouldHighlightRowCheck(alert)) {
|
||||
rowClasses[index + pagination.pageIndex * pagination.pageSize] =
|
||||
'alertsTableHighlightedRow';
|
||||
}
|
||||
|
||||
return rowClasses;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
return mappedRowClasses;
|
||||
}, [props.shouldHighlightRow, alerts, pagination.pageIndex, pagination.pageSize]);
|
||||
}, [
|
||||
props.shouldHighlightRow,
|
||||
alerts,
|
||||
pagination.pageIndex,
|
||||
pagination.pageSize,
|
||||
props.gridStyle,
|
||||
]);
|
||||
|
||||
const handleFlyoutClose = useCallback(() => setFlyoutAlertIndex(-1), [setFlyoutAlertIndex]);
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ const AlertsTableStateWithQueryProvider = ({
|
|||
: EmptyConfiguration;
|
||||
|
||||
const storage = useRef(new Storage(window.localStorage));
|
||||
const localAlertsTableConfig = storage.current.get(id) as Partial<AlertsTableStorage>;
|
||||
const localStorageAlertsTableConfig = storage.current.get(id) as Partial<AlertsTableStorage>;
|
||||
const persistentControls = alertsTableConfiguration?.usePersistentControls?.();
|
||||
const showInspectButton = alertsTableConfiguration?.showInspectButton ?? false;
|
||||
|
||||
|
@ -186,25 +186,25 @@ const AlertsTableStateWithQueryProvider = ({
|
|||
propColumns && !isEmpty(propColumns) ? propColumns : alertsTableConfiguration?.columns ?? [];
|
||||
|
||||
const columnsLocal =
|
||||
localAlertsTableConfig &&
|
||||
localAlertsTableConfig.columns &&
|
||||
!isEmpty(localAlertsTableConfig?.columns)
|
||||
? localAlertsTableConfig?.columns ?? []
|
||||
localStorageAlertsTableConfig &&
|
||||
localStorageAlertsTableConfig.columns &&
|
||||
!isEmpty(localStorageAlertsTableConfig?.columns)
|
||||
? localStorageAlertsTableConfig?.columns
|
||||
: columnConfigByClient;
|
||||
|
||||
const getStorageConfig = () => ({
|
||||
columns: columnsLocal,
|
||||
sort:
|
||||
localAlertsTableConfig &&
|
||||
localAlertsTableConfig.sort &&
|
||||
!isEmpty(localAlertsTableConfig?.sort)
|
||||
? localAlertsTableConfig?.sort ?? []
|
||||
localStorageAlertsTableConfig &&
|
||||
localStorageAlertsTableConfig.sort &&
|
||||
!isEmpty(localStorageAlertsTableConfig?.sort)
|
||||
? localStorageAlertsTableConfig?.sort
|
||||
: alertsTableConfiguration?.sort ?? [],
|
||||
visibleColumns:
|
||||
localAlertsTableConfig &&
|
||||
localAlertsTableConfig.visibleColumns &&
|
||||
!isEmpty(localAlertsTableConfig?.visibleColumns)
|
||||
? localAlertsTableConfig?.visibleColumns ?? []
|
||||
localStorageAlertsTableConfig &&
|
||||
localStorageAlertsTableConfig.visibleColumns &&
|
||||
!isEmpty(localStorageAlertsTableConfig?.visibleColumns)
|
||||
? localStorageAlertsTableConfig?.visibleColumns
|
||||
: columnsLocal.map((c) => c.id),
|
||||
});
|
||||
const storageAlertsTable = useRef<AlertsTableStorage>(getStorageConfig());
|
||||
|
|
|
@ -11,6 +11,7 @@ import { Storage } from '@kbn/kibana-utils-plugin/public';
|
|||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import { useColumns, UseColumnsArgs, UseColumnsResp } from './use_columns';
|
||||
import { AlertsTableStorage } from '../../alerts_table_state';
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana');
|
||||
|
||||
|
@ -26,12 +27,15 @@ 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 getStorageAlertsTableByDefaultColumns = (defaultColumns: EuiDataGridColumn[]) => {
|
||||
return {
|
||||
current: {
|
||||
columns: defaultColumns,
|
||||
visibleColumns: defaultColumns.map((col) => col.id),
|
||||
sort: [],
|
||||
} as AlertsTableStorage,
|
||||
};
|
||||
};
|
||||
const defaultColumns: EuiDataGridColumn[] = [
|
||||
{
|
||||
|
@ -62,51 +66,24 @@ describe('useColumn', () => {
|
|||
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 })
|
||||
// storageTable will always be in sync with defualtColumns.
|
||||
// it is an invariant. If that is the case, that can be considered an issue
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
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":[]}'
|
||||
|
@ -118,4 +95,196 @@ describe('useColumn', () => {
|
|||
schema: 'datetime',
|
||||
});
|
||||
});
|
||||
|
||||
describe('visibleColumns', () => {
|
||||
test('hide all columns with onChangeVisibleColumns', async () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onChangeVisibleColumns([]);
|
||||
});
|
||||
|
||||
expect(result.current.visibleColumns).toEqual([]);
|
||||
expect(result.current.columns).toEqual(defaultColumns);
|
||||
});
|
||||
|
||||
test('show all columns with onChangeVisibleColumns', async () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onChangeVisibleColumns([]);
|
||||
});
|
||||
act(() => {
|
||||
result.current.onChangeVisibleColumns(defaultColumns.map((dc) => dc.id));
|
||||
});
|
||||
expect(result.current.visibleColumns).toEqual([
|
||||
'event.action',
|
||||
'@timestamp',
|
||||
'kibana.alert.duration.us',
|
||||
'kibana.alert.reason',
|
||||
]);
|
||||
expect(result.current.columns).toEqual(defaultColumns);
|
||||
});
|
||||
|
||||
test('should populate visiblecolumns correctly', async () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.visibleColumns).toMatchObject(defaultColumns.map((col) => col.id));
|
||||
});
|
||||
|
||||
test('should change visiblecolumns if provided defaultColumns change', async () => {
|
||||
let localDefaultColumns = [...defaultColumns];
|
||||
let localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(localDefaultColumns);
|
||||
const { result, rerender } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns: localDefaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.visibleColumns).toMatchObject(defaultColumns.map((col) => col.id));
|
||||
|
||||
/*
|
||||
*
|
||||
* TODO : it looks like when defaultColumn is changed, the storageAlertTable
|
||||
* is also changed automatically outside this hook i.e. storageAlertsTable = localStorageColumns ?? defaultColumns
|
||||
*
|
||||
* ideally everything related to columns should be pulled in this particular hook. So that it is easy
|
||||
* to measure the effects based on single set of props. Just by looking at this hook
|
||||
* it is impossible to know that defaultColumn and storageAlertsTable both are always in sync and should
|
||||
* be kept in sync manually when running tests.
|
||||
*
|
||||
* */
|
||||
localDefaultColumns = localDefaultColumns.slice(0, 3);
|
||||
localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(localDefaultColumns);
|
||||
|
||||
rerender();
|
||||
|
||||
expect(result.current.visibleColumns).toMatchObject(localDefaultColumns.map((col) => col.id));
|
||||
});
|
||||
});
|
||||
|
||||
describe('columns', () => {
|
||||
test('should changes the column list when defaultColumns has been updated', async () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result, waitFor } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
await waitFor(() => expect(result.current.columns).toMatchObject(defaultColumns));
|
||||
});
|
||||
});
|
||||
|
||||
describe('onToggleColumns', () => {
|
||||
test('should update the list of columns when on Toggle Columns is called', () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onToggleColumn(defaultColumns[0].id);
|
||||
});
|
||||
|
||||
expect(result.current.columns).toMatchObject(defaultColumns.slice(1));
|
||||
});
|
||||
|
||||
test('should update the list of visible columns when onToggleColumn is called', async () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
// remove particular column
|
||||
act(() => {
|
||||
result.current.onToggleColumn(defaultColumns[0].id);
|
||||
});
|
||||
|
||||
expect(result.current.columns).toMatchObject(defaultColumns.slice(1));
|
||||
|
||||
// make it visible again
|
||||
act(() => {
|
||||
result.current.onToggleColumn(defaultColumns[0].id);
|
||||
});
|
||||
|
||||
expect(result.current.columns).toMatchObject(defaultColumns);
|
||||
});
|
||||
|
||||
test('should update the column details in the storage when onToggleColumn is called', () => {
|
||||
const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns);
|
||||
const { result } = renderHook<UseColumnsArgs, UseColumnsResp>(() =>
|
||||
useColumns({
|
||||
defaultColumns,
|
||||
featureIds,
|
||||
id,
|
||||
storageAlertsTable: localStorageAlertsTable,
|
||||
storage,
|
||||
})
|
||||
);
|
||||
|
||||
// remove particular column
|
||||
act(() => {
|
||||
setItemStorageMock.mockClear();
|
||||
result.current.onToggleColumn(defaultColumns[0].id);
|
||||
});
|
||||
|
||||
expect(setItemStorageMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
id,
|
||||
JSON.stringify({
|
||||
columns: defaultColumns.slice(1),
|
||||
visibleColumns: defaultColumns.slice(1).map((col) => col.id),
|
||||
sort: [],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
|||
import { BrowserField, BrowserFields } from '@kbn/rule-registry-plugin/common';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { isEmpty, isEqual } from 'lodash';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { AlertsTableStorage } from '../../alerts_table_state';
|
||||
import { toggleColumn } from './toggle_column';
|
||||
import { useFetchBrowserFieldCapabilities } from '../use_fetch_browser_fields_capabilities';
|
||||
|
@ -182,21 +182,35 @@ export const useColumns = ({
|
|||
|
||||
const defaultColumnsRef = useRef<typeof defaultColumns>(defaultColumns);
|
||||
|
||||
const didDefaultColumnChange = useMemo(
|
||||
() => !isEqual(defaultColumns, defaultColumnsRef.current),
|
||||
[defaultColumns]
|
||||
const didDefaultColumnChange = defaultColumns !== defaultColumnsRef.current;
|
||||
|
||||
const setColumnsByColumnIds = useCallback(
|
||||
(columnIds: string[]) => {
|
||||
setVisibleColumns(columnIds);
|
||||
persist({
|
||||
id,
|
||||
storage,
|
||||
storageAlertsTable,
|
||||
columns,
|
||||
visibleColumns: columnIds,
|
||||
});
|
||||
},
|
||||
[columns, id, storage, storageAlertsTable]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// if defaultColumns have changed,
|
||||
// get the latest columns provided by client and
|
||||
if (didDefaultColumnChange) {
|
||||
if (didDefaultColumnChange && defaultColumnsRef.current) {
|
||||
defaultColumnsRef.current = defaultColumns;
|
||||
setColumnsPopulated(false);
|
||||
// storageAlertTable already account for the changes in defaultColumns
|
||||
// Technically storageAlertsTable = localStorageData ?? defaultColumns
|
||||
setColumns(storageAlertsTable.current.columns);
|
||||
setVisibleColumns(storageAlertsTable.current.visibleColumns ?? visibleColumns);
|
||||
return;
|
||||
}
|
||||
}, [didDefaultColumnChange, storageAlertsTable, defaultColumns]);
|
||||
}, [didDefaultColumnChange, storageAlertsTable, defaultColumns, visibleColumns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmpty(browserFields) || isColumnsPopulated) return;
|
||||
|
@ -221,20 +235,6 @@ export const useColumns = ({
|
|||
[id, storage, storageAlertsTable]
|
||||
);
|
||||
|
||||
const setColumnsByColumnIds = useCallback(
|
||||
(columnIds: string[]) => {
|
||||
setVisibleColumns(columnIds);
|
||||
persist({
|
||||
id,
|
||||
storage,
|
||||
storageAlertsTable,
|
||||
columns,
|
||||
visibleColumns: columnIds,
|
||||
});
|
||||
},
|
||||
[columns, id, storage, storageAlertsTable]
|
||||
);
|
||||
|
||||
const onToggleColumn = useCallback(
|
||||
(columnId: string): void => {
|
||||
const column = euiColumnFactory(columnId, browserFields, defaultColumns);
|
||||
|
@ -279,7 +279,7 @@ export const useColumns = ({
|
|||
* 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.
|
||||
*
|
||||
* Additionaly, system such as o11y needs fields which are not even added in the table such as rule_type_id and hence we
|
||||
* Additionally, system such as o11y needs fields which are not even added in the table such as rule_type_id and hence we
|
||||
* additionly pass EMPTY_FIELDS so that it brings all fields apart from special fields
|
||||
*
|
||||
* */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue