Improve keyboard interaction of the DropdownMenu#1975
Conversation
|
Fixes #1880 |
components/dropdown-menu/index.js
Outdated
| if ( this.menuRef ) { | ||
| const menu = findDOMNode( this.menuRef ); | ||
| if ( this.menuNode ) { | ||
| if ( index < 0 ) { |
There was a problem hiding this comment.
With changes to focusPrevious, is it ever the case that this condition would pass? Also generally concerned about previousElementSibling potentially not being a focusable element. Might be worth dropping this.
components/dropdown-menu/index.js
Outdated
|
|
||
| this.maybeCloseMenuOnBlur = setTimeout( () => { | ||
| this.closeMenu(); | ||
| }, 100 ); |
There was a problem hiding this comment.
Completely arbitrary (I know...). There's a brief interval between blur on an element and focus on the following element. '0' didn't work. On the W3C examples, they use '300', see:
https://www.w3.org/TR/wai-aria-practices/examples/menu-button/js/Menubutton.js
https://www.w3.org/TR/wai-aria-practices/examples/menu-button/js/MenuItemAction.js
https://www.w3.org/TR/wai-aria-practices/examples/menu-button/js/PopupMenuAction.js
Open to suggestions...
components/dropdown-menu/index.js
Outdated
| this.closeMenu(); | ||
| const node = findDOMNode( this ); | ||
| const toggle = node.querySelector( '.components-dropdown-menu__toggle' ); | ||
| toggle.focus(); |
There was a problem hiding this comment.
I'm finding that occasionally pressing escape when navigating buttons will clear focus altogether (document.activeElement === document.body). Not sure if that's specific to your changes here though.
macOS Chrome 59.0.3071.115
There was a problem hiding this comment.
Same chrome here and can't reproduce. Updated to 60: same.
Instead, I think handleKeyUp() doesn't work as intended: when I keep Escape pressed for a while, the menu closes immediately so when I release Escape the condition to stop propagation isn't met and the toolbar disappears.
components/dropdown-menu/index.js
Outdated
| ref={ this.bindMenuReferenceNode } | ||
| onFocus={ this.handleFocus } | ||
| onBlur={ this.handleBlur } | ||
| tabIndex="-1" |
|
Using keyup to close the menu when pressing Escape allows to greatly simplify. No timers, no focus/blur. To recap:
|
components/dropdown-menu/index.js
Outdated
| if ( event.keyCode === ESCAPE && this.state.open ) { | ||
| event.preventDefault(); | ||
| event.stopPropagation(); | ||
| const node = findDOMNode( this ); |
There was a problem hiding this comment.
We could probably create a separate ref for the root rendered element to avoid the findDOMNode here.
components/dropdown-menu/index.js
Outdated
| event.preventDefault(); | ||
| event.stopPropagation(); | ||
| const node = findDOMNode( this ); | ||
| const toggle = node.querySelector( '.components-dropdown-menu__toggle' ); |
There was a problem hiding this comment.
Actually, for that matter, we could bind the ref to the toggle to avoid the querySelector as well.
|
Would be good to have some unit tests for this component, but that can be addressed separately I think. |
ec8272a to
ed9cd44
Compare
Codecov Report
@@ Coverage Diff @@
## master #1975 +/- ##
==========================================
+ Coverage 20.25% 20.49% +0.24%
==========================================
Files 131 135 +4
Lines 4207 4240 +33
Branches 716 728 +12
==========================================
+ Hits 852 869 +17
- Misses 2827 2839 +12
- Partials 528 532 +4
Continue to review full report at Codecov.
|
|
In e91f3a2, I revised |
When used on a component class, the value of a ref is the component instance, not the DOM node. https://facebook.github.io/react/docs/refs-and-the-dom.html#adding-a-ref-to-a-class-component
This PR tries to improve the accessibility and keyboard interaction of the DropdownMenu following the model described in the ARIA Authoring Practices.
The DropdownMenu is, in ARIA terms, a composite widget made of a Menu button and a Menu. To get how the expected keyboard interaction should work I'd recommend to jump directly to the W3C examples for the standalone Menu:
https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-actions.html
and the same example in the context of a Toolbar:
https://www.w3.org/TR/wai-aria-practices/examples/toolbar/toolbar.html
Navigation through the menu items should work with arrow keys only, while tabbing should close the menu (same for Escape). Optionally, items navigation should "loop" continuously.
Worth reminding ARIA widgets replicate common, established patterns already used in our operating systems and keyboard users are used to them. For example, the macOS Finder menu works this way: it uses arrow keys to navigate the drop-down items:
The mixed use of the Tab key and arrow keys is a great way to reduce the amount of Tab key presses necessary to navigate through content, as it allows to open a menu just if that's really needed.
As mentioned in the issue, it would be great to consider to extend this behavior to other components too.
Fixes #1880