diff --git a/src/Menu.tsx b/src/Menu.tsx index d582eb86..be2cab34 100644 --- a/src/Menu.tsx +++ b/src/Menu.tsx @@ -506,6 +506,12 @@ const Menu = React.forwardRef((props, ref) => { onInternalOpenChange(key, nextOpen); }; + const triggerAccessibilityClose = () => { + if (mergedOpenKeys.length) { + triggerOpenKeys(EMPTY_LIST, true); + } + }; + const onInternalKeyDown = useAccessibility( internalMode, mergedActiveKey, @@ -518,6 +524,7 @@ const Menu = React.forwardRef((props, ref) => { setMergedActiveKey, triggerAccessibilityOpen, + triggerAccessibilityClose, onKeyDown, ); diff --git a/src/hooks/useAccessibility.ts b/src/hooks/useAccessibility.ts index 259130cb..0938b3be 100644 --- a/src/hooks/useAccessibility.ts +++ b/src/hooks/useAccessibility.ts @@ -174,6 +174,7 @@ export function useAccessibility( triggerActiveKey: (key: string) => void, triggerAccessibilityOpen: (key: string, open?: boolean) => void, + triggerAccessibilityClose: () => void, originOnKeyDown?: React.KeyboardEventHandler, ): React.KeyboardEventHandler { @@ -196,6 +197,12 @@ export function useAccessibility( return e => { const { which } = e; + if (which === ESC && mode !== 'inline' && e.target === containerRef.current) { + triggerAccessibilityClose(); + originOnKeyDown?.(e); + return; + } + if ([...ArrowKeys, ENTER, ESC, HOME, END].includes(which)) { const keys = getKeys(); diff --git a/tests/SubMenu.spec.tsx b/tests/SubMenu.spec.tsx index 8f75f620..69ce358c 100644 --- a/tests/SubMenu.spec.tsx +++ b/tests/SubMenu.spec.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-undef */ import { act, fireEvent, render } from '@testing-library/react'; -import { resetWarned } from '@rc-component/util'; +import { KeyCode, resetWarned } from '@rc-component/util'; import React from 'react'; import Menu, { MenuItem, SubMenu } from '../src'; import { isActive, last } from './util'; @@ -203,6 +203,28 @@ describe('SubMenu', () => { }); expect(container.querySelector('.rc-menu-submenu-open')).toBeFalsy(); }); + + it('closes open submenus when Escape is pressed on the root menu', () => { + const onOpenChange = jest.fn(); + const { container } = render( + createMenu({ + triggerSubMenuAction: 'click', + onOpenChange, + }), + ); + + fireEvent.click(container.querySelector('.rc-menu-submenu-title')); + runAllTimer(); + expect(container.querySelector('.rc-menu-submenu-open')).toBeTruthy(); + + const rootMenu = container.querySelector('.rc-menu-root'); + rootMenu.focus(); + fireEvent.keyDown(rootMenu, { keyCode: KeyCode.ESC, which: KeyCode.ESC }); + runAllTimer(); + + expect(onOpenChange).toHaveBeenLastCalledWith([]); + expect(container.querySelector('.rc-menu-submenu-open')).toBeFalsy(); + }); }); it('fires openChange event', () => {