-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
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
[Menulist] Add autoFocusItem for initial focus control #17571
Conversation
@material-ui/core: parsed: +0.11% , gzip: +0.10% Details of bundle changes.Comparing: f25c643...7552485
|
Depending on the outcome of the discussion in #17539, this PR would ideally implement a proper roving tabindex in |
Here's a workaround we applied to import { MenuListProps as MUIMenuListProps } from "@material-ui/core/MenuList";
import React from "react";
import List from "../List";
// TODO: Remove wrapper and export `MUIMenuList` directly once a11y issues are resolved:
// https://github.com/mui-org/material-ui/issues/16644
const MenuList: React.FC<MUIMenuListProps> = ({ children, ...other }) => {
const menuListRef = React.useRef<HTMLUListElement>(null);
const handleKeyDown = (event: React.KeyboardEvent<any>): void => {
// See https://www.w3.org/TR/wai-aria-practices-1.1/#TreeView.
const nodes = Array.from(menuListRef.current!.children) as HTMLElement[];
let index = nodes.findIndex(el => el.contains(document.activeElement));
if (event.key === "ArrowDown") {
if (++index >= nodes.length) {
index = 0;
}
nodes[index].focus();
event.preventDefault();
} else if (event.key === "ArrowUp") {
if (--index < 0) {
index = nodes.length - 1;
}
nodes[index].focus();
event.preventDefault();
} else if (event.key === "Home") {
nodes[0].focus();
event.preventDefault();
} else if (event.key === "End") {
nodes[nodes.length - 1].focus();
event.preventDefault();
}
};
const handleFocus = (): void => {
// Implement a "roving index" so that only the current item (and its
// secondary actions) are focusable via Tab and Shift+Tab.
const nodes = Array.from(menuListRef.current!.children) as HTMLElement[];
for (const node of nodes) {
node.tabIndex = node.contains(document.activeElement) ? 0 : -1;
}
};
React.useLayoutEffect(() => {
if (menuListRef.current) {
(menuListRef.current.firstChild as HTMLElement).tabIndex = 0;
}
}, []);
return (
<List {...other} onFocus={handleFocus} onKeyDown={handleKeyDown} ref={menuListRef}>
{children}
</List>
);
};
export * from "@material-ui/core/MenuList";
export default MenuList; |
Please be careful with the wording here. You assume it implements
A menu does not need a roving tabindex. This is not to say we will not work on a Listbox solution. We need to find out where we would need this first and decide on a design. I only want to prevent false claims about a11y compliance if they are based on missing roving tab index |
Fair enough! If you guys are still open to a keyboard accessible list component that would be great. Perhaps the docs should better clarify the intention? |
I'll add a comment that this component is intended to be used as a I still don't understand what you mean by "accessible list component". If you place focus manually on one of the items then it does implement keyboard navigation according to https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-links.html. |
8c9266c
to
2d7577c
Compare
@ianschmitz Me neither. It would be great if you could link to a section in the WAI-ARIA about the feature you are looking for. Notice that React seems to work on similar problems, under the React-interactions, formerly react-ui effort (a name they save for later, based on facebook/react#16842). Also notice that I had to implement a list box with non-DOM focus (active descendant) for the autocomplete, ComboBox, MultiSelect effort #17037. |
I think listbox is the closest to what I was describing. Here's a demo: https://codesandbox.io/s/material-demo-b1z97. However in the case of the left navigation menu, the tree component may actually be desirable if you have nested menus. The left navigation bar in MUI's docs would be a great example of that. Sorry i haven't been doing a great job explaining myself 😉 |
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 think we should start with a separate component for a listbox first (because it requires persisting the active item) and then see if it makes sense to deduplicate some logic. |
Closes #17539
<MenuList autoFocusItem />
will focus the selected or first menuitem when mounting or changing the prop. It's used to manage focus in https://material-ui.com/components/menus/#menulist-composition.