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.
This commit is contained in:
Kristiyan Kostadinov
2017-12-13 22:48:45 +01:00
committed by Andrew Seguin
parent bd50fa6c36
commit fba3728821
2 changed files with 43 additions and 6 deletions
+30 -5
View File
@@ -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<DrawerWitFocusableElements>;
let testComponent: DrawerWitFocusableElements;
let fixture: ComponentFixture<DrawerWithFocusableElements>;
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 {
<a class="link2" href="#">link2</a>
</mat-drawer-container>`,
})
class DrawerWitFocusableElements {
class DrawerWithFocusableElements {
mode: string = 'over';
}
@Component({
template: `
<mat-drawer-container>
<mat-drawer position="start" mode="over">
<button disabled>Not focusable</button>
</mat-drawer>
</mat-drawer-container>`,
})
class DrawerWithoutFocusableElements {}
@Component({
template: `
+13 -1
View File
@@ -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.