From 2cff8b43e4dd12f2d14d80743e9fff473078403f Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Wed, 1 Apr 2020 11:47:59 -0600 Subject: [PATCH] [SIEM] Restores the _External alert count_ widget's subtitle (#62094) ## [SIEM] Restores the _External alert count_ widget's subtitle Fixes an issue where the _External alert count_ widget's subtitle, (e.g. `Showing: 47,642,905 external alerts`), didn't render after data is loaded ### Before ![external-alerts-before](https://user-images.githubusercontent.com/4459398/78086038-f3fe7c80-7379-11ea-8291-2ef807349aea.png) ### After ![external-alerts-after](https://user-images.githubusercontent.com/4459398/78086045-fb258a80-7379-11ea-9bc6-338dc3aba482.png) --- .../components/matrix_histogram/index.tsx | 44 +++--- .../alerts_by_category/index.test.tsx | 135 ++++++++++++++++++ .../overview/alerts_by_category/index.tsx | 8 +- 3 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index e3e0562424ffb..12a474009dc5b 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -25,7 +25,6 @@ import { HistogramAggregation, MatrixHistogramQueryProps, } from './types'; -import { ChartSeriesData } from '../charts/common'; import { InspectButtonContainer } from '../inspect'; import { State, inputsSelectors, hostsModel, networkModel } from '../../store'; @@ -120,11 +119,6 @@ export const MatrixHistogramComponent: React.FC( defaultStackByOption ); - - const [titleWithStackByField, setTitle] = useState(''); - const [subtitleWithCounts, setSubtitle] = useState(''); - const [hideHistogram, setHideHistogram] = useState(hideHistogramIfEmpty); - const [barChartData, setBarChartData] = useState(null); const setSelectedChartOptionCallback = useCallback( (event: React.ChangeEvent) => { setSelectedStackByOption( @@ -146,40 +140,36 @@ export const MatrixHistogramComponent: React.FC { - if (title != null) setTitle(typeof title === 'function' ? title(selectedStackByOption) : title); - - if (subtitle != null) - setSubtitle(typeof subtitle === 'function' ? subtitle(totalCount) : subtitle); - - if (totalCount <= 0 && hideHistogramIfEmpty) { - setHideHistogram(true); - } else { - setHideHistogram(false); - } - setBarChartData(getCustomChartData(data, mapping)); + const titleWithStackByField = useMemo( + () => (title != null && typeof title === 'function' ? title(selectedStackByOption) : title), + [title, selectedStackByOption] + ); + const subtitleWithCounts = useMemo( + () => (subtitle != null && typeof subtitle === 'function' ? subtitle(totalCount) : subtitle), + [subtitle, totalCount] + ); + const hideHistogram = useMemo(() => (totalCount <= 0 && hideHistogramIfEmpty ? true : false), [ + totalCount, + hideHistogramIfEmpty, + ]); + const barChartData = useMemo(() => getCustomChartData(data, mapping), [data, mapping]); + useEffect(() => { setQuery({ id, inspect, loading, refetch }); if (isInitialLoading && !!barChartData && data) { setIsInitialLoading(false); } }, [ - subtitle, - setSubtitle, - setHideHistogram, - setBarChartData, setQuery, - hideHistogramIfEmpty, - totalCount, id, inspect, - isInspected, loading, refetch, - data, - refetch, isInitialLoading, + barChartData, + data, + setIsInitialLoading, ]); if (hideHistogram) { diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx new file mode 100644 index 0000000000000..d838b936a2d65 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable react/display-name */ + +import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; +import { mount, ReactWrapper } from 'enzyme'; +import React from 'react'; +import { ThemeProvider } from 'styled-components'; + +import { useQuery } from '../../../containers/matrix_histogram'; +import { wait } from '../../../lib/helpers'; +import { mockIndexPattern, TestProviders } from '../../../mock'; + +import { AlertsByCategory } from '.'; + +jest.mock('../../../lib/kibana'); + +jest.mock('../../../containers/matrix_histogram', () => { + return { + useQuery: jest.fn(), + }; +}); + +const theme = () => ({ eui: { ...euiDarkVars, euiSizeL: '24px' }, darkMode: true }); +const from = new Date('2020-03-31T06:00:00.000Z').valueOf(); +const to = new Date('2019-03-31T06:00:00.000Z').valueOf(); + +describe('Alerts by category', () => { + let wrapper: ReactWrapper; + + describe('before loading data', () => { + beforeAll(async () => { + (useQuery as jest.Mock).mockReturnValue({ + data: null, + loading: false, + inspect: false, + totalCount: null, + }); + + wrapper = mount( + + + + + + ); + + await wait(); + wrapper.update(); + }); + + test('it renders the expected title', () => { + expect(wrapper.find('[data-test-subj="header-section-title"]').text()).toEqual( + 'External alert count' + ); + }); + + test('it does NOT render the subtitle', () => { + expect(wrapper.find('[data-test-subj="header-panel-subtitle"]').exists()).toBe(false); + }); + + test('it renders the expected filter fields', () => { + const expectedOptions = ['event.category', 'event.module']; + + expectedOptions.forEach(option => { + expect(wrapper.find(`option[value="${option}"]`).text()).toEqual(option); + }); + }); + + test('it renders the `View alerts` button', () => { + expect(wrapper.find('[data-test-subj="view-alerts"]').exists()).toBe(true); + }); + + test('it does NOT render the bar chart when data is not available', () => { + expect(wrapper.find(`.echChart`).exists()).toBe(false); + }); + }); + + describe('after loading data', () => { + beforeAll(async () => { + (useQuery as jest.Mock).mockReturnValue({ + data: [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + { x: 1, y: 1, g: 'g2' }, + { x: 2, y: 3, g: 'g2' }, + { x: 3, y: 5, g: 'g2' }, + ], + loading: false, + inspect: false, + totalCount: 6, + }); + + wrapper = mount( + + + + + + ); + + await wait(); + wrapper.update(); + }); + + test('it renders the expected subtitle', () => { + expect(wrapper.find('[data-test-subj="header-panel-subtitle"]').text()).toEqual( + 'Showing: 6 external alerts' + ); + }); + + test('it renders the bar chart when data is available', () => { + expect(wrapper.find(`.echChart`).exists()).toBe(true); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx index e0d383c59e2ee..744102fbac4b3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx @@ -78,7 +78,11 @@ const AlertsByCategoryComponent: React.FC = ({ const urlSearch = useGetUrlSearch(navTabs.detections); const alertsCountViewAlertsButton = useMemo( - () => {i18n.VIEW_ALERTS}, + () => ( + + {i18n.VIEW_ALERTS} + + ), [urlSearch] ); @@ -87,7 +91,7 @@ const AlertsByCategoryComponent: React.FC = ({ ...histogramConfigs, defaultStackByOption: alertsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0], - getSubtitle: (totalCount: number) => + subtitle: (totalCount: number) => `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, legendPosition: Position.Right, }),