Skip to content

Commit 0229bf0

Browse files
fix(menu): focus first item when opened (#13277)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent ad3ca2f commit 0229bf0

File tree

2 files changed

+36
-23
lines changed

2 files changed

+36
-23
lines changed

packages/react/src/components/Menu/Menu.js

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const Menu = React.forwardRef(function Menu(
6767

6868
const [position, setPosition] = useState([-1, -1]);
6969
const focusableItems = childContext.state.items.filter(
70-
(item) => !item.disabled
70+
(item) => !item.disabled && item.ref.current
7171
);
7272

7373
function returnFocus() {
@@ -80,7 +80,6 @@ const Menu = React.forwardRef(function Menu(
8080
if (menu.current) {
8181
focusReturn.current = document.activeElement;
8282
setPosition(calculatePosition());
83-
menu.current.focus();
8483
}
8584
}
8685

@@ -101,11 +100,6 @@ const Menu = React.forwardRef(function Menu(
101100
function handleKeyDown(e) {
102101
e.stopPropagation();
103102

104-
const currentItem = focusableItems.findIndex((item) =>
105-
item.ref.current.contains(document.activeElement)
106-
);
107-
let indexToFocus = currentItem;
108-
109103
// if the user presses escape or this is a submenu
110104
// and the user presses ArrowLeft, close it
111105
if (
@@ -114,28 +108,39 @@ const Menu = React.forwardRef(function Menu(
114108
) {
115109
handleClose(e);
116110
} else {
117-
// if currentItem is -1, the menu itself is focused.
118-
// in this case, the arrow keys define the first item
119-
// to be focused.
111+
focusItem(e);
112+
}
113+
}
114+
115+
function focusItem(e) {
116+
const currentItem = focusableItems.findIndex((item) =>
117+
item.ref.current.contains(document.activeElement)
118+
);
119+
let indexToFocus = currentItem;
120+
121+
// if currentItem is -1, no menu item is focused yet.
122+
// in this case, the first item should receive focus.
123+
if (currentItem === -1) {
124+
indexToFocus = 0;
125+
} else if (e) {
120126
if (match(e, keys.ArrowUp)) {
121-
indexToFocus =
122-
currentItem === -1 ? focusableItems.length - 1 : indexToFocus - 1;
127+
indexToFocus = indexToFocus - 1;
123128
}
124129
if (match(e, keys.ArrowDown)) {
125-
indexToFocus = currentItem === -1 ? 0 : indexToFocus + 1;
130+
indexToFocus = indexToFocus + 1;
126131
}
132+
}
127133

128-
if (indexToFocus < 0) {
129-
indexToFocus = 0;
130-
}
131-
if (indexToFocus >= focusableItems.length) {
132-
indexToFocus = focusableItems.length - 1;
133-
}
134+
if (indexToFocus < 0) {
135+
indexToFocus = focusableItems.length - 1;
136+
}
137+
if (indexToFocus >= focusableItems.length) {
138+
indexToFocus = 0;
139+
}
134140

135-
if (indexToFocus !== currentItem) {
136-
const nodeToFocus = focusableItems[indexToFocus];
137-
nodeToFocus.ref.current.focus();
138-
}
141+
if (indexToFocus !== currentItem) {
142+
const nodeToFocus = focusableItems[indexToFocus];
143+
nodeToFocus.ref.current.focus();
139144
}
140145
}
141146

@@ -198,6 +203,13 @@ const Menu = React.forwardRef(function Menu(
198203
return [-1, -1];
199204
}
200205

206+
useEffect(() => {
207+
if (open && focusableItems.length > 0) {
208+
focusItem();
209+
}
210+
// eslint-disable-next-line react-hooks/exhaustive-deps
211+
}, [open, focusableItems]);
212+
201213
useEffect(() => {
202214
if (open) {
203215
handleOpen();

packages/react/src/components/Menu/MenuItem.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ const MenuItem = React.forwardRef(function MenuItem(
102102
function handleKeyDown(e) {
103103
if (hasChildren && match(e, keys.ArrowRight)) {
104104
openSubmenu();
105+
e.stopPropagation();
105106
}
106107

107108
if (match(e, keys.Enter) || match(e, keys.Space)) {

0 commit comments

Comments
 (0)