Skip to content

Commit 5145c9d

Browse files
committed
fix(drawer): unable to close using keyboard if there are no focusable elements
Fixes the drawer not trapping focus if it doesn't have any focusable elements, making it impossible to close using the keyboard.
1 parent 5210b3e commit 5145c9d

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed

src/lib/sidenav/drawer.spec.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ describe('MatDrawer', () => {
1919
DrawerSetToOpenedFalse,
2020
DrawerSetToOpenedTrue,
2121
DrawerDynamicPosition,
22-
DrawerWitFocusableElements,
22+
DrawerWithFocusableElements,
2323
DrawerOpenBinding,
24+
DrawerWithoutFocusableElements,
2425
],
2526
});
2627

@@ -363,14 +364,14 @@ describe('MatDrawer', () => {
363364
});
364365

365366
describe('focus trapping behavior', () => {
366-
let fixture: ComponentFixture<DrawerWitFocusableElements>;
367-
let testComponent: DrawerWitFocusableElements;
367+
let fixture: ComponentFixture<DrawerWithFocusableElements>;
368+
let testComponent: DrawerWithFocusableElements;
368369
let drawer: MatDrawer;
369370
let firstFocusableElement: HTMLElement;
370371
let lastFocusableElement: HTMLElement;
371372

372373
beforeEach(() => {
373-
fixture = TestBed.createComponent(DrawerWitFocusableElements);
374+
fixture = TestBed.createComponent(DrawerWithFocusableElements);
374375
testComponent = fixture.debugElement.componentInstance;
375376
drawer = fixture.debugElement.query(By.directive(MatDrawer)).componentInstance;
376377
firstFocusableElement = fixture.debugElement.query(By.css('.link1')).nativeElement;
@@ -410,6 +411,21 @@ describe('MatDrawer', () => {
410411

411412
expect(document.activeElement).toBe(lastFocusableElement);
412413
}));
414+
415+
it('should focus the drawer if there are no focusable elements', fakeAsync(() => {
416+
fixture.destroy();
417+
418+
const nonFocusableFixture = TestBed.createComponent(DrawerWithoutFocusableElements);
419+
const drawerEl = nonFocusableFixture.debugElement.query(By.directive(MatDrawer));
420+
nonFocusableFixture.detectChanges();
421+
422+
drawerEl.componentInstance.open();
423+
nonFocusableFixture.detectChanges();
424+
tick();
425+
426+
expect(document.activeElement).toBe(drawerEl.nativeElement);
427+
}));
428+
413429
});
414430
});
415431

@@ -644,10 +660,19 @@ class DrawerDynamicPosition {
644660
<a class="link2" href="#">link2</a>
645661
</mat-drawer-container>`,
646662
})
647-
class DrawerWitFocusableElements {
663+
class DrawerWithFocusableElements {
648664
mode: string = 'over';
649665
}
650666

667+
@Component({
668+
template: `
669+
<mat-drawer-container>
670+
<mat-drawer position="start" mode="over">
671+
<button disabled>Not focusable</button>
672+
</mat-drawer>
673+
</mat-drawer-container>`,
674+
})
675+
class DrawerWithoutFocusableElements {}
651676

652677
@Component({
653678
template: `

src/lib/sidenav/drawer.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,21 +249,33 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
249249
private _focusTrapFactory: FocusTrapFactory,
250250
private _focusMonitor: FocusMonitor,
251251
@Optional() @Inject(DOCUMENT) private _doc: any) {
252+
252253
this.openedChange.subscribe((opened: boolean) => {
253254
if (opened) {
254255
if (this._doc) {
255256
this._elementFocusedBeforeDrawerWasOpened = this._doc.activeElement as HTMLElement;
256257
}
257258

258259
if (this._isFocusTrapEnabled && this._focusTrap) {
259-
this._focusTrap.focusInitialElementWhenReady();
260+
this._trapFocus();
260261
}
261262
} else {
262263
this._restoreFocus();
263264
}
264265
});
265266
}
266267

268+
/** Traps focus inside the drawer. */
269+
private _trapFocus() {
270+
this._focusTrap.focusInitialElementWhenReady().then(hasMovedFocus => {
271+
if (!hasMovedFocus) {
272+
// If there were no focusable elements, focus the sidenav
273+
// itself so the keyboard navigation still works.
274+
this._elementRef.nativeElement.focus();
275+
}
276+
});
277+
}
278+
267279
/**
268280
* If focus is currently inside the drawer, restores it to where it was before the drawer
269281
* opened.

0 commit comments

Comments
 (0)