From 2ec751ba9bcd8d9255667868b181ad4d2c205df8 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 26 Apr 2018 10:23:44 -0400 Subject: [PATCH] fix(overlay): always dispatch keyboard events to top overlay in OverlayKeyboardDispatcher Currently when dispatching events through the `OverlayKeyboardDispatcher` we try to match one of the overlays to the element that triggered the event. This is problematic, because some components will open an overlay, but will keep focus on the trigger element (e.g. `mat-autocomplete` and `mat-select`). These changes switch the logic so the keyboard events are always dispatched to the top-level overlay. Fixes #10799. --- .../overlay-keyboard-dispatcher.spec.ts | 22 ------------------- .../keyboard/overlay-keyboard-dispatcher.ts | 18 ++++----------- 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts index 777ab4ca8293..0f89c8c1b545 100644 --- a/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts +++ b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts @@ -86,28 +86,6 @@ describe('OverlayKeyboardDispatcher', () => { button.parentNode!.removeChild(button); }); - it('should dispatch targeted keyboard events to the overlay containing that target', () => { - const overlayOne = overlay.create(); - const overlayTwo = overlay.create(); - const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy'); - const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy'); - - overlayOne.keydownEvents().subscribe(overlayOneSpy); - overlayTwo.keydownEvents().subscribe(overlayTwoSpy); - - // Attach overlays - keyboardDispatcher.add(overlayOne); - keyboardDispatcher.add(overlayTwo); - - const overlayOnePane = overlayOne.overlayElement; - - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, overlayOnePane); - - // Targeted overlay should receive event - expect(overlayOneSpy).toHaveBeenCalled(); - expect(overlayTwoSpy).not.toHaveBeenCalled(); - }); - it('should complete the keydown stream on dispose', () => { const overlayRef = overlay.create(); const completeSpy = jasmine.createSpy('keydown complete spy'); diff --git a/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts index 983be9276492..89d5ab345d93 100644 --- a/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts +++ b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts @@ -65,18 +65,6 @@ export class OverlayKeyboardDispatcher implements OnDestroy { } } - /** Select the appropriate overlay from a keydown event. */ - private _selectOverlayFromEvent(event: KeyboardEvent): OverlayRef { - // Check if any overlays contain the event - const targetedOverlay = this._attachedOverlays.find(overlay => { - return overlay.overlayElement === event.target || - overlay.overlayElement.contains(event.target as HTMLElement); - }); - - // Use the overlay if it exists, otherwise choose the most recently attached one - return targetedOverlay || this._attachedOverlays[this._attachedOverlays.length - 1]; - } - /** Detaches the global keyboard event listener. */ private _detach() { if (this._isAttached) { @@ -88,8 +76,10 @@ export class OverlayKeyboardDispatcher implements OnDestroy { /** Keyboard event listener that will be attached to the body. */ private _keydownListener = (event: KeyboardEvent) => { if (this._attachedOverlays.length) { - // Dispatch keydown event to the correct overlay. - this._selectOverlayFromEvent(event)._keydownEvents.next(event); + // Dispatch the keydown event to the top overlay. We want to target the most recent overlay, + // rather than trying to match where the event came from, because some components might open + // an overlay, but keep focus on a trigger element (e.g. for select and autocomplete). + this._attachedOverlays[this._attachedOverlays.length - 1]._keydownEvents.next(event); } } }