Skip to content

Commit 1380ae7

Browse files
committed
fix(sidenav): don't restore focus if focus isn't inside sidenav
1 parent 6121fa1 commit 1380ae7

File tree

2 files changed

+50
-13
lines changed

2 files changed

+50
-13
lines changed

src/lib/sidenav/sidenav.spec.ts

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {fakeAsync, async, tick, ComponentFixture, TestBed} from '@angular/core/testing';
2-
import {Component, ViewChild} from '@angular/core';
2+
import {Component, ElementRef, ViewChild} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {MdSidenav, MdSidenavModule, MdSidenavToggleResult, MdSidenavContainer} from './index';
55
import {A11yModule} from '../core/a11y/index';
@@ -310,19 +310,20 @@ describe('MdSidenav', () => {
310310
expect(testComponent.closeCount).toBe(0);
311311
}));
312312

313-
it('should restore focus to the trigger element on close', fakeAsync(() => {
313+
fit('should restore focus on close if focus is inside sidenav', fakeAsync(() => {
314314
let fixture = TestBed.createComponent(BasicTestApp);
315315
let sidenav: MdSidenav = fixture.debugElement
316316
.query(By.directive(MdSidenav)).componentInstance;
317-
let trigger = document.createElement('button');
317+
let openButton = fixture.componentInstance.openButton.nativeElement;
318+
let sidenavButton = fixture.componentInstance.sidenavButton.nativeElement;
318319

319-
document.body.appendChild(trigger);
320-
trigger.focus();
320+
openButton.focus();
321321
sidenav.open();
322322

323323
fixture.detectChanges();
324324
endSidenavTransition(fixture);
325325
tick();
326+
sidenavButton.focus();
326327

327328
sidenav.close();
328329

@@ -331,9 +332,32 @@ describe('MdSidenav', () => {
331332
tick();
332333

333334
expect(document.activeElement)
334-
.toBe(trigger, 'Expected focus to be restored to the trigger on close.');
335+
.toBe(openButton, 'Expected focus to be restored to the open button on close.');
336+
}));
337+
338+
fit('should not restore focus on close if focus is outside sidenav', fakeAsync(() => {
339+
let fixture = TestBed.createComponent(BasicTestApp);
340+
let sidenav: MdSidenav = fixture.debugElement
341+
.query(By.directive(MdSidenav)).componentInstance;
342+
let openButton = fixture.componentInstance.openButton.nativeElement;
343+
let closeButton = fixture.componentInstance.closeButton.nativeElement;
344+
345+
openButton.focus();
346+
sidenav.open();
347+
348+
fixture.detectChanges();
349+
endSidenavTransition(fixture);
350+
tick();
351+
closeButton.focus();
352+
353+
sidenav.close();
335354

336-
trigger.parentNode.removeChild(trigger);
355+
fixture.detectChanges();
356+
endSidenavTransition(fixture);
357+
tick();
358+
359+
expect(document.activeElement)
360+
.toBe(closeButton, 'Expected focus not to be restored to the open button on close.');
337361
}));
338362
});
339363

@@ -508,10 +532,10 @@ class SidenavContainerTwoSidenavTestApp {
508532
(open)="open()"
509533
(close-start)="closeStart()"
510534
(close)="close()">
511-
Content.
535+
<button #sidenavButton>Content.</button>
512536
</md-sidenav>
513-
<button (click)="sidenav.open()" class="open"></button>
514-
<button (click)="sidenav.close()" class="close"></button>
537+
<button (click)="sidenav.open()" class="open" #openButton></button>
538+
<button (click)="sidenav.close()" class="close" #closeButton></button>
515539
</md-sidenav-container>`,
516540
})
517541
class BasicTestApp {
@@ -521,6 +545,10 @@ class BasicTestApp {
521545
closeCount: number = 0;
522546
backdropClickedCount: number = 0;
523547

548+
@ViewChild('sidenavButton') sidenavButton: ElementRef;
549+
@ViewChild('openButton') openButton: ElementRef;
550+
@ViewChild('closeButton') closeButton: ElementRef;
551+
524552
openStart() {
525553
this.openStartCount++;
526554
}

src/lib/sidenav/sidenav.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,24 @@ export class MdSidenav implements AfterContentInit, OnDestroy {
137137
}
138138
});
139139

140-
this.onClose.subscribe(() => {
140+
this.onClose.subscribe(() => this._restoreFocus());
141+
}
142+
143+
/**
144+
* If focus is currently inside the sidenav, restores it to where it was before the sidenav
145+
* opened.
146+
*/
147+
private _restoreFocus() {
148+
let activeEl = document.activeElement;
149+
if (activeEl && this._elementRef.nativeElement.contains(activeEl)) {
141150
if (this._elementFocusedBeforeSidenavWasOpened instanceof HTMLElement) {
142151
this._elementFocusedBeforeSidenavWasOpened.focus();
143152
} else {
144153
this._elementRef.nativeElement.blur();
145154
}
155+
}
146156

147-
this._elementFocusedBeforeSidenavWasOpened = null;
148-
});
157+
this._elementFocusedBeforeSidenavWasOpened = null;
149158
}
150159

151160
ngAfterContentInit() {

0 commit comments

Comments
 (0)