Skip to content

Commit d96fa07

Browse files
crisbetoandrewseguin
authored andcommitted
fix(overlay): always dispatch keyboard events to top overlay in OverlayKeyboardDispatcher (#10807)
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.
1 parent 45cacf7 commit d96fa07

File tree

2 files changed

+4
-36
lines changed

2 files changed

+4
-36
lines changed

src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -86,28 +86,6 @@ describe('OverlayKeyboardDispatcher', () => {
8686
button.parentNode!.removeChild(button);
8787
});
8888

89-
it('should dispatch targeted keyboard events to the overlay containing that target', () => {
90-
const overlayOne = overlay.create();
91-
const overlayTwo = overlay.create();
92-
const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy');
93-
const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy');
94-
95-
overlayOne.keydownEvents().subscribe(overlayOneSpy);
96-
overlayTwo.keydownEvents().subscribe(overlayTwoSpy);
97-
98-
// Attach overlays
99-
keyboardDispatcher.add(overlayOne);
100-
keyboardDispatcher.add(overlayTwo);
101-
102-
const overlayOnePane = overlayOne.overlayElement;
103-
104-
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, overlayOnePane);
105-
106-
// Targeted overlay should receive event
107-
expect(overlayOneSpy).toHaveBeenCalled();
108-
expect(overlayTwoSpy).not.toHaveBeenCalled();
109-
});
110-
11189
it('should complete the keydown stream on dispose', () => {
11290
const overlayRef = overlay.create();
11391
const completeSpy = jasmine.createSpy('keydown complete spy');

src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,6 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
6565
}
6666
}
6767

68-
/** Select the appropriate overlay from a keydown event. */
69-
private _selectOverlayFromEvent(event: KeyboardEvent): OverlayRef {
70-
// Check if any overlays contain the event
71-
const targetedOverlay = this._attachedOverlays.find(overlay => {
72-
return overlay.overlayElement === event.target ||
73-
overlay.overlayElement.contains(event.target as HTMLElement);
74-
});
75-
76-
// Use the overlay if it exists, otherwise choose the most recently attached one
77-
return targetedOverlay || this._attachedOverlays[this._attachedOverlays.length - 1];
78-
}
79-
8068
/** Detaches the global keyboard event listener. */
8169
private _detach() {
8270
if (this._isAttached) {
@@ -88,8 +76,10 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
8876
/** Keyboard event listener that will be attached to the body. */
8977
private _keydownListener = (event: KeyboardEvent) => {
9078
if (this._attachedOverlays.length) {
91-
// Dispatch keydown event to the correct overlay.
92-
this._selectOverlayFromEvent(event)._keydownEvents.next(event);
79+
// Dispatch the keydown event to the top overlay. We want to target the most recent overlay,
80+
// rather than trying to match where the event came from, because some components might open
81+
// an overlay, but keep focus on a trigger element (e.g. for select and autocomplete).
82+
this._attachedOverlays[this._attachedOverlays.length - 1]._keydownEvents.next(event);
9383
}
9484
}
9585
}

0 commit comments

Comments
 (0)