Skip to content

Commit f3f90ef

Browse files
crisbetovictoriaaa234
authored andcommitted
fix(overlay): don't dispatch key events to overlays that don't handle them (#11810)
1 parent 42985ce commit f3f90ef

File tree

4 files changed

+42
-8
lines changed

4 files changed

+42
-8
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('OverlayKeyboardDispatcher', () => {
5353
const overlayOne = overlay.create();
5454
const overlayTwo = overlay.create();
5555
const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy');
56-
const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy');
56+
const overlayTwoSpy = jasmine.createSpy('overlayTwo keyboard event spy');
5757

5858
overlayOne.keydownEvents().subscribe(overlayOneSpy);
5959
overlayTwo.keydownEvents().subscribe(overlayTwoSpy);
@@ -143,6 +143,20 @@ describe('OverlayKeyboardDispatcher', () => {
143143
expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function), true);
144144
});
145145

146+
it('should skip overlays that do not have keydown event subscriptions', () => {
147+
const overlayOne = overlay.create();
148+
const overlayTwo = overlay.create();
149+
const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy');
150+
151+
overlayOne.keydownEvents().subscribe(overlayOneSpy);
152+
keyboardDispatcher.add(overlayOne);
153+
keyboardDispatcher.add(overlayTwo);
154+
155+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
156+
157+
expect(overlayOneSpy).toHaveBeenCalled();
158+
});
159+
146160
});
147161

148162

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,19 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
7575

7676
/** Keyboard event listener that will be attached to the body. */
7777
private _keydownListener = (event: KeyboardEvent) => {
78-
if (this._attachedOverlays.length) {
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);
78+
const overlays = this._attachedOverlays;
79+
80+
for (let i = overlays.length - 1; i > -1; i--) {
81+
// Dispatch the keydown event to the top overlay which has subscribers to its keydown events.
82+
// We want to target the most recent overlay, rather than trying to match where the event came
83+
// from, because some components might open an overlay, but keep focus on a trigger element
84+
// (e.g. for select and autocomplete). We skip overlays without keydown event subscriptions,
85+
// because we don't want overlays that don't handle keyboard events to block the ones below
86+
// them that do.
87+
if (overlays[i]._keydownEventSubscriptions > 0) {
88+
overlays[i]._keydownEvents.next(event);
89+
break;
90+
}
8391
}
8492
}
8593
}

src/cdk/overlay/overlay-ref.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,22 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
3131
private _backdropClick: Subject<MouseEvent> = new Subject();
3232
private _attachments = new Subject<void>();
3333
private _detachments = new Subject<void>();
34+
private _keydownEventsObservable: Observable<KeyboardEvent> = Observable.create(observer => {
35+
const subscription = this._keydownEvents.subscribe(observer);
36+
this._keydownEventSubscriptions++;
37+
38+
return () => {
39+
subscription.unsubscribe();
40+
this._keydownEventSubscriptions--;
41+
};
42+
});
3443

3544
/** Stream of keydown events dispatched to this overlay. */
3645
_keydownEvents = new Subject<KeyboardEvent>();
3746

47+
/** Amount of subscriptions to the keydown events. */
48+
_keydownEventSubscriptions = 0;
49+
3850
constructor(
3951
private _portalOutlet: PortalOutlet,
4052
private _host: HTMLElement,
@@ -218,7 +230,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
218230

219231
/** Gets an observable of keydown events targeted to this overlay. */
220232
keydownEvents(): Observable<KeyboardEvent> {
221-
return this._keydownEvents.asObservable();
233+
return this._keydownEventsObservable;
222234
}
223235

224236
/** Gets the the current overlay configuration, which is immutable. */

src/lib/bottom-sheet/bottom-sheet-ref.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class MatBottomSheetRef<T = any, R = any> {
6262
if (!containerInstance.bottomSheetConfig.disableClose) {
6363
merge(
6464
_overlayRef.backdropClick(),
65-
_overlayRef._keydownEvents.pipe(filter(event => event.keyCode === ESCAPE))
65+
_overlayRef.keydownEvents().pipe(filter(event => event.keyCode === ESCAPE))
6666
).subscribe(() => this.dismiss());
6767
}
6868
}

0 commit comments

Comments
 (0)