diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index 80d7feb20a4e6..ab100bf98bd48 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -86,7 +86,7 @@ describe('AlertDetailsAppSection', () => { it('should render rule and alert data', async () => { const result = renderComponent(); - expect((await result.findByTestId('thresholdAlertOverviewSection')).children.length).toBe(3); + expect((await result.findByTestId('thresholdAlertOverviewSection')).children.length).toBe(6); expect(result.getByTestId('thresholdRule-2000-2500')).toBeTruthy(); }); @@ -148,7 +148,30 @@ describe('AlertDetailsAppSection', () => { { ['kibana.alert.end']: '2023-03-28T14:40:00.000Z' } ); - expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(3); + expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(6); expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot(); }); + + it('should render title on condition charts', async () => { + const result = renderComponent(); + + expect(result.getByTestId('chartTitle-0').textContent).toBe( + 'Equation result for count (all documents)' + ); + expect(result.getByTestId('chartTitle-1').textContent).toBe( + 'Equation result for max (system.cpu.user.pct)' + ); + expect(result.getByTestId('chartTitle-2').textContent).toBe( + 'Equation result for min (system.memory.used.pct)' + ); + expect(result.getByTestId('chartTitle-3').textContent).toBe( + 'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct...' + ); + expect(result.getByTestId('chartTitle-4').textContent).toBe( + 'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct)' + ); + expect(result.getByTestId('chartTitle-5').textContent).toBe( + 'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct)' + ); + }); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 9d2aebddb43ee..2506516efd81a 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -17,6 +17,9 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiToolTip, + useEuiTheme, + transparentize, } from '@elastic/eui'; import { Rule, RuleTypeParams } from '@kbn/alerting-plugin/common'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; @@ -35,7 +38,6 @@ import type { RangeEventAnnotationConfig, } from '@kbn/event-annotation-common'; import moment from 'moment'; -import { transparentize, useEuiTheme } from '@elastic/eui'; import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; @@ -44,6 +46,7 @@ import { AlertParams, CustomThresholdAlertFields, CustomThresholdRuleTypeParams, + MetricExpression, } from '../../types'; import { TIME_LABELS } from '../criterion_preview_chart/criterion_preview_chart'; import { Threshold } from '../custom_threshold'; @@ -64,6 +67,44 @@ interface AppSectionProps { setAlertSummaryFields: React.Dispatch>; } +const CHART_TITLE_LIMIT = 120; + +const equationResultText = i18n.translate('xpack.observability.customThreshold.alertChartTitle', { + defaultMessage: 'Equation result for ', +}); + +const generateChartTitleAndTooltip = (criterion: MetricExpression) => { + const metricNameResolver: Record = {}; + + criterion.metrics.forEach( + (metric) => + (metricNameResolver[metric.name] = `${metric.aggType} (${ + metric.field ? metric.field : metric.filter ? metric.filter : 'all documents' + })`) + ); + + let equation = criterion.equation + ? criterion.equation + : criterion.metrics.map((m) => m.name).join(' + '); + + Object.keys(metricNameResolver) + .sort() + .reverse() + .forEach((metricName) => { + equation = equation.replaceAll(metricName, metricNameResolver[metricName]); + }); + + const chartTitle = + equation.length > CHART_TITLE_LIMIT + ? `${equation.substring(0, CHART_TITLE_LIMIT)}...` + : equation; + + return { + tooltip: `${equationResultText}${equation}`, + title: `${equationResultText}${chartTitle}`, + }; +}; + // eslint-disable-next-line import/no-default-export export default function AlertDetailsAppSection({ alert, @@ -89,6 +130,12 @@ export default function AlertDetailsAppSection({ const groups = alert.fields[ALERT_GROUP]; const tags = alert.fields[TAGS]; + const chartTitleAndTooltip: Array<{ title: string; tooltip: string }> = []; + + ruleParams.criteria.forEach((criterion) => { + chartTitleAndTooltip.push(generateChartTitleAndTooltip(criterion)); + }); + const alertStartAnnotation: PointInTimeEventAnnotationConfig = { label: 'Alert', type: 'manual', @@ -182,9 +229,11 @@ export default function AlertDetailsAppSection({ {ruleParams.criteria.map((criterion, index) => ( - -

{criterion.label || 'CUSTOM'}

-
+ + +

{chartTitleAndTooltip[index].title}

+
+