Skip to content

Commit

Permalink
[8.x] [Security Solution][Alert details] - refactor UI on insights (#…
Browse files Browse the repository at this point in the history
…197349) (#198417)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution][Alert details] - refactor UI on insights
(#197349)](#197349)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Philippe
Oberti","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-30T17:33:50Z","message":"[Security
Solution][Alert details] - refactor UI on insights (#197349)\n\n##
Summary\r\n\r\nThis PR refactors the Insights section of the expandable
flyout for\r\nalerts and events.\r\n\r\nThe changes are applied to the
following section:\r\n- Threat Intelligence: when the user clicks on the
number, we open the\r\nleft section to the Insights Threat Intelligence
tab\r\n- Correlations: when the user clicks on the number, we open the
left\r\nsection to the Insights Correlations tab\r\n- Prevalence: no
user interactions\r\n\r\nWhen in preview mode, none of the number are
clickable and the buttons\r\nare disabled.\r\n\r\n#### New UI\r\n\r\n|
Normal flyout | Preview flyout |\r\n| ------------- | -------------
|\r\n| ![Screenshot 2024-10-22 at 6
01\r\n38 PM](https://github.com/user-attachments/assets/de179a2b-c8ab-42f6-b5b7-839dae0139d5)\r\n|
![Screenshot 2024-10-22 at 6
01\r\n54 PM](https://github.com/user-attachments/assets/63ed125e-5e3b-4c4c-a10e-7cc01d291660)\r\n|\r\n\r\n####
UX flows to expand the
flyout\r\n\r\n\r\nhttps://github.com/user-attachments/assets/30031a12-c2f3-47e6-a783-5b9482359ee5\r\n\r\nhttps://github.com/elastic/security-team/issues/7033\r\n\r\n###
Checklist\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","sha":"9b9eb7e64d59fe41e4513a992ec77eb9fc051e4b","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","Team:Threat
Hunting:Investigations","backport:version","v8.17.0"],"title":"[Security
Solution][Alert details] - refactor UI on
insights","number":197349,"url":"https://github.com/elastic/kibana/pull/197349","mergeCommit":{"message":"[Security
Solution][Alert details] - refactor UI on insights (#197349)\n\n##
Summary\r\n\r\nThis PR refactors the Insights section of the expandable
flyout for\r\nalerts and events.\r\n\r\nThe changes are applied to the
following section:\r\n- Threat Intelligence: when the user clicks on the
number, we open the\r\nleft section to the Insights Threat Intelligence
tab\r\n- Correlations: when the user clicks on the number, we open the
left\r\nsection to the Insights Correlations tab\r\n- Prevalence: no
user interactions\r\n\r\nWhen in preview mode, none of the number are
clickable and the buttons\r\nare disabled.\r\n\r\n#### New UI\r\n\r\n|
Normal flyout | Preview flyout |\r\n| ------------- | -------------
|\r\n| ![Screenshot 2024-10-22 at 6
01\r\n38 PM](https://github.com/user-attachments/assets/de179a2b-c8ab-42f6-b5b7-839dae0139d5)\r\n|
![Screenshot 2024-10-22 at 6
01\r\n54 PM](https://github.com/user-attachments/assets/63ed125e-5e3b-4c4c-a10e-7cc01d291660)\r\n|\r\n\r\n####
UX flows to expand the
flyout\r\n\r\n\r\nhttps://github.com/user-attachments/assets/30031a12-c2f3-47e6-a783-5b9482359ee5\r\n\r\nhttps://github.com/elastic/security-team/issues/7033\r\n\r\n###
Checklist\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","sha":"9b9eb7e64d59fe41e4513a992ec77eb9fc051e4b"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197349","number":197349,"mergeCommit":{"message":"[Security
Solution][Alert details] - refactor UI on insights (#197349)\n\n##
Summary\r\n\r\nThis PR refactors the Insights section of the expandable
flyout for\r\nalerts and events.\r\n\r\nThe changes are applied to the
following section:\r\n- Threat Intelligence: when the user clicks on the
number, we open the\r\nleft section to the Insights Threat Intelligence
tab\r\n- Correlations: when the user clicks on the number, we open the
left\r\nsection to the Insights Correlations tab\r\n- Prevalence: no
user interactions\r\n\r\nWhen in preview mode, none of the number are
clickable and the buttons\r\nare disabled.\r\n\r\n#### New UI\r\n\r\n|
Normal flyout | Preview flyout |\r\n| ------------- | -------------
|\r\n| ![Screenshot 2024-10-22 at 6
01\r\n38 PM](https://github.com/user-attachments/assets/de179a2b-c8ab-42f6-b5b7-839dae0139d5)\r\n|
![Screenshot 2024-10-22 at 6
01\r\n54 PM](https://github.com/user-attachments/assets/63ed125e-5e3b-4c4c-a10e-7cc01d291660)\r\n|\r\n\r\n####
UX flows to expand the
flyout\r\n\r\n\r\nhttps://github.com/user-attachments/assets/30031a12-c2f3-47e6-a783-5b9482359ee5\r\n\r\nhttps://github.com/elastic/security-team/issues/7033\r\n\r\n###
Checklist\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","sha":"9b9eb7e64d59fe41e4513a992ec77eb9fc051e4b"}},{"branch":"8.x","label":"v8.17.0","branchLabelMappingKey":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Philippe Oberti <[email protected]>
  • Loading branch information
kibanamachine and PhilippeOberti authored Oct 30, 2024
1 parent 2c514f3 commit 85090ca
Show file tree
Hide file tree
Showing 25 changed files with 806 additions and 512 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
CORRELATIONS_RELATED_CASES_TEST_ID,
CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID,
CORRELATIONS_TEST_ID,
SUMMARY_ROW_VALUE_TEST_ID,
SUMMARY_ROW_BUTTON_TEST_ID,
SUMMARY_ROW_TEXT_TEST_ID,
} from './test_ids';
import { useShowRelatedAlertsByAncestry } from '../../shared/hooks/use_show_related_alerts_by_ancestry';
import { useShowRelatedAlertsBySameSourceEvent } from '../../shared/hooks/use_show_related_alerts_by_same_source_event';
Expand Down Expand Up @@ -58,17 +59,32 @@ const TITLE_LINK_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(CORRELATIO
const TITLE_ICON_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(CORRELATIONS_TEST_ID);
const TITLE_TEXT_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(CORRELATIONS_TEST_ID);

const SUPPRESSED_ALERTS_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID);
const RELATED_ALERTS_BY_ANCESTRY_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(
const SUPPRESSED_ALERTS_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(
CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID
);
const SUPPRESSED_ALERTS_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(
CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID
);
const RELATED_ALERTS_BY_ANCESTRY_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(
CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID
);
const RELATED_ALERTS_BY_ANCESTRY_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(
CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID
);
const RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(
const RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(
CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID
);
const RELATED_ALERTS_BY_SESSION_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(
const RELATED_ALERTS_BY_SAME_SOURCE_EVENT_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(
CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID
);
const RELATED_ALERTS_BY_SESSION_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(
CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID
);
const RELATED_ALERTS_BY_SESSION_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(
CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID
);
const RELATED_CASES_TEST_ID = SUMMARY_ROW_VALUE_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID);
const RELATED_CASES_TEXT_TEST_ID = SUMMARY_ROW_TEXT_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID);
const RELATED_CASES_VALUE_TEST_ID = SUMMARY_ROW_BUTTON_TEST_ID(CORRELATIONS_RELATED_CASES_TEST_ID);

const panelContextValue = {
eventId: 'event id',
Expand Down Expand Up @@ -193,11 +209,16 @@ describe('<CorrelationsOverview />', () => {
});

const { getByTestId, queryByText } = render(renderCorrelationsOverview(panelContextValue));
expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_CASES_TEST_ID)).toBeInTheDocument();
expect(getByTestId(SUPPRESSED_ALERTS_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_TEXT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_VALUE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEXT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_VALUE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_SESSION_TEXT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_ALERTS_BY_SESSION_VALUE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_CASES_TEXT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(RELATED_CASES_VALUE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(SUPPRESSED_ALERTS_TEXT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(SUPPRESSED_ALERTS_VALUE_TEST_ID)).toBeInTheDocument();
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
});

Expand All @@ -215,11 +236,18 @@ describe('<CorrelationsOverview />', () => {
jest.mocked(useShowSuppressedAlerts).mockReturnValue({ show: false, alertSuppressionCount: 0 });

const { getByText, queryByTestId } = render(renderCorrelationsOverview(panelContextValue));
expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_CASES_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(SUPPRESSED_ALERTS_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_TEXT_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_VALUE_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEXT_TEST_ID)).not.toBeInTheDocument();
expect(
queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_VALUE_TEST_ID)
).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_SESSION_TEXT_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_ALERTS_BY_SESSION_VALUE_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_CASES_TEXT_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(RELATED_CASES_VALUE_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(SUPPRESSED_ALERTS_TEXT_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(SUPPRESSED_ALERTS_VALUE_TEST_ID)).not.toBeInTheDocument();
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export const CorrelationsOverview: React.FC = () => {
data-test-subj={CORRELATIONS_TEST_ID}
>
{canShowAtLeastOneInsight ? (
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexGroup direction="column" gutterSize="s">
{showSuppressedAlerts && (
<SuppressedAlerts alertSuppressionCount={alertSuppressionCount} ruleType={ruleType} />
)}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,74 +9,147 @@ import React from 'react';
import { render } from '@testing-library/react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { InsightsSummaryRow } from './insights_summary_row';
import { useDocumentDetailsContext } from '../../shared/context';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys';
import { LeftPanelInsightsTab } from '../../left';

jest.mock('@kbn/expandable-flyout');
jest.mock('../../shared/context');

const mockOpenLeftPanel = jest.fn();
const scopeId = 'scopeId';
const eventId = 'eventId';
const indexName = 'indexName';

const testId = 'test';
const iconTestId = `${testId}Icon`;
const textTestId = `${testId}Text`;
const buttonTestId = `${testId}Button`;
const valueTestId = `${testId}Value`;
const colorTestId = `${testId}Color`;
const loadingTestId = `${testId}Loading`;

describe('<InsightsSummaryRow />', () => {
it('should render by default', () => {
beforeEach(() => {
jest.clearAllMocks();

(useDocumentDetailsContext as jest.Mock).mockReturnValue({
eventId,
indexName,
scopeId,
isPreviewMode: false,
});
(useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel });
});

it('should render loading skeleton if loading is true', () => {
const { getByTestId } = render(
<InsightsSummaryRow
loading={true}
text={'text'}
value={<div>{'value for this'}</div>}
data-test-subj={testId}
/>
);

expect(getByTestId(loadingTestId)).toBeInTheDocument();
});

it('should only render null when error is true', () => {
const { container } = render(
<InsightsSummaryRow error={true} text={'text'} value={<div>{'value for this'}</div>} />
);

expect(container).toBeEmptyDOMElement();
});

it('should render the value component', () => {
const { getByTestId, queryByTestId } = render(
<IntlProvider locale="en">
<InsightsSummaryRow
icon={'image'}
value={1}
text={'this is a test for red'}
color={'rgb(189,39,30)'}
value={<div>{'value for this'}</div>}
data-test-subj={testId}
/>
</IntlProvider>
);

expect(getByTestId(iconTestId)).toBeInTheDocument();
expect(getByTestId(valueTestId)).toHaveTextContent('1 this is a test for red');
expect(getByTestId(colorTestId)).toBeInTheDocument();
expect(getByTestId(textTestId)).toHaveTextContent('this is a test for red');
expect(getByTestId(valueTestId)).toHaveTextContent('value for this');
expect(queryByTestId(buttonTestId)).not.toBeInTheDocument();
});

it('should render loading skeletton if loading is true', () => {
const { getByTestId } = render(
<InsightsSummaryRow loading={true} icon={'image'} text={'text'} data-test-subj={testId} />
it('should render the value as EuiBadge and EuiButtonEmpty', () => {
const { getByTestId, queryByTestId } = render(
<IntlProvider locale="en">
<InsightsSummaryRow text={'this is a test for red'} value={2} data-test-subj={testId} />
</IntlProvider>
);

expect(getByTestId(loadingTestId)).toBeInTheDocument();
expect(getByTestId(textTestId)).toHaveTextContent('this is a test for red');
expect(getByTestId(buttonTestId)).toHaveTextContent('2');
expect(queryByTestId(valueTestId)).not.toBeInTheDocument();
});

it('should only render null when error is true', () => {
const { container } = render(<InsightsSummaryRow error={true} icon={'image'} text={'text'} />);
it('should render big numbers formatted correctly', () => {
const { getByTestId } = render(
<IntlProvider locale="en">
<InsightsSummaryRow text={'this is a test for red'} value={2000} data-test-subj={testId} />
</IntlProvider>
);

expect(container).toBeEmptyDOMElement();
expect(getByTestId(buttonTestId)).toHaveTextContent('2k');
});

it('should handle big number in a compact notation', () => {
it('should open the expanded section to the correct tab when the number is clicked', () => {
const { getByTestId } = render(
<IntlProvider locale="en">
<InsightsSummaryRow
icon={'image'}
value={160000}
text={'this is a test for red'}
color={'rgb(189,39,30)'}
value={2}
expandedSubTab={'subTab'}
data-test-subj={testId}
/>
</IntlProvider>
);
getByTestId(buttonTestId).click();

expect(getByTestId(valueTestId)).toHaveTextContent('160k this is a test for red');
expect(mockOpenLeftPanel).toHaveBeenCalledWith({
id: DocumentDetailsLeftPanelKey,
path: {
tab: LeftPanelInsightsTab,
subTab: 'subTab',
},
params: {
id: eventId,
indexName,
scopeId,
},
});
});

it(`should not show the colored dot if color isn't provided`, () => {
const { queryByTestId } = render(
it('should disabled the click when in preview mode', () => {
(useDocumentDetailsContext as jest.Mock).mockReturnValue({
eventId,
indexName,
scopeId,
isPreviewMode: true,
});

const { getByTestId } = render(
<IntlProvider locale="en">
<InsightsSummaryRow
icon={'image'}
value={160000}
text={'this is a test for no color'}
text={'this is a test for red'}
value={2}
expandedSubTab={'subTab'}
data-test-subj={testId}
/>
</IntlProvider>
);
const button = getByTestId(buttonTestId);

expect(button).toHaveAttribute('disabled');

expect(queryByTestId(colorTestId)).not.toBeInTheDocument();
button.click();
expect(mockOpenLeftPanel).not.toHaveBeenCalled();
});
});
Loading

0 comments on commit 85090ca

Please sign in to comment.