From 53a8998c851e077d972f83c397594bcd3e43e2d4 Mon Sep 17 00:00:00 2001 From: Konrad Lalik Date: Fri, 10 Feb 2023 10:23:40 +0100 Subject: [PATCH] Alerting: a11y improvements (#63072) --- .../features/alerting/TestRuleResult.test.tsx | 21 ++- .../app/features/alerting/TestRuleResult.tsx | 22 ++- .../expressions/AlertConditionIndicator.tsx | 29 ++-- .../components/expressions/Expression.tsx | 15 +- .../receivers/form/CollapsibleSection.tsx | 27 ++-- .../rule-editor/DashboardPicker.tsx | 145 ++++++++++-------- .../panel/alertlist/AlertInstances.tsx | 12 +- 7 files changed, 162 insertions(+), 109 deletions(-) diff --git a/public/app/features/alerting/TestRuleResult.test.tsx b/public/app/features/alerting/TestRuleResult.test.tsx index d80398c0564..c62f8ace394 100644 --- a/public/app/features/alerting/TestRuleResult.test.tsx +++ b/public/app/features/alerting/TestRuleResult.test.tsx @@ -4,20 +4,22 @@ import React from 'react'; import { PanelModel } from '../dashboard/state'; import { createDashboardModelFixture, createPanelJSONFixture } from '../dashboard/state/__fixtures__/dashboardFixtures'; -import { TestRuleResult, Props } from './TestRuleResult'; +import { TestRuleResult } from './TestRuleResult'; + +const backendSrv = { + post: jest.fn(), +}; jest.mock('@grafana/runtime', () => { const original = jest.requireActual('@grafana/runtime'); return { ...original, - getBackendSrv: () => ({ - post: jest.fn(), - }), + getBackendSrv: () => backendSrv, }; }); -const props: Props = { +const props: React.ComponentProps = { panel: new PanelModel({ id: 1 }), dashboard: createDashboardModelFixture({ panels: [createPanelJSONFixture({ id: 1 })], @@ -30,9 +32,14 @@ describe('TestRuleResult', () => { }); it('should call testRule when mounting', () => { - jest.spyOn(TestRuleResult.prototype, 'testRule'); + jest.spyOn(backendSrv, 'post'); render(); - expect(TestRuleResult.prototype.testRule).toHaveBeenCalled(); + expect(backendSrv.post).toHaveBeenCalledWith( + '/api/alerts/test', + expect.objectContaining({ + panelId: 1, + }) + ); }); }); diff --git a/public/app/features/alerting/TestRuleResult.tsx b/public/app/features/alerting/TestRuleResult.tsx index 3de19747775..dc1504f916f 100644 --- a/public/app/features/alerting/TestRuleResult.tsx +++ b/public/app/features/alerting/TestRuleResult.tsx @@ -1,11 +1,20 @@ import React, { PureComponent } from 'react'; import { getBackendSrv } from '@grafana/runtime'; -import { LoadingPlaceholder, JSONFormatter, Icon, HorizontalGroup, ClipboardButton } from '@grafana/ui'; +import { + LoadingPlaceholder, + JSONFormatter, + Icon, + HorizontalGroup, + ClipboardButton, + clearButtonStyles, + withTheme2, + Themeable2, +} from '@grafana/ui'; import { DashboardModel, PanelModel } from '../dashboard/state'; -export interface Props { +export interface Props extends Themeable2 { dashboard: DashboardModel; panel: PanelModel; } @@ -16,7 +25,7 @@ interface State { testRuleResponse: {}; } -export class TestRuleResult extends PureComponent { +class UnThemedTestRuleResult extends PureComponent { readonly state: State = { isLoading: false, allNodesExpanded: null, @@ -90,6 +99,7 @@ export class TestRuleResult extends PureComponent { render() { const { testRuleResponse, isLoading } = this.state; + const clearButton = clearButtonStyles(this.props.theme); if (isLoading === true) { return ; @@ -101,7 +111,9 @@ export class TestRuleResult extends PureComponent { <>
-
{this.renderExpandCollapse()}
+ Copy to Clipboard @@ -113,3 +125,5 @@ export class TestRuleResult extends PureComponent { ); } } + +export const TestRuleResult = withTheme2(UnThemedTestRuleResult); diff --git a/public/app/features/alerting/unified/components/expressions/AlertConditionIndicator.tsx b/public/app/features/alerting/unified/components/expressions/AlertConditionIndicator.tsx index f48efde64b7..3572af7d877 100644 --- a/public/app/features/alerting/unified/components/expressions/AlertConditionIndicator.tsx +++ b/public/app/features/alerting/unified/components/expressions/AlertConditionIndicator.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import React, { FC } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { Badge, useStyles2 } from '@grafana/ui'; +import { Badge, clearButtonStyles, useStyles2 } from '@grafana/ui'; interface AlertConditionProps { enabled?: boolean; @@ -33,22 +33,27 @@ export const AlertConditionIndicator: FC = ({ if (!enabled) { return ( -
onSetCondition && onSetCondition()}> +
+ ); } return null; }; -const getStyles = (theme: GrafanaTheme2) => ({ - actionLink: css` - color: ${theme.colors.text.link}; - cursor: pointer; +const getStyles = (theme: GrafanaTheme2) => { + const clearButton = clearButtonStyles(theme); - &:hover { - text-decoration: underline; - } - `, -}); + return { + actionLink: css` + ${clearButton}; + color: ${theme.colors.text.link}; + cursor: pointer; + + &:hover { + text-decoration: underline; + } + `, + }; +}; diff --git a/public/app/features/alerting/unified/components/expressions/Expression.tsx b/public/app/features/alerting/unified/components/expressions/Expression.tsx index 01b898564eb..b2767a1ea3d 100644 --- a/public/app/features/alerting/unified/components/expressions/Expression.tsx +++ b/public/app/features/alerting/unified/components/expressions/Expression.tsx @@ -5,7 +5,7 @@ import React, { FC, useCallback, useState } from 'react'; import { DataFrame, dateTimeFormat, GrafanaTheme2, LoadingState, PanelData } from '@grafana/data'; import { isTimeSeries } from '@grafana/data/src/dataframe/utils'; import { Stack } from '@grafana/experimental'; -import { AutoSizeInput, Icon, IconButton, Select, useStyles2 } from '@grafana/ui'; +import { AutoSizeInput, clearButtonStyles, Icon, IconButton, Select, useStyles2 } from '@grafana/ui'; import { ClassicConditions } from 'app/features/expressions/components/ClassicConditions'; import { Math } from 'app/features/expressions/components/Math'; import { Reduce } from 'app/features/expressions/components/Reduce'; @@ -175,6 +175,7 @@ interface HeaderProps { const Header: FC = ({ refId, queryType, onUpdateRefId, onUpdateExpressionType, onRemoveExpression }) => { const styles = useStyles2(getStyles); + const clearButton = useStyles2(clearButtonStyles); /** * There are 3 edit modes: * @@ -195,9 +196,9 @@ const Header: FC = ({ refId, queryType, onUpdateRefId, onUpdateExpr {!editingRefId && ( -
setEditMode('refId')}> +
+ )} {editingRefId && ( = ({ refId, queryType, onUpdateRefId, onUpdateExpr /> )} {!editingType && ( -
setEditMode('expressionType')}> +
+ )} {editingType && (