Skip to content

Commit fba3728

Browse files
crisbetoandrewseguin
authored andcommitted
fix(drawer): unable to close using keyboard if there are no focusable elements (#8783)
Fixes the drawer not trapping focus if it doesn't have any focusable elements, making it impossible to close using the keyboard.
1 parent bd50fa6 commit fba3728

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
@@ -26,8 +26,9 @@ describe('MatDrawer', () => {
2626
DrawerSetToOpenedFalse,
2727
DrawerSetToOpenedTrue,
2828
DrawerDynamicPosition,
29-
DrawerWitFocusableElements,
29+
DrawerWithFocusableElements,
3030
DrawerOpenBinding,
31+
DrawerWithoutFocusableElements,
3132
],
3233
});
3334

@@ -370,14 +371,14 @@ describe('MatDrawer', () => {
370371
});
371372

372373
describe('focus trapping behavior', () => {
373-
let fixture: ComponentFixture<DrawerWitFocusableElements>;
374-
let testComponent: DrawerWitFocusableElements;
374+
let fixture: ComponentFixture<DrawerWithFocusableElements>;
375+
let testComponent: DrawerWithFocusableElements;
375376
let drawer: MatDrawer;
376377
let firstFocusableElement: HTMLElement;
377378
let lastFocusableElement: HTMLElement;
378379

379380
beforeEach(() => {
380-
fixture = TestBed.createComponent(DrawerWitFocusableElements);
381+
fixture = TestBed.createComponent(DrawerWithFocusableElements);
381382
testComponent = fixture.debugElement.componentInstance;
382383
drawer = fixture.debugElement.query(By.directive(MatDrawer)).componentInstance;
383384
firstFocusableElement = fixture.debugElement.query(By.css('.link1')).nativeElement;
@@ -417,6 +418,21 @@ describe('MatDrawer', () => {
417418

418419
expect(document.activeElement).toBe(lastFocusableElement);
419420
}));
421+
422+
it('should focus the drawer if there are no focusable elements', fakeAsync(() => {
423+
fixture.destroy();
424+
425+
const nonFocusableFixture = TestBed.createComponent(DrawerWithoutFocusableElements);
426+
const drawerEl = nonFocusableFixture.debugElement.query(By.directive(MatDrawer));
427+
nonFocusableFixture.detectChanges();
428+
429+
drawerEl.componentInstance.open();
430+
nonFocusableFixture.detectChanges();
431+
tick();
432+
433+
expect(document.activeElement).toBe(drawerEl.nativeElement);
434+
}));
435+
420436
});
421437
});
422438

@@ -676,10 +692,19 @@ class DrawerDynamicPosition {
676692
<a class="link2" href="#">link2</a>
677693
</mat-drawer-container>`,
678694
})
679-
class DrawerWitFocusableElements {
695+
class DrawerWithFocusableElements {
680696
mode: string = 'over';
681697
}
682698

699+
@Component({
700+
template: `
701+
<mat-drawer-container>
702+
<mat-drawer position="start" mode="over">
703+
<button disabled>Not focusable</button>
704+
</mat-drawer>
705+
</mat-drawer-container>`,
706+
})
707+
class DrawerWithoutFocusableElements {}
683708

684709
@Component({
685710
template: `

src/lib/sidenav/drawer.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,21 +261,33 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr
261261
private _focusMonitor: FocusMonitor,
262262
private _platform: Platform,
263263
@Optional() @Inject(DOCUMENT) private _doc: any) {
264+
264265
this.openedChange.subscribe((opened: boolean) => {
265266
if (opened) {
266267
if (this._doc) {
267268
this._elementFocusedBeforeDrawerWasOpened = this._doc.activeElement as HTMLElement;
268269
}
269270

270271
if (this._isFocusTrapEnabled && this._focusTrap) {
271-
this._focusTrap.focusInitialElementWhenReady();
272+
this._trapFocus();
272273
}
273274
} else {
274275
this._restoreFocus();
275276
}
276277
});
277278
}
278279

280+
/** Traps focus inside the drawer. */
281+
private _trapFocus() {
282+
this._focusTrap.focusInitialElementWhenReady().then(hasMovedFocus => {
283+
// If there were no focusable elements, focus the sidenav itself so the keyboard navigation
284+
// still works. We need to check that `focus` is a function due to Universal.
285+
if (!hasMovedFocus && typeof this._elementRef.nativeElement.focus === 'function') {
286+
this._elementRef.nativeElement.focus();
287+
}
288+
});
289+
}
290+
279291
/**
280292
* If focus is currently inside the drawer, restores it to where it was before the drawer
281293
* opened.

0 commit comments

Comments
 (0)