-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[Popover] Prevent setting a11y attributes when activator disabled #2473
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
Conversation
8309ed4
to
2170080
Compare
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.
Nice catch 😄
const focusableActivator = firstFocusable || activatorContainer.current; | ||
setActivatorAttributes(focusableActivator, {id, active, ariaHaspopup}); | ||
}, [active, ariaHaspopup, id]); | ||
setActivatorAttributes(focusableActivator, { |
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.
Rather than adding an activatorDisabled
prop, what do you think about deriving the value rather than explicitly setting it?
// HTMLElement doesn't contain `disabled` in its interface
// We could alternatively use instanceof `focusableActivator instnaceof HTMLButton && focusableActivator.disabled`
const activatorDisabled = (focusableActivator as HTMLButtonElement).disabled;
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.
The reason I ended up going the prop route is that the Popover
can't expect/know what kind of element its activator is or whether it's even focusable. The above snippet would work, but we'd need to chain all other possibilities to that conditional (it could be a textfield, etc). Since disabling a Button
, TextField
etc is done explicitly, just telling the Popover
that the activator, whatever it may be, is disabled explicitly is a lot easier to test.
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 wasn't aware that activators can be non-focusable. Maybe this is something we should try and enforce through types. Seems like a huge accessibility issue to not be able to open a popover through keyboard interactions 😬 cc/ @dpersing since your assigned as well, what do you think?
Using HTMLButtonElement
type was just a quick example. There's other ways we can achieve the same type safety that'll work on any disablable element e.g.
const focusableActivator: HTMLElement & {disabled?: boolean} =
firstFocusable || activatorContainer.current;
const activatorDisabled =
'disabled' in focusableActivator && Boolean(focusableActivator.disabled);
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.
Unfortunately adding the typing alone doesn't help. The real source of the problem is the findFirstFocusableNode
utility because it eliminates disabled
elements as focusable selectors.
Modifying the FOCUSABLE_SELECTOR
s to remove the :not(disabled)
in @shopify/javascript-utilities
could cause unexpected breaking changes for other consumers of the utility, so I've updated the PR to use our own version.
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.
The attribute assigning situation looks great!
One note on the default collapsed state of the popover button: the value of the aria-expanded
attribute is currently undefined
on load. It should be collapsed
. Without a valid value, the fact that the button expands/collapses isn't conveyed when it is read, so users won't know what to expect.
<button id="Activator-fileType" type="button" class="Button-Button_2pL_i" tabindex="0" aria-controls="Polarispopover10" aria-owns="Polarispopover10" aria-expanded="undefined">...</button>
0933908
to
1f2c6f3
Compare
Issue addressed (added missing default value of false for aria-expanded)
1f2c6f3
to
271c2ea
Compare
{ | ||
id, | ||
active, | ||
active = false, |
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.
@dpersing Adding a default value for the active
prop prevents aria-expanded
being defined as undefined
on line 18/24.
e84c9ea
to
88090f0
Compare
[Popover] Only set accessibility attributes when activatorDisabled is false [Popover] update change log [Popover] Support finding disabled focusable node
88090f0
to
ea4b453
Compare
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.
💯
WHY are these changes introduced?
Currently, the
Popover
sets accessibility attributes on either the activator (if focusable) or the element it wraps the activator with. If the activator is not focusable because it is disabled, the activator's wrapper gets these attributes. This is a problem because the activator wrapper is adiv
(unless theactivatorWrapper
prop is set to something else) and thediv
having atabIndex="-1"
makes it focusable ironically!A good example of this can be seen in the "All filters disabled"
Filters
example. When viewing the playground code onmaster
, you can view the unwanted attributes being set on and staying set on the wrapperdiv
after toggling the disabled state of the filters.WHAT is this pull request doing?
findFirstFocusableNode
utility that doesn't eliminate disabled elements.tabIndex
from being set on the activator when the activator is disabledHow to 🎩
🖥 Local development instructions
🗒 General tophatting guidelines
📄 Changelog guidelines
/playground/Playground.tsx
master
yarn dev
tabindex="-1"
on the wrapperdiv
as well as the other accessibility attributes.button
is no longer disabled it now has all of the expected accessibility attributes, but you'll see that the wrapperdiv
still has all of the accessibility attributes on it as well.git checkout popover-activatorDisabled
div
Click to view collapsed example code
🎩 checklist
README.md
with documentation changes