From 1c108c20e3f426381d9f25ad9286829a6e05341c Mon Sep 17 00:00:00 2001 From: yoyo837 Date: Mon, 9 Nov 2026 14:18:01 +0800 Subject: [PATCH 1/2] fix: block interactions with disabled submenus --- src/SubMenu/index.tsx | 1 + src/hooks/useAccessibility.ts | 6 ++++ tests/DisabledKeyboard.spec.tsx | 58 +++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/SubMenu/index.tsx b/src/SubMenu/index.tsx index a40340e9..daf013f9 100644 --- a/src/SubMenu/index.tsx +++ b/src/SubMenu/index.tsx @@ -382,6 +382,7 @@ const InternalSubMenu = React.forwardRef((props, re ( tryFocus(targetElement); // ======================= InlineTrigger ======================= + } else if ( + (offsetObj.inlineTrigger || offsetObj.offset > 0) && + focusMenuElement.getAttribute('aria-disabled') === 'true' + ) { + originOnKeyDown?.(e); + return; } else if (offsetObj.inlineTrigger) { // Inline trigger no need switch to sub menu item triggerAccessibilityOpen(focusMenuKey); diff --git a/tests/DisabledKeyboard.spec.tsx b/tests/DisabledKeyboard.spec.tsx index aac1b572..97e36cb1 100644 --- a/tests/DisabledKeyboard.spec.tsx +++ b/tests/DisabledKeyboard.spec.tsx @@ -1,7 +1,7 @@ -import { fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render } from '@testing-library/react'; import { KeyCode } from '@rc-component/util'; import React from 'react'; -import Menu, { MenuItem } from '../src'; +import Menu, { MenuItem, SubMenu } from '../src'; describe('Disabled menu keyboard selection', () => { it('ignores Enter after a focused item becomes disabled', () => { @@ -25,3 +25,57 @@ describe('Disabled menu keyboard selection', () => { expect(onSelect).toHaveBeenCalledTimes(1); }); }); + +describe('Disabled submenus', () => { + it.each(['inline', 'vertical'] as const)( + 'ignores keyboard opening after a focused %s submenu becomes disabled', + mode => { + const onOpenChange = jest.fn(); + const menu = (disabled: boolean) => ( + + + Child + + + ); + const { getByRole, rerender } = render(menu(false)); + const submenu = getByRole('menuitem', { name: 'Parent' }); + act(() => submenu.focus()); + rerender(menu(true)); + + const key = mode === 'inline' ? KeyCode.ENTER : KeyCode.RIGHT; + fireEvent.keyDown(submenu, { which: key, keyCode: key }); + expect(onOpenChange).not.toHaveBeenCalled(); + + rerender(menu(false)); + fireEvent.keyDown(submenu, { which: key, keyCode: key }); + expect(onOpenChange).toHaveBeenCalledWith(['parent']); + }, + ); + + it.each(['inline', 'vertical'] as const)( + 'blocks selection in an open %s submenu while it is disabled', + async mode => { + const onSelect = jest.fn(); + const menu = (disabled: boolean) => ( + + + Child + + + ); + const { findByRole, rerender } = render(menu(false)); + const item = await findByRole('menuitem', { name: 'Child' }); + act(() => item.focus()); + rerender(menu(true)); + + fireEvent.click(item); + fireEvent.keyDown(item, { which: KeyCode.ENTER, keyCode: KeyCode.ENTER }); + expect(onSelect).not.toHaveBeenCalled(); + + rerender(menu(false)); + fireEvent.click(item); + expect(onSelect).toHaveBeenCalledTimes(1); + }, + ); +}); From aeed0b3cc7561045d4b10533061b62a0006a66eb Mon Sep 17 00:00:00 2001 From: yoyo837 Date: Mon, 9 Nov 2026 16:16:59 +0800 Subject: [PATCH 2/2] test: guard open keys when a submenu becomes disabled --- tests/DisabledKeyboard.spec.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/DisabledKeyboard.spec.tsx b/tests/DisabledKeyboard.spec.tsx index 97e36cb1..6249cf12 100644 --- a/tests/DisabledKeyboard.spec.tsx +++ b/tests/DisabledKeyboard.spec.tsx @@ -57,8 +57,9 @@ describe('Disabled submenus', () => { 'blocks selection in an open %s submenu while it is disabled', async mode => { const onSelect = jest.fn(); + const onOpenChange = jest.fn(); const menu = (disabled: boolean) => ( - + Child @@ -72,6 +73,7 @@ describe('Disabled submenus', () => { fireEvent.click(item); fireEvent.keyDown(item, { which: KeyCode.ENTER, keyCode: KeyCode.ENTER }); expect(onSelect).not.toHaveBeenCalled(); + expect(onOpenChange).not.toHaveBeenCalled(); rerender(menu(false)); fireEvent.click(item);