diff --git a/eslint-suppressions.json b/eslint-suppressions.json
index b0566228338..120a475bac4 100644
--- a/eslint-suppressions.json
+++ b/eslint-suppressions.json
@@ -810,11 +810,6 @@
"count": 1
}
},
- "packages/grafana-ui/src/components/Modal/Modal.story.tsx": {
- "no-restricted-syntax": {
- "count": 1
- }
- },
"packages/grafana-ui/src/components/Modal/ModalsContext.tsx": {
"@typescript-eslint/no-explicit-any": {
"count": 4
diff --git a/packages/grafana-sql/src/components/ConfirmModal.tsx b/packages/grafana-sql/src/components/ConfirmModal.tsx
index 62eba4bab5b..191c32a6835 100644
--- a/packages/grafana-sql/src/components/ConfirmModal.tsx
+++ b/packages/grafana-sql/src/components/ConfirmModal.tsx
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { useRef, useEffect } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
-import { Trans } from '@grafana/i18n';
+import { t, Trans } from '@grafana/i18n';
import { Button, Icon, Modal, useStyles2 } from '@grafana/ui';
type ConfirmModalProps = {
@@ -33,6 +33,7 @@ export function ConfirmModal({ isOpen, onCancel, onDiscard, onCopy }: ConfirmMod
}
+ ariaLabel={t('grafana-sql.components.confirm-modal.warning', 'Warning')}
onDismiss={onCancel}
isOpen={isOpen}
>
diff --git a/packages/grafana-ui/src/components/Modal/Modal.story.tsx b/packages/grafana-ui/src/components/Modal/Modal.story.tsx
index df05238cb3a..203d5fe3119 100644
--- a/packages/grafana-ui/src/components/Modal/Modal.story.tsx
+++ b/packages/grafana-ui/src/components/Modal/Modal.story.tsx
@@ -21,8 +21,6 @@ const meta: Meta = {
controls: {
exclude: ['className', 'contentClassName', 'onDismiss', 'onClickBackdrop'],
},
- // TODO fix a11y issue in story and remove this
- a11y: { test: 'off' },
},
args: {
body: oneLineTrim(`Id incididunt do pariatur qui labore. Sint culpa irure cillum et ullamco proident. Deserunt ipsum velit dolore est enim proident dolore consectetur. Et cillum tempor pariatur et. Est tempor cillum ad id nulla. Cillum ut proident
@@ -92,7 +90,7 @@ export const WithTabs: StoryFn = (args) => {
);
return (
-
+
{activeTab === tabs[0].value && {args.body}
}
{activeTab === tabs[1].value && Second tab content
}
@@ -110,7 +108,8 @@ WithTabs.args = {
export const UsingContentClassName: StoryFn = ({ title, body, ...args }) => {
const override = {
modalContent: css({
- backgroundColor: 'darkorange',
+ backgroundColor: 'red',
+ color: 'black',
}),
};
return (
diff --git a/packages/grafana-ui/src/components/Modal/Modal.tsx b/packages/grafana-ui/src/components/Modal/Modal.tsx
index 5ce59a89fca..9937fe93f9f 100644
--- a/packages/grafana-ui/src/components/Modal/Modal.tsx
+++ b/packages/grafana-ui/src/components/Modal/Modal.tsx
@@ -15,13 +15,11 @@ import { Stack } from '../Layout/Stack/Stack';
import { ModalHeader } from './ModalHeader';
import { getModalStyles } from './getModalStyles';
-export interface Props {
+interface BaseProps {
/** @deprecated no longer used */
icon?: IconName;
/** @deprecated no longer used */
iconTooltip?: string;
- /** Title for the modal or custom header element */
- title: string | JSX.Element;
className?: string;
contentClassName?: string;
closeOnEscape?: boolean;
@@ -35,9 +33,25 @@ export interface Props {
onClickBackdrop?: () => void;
}
+interface WithStringTitleProps extends BaseProps {
+ /** Title for the modal or custom header element */
+ title: string;
+ ariaLabel?: never;
+}
+
+interface WithCustomTitleProps extends BaseProps {
+ /** Title for the modal or custom header element */
+ title: JSX.Element;
+ /** aria-label for the dialog. only needed when passing a custom title element */
+ ariaLabel: string;
+}
+
+export type Props = WithStringTitleProps | WithCustomTitleProps;
+
export function Modal(props: PropsWithChildren) {
const {
title,
+ ariaLabel,
children,
isOpen = false,
closeOnEscape = true,
@@ -60,7 +74,12 @@ export function Modal(props: PropsWithChildren) {
);
// Get props for the dialog and its title
- const { dialogProps, titleProps } = useDialog({}, ref);
+ const { dialogProps, titleProps } = useDialog(
+ {
+ 'aria-label': ariaLabel,
+ },
+ ref
+ );
if (!isOpen) {
return null;
diff --git a/public/app/features/alerting/unified/components/notification-policies/Modals.tsx b/public/app/features/alerting/unified/components/notification-policies/Modals.tsx
index 1c0d0fb5895..d3187c41a7e 100644
--- a/public/app/features/alerting/unified/components/notification-policies/Modals.tsx
+++ b/public/app/features/alerting/unified/components/notification-policies/Modals.tsx
@@ -263,6 +263,7 @@ const useAlertGroupsModal = (
onDismiss={handleDismiss}
closeOnBackdropClick={true}
closeOnEscape={true}
+ ariaLabel={t('alerting.policies.matchers', 'Matchers')}
title={
@@ -303,6 +304,7 @@ const UpdatingModal: FC> = ({ isOpen }) => (
onDismiss={() => {}}
closeOnBackdropClick={false}
closeOnEscape={false}
+ ariaLabel={t('alerting.policies.update.updating', 'Updating...')}
title={
Updating...
diff --git a/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx b/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx
index 6e55f960824..40dab8f3fe8 100644
--- a/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx
+++ b/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx
@@ -65,6 +65,7 @@ export function NoAccessModal({ item, isOpen, onDismiss }: NoAccessModalProps) {
className={styles.modal}
contentClassName={styles.modalContent}
title={}
+ ariaLabel={item.name}
isOpen={isOpen}
onDismiss={onDismiss}
>
diff --git a/public/app/features/dashboard/components/ShareModal/ShareModal.tsx b/public/app/features/dashboard/components/ShareModal/ShareModal.tsx
index d8762d837b7..df096a63473 100644
--- a/public/app/features/dashboard/components/ShareModal/ShareModal.tsx
+++ b/public/app/features/dashboard/components/ShareModal/ShareModal.tsx
@@ -130,16 +130,14 @@ class UnthemedShareModal extends React.Component {
return tabs.find((t) => t.value === activeTab)!;
}
- renderTitle() {
- const { panel } = this.props;
+ renderTitle(modalTitle: string) {
const { activeTab } = this.state;
- const title = panel ? t('share-modal.panel.title', 'Share Panel') : t('share-modal.dashboard.title', 'Share');
const canEditDashboard = this.props.dashboard.canEditDashboard();
const tabs = getTabs(canEditDashboard, this.props.panel, this.state.activeTab).tabs;
return (
{
const { dashboard, panel } = this.props;
const activeTabModel = this.getActiveTab();
const ActiveTab = activeTabModel.component;
+ const modalTitle = panel ? t('share-modal.panel.title', 'Share Panel') : t('share-modal.dashboard.title', 'Share');
return (
-
+
diff --git a/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx b/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx
index 311c7964c99..00211b61c6e 100644
--- a/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx
+++ b/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
-import { Trans } from '@grafana/i18n';
+import { t, Trans } from '@grafana/i18n';
import { reportInteraction } from '@grafana/runtime';
import { PageInfoItem } from '@grafana/runtime/internal';
import {
@@ -242,7 +242,7 @@ export function PluginDetailsPanel(props: Props): React.ReactElement | null {
{reportAbuseModalOpen && (
Report a plugin concern}
+ title={t('plugins.details.modal.title', 'Report a plugin concern')}
isOpen
onDismiss={() => setReportAbuseModalOpen(false)}
>
diff --git a/public/app/features/search/page/components/ExplainScorePopup.tsx b/public/app/features/search/page/components/ExplainScorePopup.tsx
index 3d4d304a43a..fa3d886ef66 100644
--- a/public/app/features/search/page/components/ExplainScorePopup.tsx
+++ b/public/app/features/search/page/components/ExplainScorePopup.tsx
@@ -34,7 +34,14 @@ export function ExplainScorePopup({ name, explain, frame, row }: Props) {
);
return (
- setOpen(false)} closeOnBackdropClick closeOnEscape>
+ setOpen(false)}
+ closeOnBackdropClick
+ closeOnEscape
+ >
{activeTab === tabs[0].value && (