Skip to content

Commit

Permalink
Focus page content after navigation for a11y. (angular#267)
Browse files Browse the repository at this point in the history
  • Loading branch information
josephperrott authored and jelbourn committed Sep 20, 2017
1 parent 62706dc commit b6da10c
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
<md-icon>menu</md-icon>
</button>

<h1>{{getTitle()}} </h1>
<h1 focusOnNavigation>{{getTitle()}} </h1>
</div>
5 changes: 3 additions & 2 deletions src/app/pages/component-page-header/component-page-header.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Component, EventEmitter, NgModule, Output} from '@angular/core';
import 'rxjs/add/operator/first';
import {ComponentPageTitle} from '../page-title/page-title';
import {NavigationFocusModule} from '../../shared/navigation-focus/navigation-focus';
import {MdButtonModule, MdIconModule} from '@angular/material';

@Component({
Expand All @@ -9,7 +10,7 @@ import {MdButtonModule, MdIconModule} from '@angular/material';
styleUrls: ['./component-page-header.scss']
})
export class ComponentPageHeader {
constructor(public _componentPageTitle: ComponentPageTitle) { }
constructor(public _componentPageTitle: ComponentPageTitle) {}

@Output() toggleSidenav = new EventEmitter<void>();

Expand All @@ -19,7 +20,7 @@ export class ComponentPageHeader {
}

@NgModule({
imports: [MdButtonModule, MdIconModule],
imports: [MdButtonModule, MdIconModule, NavigationFocusModule],
exports: [ComponentPageHeader],
declarations: [ComponentPageHeader],
providers: [ComponentPageTitle],
Expand Down
9 changes: 6 additions & 3 deletions src/app/pages/component-viewer/component-api.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<doc-viewer
documentUrl="/assets/documents/api/{{componentViewer.componentDocItem.id}}.html"
class="docs-component-view-text-content"></doc-viewer>
<span class="cdk-visually-hidden" tabindex="-1" #intialFocusTarget>
API for {{componentViewer.componentDocItem.id}}
</span>
<doc-viewer
documentUrl="/assets/documents/api/{{componentViewer.componentDocItem.id}}.html"
class="docs-component-view-text-content"></doc-viewer>
7 changes: 5 additions & 2 deletions src/app/pages/component-viewer/component-examples.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<span class="cdk-visually-hidden" tabindex="-1" #intialFocusTarget>
Examples for {{componentViewer.componentDocItem.id}}
</span>
<example-viewer
*ngFor="let example of componentViewer.componentDocItem.examples"
[example]="example"></example-viewer>
*ngFor="let example of componentViewer.componentDocItem.examples"
[example]="example"></example-viewer>
3 changes: 3 additions & 0 deletions src/app/pages/component-viewer/component-overview.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<span class="cdk-visually-hidden" tabindex="-1" #intialFocusTarget>
Overview for {{componentViewer.componentDocItem.id}}
</span>
<doc-viewer
documentUrl="/assets/documents/overview/{{componentViewer.componentDocItem.id}}.html"
class="docs-component-view-text-content docs-component-overview"
Expand Down
13 changes: 10 additions & 3 deletions src/app/pages/component-viewer/component-viewer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
import {Component, OnInit, NgModule, ElementRef, ViewEncapsulation, ViewChild} from '@angular/core';
import {ActivatedRoute, Router, RouterModule} from '@angular/router';
import {DocumentationItems, DocItem} from '../../shared/documentation-items/documentation-items';
import {ComponentPageTitle} from '../page-title/page-title';
Expand Down Expand Up @@ -38,8 +38,15 @@ export class ComponentViewer {
styleUrls: ['./component-overview.scss'],
encapsulation: ViewEncapsulation.None,
})
export class ComponentOverview {
export class ComponentOverview implements OnInit {
@ViewChild('intialFocusTarget') focusTarget: ElementRef;

constructor(public componentViewer: ComponentViewer) {}

ngOnInit() {
// 100ms timeout is used to allow the page to settle before moving focus for screen readers.
setTimeout(() => this.focusTarget.nativeElement.focus(), 100);
}
}

@Component({
Expand All @@ -62,7 +69,7 @@ export class ComponentExamples extends ComponentOverview {}
RouterModule,
DocViewerModule,
CommonModule,
TableOfContentsModule
TableOfContentsModule,
],
exports: [ComponentViewer],
declarations: [ComponentViewer, ComponentOverview, ComponentApi, ComponentExamples],
Expand Down
25 changes: 25 additions & 0 deletions src/app/shared/navigation-focus/navigation-focus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {NgModule, Injectable, OnInit, Directive, Input, ElementRef, Renderer2} from '@angular/core';
import {Subscription} from 'rxjs/Subscription';

/** The timeout id of the previous focus change. */
let lastTimeoutId = -1;

@Directive({
selector: '[focusOnNavigation]',
host: {'tabindex': '-1'},
})
export class NavigationFocus implements OnInit {
constructor(private el: ElementRef) {}

ngOnInit() {
clearTimeout(lastTimeoutId);
// 100ms timeout is used to allow the page to settle before moving focus for screen readers.
lastTimeoutId = setTimeout(() => this.el.nativeElement.focus(), 100);
}
}

@NgModule({
declarations: [NavigationFocus],
exports: [NavigationFocus],
})
export class NavigationFocusModule {}
3 changes: 2 additions & 1 deletion src/app/shared/theme-picker/theme-picker.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<button md-icon-button [md-menu-trigger-for]="themeMenu" mdTooltip="Select a theme!">
<button md-icon-button [md-menu-trigger-for]="themeMenu" mdTooltip="Select a theme!"
tabindex="-1">
<md-icon>format_color_fill</md-icon>
</button>

Expand Down
4 changes: 1 addition & 3 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
"outDir": "../dist/out-tsc",
"sourceMap": true,
"target": "es5",
"typeRoots": [
"../node_modules/@types"
]
"types": ["q", "selenium-driver", "jasmine"]
},
"exclude": [
"assets"
Expand Down

0 comments on commit b6da10c

Please sign in to comment.