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..6249cf12 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,59 @@ 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 onOpenChange = 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(); + expect(onOpenChange).not.toHaveBeenCalled(); + + rerender(menu(false)); + fireEvent.click(item); + expect(onSelect).toHaveBeenCalledTimes(1); + }, + ); +});