-
Notifications
You must be signed in to change notification settings - Fork 844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG][EuiModal] Fix VoiceOver + Safari escaping focus trap #7564
[BUG][EuiModal] Fix VoiceOver + Safari escaping focus trap #7564
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two clarifying comments into my "why".
src/components/modal/modal.tsx
Outdated
* Identifies a modal dialog to screen readers. Modal dialogs that confirm destructive actions | ||
* or need a user's attention should use "alertdialog". | ||
*/ | ||
ariaRole?: 'dialog' | 'alertdialog'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this b/c there are a handful of cases where I'd like the modal to break user flow and announce itself immediately. Thinking specifically about modals that confirm deleting or removing an object here. By adding it as a new prop and setting a standard default, I thought we could capture the best of both worlds without accidentally regressing the container to a div[role="presentation"]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, is there a specific reason this needs to be named ariaRole
or can we just go with role
to match the DOM attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, bonus/optional question: is there ever a situation where consumers would need to unset the role
or set a non dialog role? I'm leaning towards no, but just wanted to at least raise the question and get people's thoughts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this b/c there are a handful of cases where I'd like the modal to break user flow and announce itself immediately. Thinking specifically about modals that confirm deleting or removing an object here
Should we should go ahead and add this prop to EuiConfirmModal
as well with a default of alertdialogue
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No specific reason to name it ariaRole
. I was thinking role
at first but talked myself out of it. I like your point that role
is the HTML attribute name, and changing to that feels fitting.
I also like the suggestion to make EuiConfirmModal
default to alertdialog
. I'll update and retest those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead and added aria-label
attributes to each example in docs. The axe-core plugin was emphasizing elements with role="dialog | alertdialog"
should have accessible labels. Ideally we enforce having an EuiModalHeader
and title in each modal, but that's increasing scope more than the original PR set out to do.
@@ -22,6 +22,7 @@ export default () => { | |||
if (isModalVisible) { | |||
modal = ( | |||
<EuiConfirmModal | |||
aria-label="EuiModal confirm example" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused about the aria-label examples that you added. These don't feel like useful production examples to me that a developer would be able to extrapolate from and write something themselves for their own modals.
- Why would the aria-label for the modal be different from the title? Should we not use
aria-labelledby
instead and point that at the title? - If the modal label should be different from the title, then let's make the copy actually specific to the modal intent. e.g., for this one, something like
Confirm subscription
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally the accessible label uses the title and an ID to create an aria-labelledby
situation. Looking at the code as it sits, we can't count on a title always being present, at least not in the basic EuiModal
. The confirmation modal is a bit easier to reason about where we're checking if the title
prop is present.
I'm at a crossroads what is the best approach here. I can either keep this one scoped tightly to fix the immediate problem of VoiceOver escaping the modal and make a follow on item to improve accessible labels, or widen the scope of this issue and possibly it becomes a breaking change. Either seems an acceptable path forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When in doubt, always keep the scope smaller :)
I refactored the examples to use explicit IDs and |
- already inherited from `HTMLAttributes`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@1Copenut Code changes LGTM at this point - feel free to re-test that the aria-labelledby
works as expected for you in VO/etc before merging!
Preview staging links for this PR:
|
💚 Build Succeeded
History
cc @1Copenut |
UPDATE March 12: Thank you @cee-chen. I'll give this one more pass with screen readers in the morning and merge. |
`v93.3.0`⏩ `v93.4.0` --- ## [`v93.4.0`](https://github.com/elastic/eui/releases/v93.4.0) - Added the following properties to `EuiButtonGroup`'s `options` configs: `toolTipContent`, `toolTipProps`, and `title`. These new properties allow wrapping buttons in `EuiToolTips`, and additionally customizing or disabling the native browser `title` tooltip. ([#7461](elastic/eui#7461)) - Enhanced `EuiResizeObserver` and `useResizeObserver`'s performance to not trigger page reflows on resize event ([#7575](elastic/eui#7575)) - Updated `EuiSuperUpdateButton` to support custom button text via an optional `children` prop ([#7576](elastic/eui#7576)) **Bug fixes** - Fixed `EuiFlyout` to not repeatedly remove/add a body class on resize ([#7462](elastic/eui#7462)) - Fixed `EuiToast` title text to wrap instead of overflowing out of the container ([#7568](elastic/eui#7568)) - Fixed a visual bug with `EuiHeaderBreadcrumbs` with popovers ([#7580](elastic/eui#7580)) **Deprecations** - Deprecated `euiPalettePositive` and `euiPaletteNegative` in favour of a more culturally inclusive `euiPaletteGreen` and `euiPaletteRed` ([#7570](elastic/eui#7570)) - Deprecated all charts theme exports in favor of `@elastic/charts` exports: ([#7572](elastic/eui#7572)) - Deprecated `EUI_CHARTS_THEME_<DARK|LIGHT>` in favor of `<DARK|LIGHT>_THEME` from `@elastic/charts`. ([#7572](elastic/eui#7572)) - Deprecated `EUI_SPARKLINE_THEME_PARTIAL` in favor of `useSparklineOverrides` theme from the kibana `charts` plugin `theme` service. **Accessibility** - Updated `EuiModal` to set an `aria-modal` attribute and a default `dialog` role ([#7564](elastic/eui#7564)) - Updated `EuiConfirmModal` to set a default `alertdialog` role ([#7564](elastic/eui#7564)) - Fixed `EuiModal` and `EuiConfirmModal` to properly trap Safari+VoiceOver's virtual cursor ([#7564](elastic/eui#7564))
Summary
I've recently noticed I'm able to escape the
EuiModal
focus trap to the inert page when using VoiceOver + Safari. This only happens when I'm navigating by virtual cursor chordsCtrl + Opt + ARROW_KEY
, but this is a primary navigation pattern so it needed to be remediated.PR closes #7532 and closes #7563.
QA
Remove or strikethrough items that do not apply to your PR.
General checklist
@default
if default values are missing) and playground togglesIf applicable, added the breaking change issue label (and filled out the breaking change checklist)Screen reader test pairings
All pairings trapped focus correctly. This represents an improved UX for Safari + VO, and no regression for the other pairings.