diff --git a/docs/data/material/components/alert/alert.md b/docs/data/material/components/alert/alert.md index 5152d5276f38a1..b39c79e8cb584a 100644 --- a/docs/data/material/components/alert/alert.md +++ b/docs/data/material/components/alert/alert.md @@ -29,7 +29,7 @@ This component is no longer documented in the [Material Design guidelines](https A key trait of the alert pattern is that [it should not interrupt the user's experience](https://www.w3.org/WAI/ARIA/apg/patterns/alert/) of the app. Alerts should not be confused with alert _dialogs_ ([ARIA](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/)), which _are_ intended to interrupt the user to obtain a response. -Use the Material UI [Dialog](/material-ui/react-dialog/) component if you need this behavior. +Use the Material UI Dialog component (see the [alert dialog](/material-ui/react-dialog/#alerts) example) if you need this behavior. ## Basics @@ -121,6 +121,7 @@ Here are some factors to consider to ensure that your Alert is accessible: - Because alerts are not intended to interfere with the use of the app, your Alert component should _never_ affect the keyboard focus. - If an alert contains an action, that action must have a `tabindex` of `0` so it can be reached by keyboard-only users. - Essential alerts should not disappear automatically—[timed interactions](https://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-no-exceptions.html) can make your app inaccessible to users who need extra time to understand or locate the alert. +- By default, the Alert renders an element with the `role="alert"`, which is the same as using `aria-live="assertive"` and `aria-atomic="true"`. This assumes the message requires the user's immediate attention. Less urgent messages should use a less aggressive method, such as overriding the default role with a `role="status"`. Check this [alert role page](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/alert_role) for more information. - Alerts that occur too frequently can [inhibit the usability](https://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-postponed.html) of your app. - Dynamically rendered alerts are announced by screen readers; alerts that are already present on the page when it loads are _not_ announced. - Color does not add meaning to the UI for users who require assistive technology. You must ensure that any information conveyed through color is also denoted in other ways, such as within the text of the alert itself, or with additional hidden text that's read by screen readers. diff --git a/docs/data/material/components/dialogs/AlertDialog.js b/docs/data/material/components/dialogs/AlertDialog.js index c6d93e2a0ad51c..29f272a910bc0f 100644 --- a/docs/data/material/components/dialogs/AlertDialog.js +++ b/docs/data/material/components/dialogs/AlertDialog.js @@ -27,6 +27,7 @@ export default function AlertDialog() { onClose={handleClose} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" + role="alertdialog" > {"Use Google's location service?"} @@ -38,10 +39,10 @@ export default function AlertDialog() { - + diff --git a/docs/data/material/components/dialogs/AlertDialog.tsx b/docs/data/material/components/dialogs/AlertDialog.tsx index c6d93e2a0ad51c..29f272a910bc0f 100644 --- a/docs/data/material/components/dialogs/AlertDialog.tsx +++ b/docs/data/material/components/dialogs/AlertDialog.tsx @@ -27,6 +27,7 @@ export default function AlertDialog() { onClose={handleClose} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" + role="alertdialog" > {"Use Google's location service?"} @@ -38,10 +39,10 @@ export default function AlertDialog() { - + diff --git a/docs/data/material/components/dialogs/AlertDialogSlide.js b/docs/data/material/components/dialogs/AlertDialogSlide.js index 8034260174a6aa..22e3025be11f8c 100644 --- a/docs/data/material/components/dialogs/AlertDialogSlide.js +++ b/docs/data/material/components/dialogs/AlertDialogSlide.js @@ -35,6 +35,7 @@ export default function AlertDialogSlide() { keepMounted onClose={handleClose} aria-describedby="alert-dialog-slide-description" + role="alertdialog" > {"Use Google's location service?"} @@ -44,7 +45,9 @@ export default function AlertDialogSlide() { - + diff --git a/docs/data/material/components/dialogs/AlertDialogSlide.tsx b/docs/data/material/components/dialogs/AlertDialogSlide.tsx index e3ffc44aba09f8..97fbc56f241983 100644 --- a/docs/data/material/components/dialogs/AlertDialogSlide.tsx +++ b/docs/data/material/components/dialogs/AlertDialogSlide.tsx @@ -41,6 +41,7 @@ export default function AlertDialogSlide() { keepMounted onClose={handleClose} aria-describedby="alert-dialog-slide-description" + role="alertdialog" > {"Use Google's location service?"} @@ -50,7 +51,9 @@ export default function AlertDialogSlide() { - + diff --git a/docs/data/material/components/dialogs/dialogs.md b/docs/data/material/components/dialogs/dialogs.md index 8f2b122530406a..633208069ac433 100644 --- a/docs/data/material/components/dialogs/dialogs.md +++ b/docs/data/material/components/dialogs/dialogs.md @@ -42,6 +42,8 @@ import DialogTitle from '@mui/material/DialogTitle'; Alerts are urgent interruptions, requiring acknowledgement, that inform the user about a situation. +Use `role="alertdialog"` to create an Alert Dialog. This provides assistive technologies the correct purpose of the Dialog. + Most alerts don't need titles. They summarize a decision in a sentence or two by either: diff --git a/docs/pages/material-ui/api/dialog.json b/docs/pages/material-ui/api/dialog.json index e8e7c66b6f3280..a2b1a7f66d639d 100644 --- a/docs/pages/material-ui/api/dialog.json +++ b/docs/pages/material-ui/api/dialog.json @@ -29,6 +29,10 @@ } }, "PaperComponent": { "type": { "name": "elementType" }, "default": "Paper" }, + "role": { + "type": { "name": "enum", "description": "'alertdialog'
| 'dialog'" }, + "default": "'dialog'" + }, "scroll": { "type": { "name": "enum", "description": "'body'
| 'paper'" }, "default": "'paper'" diff --git a/docs/translations/api-docs/dialog/dialog.json b/docs/translations/api-docs/dialog/dialog.json index 33c0313ad87e02..2b7660937557fa 100644 --- a/docs/translations/api-docs/dialog/dialog.json +++ b/docs/translations/api-docs/dialog/dialog.json @@ -27,6 +27,9 @@ }, "open": { "description": "If true, the component is shown." }, "PaperComponent": { "description": "The component used to render the body of the dialog." }, + "role": { + "description": "The ARIA role for the dialog element. The main dialog role is dialog, but alertdialog can be used if the content of the dialog requires immediate attention. See https://www.w3.org/TR/wai-aria-1.2/#dialog and https://www.w3.org/TR/wai-aria-1.2/#alertdialog for more details." + }, "scroll": { "description": "Determine the container for scrolling the dialog." }, "slotProps": { "description": "The props used for each slot inside." }, "slots": { "description": "The components used for each slot inside." }, diff --git a/packages/mui-material/src/Dialog/Dialog.d.ts b/packages/mui-material/src/Dialog/Dialog.d.ts index b2c57ebf310cf2..fc508b5e7d8852 100644 --- a/packages/mui-material/src/Dialog/Dialog.d.ts +++ b/packages/mui-material/src/Dialog/Dialog.d.ts @@ -140,6 +140,13 @@ export interface DialogProps * If `true`, the component is shown. */ open: ModalProps['open']; + /** + * The ARIA role for the dialog element. + * The main dialog role is `dialog`, but `alertdialog` can be used if the content of the dialog requires immediate attention. + * See https://www.w3.org/TR/wai-aria-1.2/#dialog and https://www.w3.org/TR/wai-aria-1.2/#alertdialog for more details. + * @default 'dialog' + */ + role?: 'dialog' | 'alertdialog' | undefined; /** * The component used to render the body of the dialog. * @default Paper diff --git a/packages/mui-material/src/Dialog/Dialog.js b/packages/mui-material/src/Dialog/Dialog.js index d46bd86dd0a482..a650e75499debd 100644 --- a/packages/mui-material/src/Dialog/Dialog.js +++ b/packages/mui-material/src/Dialog/Dialog.js @@ -239,6 +239,7 @@ const Dialog = React.forwardRef(function Dialog(inProps, ref) { onClose, open, PaperComponent = Paper, + role = 'dialog', scroll = 'paper', slots = {}, slotProps = {}, @@ -356,7 +357,7 @@ const Dialog = React.forwardRef(function Dialog(inProps, ref) { ; +} + function Custom(props: DialogProps) { const { slotProps, ...other } = props; return ( diff --git a/packages/mui-material/src/Dialog/Dialog.test.js b/packages/mui-material/src/Dialog/Dialog.test.js index ec851102200a45..81424bd9e98274 100644 --- a/packages/mui-material/src/Dialog/Dialog.test.js +++ b/packages/mui-material/src/Dialog/Dialog.test.js @@ -380,6 +380,18 @@ describe('', () => { expect(label).to.have.text('Choose either one'); }); + it('should be described by another element', () => { + render( + +

Choose either one

+
Actually no
+
, + ); + + const dialog = screen.getByRole('dialog'); + expect(dialog).to.have.attr('aria-describedby', 'dialog-description'); + }); + it('should add the aria-modal="true" by default', function test() { render(); @@ -393,6 +405,12 @@ describe('', () => { const dialog = screen.getByRole('dialog'); expect(dialog).to.have.attr('aria-modal', 'false'); }); + + it('should override the role if provided', function test() { + render(); + + expect(screen.getByRole('alertdialog')).not.to.equal(null); + }); }); describe('prop: transitionDuration', () => { diff --git a/packages/mui-material/src/DialogTitle/DialogTitle.test.js b/packages/mui-material/src/DialogTitle/DialogTitle.test.js index cdc77374f19b5d..067632f36d5ffc 100644 --- a/packages/mui-material/src/DialogTitle/DialogTitle.test.js +++ b/packages/mui-material/src/DialogTitle/DialogTitle.test.js @@ -31,6 +31,13 @@ describe('', () => { screen.getByText('Hello'); }); + it('should render the title as an h2 by default', () => { + render(foo); + + const title = screen.getByRole('heading', { name: 'foo', level: 2 }); + expect(title.tagName).to.equal('H2'); + }); + describe('prop: id', () => { it('should apply the id attribute provided to the Dialog title', () => { render(