Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f1af7c1

Browse files
committedMar 25, 2025·
fix(material/sidenav): ignore escape events while overlay is open
The sidenav isn't an overlay so it doesn't participate in the common event handling. This means that if there's an overlay in it that closes by pressing escape, the sidenav will close instead of the overlay. These changes add a check that will skip escape key presses while there are open overlays. Fixes #30507.
1 parent d4cccb7 commit f1af7c1

File tree

3 files changed

+60
-13
lines changed

3 files changed

+60
-13
lines changed
 

‎src/material/sidenav/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ ng_module(
2424
"//src/cdk/bidi",
2525
"//src/cdk/coercion",
2626
"//src/cdk/keycodes",
27+
"//src/cdk/overlay",
2728
"//src/cdk/scrolling",
2829
"//src/material/core",
2930
"@npm//@angular/core",
@@ -57,6 +58,7 @@ ng_test_library(
5758
"//src/cdk/a11y",
5859
"//src/cdk/bidi",
5960
"//src/cdk/keycodes",
61+
"//src/cdk/overlay",
6062
"//src/cdk/platform",
6163
"//src/cdk/scrolling",
6264
"//src/cdk/testing",

‎src/material/sidenav/drawer.spec.ts

+40
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {A11yModule} from '@angular/cdk/a11y';
22
import {Direction} from '@angular/cdk/bidi';
33
import {ESCAPE} from '@angular/cdk/keycodes';
44
import {CdkScrollable} from '@angular/cdk/scrolling';
5+
import {OverlayKeyboardDispatcher, OverlayRef} from '@angular/cdk/overlay';
56
import {
67
createKeyboardEvent,
78
dispatchEvent,
@@ -22,7 +23,13 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
2223
import {MatDrawer, MatDrawerContainer, MatSidenavModule} from './index';
2324

2425
describe('MatDrawer', () => {
26+
let fakeKeyboardDispatcher: OverlayKeyboardDispatcher;
27+
2528
beforeEach(waitForAsync(() => {
29+
fakeKeyboardDispatcher = {
30+
_attachedOverlays: [] as OverlayRef[],
31+
} as OverlayKeyboardDispatcher;
32+
2633
TestBed.configureTestingModule({
2734
imports: [
2835
MatSidenavModule,
@@ -39,6 +46,12 @@ describe('MatDrawer', () => {
3946
IndirectDescendantDrawer,
4047
NestedDrawerContainers,
4148
],
49+
providers: [
50+
{
51+
provide: OverlayKeyboardDispatcher,
52+
useValue: fakeKeyboardDispatcher,
53+
},
54+
],
4255
});
4356
}));
4457

@@ -237,6 +250,33 @@ describe('MatDrawer', () => {
237250
expect(event.defaultPrevented).toBe(false);
238251
}));
239252

253+
fit('should not close when pressing escape while an overlay is open', fakeAsync(() => {
254+
const fixture = TestBed.createComponent(BasicTestApp);
255+
fixture.detectChanges();
256+
257+
const testComponent: BasicTestApp = fixture.debugElement.componentInstance;
258+
const drawer = fixture.debugElement.query(By.directive(MatDrawer))!;
259+
260+
drawer.componentInstance.open();
261+
fixture.detectChanges();
262+
tick();
263+
264+
expect(testComponent.closeCount).withContext('Expected no close events.').toBe(0);
265+
expect(testComponent.closeStartCount).withContext('Expected no close start events.').toBe(0);
266+
267+
fakeKeyboardDispatcher._attachedOverlays.push(null!);
268+
const event = createKeyboardEvent('keydown', ESCAPE);
269+
dispatchEvent(drawer.nativeElement, event);
270+
fixture.detectChanges();
271+
flush();
272+
273+
expect(testComponent.closeCount).withContext('Expected still no close events.').toBe(0);
274+
expect(testComponent.closeStartCount)
275+
.withContext('Expected still no close start events.')
276+
.toBe(0);
277+
expect(event.defaultPrevented).toBe(false);
278+
}));
279+
240280
it('should fire the open event when open on init', fakeAsync(() => {
241281
const fixture = TestBed.createComponent(DrawerSetToOpenedTrue);
242282

‎src/material/sidenav/drawer.ts

+18-13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {Directionality} from '@angular/cdk/bidi';
1616
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
1717
import {ESCAPE, hasModifierKey} from '@angular/cdk/keycodes';
1818
import {Platform} from '@angular/cdk/platform';
19+
import {OverlayKeyboardDispatcher} from '@angular/cdk/overlay';
1920
import {CdkScrollable, ScrollDispatcher, ViewportRuler} from '@angular/cdk/scrolling';
2021
import {DOCUMENT} from '@angular/common';
2122
import {
@@ -179,6 +180,7 @@ export class MatDrawer implements AfterViewInit, OnDestroy {
179180
private _renderer = inject(Renderer2);
180181
private readonly _interactivityChecker = inject(InteractivityChecker);
181182
private _doc = inject(DOCUMENT, {optional: true})!;
183+
private _keyboardDispatcher = inject(OverlayKeyboardDispatcher, {optional: true});
182184
_container? = inject<MatDrawerContainer>(MAT_DRAWER_CONTAINER, {optional: true});
183185

184186
private _focusTrap: FocusTrap | null = null;
@@ -360,19 +362,22 @@ export class MatDrawer implements AfterViewInit, OnDestroy {
360362
this._ngZone.runOutsideAngular(() => {
361363
const element = this._elementRef.nativeElement;
362364
(fromEvent(element, 'keydown') as Observable<KeyboardEvent>)
363-
.pipe(
364-
filter(event => {
365-
return event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event);
366-
}),
367-
takeUntil(this._destroyed),
368-
)
369-
.subscribe(event =>
370-
this._ngZone.run(() => {
371-
this.close();
372-
event.stopPropagation();
373-
event.preventDefault();
374-
}),
375-
);
365+
.pipe(takeUntil(this._destroyed))
366+
.subscribe(event => {
367+
// Skip keyboard events if there are open overlays since they may be
368+
// placed inside the sidenav and cause it to close unexpectedly.
369+
if (this._keyboardDispatcher && this._keyboardDispatcher._attachedOverlays.length > 0) {
370+
return;
371+
}
372+
373+
if (event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event)) {
374+
this._ngZone.run(() => {
375+
this.close();
376+
event.stopPropagation();
377+
event.preventDefault();
378+
});
379+
}
380+
});
376381

377382
this._eventCleanups = [
378383
this._renderer.listen(element, 'transitionrun', this._handleTransitionEvent),

0 commit comments

Comments
 (0)
Please sign in to comment.