mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(overlay): don't dispatch key events to overlays that don't handle them (#11810)
This commit is contained in:
committed by
Joey Perrott
parent
c59abc6c8f
commit
c3fdc32445
@@ -53,7 +53,7 @@ describe('OverlayKeyboardDispatcher', () => {
|
||||
const overlayOne = overlay.create();
|
||||
const overlayTwo = overlay.create();
|
||||
const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy');
|
||||
const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy');
|
||||
const overlayTwoSpy = jasmine.createSpy('overlayTwo keyboard event spy');
|
||||
|
||||
overlayOne.keydownEvents().subscribe(overlayOneSpy);
|
||||
overlayTwo.keydownEvents().subscribe(overlayTwoSpy);
|
||||
@@ -143,6 +143,20 @@ describe('OverlayKeyboardDispatcher', () => {
|
||||
expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function), true);
|
||||
});
|
||||
|
||||
it('should skip overlays that do not have keydown event subscriptions', () => {
|
||||
const overlayOne = overlay.create();
|
||||
const overlayTwo = overlay.create();
|
||||
const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy');
|
||||
|
||||
overlayOne.keydownEvents().subscribe(overlayOneSpy);
|
||||
keyboardDispatcher.add(overlayOne);
|
||||
keyboardDispatcher.add(overlayTwo);
|
||||
|
||||
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
|
||||
|
||||
expect(overlayOneSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -75,11 +75,19 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
|
||||
|
||||
/** Keyboard event listener that will be attached to the body. */
|
||||
private _keydownListener = (event: KeyboardEvent) => {
|
||||
if (this._attachedOverlays.length) {
|
||||
// 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);
|
||||
const overlays = this._attachedOverlays;
|
||||
|
||||
for (let i = overlays.length - 1; i > -1; i--) {
|
||||
// Dispatch the keydown event to the top overlay which has subscribers to its keydown events.
|
||||
// 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). We skip overlays without keydown event subscriptions,
|
||||
// because we don't want overlays that don't handle keyboard events to block the ones below
|
||||
// them that do.
|
||||
if (overlays[i]._keydownEventSubscriptions > 0) {
|
||||
overlays[i]._keydownEvents.next(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,22 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
|
||||
private _backdropClick: Subject<MouseEvent> = new Subject();
|
||||
private _attachments = new Subject<void>();
|
||||
private _detachments = new Subject<void>();
|
||||
private _keydownEventsObservable: Observable<KeyboardEvent> = Observable.create(observer => {
|
||||
const subscription = this._keydownEvents.subscribe(observer);
|
||||
this._keydownEventSubscriptions++;
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
this._keydownEventSubscriptions--;
|
||||
};
|
||||
});
|
||||
|
||||
/** Stream of keydown events dispatched to this overlay. */
|
||||
_keydownEvents = new Subject<KeyboardEvent>();
|
||||
|
||||
/** Amount of subscriptions to the keydown events. */
|
||||
_keydownEventSubscriptions = 0;
|
||||
|
||||
constructor(
|
||||
private _portalOutlet: PortalOutlet,
|
||||
private _host: HTMLElement,
|
||||
@@ -218,7 +230,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
|
||||
|
||||
/** Gets an observable of keydown events targeted to this overlay. */
|
||||
keydownEvents(): Observable<KeyboardEvent> {
|
||||
return this._keydownEvents.asObservable();
|
||||
return this._keydownEventsObservable;
|
||||
}
|
||||
|
||||
/** Gets the the current overlay configuration, which is immutable. */
|
||||
|
||||
@@ -62,7 +62,7 @@ export class MatBottomSheetRef<T = any, R = any> {
|
||||
if (!containerInstance.bottomSheetConfig.disableClose) {
|
||||
merge(
|
||||
_overlayRef.backdropClick(),
|
||||
_overlayRef._keydownEvents.pipe(filter(event => event.keyCode === ESCAPE))
|
||||
_overlayRef.keydownEvents().pipe(filter(event => event.keyCode === ESCAPE))
|
||||
).subscribe(() => this.dismiss());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user