Skip to content

Commit 67b3e5f

Browse files
stevenyxujosephperrott
authored andcommitted
fix(overlay): stop using capture phase for overlay keyboard handling (#16019)
OverlayKeyboardDispatcher is a document-level keyboard listener that dispatches events to the topmost overlay that's listening for them. It does it on capture as of e30852a in order to avoid missing events whose propagation have been stopped. However, there are legitimate use cases for stopping propagation and not wanting the overlay to hear about it. For example, grid navigation based on the ARIA best practice allows for enter to trap focus inside a grid cell and escape to leave it and restore focus to the grid cell. https://www.w3.org/TR/wai-aria-practices-1.1/#gridNav_inside In these cases, we must be able to call stopPropagation and prevent the escape keypress event from reaching the overlay. Fixes #9928. BREAKING CHANGE: OverlayKeyboardDispatcher (used in dialog and overlay and available in the CDK) now listens on bubbling/propagation phase instead of capture phase. This means that overlay keydown handlers are now called after any applicable handlers inside of an overlay, and it respects any stopPropagation() calls from inside the overlay.
1 parent e391869 commit 67b3e5f

File tree

2 files changed

+6
-6
lines changed

2 files changed

+6
-6
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ describe('OverlayKeyboardDispatcher', () => {
6969
expect(overlayTwoSpy).toHaveBeenCalled();
7070
});
7171

72-
it('should dispatch keyboard events when propagation is stopped', () => {
72+
it('should not dispatch keyboard events when propagation is stopped', () => {
7373
const overlayRef = overlay.create();
7474
const spy = jasmine.createSpy('keyboard event spy');
7575
const button = document.createElement('button');
@@ -81,7 +81,7 @@ describe('OverlayKeyboardDispatcher', () => {
8181
keyboardDispatcher.add(overlayRef);
8282
dispatchKeyboardEvent(button, 'keydown', ESCAPE);
8383

84-
expect(spy).toHaveBeenCalled();
84+
expect(spy).not.toHaveBeenCalled();
8585

8686
button.parentNode!.removeChild(button);
8787
});
@@ -137,10 +137,10 @@ describe('OverlayKeyboardDispatcher', () => {
137137
spyOn(body, 'removeEventListener');
138138

139139
keyboardDispatcher.add(overlayRef);
140-
expect(body.addEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function), true);
140+
expect(body.addEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function));
141141

142142
overlayRef.dispose();
143-
expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function), true);
143+
expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function));
144144
});
145145

146146
it('should skip overlays that do not have keydown event subscriptions', () => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
4747

4848
// Lazily start dispatcher once first overlay is added
4949
if (!this._isAttached) {
50-
this._document.body.addEventListener('keydown', this._keydownListener, true);
50+
this._document.body.addEventListener('keydown', this._keydownListener);
5151
this._isAttached = true;
5252
}
5353

@@ -71,7 +71,7 @@ export class OverlayKeyboardDispatcher implements OnDestroy {
7171
/** Detaches the global keyboard event listener. */
7272
private _detach() {
7373
if (this._isAttached) {
74-
this._document.body.removeEventListener('keydown', this._keydownListener, true);
74+
this._document.body.removeEventListener('keydown', this._keydownListener);
7575
this._isAttached = false;
7676
}
7777
}

0 commit comments

Comments
 (0)