From e1cad93a2f290857a315e5397f71edced2049bed Mon Sep 17 00:00:00 2001 From: Chase McCoy Date: Tue, 12 May 2026 18:09:11 -0500 Subject: [PATCH] [Menu] Always highlight the first item on open --- .../react/src/menu/root/MenuRoot.test.tsx | 26 ++++++++++++++++++- packages/react/src/menu/root/MenuRoot.tsx | 2 ++ packages/react/src/menubar/Menubar.test.tsx | 4 +-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/react/src/menu/root/MenuRoot.test.tsx b/packages/react/src/menu/root/MenuRoot.test.tsx index 9d01b4a7b16..ad2f3326e7d 100644 --- a/packages/react/src/menu/root/MenuRoot.test.tsx +++ b/packages/react/src/menu/root/MenuRoot.test.tsx @@ -692,7 +692,6 @@ describe('', () => { await user.keyboard('[ArrowDown]'); await user.keyboard('[ArrowDown]'); await user.keyboard('[ArrowDown]'); - await user.keyboard('[ArrowDown]'); const submenuTrigger1 = await screen.findByTestId('submenu-trigger'); await waitFor(() => { @@ -999,6 +998,31 @@ describe('', () => { }); }); + it('focuses the first item when the menu is opened programmatically', async () => { + function App() { + const [open, setOpen] = React.useState(false); + return ( +
+ + +
+ ); + } + + const { user } = await render(); + + await user.click(screen.getByRole('button', { name: 'external' })); + + const [firstItem, ...otherItems] = await screen.findAllByRole('menuitem'); + await waitFor(() => expect(firstItem).toHaveFocus()); + expect(firstItem.tabIndex).toBe(0); + otherItems.forEach((item) => { + expect(item.tabIndex).toBe(-1); + }); + }); + it('focuses the trigger after the menu is closed', async () => { const { user } = await render(
diff --git a/packages/react/src/menu/root/MenuRoot.tsx b/packages/react/src/menu/root/MenuRoot.tsx index ead61132115..933f1268a3f 100644 --- a/packages/react/src/menu/root/MenuRoot.tsx +++ b/packages/react/src/menu/root/MenuRoot.tsx @@ -423,6 +423,8 @@ export const MenuRoot = fastComponent(function MenuRoot(props: MenuRoot openOnArrowKeyDown: parent.type !== 'context-menu', externalTree: nested ? floatingTreeRoot : undefined, focusItemOnHover: highlightItemOnHover, + // Ensure an item is highlighted on open even when opened programmatically (no trigger keydown). + focusItemOnOpen: true, }); const onTyping = React.useCallback( diff --git a/packages/react/src/menubar/Menubar.test.tsx b/packages/react/src/menubar/Menubar.test.tsx index eec2054b247..5a0ec720842 100644 --- a/packages/react/src/menubar/Menubar.test.tsx +++ b/packages/react/src/menubar/Menubar.test.tsx @@ -587,13 +587,11 @@ describe('', () => { const fileTrigger = screen.getByTestId('file-trigger'); await user.click(fileTrigger); - // Menu should be open + // Menu should be open with the first item focused await waitFor(() => { expect(screen.queryByTestId('file-menu')).not.toBe(null); }); - // Navigate with keyboard - await user.keyboard('{ArrowDown}'); const firstItem = screen.getByTestId('file-item-1'); await waitFor(() => { expect(firstItem).toHaveFocus();