react-aria-menubutton

A React component (set of components, really) that will help you build accessible menu buttons by providing keyboard interactions and ARIA attributes aligned with the WAI-ARIA Menu Button Design Pattern.

Project Goals

Full accessibility

Maximum flexibility

Absolutely minimal styling

Thorough testing

Useful modularity

"Useful modularity" means that when it makes sense, chunks of lower-level code that solve specific problems are split off into vanilla JS, framework-agnostic modules that could be shared with other similar projects (e.g. a menu button for Angular or Ember).

Accessibility

Just hiding and showing a menu is easy; but the required keyboard interactions are kind of tricky, the required ARIA attributes are easy to forget, and some other aspects of opening and closing the menu based on behaviors, and managing focus, proved less than pleasant.
So I decided to try to abstract the component enough that it would be worth sharing with others.

If you think that this component could be even more accessible, please file an issue.

Letter Navigation

When focus is on the menu button or within the menu and you type a letter key, a search begins. Focus will move to the first item that starts with the letter you typed; but if you continue to type more letters, the search string extends and the focus becomes more accurate.

So if you type f focus might arrive at farm; but then if you keep typing until you've typed foo, focus will skip ahead (past farm and fit and fog) to foot. This significantly improves your ability to type your way to your intended selection.

This keyboard interaction (as well as the arrow keys) is enabled by the module focus-group. You can read more about the way letter navigation works in that documentation.

(In 3.x.x, when you typed a letter key focus moved to the next item in the menu (i.e. after the current focused item) that started with that letter, looping around to the front if if reached the end. This was more or less the suggested behavior from the ARIA suggestion and what I saw in jQuery UI. But I think the UX was insufficient, so when I separated out the letter navigation into the module focus-group, I tried to *improve letter navigation by more closely mimicking native <select> menus.)

Please file an issue if anything is unclear or doesn't work as expected.

Flexibility and minimal styling

Instead of providing a pre-fabricated, fully styled widget, this module's goal is to provide a set of components that others can build their own stuff on top of.

It does not provide any classes or a stylesheet that you'll have to figure out how to include; and it does not include inline styles that would be hard to override. It only provides "smart" components to wrap your (dumb, styled) components. The library's components take care of keyboard interaction and ARIA attributes, while your components just do whatever you want your components to do.

Installation

npm install react-aria-menubutton

The modular approach of this library means you're much better off building it into your code with a module bundling system like browserify or webpack.

But if you need a UMD version (which will include focus-group and teeny-tap in the bundle, but of course not React or ReactDOM), you can get it via npmcdm at https://unpkg.com/react-aria-menubutton@[version-of-choice]/umd/ReactAriaMenuButton.js.
If you don't know about unpkg, read about it here.

API

The module exposes four components: Wrapper, Button, Menu, and MenuItem. Each of these is documented below.

Button, Menu, and MenuItem must always be wrapped in a Wrapper.

Wrapper

A simple component to group a Button/Menu/MenuItem set, coordinating their interactions.
It should wrap your entire menu button widget.

All Button, Menu, and MenuItem components must be nested within a Wrapper component.

Each wrapper can contain only oneButton, only oneMenu, and multipleMenuItems.

props

All props except onSelection, are optional.

onSelection { Function }: Required. A callback to run when the user makes a selection (i.e. clicks or presses Enter or Space on a MenuItem). It will be passed the value of the selected MenuItem and the React SyntheticEvent.