From d5035db0c42d3e80f4ac61710109059a469d4666 Mon Sep 17 00:00:00 2001 From: Elliott Marquez Date: Wed, 21 Jun 2023 12:07:10 -0700 Subject: [PATCH] fix(menu): close menu when escape is pressed on list root PiperOrigin-RevId: 542325162 --- menu/lib/menu.ts | 8 +++++++- menu/menu_test.ts | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/menu/lib/menu.ts b/menu/lib/menu.ts index dfdda8df2b..e0690a9689 100644 --- a/menu/lib/menu.ts +++ b/menu/lib/menu.ts @@ -18,7 +18,7 @@ import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js'; import {createAnimationSignal, EASING} from '../../internal/motion/animation.js'; import {List} from '../../list/lib/list.js'; -import {ActivateTypeaheadEvent, DeactivateTypeaheadEvent, isElementInSubtree, MenuItem} from './shared.js'; +import {ActivateTypeaheadEvent, DeactivateTypeaheadEvent, isClosableKey, isElementInSubtree, MenuItem} from './shared.js'; import {Corner, SurfacePositionController, SurfacePositionTarget} from './surfacePositionController.js'; import {TypeaheadController} from './typeaheadController.js'; @@ -340,6 +340,12 @@ export abstract class Menu extends LitElement { // and we don't want the menu item to close the menu. @eventOptions({capture: true}) private handleListKeydown(e: KeyboardEvent) { + if (e.target === this.listElement && !e.defaultPrevented && + isClosableKey(e.code)) { + e.preventDefault(); + this.close(); + } + this.typeaheadController.onKeydown(e); } diff --git a/menu/menu_test.ts b/menu/menu_test.ts index 58ed453e5a..b2c254e62f 100644 --- a/menu/menu_test.ts +++ b/menu/menu_test.ts @@ -5,6 +5,9 @@ */ // import 'jasmine'; (google3-only) +import './menu.js'; + +import {html, render} from 'lit'; import {createTokenTests} from '../testing/tokens.js'; @@ -15,6 +18,51 @@ describe('', () => { describe('.styles', () => { createTokenTests(MdMenu.styles); }); + + let root: HTMLDivElement; + + beforeEach(() => { + root = document.createElement('div'); + document.body.appendChild(root); + }); + + afterEach(() => { + root?.remove(); + }); + + it('escape on list root closes menu', async () => { + render( + html` + + + `, + root); + + const button = root.querySelector('button')!; + const menu = root.querySelector('md-menu')!; + menu.anchor = button; + menu.show(); + await menu.updateComplete; + const listEl = menu.renderRoot.querySelector('md-list')!; + await listEl.updateComplete; + const listRoot = listEl.renderRoot.querySelector('ul')!; + + expect(menu.open).toBeTrue(); + + const escapeKeydownEvent = new KeyboardEvent('keydown', { + key: 'Escape', + code: 'Escape', + bubbles: true, + composed: true, + cancelable: true + }); + listRoot.dispatchEvent(escapeKeydownEvent); + + await menu.updateComplete; + + expect(menu.open).toBeFalse(); + expect(escapeKeydownEvent.defaultPrevented).toBeTrue(); + }); }); describe('', () => {