feat(focus-trap): add the ability to specify a focus target (#1752)

Fixes #1468.
This commit is contained in:
Kristiyan Kostadinov
2016-11-14 22:31:22 +01:00
committed by Kara
parent bbc5f6a954
commit 72ac7a0982
2 changed files with 60 additions and 12 deletions
+46 -10
View File
@@ -6,24 +6,21 @@ import {InteractivityChecker} from './interactivity-checker';
describe('FocusTrap', () => {
let checker: InteractivityChecker;
let fixture: ComponentFixture<FocusTrapTestApp>;
describe('with default element', () => {
let fixture: ComponentFixture<FocusTrapTestApp>;
let focusTrapInstance: FocusTrap;
beforeEach(() => TestBed.configureTestingModule({
declarations: [FocusTrap, FocusTrapTestApp],
providers: [InteractivityChecker]
}));
beforeEach(inject([InteractivityChecker], (c: InteractivityChecker) => {
checker = c;
fixture = TestBed.createComponent(FocusTrapTestApp);
focusTrapInstance = fixture.debugElement.query(By.directive(FocusTrap)).componentInstance;
}));
it('wrap focus from end to start', () => {
let focusTrap = fixture.debugElement.query(By.directive(FocusTrap));
let focusTrapInstance = focusTrap.componentInstance as FocusTrap;
// Because we can't mimic a real tab press focus change in a unit test, just call the
// focus event handler directly.
focusTrapInstance.focusFirstTabbableElement();
@@ -33,9 +30,6 @@ describe('FocusTrap', () => {
});
it('should wrap focus from start to end', () => {
let focusTrap = fixture.debugElement.query(By.directive(FocusTrap));
let focusTrapInstance = focusTrap.componentInstance as FocusTrap;
// Because we can't mimic a real tab press focus change in a unit test, just call the
// focus event handler directly.
focusTrapInstance.focusLastTabbableElement();
@@ -44,6 +38,35 @@ describe('FocusTrap', () => {
.toBe('button', 'Expected button element to be focused');
});
});
describe('with focus targets', () => {
let fixture: ComponentFixture<FocusTrapTargetTestApp>;
let focusTrapInstance: FocusTrap;
beforeEach(() => TestBed.configureTestingModule({
declarations: [FocusTrap, FocusTrapTargetTestApp],
providers: [InteractivityChecker]
}));
beforeEach(inject([InteractivityChecker], (c: InteractivityChecker) => {
fixture = TestBed.createComponent(FocusTrapTargetTestApp);
focusTrapInstance = fixture.debugElement.query(By.directive(FocusTrap)).componentInstance;
}));
it('should be able to prioritize the first focus target', () => {
// Because we can't mimic a real tab press focus change in a unit test, just call the
// focus event handler directly.
focusTrapInstance.focusFirstTabbableElement();
expect(document.activeElement.id).toBe('first');
});
it('should be able to prioritize the last focus target', () => {
// Because we can't mimic a real tab press focus change in a unit test, just call the
// focus event handler directly.
focusTrapInstance.focusLastTabbableElement();
expect(document.activeElement.id).toBe('last');
});
});
});
@@ -56,3 +79,16 @@ describe('FocusTrap', () => {
`
})
class FocusTrapTestApp { }
@Component({
template: `
<focus-trap>
<input>
<button id="last" md-focus-end></button>
<button id="first" md-focus-start>SAVE</button>
<input>
</focus-trap>
`
})
class FocusTrapTargetTestApp { }
+14 -2
View File
@@ -27,7 +27,10 @@ export class FocusTrap {
/** Focuses the first tabbable element within the focus trap region. */
focusFirstTabbableElement() {
let redirectToElement = this._getFirstTabbableElement(this.trappedContent.nativeElement);
let rootElement = this.trappedContent.nativeElement;
let redirectToElement = rootElement.querySelector('[md-focus-start]') as HTMLElement ||
this._getFirstTabbableElement(rootElement);
if (redirectToElement) {
redirectToElement.focus();
}
@@ -35,7 +38,16 @@ export class FocusTrap {
/** Focuses the last tabbable element within the focus trap region. */
focusLastTabbableElement() {
let redirectToElement = this._getLastTabbableElement(this.trappedContent.nativeElement);
let rootElement = this.trappedContent.nativeElement;
let focusTargets = rootElement.querySelectorAll('[md-focus-end]');
let redirectToElement: HTMLElement = null;
if (focusTargets.length) {
redirectToElement = focusTargets[focusTargets.length - 1] as HTMLElement;
} else {
redirectToElement = this._getLastTabbableElement(rootElement);
}
if (redirectToElement) {
redirectToElement.focus();
}