diff --git a/src/lib/sidenav/drawer.spec.ts b/src/lib/sidenav/drawer.spec.ts index 493490d8b6bf..3942603b532f 100644 --- a/src/lib/sidenav/drawer.spec.ts +++ b/src/lib/sidenav/drawer.spec.ts @@ -26,8 +26,9 @@ describe('MatDrawer', () => { DrawerSetToOpenedFalse, DrawerSetToOpenedTrue, DrawerDynamicPosition, - DrawerWitFocusableElements, + DrawerWithFocusableElements, DrawerOpenBinding, + DrawerWithoutFocusableElements, ], }); @@ -370,14 +371,14 @@ describe('MatDrawer', () => { }); describe('focus trapping behavior', () => { - let fixture: ComponentFixture; - let testComponent: DrawerWitFocusableElements; + let fixture: ComponentFixture; + let testComponent: DrawerWithFocusableElements; let drawer: MatDrawer; let firstFocusableElement: HTMLElement; let lastFocusableElement: HTMLElement; beforeEach(() => { - fixture = TestBed.createComponent(DrawerWitFocusableElements); + fixture = TestBed.createComponent(DrawerWithFocusableElements); testComponent = fixture.debugElement.componentInstance; drawer = fixture.debugElement.query(By.directive(MatDrawer)).componentInstance; firstFocusableElement = fixture.debugElement.query(By.css('.link1')).nativeElement; @@ -417,6 +418,21 @@ describe('MatDrawer', () => { expect(document.activeElement).toBe(lastFocusableElement); })); + + it('should focus the drawer if there are no focusable elements', fakeAsync(() => { + fixture.destroy(); + + const nonFocusableFixture = TestBed.createComponent(DrawerWithoutFocusableElements); + const drawerEl = nonFocusableFixture.debugElement.query(By.directive(MatDrawer)); + nonFocusableFixture.detectChanges(); + + drawerEl.componentInstance.open(); + nonFocusableFixture.detectChanges(); + tick(); + + expect(document.activeElement).toBe(drawerEl.nativeElement); + })); + }); }); @@ -676,10 +692,19 @@ class DrawerDynamicPosition { link2 `, }) -class DrawerWitFocusableElements { +class DrawerWithFocusableElements { mode: string = 'over'; } +@Component({ + template: ` + + + + + `, +}) +class DrawerWithoutFocusableElements {} @Component({ template: ` diff --git a/src/lib/sidenav/drawer.ts b/src/lib/sidenav/drawer.ts index 9a5fbbc86c1a..729c1aa2f73b 100644 --- a/src/lib/sidenav/drawer.ts +++ b/src/lib/sidenav/drawer.ts @@ -261,6 +261,7 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr private _focusMonitor: FocusMonitor, private _platform: Platform, @Optional() @Inject(DOCUMENT) private _doc: any) { + this.openedChange.subscribe((opened: boolean) => { if (opened) { if (this._doc) { @@ -268,7 +269,7 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr } if (this._isFocusTrapEnabled && this._focusTrap) { - this._focusTrap.focusInitialElementWhenReady(); + this._trapFocus(); } } else { this._restoreFocus(); @@ -276,6 +277,17 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr }); } + /** Traps focus inside the drawer. */ + private _trapFocus() { + this._focusTrap.focusInitialElementWhenReady().then(hasMovedFocus => { + // If there were no focusable elements, focus the sidenav itself so the keyboard navigation + // still works. We need to check that `focus` is a function due to Universal. + if (!hasMovedFocus && typeof this._elementRef.nativeElement.focus === 'function') { + this._elementRef.nativeElement.focus(); + } + }); + } + /** * If focus is currently inside the drawer, restores it to where it was before the drawer * opened.