From f30f62cfd83ff117153448be140dcd3b225a0f73 Mon Sep 17 00:00:00 2001 From: mgiota Date: Thu, 5 May 2022 15:29:05 +0200 Subject: [PATCH 01/11] make stat counts clickable --- .../alerts/components/rule_stats/index.ts | 8 + .../components/rule_stats/rule_stats.tsx | 150 ++++++++++++++++++ .../containers/alerts_page/alerts_page.tsx | 59 +------ 3 files changed, 161 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts create mode 100644 x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts new file mode 100644 index 0000000000000..6b9c2d6b67f1f --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { renderRuleStats } from './rule_stats'; diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx new file mode 100644 index 0000000000000..c48bacf57fa4a --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx @@ -0,0 +1,150 @@ +/* + * 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 React from 'react'; +import { EuiButtonEmpty, EuiStat } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; + +interface RuleStatsState { + total: number; + disabled: number; + muted: number; + error: number; + snoozed: number; +} +type StatType = 'disabled' | 'snoozed' | 'error'; + +const Divider = euiStyled.div` + border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; + height: 100%; +`; + +const ConditionalWrap = ({ + condition, + wrap, + children, +}: { + condition: boolean; + wrap: any; + children: any; +}) => (condition ? wrap(children) : children); + +export const renderRuleStats = ( + ruleStats: RuleStatsState, + manageRulesHref: string, + ruleStatsLoading: boolean +) => { + const createRuleStatsLink = (stats: RuleStatsState, statType: StatType) => { + const count = stats[statType]; + let statsLink = `${manageRulesHref}?_a=(lastResponse:!(),status:!())`; + if (count > 0) { + switch (statType) { + case 'error': + statsLink = `${manageRulesHref}?_a=(lastResponse:!(error),status:!())`; + break; + case 'snoozed': + case 'disabled': + statsLink = `${manageRulesHref}?_a=(lastResponse:!(),status:!(${statType}))`; + break; + default: + break; + } + } + return statsLink; + }; + + const disabledStatsComponent = ( + 0} + wrap={(wrappedChildren: any) => ( + + {wrappedChildren} + + )} + > + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={ruleStatsLoading} + data-test-subj="statDisabled" + /> + + ); + + const snoozedStatsComponent = ( + 0} + wrap={(wrappedChildren: any) => ( + + {wrappedChildren} + + )} + > + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={ruleStatsLoading} + data-test-subj="statMuted" + /> + + ); + + const errorStatsComponent = ( + 0} + wrap={(wrappedChildren: any) => ( + + {wrappedChildren} + + )} + > + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={ruleStatsLoading} + data-test-subj="statErrors" + /> + + ); + return [ + , + disabledStatsComponent, + snoozedStatsComponent, + errorStatsComponent, + , + + {i18n.translate('xpack.observability.alerts.manageRulesButtonLabel', { + defaultMessage: 'Manage Rules', + })} + , + ].reverse(); +}; diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index e99a3195d0f30..2fe114771c329 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -5,15 +5,13 @@ * 2.0. */ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { DataViewBase } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { ALERT_STATUS, AlertStatus } from '@kbn/rule-data-utils'; - -import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields'; @@ -38,6 +36,7 @@ import { } from '../state_container'; import './styles.scss'; import { AlertsStatusFilter, AlertsDisclaimer, AlertsSearchBar } from '../../components'; +import { renderRuleStats } from '../../components/rule_stats'; import { ObservabilityAppServices } from '../../../../application/types'; import { OBSERVABILITY_RULE_TYPES } from '../../../rules/config'; @@ -57,11 +56,6 @@ export interface TopAlert { active: boolean; } -const Divider = euiStyled.div` - border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; - height: 100%; -`; - const regExpEscape = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); const NO_INDEX_PATTERNS: DataViewBase[] = []; const BASE_ALERT_REGEX = new RegExp(`\\s*${regExpEscape(ALERT_STATUS)}\\s*:\\s*"(.*?|\\*?)"`); @@ -251,54 +245,7 @@ function AlertsPage() { ), - rightSideItems: [ - , - , - , - , - , - - {i18n.translate('xpack.observability.alerts.manageRulesButtonLabel', { - defaultMessage: 'Manage Rules', - })} - , - ].reverse(), + rightSideItems: renderRuleStats(ruleStats, manageRulesHref, ruleStatsLoading), }} > From 69f6911c5e03cf4aea012256e84e9f8496cd23ca Mon Sep 17 00:00:00 2001 From: mgiota Date: Thu, 5 May 2022 23:50:22 +0200 Subject: [PATCH 02/11] unit tests for rule stats --- .../alerts/components/rule_stats/index.ts | 1 + .../components/rule_stats/rule_stats.test.tsx | 72 +++++++++++++++++++ .../alerts/components/rule_stats/types.ts | 16 +++++ 3 files changed, 89 insertions(+) create mode 100644 x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx create mode 100644 x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.ts diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts index 6b9c2d6b67f1f..e5245b71b3fca 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts @@ -6,3 +6,4 @@ */ export { renderRuleStats } from './rule_stats'; +export { RuleStatsState } from './types'; diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx new file mode 100644 index 0000000000000..1262d051e2cbb --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -0,0 +1,72 @@ +/* + * 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 { renderRuleStats } from './rule_stats'; +import { render } from '@testing-library/react'; + +describe('Rule stats', () => { + test('renders all rule stats', () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + expect(stats.length).toEqual(6); + }); + test('no disabled rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { findByText, container } = render(stats[4]); + const disabledElement = await findByText('Disabled'); + expect(disabledElement).toBeInTheDocument(); + expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); + }); + + test('disabled rules are present', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 1, + muted: 0, + error: 0, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { findByText, container } = render(stats[4]); + const disabledElement = await findByText('Disabled'); + expect(disabledElement).toBeInTheDocument(); + expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); + }); + + test('no snoozed rules', () => {}); + + test('snoozed rules are present', () => {}); + + test('no error rules', () => {}); + + test('error rules are present', () => {}); +}); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.ts b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.ts new file mode 100644 index 0000000000000..87ff668ebf87f --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ + +export interface RuleStatsState { + total: number; + disabled: number; + muted: number; + error: number; + snoozed: number; +} + +export type StatType = 'disabled' | 'snoozed' | 'error'; From 11518d8467f09f6e4a0a752dd9ae8b0ad89942e1 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 00:14:00 +0200 Subject: [PATCH 03/11] rename test cases & add more cases --- .../components/rule_stats/rule_stats.test.tsx | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx index 1262d051e2cbb..a8c151f7cb56b 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -9,7 +9,7 @@ import { renderRuleStats } from './rule_stats'; import { render } from '@testing-library/react'; describe('Rule stats', () => { - test('renders all rule stats', () => { + test('renders all rule stats', async () => { const stats = renderRuleStats( { total: 11, @@ -23,7 +23,7 @@ describe('Rule stats', () => { ); expect(stats.length).toEqual(6); }); - test('no disabled rules', async () => { + test('disabled count is not clickable, when there are no disabled rules', async () => { const stats = renderRuleStats( { total: 11, @@ -42,7 +42,7 @@ describe('Rule stats', () => { expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); }); - test('disabled rules are present', async () => { + test('disabled count is clickable, when there are disabled rules', async () => { const stats = renderRuleStats( { total: 11, @@ -62,11 +62,45 @@ describe('Rule stats', () => { expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); }); - test('no snoozed rules', () => {}); + test('snoozed count is not clickable, when there are no snoozed rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { findByText, container } = render(stats[3]); + const snoozedElement = await findByText('Snoozed'); + expect(snoozedElement).toBeInTheDocument(); + expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); + }); - test('snoozed rules are present', () => {}); + test('snoozed count is clickable, when there are snoozed rules', () => {}); - test('no error rules', () => {}); + test('errors count is not clickable, when there are no error rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { findByText, container } = render(stats[2]); + const errorsElement = await findByText('Errors'); + expect(errorsElement).toBeInTheDocument(); + expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); + }); - test('error rules are present', () => {}); + test('errors count is clickable, when there are error rules', () => {}); }); From f16fc71aa6e3ba80530ff65ab0f3bf1e06859992 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 00:17:21 +0200 Subject: [PATCH 04/11] more test cases --- .../components/rule_stats/rule_stats.test.tsx | 95 +++++++++++++++++-- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx index a8c151f7cb56b..4d57874eaa50c 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -23,7 +23,7 @@ describe('Rule stats', () => { ); expect(stats.length).toEqual(6); }); - test('disabled count is not clickable, when there are no disabled rules', async () => { + test('disabled stat is not clickable, when there are no disabled rules', async () => { const stats = renderRuleStats( { total: 11, @@ -39,10 +39,11 @@ describe('Rule stats', () => { const disabledElement = await findByText('Disabled'); expect(disabledElement).toBeInTheDocument(); expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(0); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); }); - test('disabled count is clickable, when there are disabled rules', async () => { + test('disabled stat is clickable, when there are disabled rules', async () => { const stats = renderRuleStats( { total: 11, @@ -54,15 +55,27 @@ describe('Rule stats', () => { '/app/observability/alerts/rules', false ); - const { findByText, container } = render(stats[4]); - const disabledElement = await findByText('Disabled'); - expect(disabledElement).toBeInTheDocument(); - expect(container.getElementsByClassName('euiStat').length).toBe(1); + const { container } = render(stats[4]); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + }); + + test('disabled stat count is link-colored, when there are disabled rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 1, + muted: 0, + error: 0, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { container } = render(stats[4]); expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); }); - test('snoozed count is not clickable, when there are no snoozed rules', async () => { + test('snoozed stat is not clickable, when there are no snoozed rules', async () => { const stats = renderRuleStats( { total: 11, @@ -78,12 +91,43 @@ describe('Rule stats', () => { const snoozedElement = await findByText('Snoozed'); expect(snoozedElement).toBeInTheDocument(); expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(0); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); }); - test('snoozed count is clickable, when there are snoozed rules', () => {}); + test('snoozed stat is clickable, when there are snoozed rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 1, + error: 0, + snoozed: 1, + }, + '/app/observability/alerts/rules', + false + ); + const { container } = render(stats[3]); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + }); - test('errors count is not clickable, when there are no error rules', async () => { + test('snoozed stat count is link-colored, when there are snoozed rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 1, + error: 0, + snoozed: 1, + }, + '/app/observability/alerts/rules', + false + ); + const { container } = render(stats[3]); + expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); + }); + + test('errors stat is not clickable, when there are no error rules', async () => { const stats = renderRuleStats( { total: 11, @@ -99,8 +143,39 @@ describe('Rule stats', () => { const errorsElement = await findByText('Errors'); expect(errorsElement).toBeInTheDocument(); expect(container.getElementsByClassName('euiStat').length).toBe(1); + expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(0); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); }); - test('errors count is clickable, when there are error rules', () => {}); + test('errors stat is clickable, when there are error rules', () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 2, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { container } = render(stats[2]); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + }); + + test('errors stat count is link-colored, when there are error rules', () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 2, + snoozed: 0, + }, + '/app/observability/alerts/rules', + false + ); + const { container } = render(stats[2]); + expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); + }); }); From e4a992cd76a97f6af338f1b20bf8186b2ec3ebe1 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 08:26:11 +0200 Subject: [PATCH 05/11] use type for exporting types --- .../public/pages/alerts/components/rule_stats/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts index e5245b71b3fca..b2b4e144952e0 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts @@ -6,4 +6,4 @@ */ export { renderRuleStats } from './rule_stats'; -export { RuleStatsState } from './types'; +export type { RuleStatsState } from './types'; From e18906a373cb1234f4e5c21f8612945f30264b03 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 08:51:34 +0200 Subject: [PATCH 06/11] add types --- .../alerts/components/rule_stats/rule_stats.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx index c48bacf57fa4a..419c9bc83a04d 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { FunctionComponent } from 'react'; import { EuiButtonEmpty, EuiStat } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; @@ -30,9 +30,9 @@ const ConditionalWrap = ({ children, }: { condition: boolean; - wrap: any; + wrap: FunctionComponent; children: any; -}) => (condition ? wrap(children) : children); +}): JSX.Element => (condition ? wrap(children) : children); export const renderRuleStats = ( ruleStats: RuleStatsState, @@ -61,7 +61,7 @@ export const renderRuleStats = ( const disabledStatsComponent = ( 0} - wrap={(wrappedChildren: any) => ( + wrap={(wrappedChildren) => ( {wrappedChildren} @@ -84,7 +84,7 @@ export const renderRuleStats = ( const snoozedStatsComponent = ( 0} - wrap={(wrappedChildren: any) => ( + wrap={(wrappedChildren) => ( {wrappedChildren} @@ -107,7 +107,7 @@ export const renderRuleStats = ( const errorStatsComponent = ( 0} - wrap={(wrappedChildren: any) => ( + wrap={(wrappedChildren) => ( {wrappedChildren} From 23b4a4fb6117993884211cc47bf1d8c8f1516823 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 09:35:01 +0200 Subject: [PATCH 07/11] UI adjustments --- .../alerts/components/rule_stats/rule_stats.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx index 419c9bc83a04d..d69a35651436e 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx @@ -24,6 +24,12 @@ const Divider = euiStyled.div` height: 100%; `; +const StyledStat = euiStyled(EuiStat)` + .euiText { + line-height: 1; + } +`; + const ConditionalWrap = ({ condition, wrap, @@ -67,7 +73,7 @@ export const renderRuleStats = ( )} > - )} > - )} > - ); return [ - Date: Fri, 6 May 2022 11:15:22 +0200 Subject: [PATCH 08/11] add more test cases that check the href link --- .../components/rule_stats/rule_stats.test.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx index 4d57874eaa50c..bfd5968f50f04 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -6,7 +6,7 @@ */ import { renderRuleStats } from './rule_stats'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; describe('Rule stats', () => { test('renders all rule stats', async () => { @@ -56,6 +56,11 @@ describe('Rule stats', () => { false ); const { container } = render(stats[4]); + expect(screen.getByText('Disabled').closest('a')).toHaveAttribute( + 'href', + '/app/observability/alerts/rules?_a=(lastResponse:!(),status:!(disabled))' + ); + expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); }); @@ -95,7 +100,7 @@ describe('Rule stats', () => { expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); }); - test('snoozed stat is clickable, when there are snoozed rules', async () => { + test('snoozed stat is clickable, when there are snoozed rules', () => { const stats = renderRuleStats( { total: 11, @@ -109,6 +114,10 @@ describe('Rule stats', () => { ); const { container } = render(stats[3]); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + expect(screen.getByText('Snoozed').closest('a')).toHaveAttribute( + 'href', + '/app/observability/alerts/rules?_a=(lastResponse:!(),status:!(snoozed))' + ); }); test('snoozed stat count is link-colored, when there are snoozed rules', async () => { @@ -161,6 +170,10 @@ describe('Rule stats', () => { ); const { container } = render(stats[2]); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + expect(screen.getByText('Errors').closest('a')).toHaveAttribute( + 'href', + '/app/observability/alerts/rules?_a=(lastResponse:!(error),status:!())' + ); }); test('errors stat count is link-colored, when there are error rules', () => { From ad247235a0a59662ada916c3a357ddf5f1b4c97d Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 11:27:15 +0200 Subject: [PATCH 09/11] use const for the rules page link --- .../components/rule_stats/rule_stats.test.tsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx index bfd5968f50f04..6985b57ca07df 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -8,6 +8,8 @@ import { renderRuleStats } from './rule_stats'; import { render, screen } from '@testing-library/react'; +const RULES_PAGE_LINK = '/app/observability/alerts/rules'; + describe('Rule stats', () => { test('renders all rule stats', async () => { const stats = renderRuleStats( @@ -18,7 +20,7 @@ describe('Rule stats', () => { error: 0, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); expect(stats.length).toEqual(6); @@ -32,7 +34,7 @@ describe('Rule stats', () => { error: 0, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { findByText, container } = render(stats[4]); @@ -52,13 +54,13 @@ describe('Rule stats', () => { error: 0, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { container } = render(stats[4]); expect(screen.getByText('Disabled').closest('a')).toHaveAttribute( 'href', - '/app/observability/alerts/rules?_a=(lastResponse:!(),status:!(disabled))' + `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(disabled))` ); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); @@ -73,7 +75,7 @@ describe('Rule stats', () => { error: 0, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { container } = render(stats[4]); @@ -89,7 +91,7 @@ describe('Rule stats', () => { error: 0, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { findByText, container } = render(stats[3]); @@ -109,14 +111,14 @@ describe('Rule stats', () => { error: 0, snoozed: 1, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { container } = render(stats[3]); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); expect(screen.getByText('Snoozed').closest('a')).toHaveAttribute( 'href', - '/app/observability/alerts/rules?_a=(lastResponse:!(),status:!(snoozed))' + `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(snoozed))` ); }); @@ -129,7 +131,7 @@ describe('Rule stats', () => { error: 0, snoozed: 1, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { container } = render(stats[3]); @@ -145,7 +147,7 @@ describe('Rule stats', () => { error: 0, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { findByText, container } = render(stats[2]); @@ -165,14 +167,14 @@ describe('Rule stats', () => { error: 2, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { container } = render(stats[2]); expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); expect(screen.getByText('Errors').closest('a')).toHaveAttribute( 'href', - '/app/observability/alerts/rules?_a=(lastResponse:!(error),status:!())' + `${RULES_PAGE_LINK}?_a=(lastResponse:!(error),status:!())` ); }); @@ -185,7 +187,7 @@ describe('Rule stats', () => { error: 2, snoozed: 0, }, - '/app/observability/alerts/rules', + RULES_PAGE_LINK, false ); const { container } = render(stats[2]); From 77ea58cca0417c79cdaf29eaaf092bf5567a2853 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 11:35:13 +0200 Subject: [PATCH 10/11] use consts for classes --- .../components/rule_stats/rule_stats.test.tsx | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx index 6985b57ca07df..6f2edf5d0b1b6 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -9,6 +9,9 @@ import { renderRuleStats } from './rule_stats'; import { render, screen } from '@testing-library/react'; const RULES_PAGE_LINK = '/app/observability/alerts/rules'; +const STAT_CLASS = 'euiStat'; +const STAT_TITLE_PRIMARY_CLASS = 'euiStat__title--primary'; +const STAT_BUTTON_CLASS = 'euiButtonEmpty'; describe('Rule stats', () => { test('renders all rule stats', async () => { @@ -40,9 +43,9 @@ describe('Rule stats', () => { const { findByText, container } = render(stats[4]); const disabledElement = await findByText('Disabled'); expect(disabledElement).toBeInTheDocument(); - expect(container.getElementsByClassName('euiStat').length).toBe(1); - expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(0); - expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); + expect(container.getElementsByClassName(STAT_CLASS).length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(0); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(0); }); test('disabled stat is clickable, when there are disabled rules', async () => { @@ -63,7 +66,7 @@ describe('Rule stats', () => { `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(disabled))` ); - expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); }); test('disabled stat count is link-colored, when there are disabled rules', async () => { @@ -79,7 +82,7 @@ describe('Rule stats', () => { false ); const { container } = render(stats[4]); - expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(1); }); test('snoozed stat is not clickable, when there are no snoozed rules', async () => { @@ -97,9 +100,9 @@ describe('Rule stats', () => { const { findByText, container } = render(stats[3]); const snoozedElement = await findByText('Snoozed'); expect(snoozedElement).toBeInTheDocument(); - expect(container.getElementsByClassName('euiStat').length).toBe(1); - expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(0); - expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); + expect(container.getElementsByClassName(STAT_CLASS).length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(0); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(0); }); test('snoozed stat is clickable, when there are snoozed rules', () => { @@ -115,7 +118,7 @@ describe('Rule stats', () => { false ); const { container } = render(stats[3]); - expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); expect(screen.getByText('Snoozed').closest('a')).toHaveAttribute( 'href', `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(snoozed))` @@ -135,7 +138,7 @@ describe('Rule stats', () => { false ); const { container } = render(stats[3]); - expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(1); }); test('errors stat is not clickable, when there are no error rules', async () => { @@ -153,9 +156,9 @@ describe('Rule stats', () => { const { findByText, container } = render(stats[2]); const errorsElement = await findByText('Errors'); expect(errorsElement).toBeInTheDocument(); - expect(container.getElementsByClassName('euiStat').length).toBe(1); - expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(0); - expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(0); + expect(container.getElementsByClassName(STAT_CLASS).length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(0); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(0); }); test('errors stat is clickable, when there are error rules', () => { @@ -171,7 +174,7 @@ describe('Rule stats', () => { false ); const { container } = render(stats[2]); - expect(container.getElementsByClassName('euiButtonEmpty').length).toBe(1); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); expect(screen.getByText('Errors').closest('a')).toHaveAttribute( 'href', `${RULES_PAGE_LINK}?_a=(lastResponse:!(error),status:!())` @@ -191,6 +194,6 @@ describe('Rule stats', () => { false ); const { container } = render(stats[2]); - expect(container.getElementsByClassName('euiStat__title--primary').length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(1); }); }); From b499f66a5e2f07342bbc49512e6b7991b9386936 Mon Sep 17 00:00:00 2001 From: mgiota Date: Fri, 6 May 2022 11:48:51 +0200 Subject: [PATCH 11/11] fix types in ConditionalWrap --- .../pages/alerts/components/rule_stats/rule_stats.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx index d69a35651436e..62c520c7b7442 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import { EuiButtonEmpty, EuiStat } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; @@ -36,8 +36,8 @@ const ConditionalWrap = ({ children, }: { condition: boolean; - wrap: FunctionComponent; - children: any; + wrap: (wrappedChildren: React.ReactNode) => JSX.Element; + children: JSX.Element; }): JSX.Element => (condition ? wrap(children) : children); export const renderRuleStats = (