fix(#2410): modal open accessibility
Before (the change)
When modal opened it scrolls down to the first focusable element inside the dialog (play with sound on):
After (the change)
When modal opens it does not scroll down because, ~it focuses to an empty element at top of dialog~ it focuses on modal container that's labelled by modal header (sound on):
Make sure that you've checked the boxes below before you submit the PR
- [x] I have read and followed the setup steps
- [x] I have created necessary unit tests
- [x] I have tested the functionality in both React and Angular.
Other info
- When modal is open Tab forwards or backwards remains within focus trap
- Screenreader reads inside "alertdialog" until first focusable element: #1689
- Recommendation from WAI-W3C for dialog accessibility is when dialog opens is to either a) set focus to first focusable element inside the dialog, or b) set focus to an element that has
tabindex="-1"near beginning of dialog. We had previously went with the former recommendation but, this was causing a problem in a Modal with lots of content which needed to scroll to first focusable element. It still works this way for "alertdialog" but for "dialog" role the screenreader will read out the dialog's aria-label (the header content, if there is any) then stops. - Previously there was logic to allow skipping elements after the first focus, such as on a close Modal "x" icon button. These were misleadingly named "ignore-focus" and now "first-focus" is used. There were changes to focus trap, currently used by Modal and Popover component. There was an accessibility problem with that previous logic because, even when "x" close button in header was skipped and it would set focus to next focusable element, a screenreader would still stop reading aloud when it reached the "x" close button.
Steps needed to test
Code snippet provided in original issue can be used for testing a modal that has first focusable element appearing after lots of content.
https://github.com/user-attachments/assets/c1cd2307-33bd-48c3-85f2-fba0c994a52c
It fixed the bug but I recognize one new point.
From the beginning to the 0:34s of the above video, is using this PR's version to test.
For the Basic Modal, role=dialog, the screen reader reads everything:
"Heading
clickable button Close the modal
Content
button unavailable Secondary
button unavailable Primary
"
While on the production site, it reads everything except "Content"
For the AlertDialog modal, role = alertdialog, the screen reader behaves the same (read everything, for Delete Application example)
To sum up, this PR changed the behaviour of role dialog and alertdialog , it behaves the same. I talked to @BumbleB2na offline, and we can involve @twjeffery to get his insights on this.
@twjeffery suggestion for a follow up issue that would introduce a breaking change:
- if role is "alertdialog" then prevent allowing it to be closeable so that screenreader will continue read until first focusable element. Ideally that first focusable element is a button in the actions slot where user can confirm they understand or is given a choice, but we give developers control over what goes in there. It's a breaking change because developers may have been relying on their "alertdialog" to be closable with the "x" icon button.
@BumbleB2na Remember, if a PR is blocked to move it back to draft mode