demo(sidenav): add accessibility demo pages (#7498)

* change sidenav demo to drawer

* create sidenav demo

* create sidenav example shells

* basic example

* mobile example

* dual demo

* some cleanup

* cleanup after rebase

* fix minor issues

* auto-focus first playlist element
This commit is contained in:
mmalerba
2017-10-10 16:39:04 -07:00
committed by Andrew Seguin
parent 94584dbc56
commit 9b0771286a
17 changed files with 308 additions and 31 deletions
+15 -7
View File
@@ -40,6 +40,10 @@ import {
FoggyTabContent,
} from './tabs/tabs-a11y';
import {TooltipAccessibilityDemo} from './tooltip/tooltip-a11y';
import {SidenavAccessibilityDemo} from './sidenav/sidenav-a11y';
import {SidenavBasicAccessibilityDemo} from './sidenav/basic-sidenav-a11y';
import {SidenavDualAccessibilityDemo} from './sidenav/dual-sidenav-a11y';
import {SidenavMobileAccessibilityDemo} from './sidenav/mobile-sidenav-a11y';
@NgModule({
@@ -77,6 +81,7 @@ export class AccessibilityRoutingModule {}
DialogNeptuneIFrameDialog,
DialogWelcomeExampleDialog,
ExpansionPanelAccessibilityDemo,
FoggyTabContent,
GridListAccessibilityDemo,
IconAccessibilityDemo,
InputAccessibilityDemo,
@@ -84,15 +89,18 @@ export class AccessibilityRoutingModule {}
ProgressBarAccessibilityDemo,
ProgressSpinnerAccessibilityDemo,
RadioAccessibilityDemo,
ToolbarAccessibilityDemo,
RainyTabContent,
SelectAccessibilityDemo,
SidenavAccessibilityDemo,
SidenavBasicAccessibilityDemo,
SidenavDualAccessibilityDemo,
SidenavMobileAccessibilityDemo,
SliderAccessibilityDemo,
SlideToggleAccessibilityDemo,
SnackBarAccessibilityDemo,
SelectAccessibilityDemo,
TabsAccessibilityDemo,
SunnyTabContent,
RainyTabContent,
FoggyTabContent,
TabsAccessibilityDemo,
ToolbarAccessibilityDemo,
TooltipAccessibilityDemo,
],
entryComponents: [
@@ -102,9 +110,9 @@ export class AccessibilityRoutingModule {}
DialogNeptuneExampleDialog,
DialogNeptuneIFrameDialog,
DialogWelcomeExampleDialog,
SunnyTabContent,
RainyTabContent,
FoggyTabContent,
RainyTabContent,
SunnyTabContent,
]
})
export class AccessibilityDemoModule {}
+11 -7
View File
@@ -1,11 +1,15 @@
<h1>Accessibility Demo</h1>
<ng-container *ngIf="!fullscreen">
<h1>
Accessibility Demo
</h1>
<nav>
<a (click)="skipNavigation()" href="javascript:void(0)">Skip navigation</a>
<a *ngFor="let navItem of navItems" [routerLink]="[navItem.route]" (click)="skipNavigation()">
{{navItem.name}}
</a>
</nav>
<nav>
<a (click)="skipNavigation()" href="javascript:void(0)">Skip navigation</a>
<a *ngFor="let navItem of navItems" [routerLink]="[navItem.route]" (click)="skipNavigation()">
{{navItem.name}}
</a>
</nav>
</ng-container>
<div #maincontent tabindex="-1">
<h1 #header tabindex="-1" *ngIf="!!currentComponent">{{currentComponent}} Demo</h1>
+30 -12
View File
@@ -1,5 +1,6 @@
import {Component, ElementRef, ViewChild, ViewEncapsulation} from '@angular/core';
import {Component, ElementRef, OnDestroy, ViewChild, ViewEncapsulation} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {Subscription} from 'rxjs/Subscription';
@Component({
moduleId: module.id,
@@ -16,17 +17,21 @@ export class AccessibilityHome {}
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
})
export class AccessibilityDemo {
export class AccessibilityDemo implements OnDestroy {
currentComponent: string = '';
fullscreen = false;
private _routerSubscription = Subscription.EMPTY;
@ViewChild('maincontent') mainContent: ElementRef;
@ViewChild('header') sectionHeader: ElementRef;
navItems = [
{name: 'Home', route: '.'},
{name: 'Autocomplete', route: 'autocomplete'},
{name: 'Button', route: 'button'},
{name: 'Button toggle', route: 'button-toggle'},
{name: 'Button', route: 'button'},
{name: 'Card', route: 'card'},
{name: 'Checkbox', route: 'checkbox'},
{name: 'Chips', route: 'chips'},
@@ -40,26 +45,39 @@ export class AccessibilityDemo {
{name: 'Progress bar', route: 'progress-bar'},
{name: 'Progress spinner', route: 'progress-spinner'},
{name: 'Radio buttons', route: 'radio'},
{name: 'Slider', route: 'slider'},
{name: 'Slide toggle', route: 'slide-toggle'},
{name: 'Snack bar', route: 'snack-bar'},
{name: 'Select', route: 'select'},
{name: 'Sidenav', route: 'sidenav'},
{name: 'Slide toggle', route: 'slide-toggle'},
{name: 'Slider', route: 'slider'},
{name: 'Snack bar', route: 'snack-bar'},
{name: 'Tabs', route: 'tabs'},
{name: 'Toolbar', route: 'toolbar'},
{name: 'Tooltip', route: 'tooltip'},
];
constructor(router: Router) {
router.events.subscribe(event => {
let nav = this.navItems.find(navItem => {
let fragments = (event as NavigationEnd).url.split('/');
return fragments[fragments.length - 1] === navItem.route;
});
this.currentComponent = nav ? nav.name : '';
this._routerSubscription = router.events.subscribe((e) => {
if (e instanceof NavigationEnd) {
let fragments = e.url.split('/');
let nav = this.navItems.find(navItem => {
return fragments[fragments.length - 1] === navItem.route;
});
this.currentComponent = nav ? nav.name : '';
let routerState = router.routerState.root;
while (routerState.children.length) {
routerState = routerState.children[0];
}
this.fullscreen = !!routerState.snapshot.data.fullscreen;
}
});
}
skipNavigation() {
(this.currentComponent ? this.sectionHeader : this.mainContent).nativeElement.focus();
}
ngOnDestroy() {
this._routerSubscription.unsubscribe();
}
}
+11 -3
View File
@@ -24,6 +24,10 @@ import {SelectAccessibilityDemo} from './select/select-a11y';
import {TabsAccessibilityDemo} from './tabs/tabs-a11y';
import {TABS_DEMO_ROUTES} from './tabs/routes';
import {TooltipAccessibilityDemo} from './tooltip/tooltip-a11y';
import {SidenavAccessibilityDemo} from './sidenav/sidenav-a11y';
import {SidenavBasicAccessibilityDemo} from './sidenav/basic-sidenav-a11y';
import {SidenavDualAccessibilityDemo} from './sidenav/dual-sidenav-a11y';
import {SidenavMobileAccessibilityDemo} from './sidenav/mobile-sidenav-a11y';
export const ACCESSIBILITY_DEMO_ROUTES: Routes = [
{path: '', component: AccessibilityHome},
@@ -43,10 +47,14 @@ export const ACCESSIBILITY_DEMO_ROUTES: Routes = [
{path: 'progress-bar', component: ProgressBarAccessibilityDemo},
{path: 'progress-spinner', component: ProgressSpinnerAccessibilityDemo},
{path: 'radio', component: RadioAccessibilityDemo},
{path: 'slider', component: SliderAccessibilityDemo},
{path: 'slide-toggle', component: SlideToggleAccessibilityDemo},
{path: 'snack-bar', component: SnackBarAccessibilityDemo},
{path: 'select', component: SelectAccessibilityDemo},
{path: 'sidenav', component: SidenavAccessibilityDemo},
{path: 'sidenav/basic', component: SidenavBasicAccessibilityDemo, data: {fullscreen: true}},
{path: 'sidenav/dual', component: SidenavDualAccessibilityDemo, data: {fullscreen: true}},
{path: 'sidenav/mobile', component: SidenavMobileAccessibilityDemo, data: {fullscreen: true}},
{path: 'slide-toggle', component: SlideToggleAccessibilityDemo},
{path: 'slider', component: SliderAccessibilityDemo},
{path: 'snack-bar', component: SnackBarAccessibilityDemo},
{path: 'tabs', component: TabsAccessibilityDemo, children: TABS_DEMO_ROUTES},
{path: 'toolbar', component: ToolbarAccessibilityDemo},
{path: 'tooltip', component: TooltipAccessibilityDemo},
@@ -0,0 +1,24 @@
<mat-toolbar color="primary" role="header" aria-label="Basic sidenav app">
<button mat-icon-button aria-label="Navigation menu" (click)="snav.toggle()">
<mat-icon aria-hidden="true">menu</mat-icon>
</button>
<h1 class="a11y-demo-sidenav-app-name">Basic Sidenav App</h1>
</mat-toolbar>
<mat-sidenav-container class="a11y-demo-sidenav-container">
<mat-sidenav #snav mode="side" role="navigation">
<mat-nav-list>
<a mat-list-item routerLink="..">Home</a>
<a mat-list-item routerLink="../basic">Basic sidenav example</a>
<a mat-list-item routerLink="../mobile">Responsive sidenav example</a>
<a mat-list-item routerLink="../dual">Dual sidenavs example</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content role="region">
<button mat-raised-button aria-label="Close basic sidenav example" color="primary"
class="a11y-demo-sidenav-close" routerLink="..">
Close example
</button>
</mat-sidenav-content>
</mat-sidenav-container>
@@ -0,0 +1,13 @@
import {Component, ViewEncapsulation} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'basic-sidenav-a11y',
templateUrl: 'basic-sidenav-a11y.html',
styleUrls: ['shared.css'],
host: {'class': 'a11y-demo-sidenav-app'},
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
})
export class SidenavBasicAccessibilityDemo {}
@@ -0,0 +1,43 @@
<mat-toolbar color="primary" role="header" aria-label="Dual sidenav app">
<button mat-icon-button aria-label="Navigation menu" (click)="startSnav.toggle()">
<mat-icon aria-hidden="true">menu</mat-icon>
</button>
<h1 class="a11y-demo-sidenav-app-name">Dual Sidenav App</h1>
<span class="a11y-demo-sidenav-spacer" aria-hidden="true"></span>
<button mat-icon-button aria-label="Playlist menu" (click)="endSnav.toggle()">
<mat-icon aria-hidden="true">playlist_play</mat-icon>
</button>
</mat-toolbar>
<mat-sidenav-container class="a11y-demo-sidenav-container">
<mat-sidenav #startSnav mode="side" role="navigation">
<mat-nav-list>
<a mat-list-item routerLink="..">Home</a>
<a mat-list-item routerLink="../basic">Basic sidenav example</a>
<a mat-list-item routerLink="../mobile">Responsive sidenav example</a>
<a mat-list-item routerLink="../dual">Dual sidenavs example</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content role="region">
<button mat-raised-button aria-label="Close dual sidenav example" color="primary"
class="a11y-demo-sidenav-close" routerLink="..">
Close example
</button>
</mat-sidenav-content>
<mat-sidenav #endSnav mode="side" position="end" class="a11y-demo-sidenav-playlist" role="region"
(open)="playlist1.focus()">
<h2 class="a11y-demo-sidenav-playlist-header">Playlists</h2>
<button #playlist1 mat-button aria-label="Thumbs up playlist" (click)="play('Thumbs up')">
Thumbs up
</button>
<button mat-button aria-label="Last added playlist" (click)="play('Last added')">
Last added
</button>
<button mat-button aria-label="Free and purchased playlist"
(click)="play('Free and purchased')">
Free and purchased
</button>
</mat-sidenav>
</mat-sidenav-container>
@@ -0,0 +1,13 @@
.a11y-demo-sidenav-spacer {
flex: 1;
}
mat-sidenav.a11y-demo-sidenav-playlist {
display: flex;
flex-direction: column;
width: 200px;
}
.a11y-demo-sidenav-playlist-header {
text-align: center;
}
@@ -0,0 +1,20 @@
import {Component, ViewEncapsulation} from '@angular/core';
import {MatSnackBar} from '@angular/material/snack-bar';
@Component({
moduleId: module.id,
selector: 'dual-sidenav-a11y',
templateUrl: 'dual-sidenav-a11y.html',
styleUrls: ['shared.css', 'dual-sidenav-a11y.css'],
host: {'class': 'a11y-demo-sidenav-app'},
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
})
export class SidenavDualAccessibilityDemo {
constructor(private _snackbar: MatSnackBar) {}
play(list: string) {
this._snackbar.open(`Playing "${list}"`, '', {duration: 1000});
}
}
@@ -0,0 +1,34 @@
<mat-toolbar color="primary" role="header" aria-label="Responsive sidenav app"
[class.a11y-demo-sidenav-header-fixed]="mobileQuery.matches">
<button mat-icon-button aria-label="Navigation menu" (click)="snav.toggle()">
<mat-icon aria-hidden="true">menu</mat-icon>
</button>
<h1 class="a11y-demo-sidenav-app-name">Responsive Sidenav App</h1>
</mat-toolbar>
<mat-sidenav-container
class="a11y-demo-sidenav-container"
[class.a11y-demo-sidenav-container-fixed]="mobileQuery.matches"
[style.marginTop.px]="mobileQuery.matches ? 56 : 0">
<mat-sidenav
#snav
role="navigation"
[mode]="mobileQuery.matches ? 'over' : 'side'"
[fixedInViewport]="mobileQuery.matches">
<mat-nav-list>
<a mat-list-item routerLink="..">Home</a>
<a mat-list-item routerLink="../basic">Basic sidenav example</a>
<a mat-list-item routerLink="../mobile">Responsive sidenav example</a>
<a mat-list-item routerLink="../dual">Dual sidenavs example</a>
</mat-nav-list>
<p class="a11y-demo-sidenav-filler" *ngFor="let f of filler">Filler content</p>
</mat-sidenav>
<mat-sidenav-content role="region">
<button mat-raised-button aria-label="Close responsive sidenav example" color="primary"
class="a11y-demo-sidenav-close" routerLink="..">
Close example
</button>
<p class="a11y-demo-sidenav-filler" *ngFor="let f of filler">Filler content</p>
</mat-sidenav-content>
</mat-sidenav-container>
@@ -0,0 +1,4 @@
.a11y-demo-sidenav-header-fixed {
position: fixed;
z-index: 2;
}
@@ -0,0 +1,28 @@
import {ChangeDetectorRef, Component, OnDestroy, ViewEncapsulation} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'mobile-sidenav-a11y',
templateUrl: 'mobile-sidenav-a11y.html',
styleUrls: ['shared.css', 'mobile-sidenav-a11y.css'],
host: {'class': 'a11y-demo-sidenav-app'},
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
})
export class SidenavMobileAccessibilityDemo implements OnDestroy {
mobileQuery = matchMedia('(max-width: 600px)');
filler = Array(20).fill(0);
_mobileQueryListener: () => void;
constructor(changeDetectorRef: ChangeDetectorRef) {
this._mobileQueryListener = () => changeDetectorRef.detectChanges();
this.mobileQuery.addListener(this._mobileQueryListener);
}
ngOnDestroy(): void {
this.mobileQuery.removeListener(this._mobileQueryListener);
}
}
+29
View File
@@ -0,0 +1,29 @@
.a11y-demo-sidenav-app {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.a11y-demo-sidenav-container {
flex: 1;
}
.a11y-demo-sidenav-container-fixed {
flex: none;
}
h1.a11y-demo-sidenav-app-name {
margin-left: 8px;
}
button.a11y-demo-sidenav-close {
margin: 20px;
}
.a11y-demo-sidenav-filler {
margin: 100px 20px;
}
@@ -0,0 +1,20 @@
<section>
<h2>Basic sidenav (e.g. desktop app with hamburger menu)</h2>
<a mat-raised-button aria-label="View basic sidenav example" color="primary" routerLink="basic">
View example
</a>
</section>
<section>
<h2>Mobile sidenav (e.g. responsive design hamburger menu)</h2>
<a mat-raised-button aria-label="View mobile sidenav example" color="primary" routerLink="mobile">
View example
</a>
</section>
<section>
<h2>Dual sidenavs (e.g. music app with hamburger menu and playlist sidenav)</h2>
<a mat-raised-button aria-label="View dual sidenav example" color="primary" routerLink="dual">
View example
</a>
</section>
+10
View File
@@ -0,0 +1,10 @@
import {Component} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'sidenav-a11y',
templateUrl: 'sidenav-a11y.html',
preserveWhitespaces: false,
})
export class SidenavAccessibilityDemo {}
+2 -2
View File
@@ -6,7 +6,7 @@
<mat-toolbar *ngIf="showHeader && !coverHeader">Header</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav #start (open)="myinput.focus()" [mode]="mode"
[fixedInViewport]="fixed" [fixedTopGap]="fixedTop" [fixedBottomGap]="fixedBottom">
[fixedInViewport]="fixed" [fixedTopGap]="fixedTop" [fixedBottomGap]="fixedBottom">
Start Side Sidenav
<br>
<button mat-button (click)="start.close()">Close</button>
@@ -21,7 +21,7 @@
</mat-sidenav>
<mat-sidenav #end position="end"
[fixedInViewport]="fixed" [fixedTopGap]="fixedTop" [fixedBottomGap]="fixedBottom">
[fixedInViewport]="fixed" [fixedTopGap]="fixedTop" [fixedBottomGap]="fixedBottom">
End Side Sidenav
<br>
<button mat-button (click)="end.close()">Close</button>
+1
View File
@@ -7,6 +7,7 @@ import {Component, ViewEncapsulation} from '@angular/core';
templateUrl: 'sidenav-demo.html',
styleUrls: ['sidenav-demo.css'],
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
})
export class SidenavDemo {
isLaunched = false;