Skip to content

Commit 9b07712

Browse files
mmalerbaandrewseguin
authored andcommitted
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
1 parent 94584db commit 9b07712

17 files changed

+306
-29
lines changed

src/demo-app/a11y/a11y-module.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ import {
4040
FoggyTabContent,
4141
} from './tabs/tabs-a11y';
4242
import {TooltipAccessibilityDemo} from './tooltip/tooltip-a11y';
43+
import {SidenavAccessibilityDemo} from './sidenav/sidenav-a11y';
44+
import {SidenavBasicAccessibilityDemo} from './sidenav/basic-sidenav-a11y';
45+
import {SidenavDualAccessibilityDemo} from './sidenav/dual-sidenav-a11y';
46+
import {SidenavMobileAccessibilityDemo} from './sidenav/mobile-sidenav-a11y';
4347

4448

4549
@NgModule({
@@ -77,22 +81,26 @@ export class AccessibilityRoutingModule {}
7781
DialogNeptuneIFrameDialog,
7882
DialogWelcomeExampleDialog,
7983
ExpansionPanelAccessibilityDemo,
84+
FoggyTabContent,
8085
GridListAccessibilityDemo,
8186
IconAccessibilityDemo,
8287
InputAccessibilityDemo,
8388
MenuAccessibilityDemo,
8489
ProgressBarAccessibilityDemo,
8590
ProgressSpinnerAccessibilityDemo,
8691
RadioAccessibilityDemo,
87-
ToolbarAccessibilityDemo,
92+
RainyTabContent,
93+
SelectAccessibilityDemo,
94+
SidenavAccessibilityDemo,
95+
SidenavBasicAccessibilityDemo,
96+
SidenavDualAccessibilityDemo,
97+
SidenavMobileAccessibilityDemo,
8898
SliderAccessibilityDemo,
8999
SlideToggleAccessibilityDemo,
90100
SnackBarAccessibilityDemo,
91-
SelectAccessibilityDemo,
92-
TabsAccessibilityDemo,
93101
SunnyTabContent,
94-
RainyTabContent,
95-
FoggyTabContent,
102+
TabsAccessibilityDemo,
103+
ToolbarAccessibilityDemo,
96104
TooltipAccessibilityDemo,
97105
],
98106
entryComponents: [
@@ -102,9 +110,9 @@ export class AccessibilityRoutingModule {}
102110
DialogNeptuneExampleDialog,
103111
DialogNeptuneIFrameDialog,
104112
DialogWelcomeExampleDialog,
105-
SunnyTabContent,
106-
RainyTabContent,
107113
FoggyTabContent,
114+
RainyTabContent,
115+
SunnyTabContent,
108116
]
109117
})
110118
export class AccessibilityDemoModule {}

src/demo-app/a11y/a11y.html

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
<h1>Accessibility Demo</h1>
1+
<ng-container *ngIf="!fullscreen">
2+
<h1>
3+
Accessibility Demo
4+
</h1>
25

3-
<nav>
4-
<a (click)="skipNavigation()" href="javascript:void(0)">Skip navigation</a>
5-
<a *ngFor="let navItem of navItems" [routerLink]="[navItem.route]" (click)="skipNavigation()">
6-
{{navItem.name}}
7-
</a>
8-
</nav>
6+
<nav>
7+
<a (click)="skipNavigation()" href="javascript:void(0)">Skip navigation</a>
8+
<a *ngFor="let navItem of navItems" [routerLink]="[navItem.route]" (click)="skipNavigation()">
9+
{{navItem.name}}
10+
</a>
11+
</nav>
12+
</ng-container>
913

1014
<div #maincontent tabindex="-1">
1115
<h1 #header tabindex="-1" *ngIf="!!currentComponent">{{currentComponent}} Demo</h1>

src/demo-app/a11y/a11y.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {Component, ElementRef, ViewChild, ViewEncapsulation} from '@angular/core';
1+
import {Component, ElementRef, OnDestroy, ViewChild, ViewEncapsulation} from '@angular/core';
22
import {NavigationEnd, Router} from '@angular/router';
3+
import {Subscription} from 'rxjs/Subscription';
34

45
@Component({
56
moduleId: module.id,
@@ -16,17 +17,21 @@ export class AccessibilityHome {}
1617
encapsulation: ViewEncapsulation.None,
1718
preserveWhitespaces: false,
1819
})
19-
export class AccessibilityDemo {
20+
export class AccessibilityDemo implements OnDestroy {
2021
currentComponent: string = '';
2122

23+
fullscreen = false;
24+
25+
private _routerSubscription = Subscription.EMPTY;
26+
2227
@ViewChild('maincontent') mainContent: ElementRef;
2328
@ViewChild('header') sectionHeader: ElementRef;
2429

2530
navItems = [
2631
{name: 'Home', route: '.'},
2732
{name: 'Autocomplete', route: 'autocomplete'},
28-
{name: 'Button', route: 'button'},
2933
{name: 'Button toggle', route: 'button-toggle'},
34+
{name: 'Button', route: 'button'},
3035
{name: 'Card', route: 'card'},
3136
{name: 'Checkbox', route: 'checkbox'},
3237
{name: 'Chips', route: 'chips'},
@@ -40,26 +45,39 @@ export class AccessibilityDemo {
4045
{name: 'Progress bar', route: 'progress-bar'},
4146
{name: 'Progress spinner', route: 'progress-spinner'},
4247
{name: 'Radio buttons', route: 'radio'},
43-
{name: 'Slider', route: 'slider'},
48+
{name: 'Select', route: 'select'},
49+
{name: 'Sidenav', route: 'sidenav'},
4450
{name: 'Slide toggle', route: 'slide-toggle'},
51+
{name: 'Slider', route: 'slider'},
4552
{name: 'Snack bar', route: 'snack-bar'},
46-
{name: 'Select', route: 'select'},
4753
{name: 'Tabs', route: 'tabs'},
4854
{name: 'Toolbar', route: 'toolbar'},
4955
{name: 'Tooltip', route: 'tooltip'},
5056
];
5157

5258
constructor(router: Router) {
53-
router.events.subscribe(event => {
54-
let nav = this.navItems.find(navItem => {
55-
let fragments = (event as NavigationEnd).url.split('/');
56-
return fragments[fragments.length - 1] === navItem.route;
57-
});
58-
this.currentComponent = nav ? nav.name : '';
59+
this._routerSubscription = router.events.subscribe((e) => {
60+
if (e instanceof NavigationEnd) {
61+
let fragments = e.url.split('/');
62+
let nav = this.navItems.find(navItem => {
63+
return fragments[fragments.length - 1] === navItem.route;
64+
});
65+
this.currentComponent = nav ? nav.name : '';
66+
67+
let routerState = router.routerState.root;
68+
while (routerState.children.length) {
69+
routerState = routerState.children[0];
70+
}
71+
this.fullscreen = !!routerState.snapshot.data.fullscreen;
72+
}
5973
});
6074
}
6175

6276
skipNavigation() {
6377
(this.currentComponent ? this.sectionHeader : this.mainContent).nativeElement.focus();
6478
}
79+
80+
ngOnDestroy() {
81+
this._routerSubscription.unsubscribe();
82+
}
6583
}

src/demo-app/a11y/routes.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import {SelectAccessibilityDemo} from './select/select-a11y';
2424
import {TabsAccessibilityDemo} from './tabs/tabs-a11y';
2525
import {TABS_DEMO_ROUTES} from './tabs/routes';
2626
import {TooltipAccessibilityDemo} from './tooltip/tooltip-a11y';
27+
import {SidenavAccessibilityDemo} from './sidenav/sidenav-a11y';
28+
import {SidenavBasicAccessibilityDemo} from './sidenav/basic-sidenav-a11y';
29+
import {SidenavDualAccessibilityDemo} from './sidenav/dual-sidenav-a11y';
30+
import {SidenavMobileAccessibilityDemo} from './sidenav/mobile-sidenav-a11y';
2731

2832
export const ACCESSIBILITY_DEMO_ROUTES: Routes = [
2933
{path: '', component: AccessibilityHome},
@@ -43,10 +47,14 @@ export const ACCESSIBILITY_DEMO_ROUTES: Routes = [
4347
{path: 'progress-bar', component: ProgressBarAccessibilityDemo},
4448
{path: 'progress-spinner', component: ProgressSpinnerAccessibilityDemo},
4549
{path: 'radio', component: RadioAccessibilityDemo},
46-
{path: 'slider', component: SliderAccessibilityDemo},
50+
{path: 'select', component: SelectAccessibilityDemo},
51+
{path: 'sidenav', component: SidenavAccessibilityDemo},
52+
{path: 'sidenav/basic', component: SidenavBasicAccessibilityDemo, data: {fullscreen: true}},
53+
{path: 'sidenav/dual', component: SidenavDualAccessibilityDemo, data: {fullscreen: true}},
54+
{path: 'sidenav/mobile', component: SidenavMobileAccessibilityDemo, data: {fullscreen: true}},
4755
{path: 'slide-toggle', component: SlideToggleAccessibilityDemo},
56+
{path: 'slider', component: SliderAccessibilityDemo},
4857
{path: 'snack-bar', component: SnackBarAccessibilityDemo},
49-
{path: 'select', component: SelectAccessibilityDemo},
5058
{path: 'tabs', component: TabsAccessibilityDemo, children: TABS_DEMO_ROUTES},
5159
{path: 'toolbar', component: ToolbarAccessibilityDemo},
5260
{path: 'tooltip', component: TooltipAccessibilityDemo},
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<mat-toolbar color="primary" role="header" aria-label="Basic sidenav app">
2+
<button mat-icon-button aria-label="Navigation menu" (click)="snav.toggle()">
3+
<mat-icon aria-hidden="true">menu</mat-icon>
4+
</button>
5+
<h1 class="a11y-demo-sidenav-app-name">Basic Sidenav App</h1>
6+
</mat-toolbar>
7+
8+
<mat-sidenav-container class="a11y-demo-sidenav-container">
9+
<mat-sidenav #snav mode="side" role="navigation">
10+
<mat-nav-list>
11+
<a mat-list-item routerLink="..">Home</a>
12+
<a mat-list-item routerLink="../basic">Basic sidenav example</a>
13+
<a mat-list-item routerLink="../mobile">Responsive sidenav example</a>
14+
<a mat-list-item routerLink="../dual">Dual sidenavs example</a>
15+
</mat-nav-list>
16+
</mat-sidenav>
17+
18+
<mat-sidenav-content role="region">
19+
<button mat-raised-button aria-label="Close basic sidenav example" color="primary"
20+
class="a11y-demo-sidenav-close" routerLink="..">
21+
Close example
22+
</button>
23+
</mat-sidenav-content>
24+
</mat-sidenav-container>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {Component, ViewEncapsulation} from '@angular/core';
2+
3+
4+
@Component({
5+
moduleId: module.id,
6+
selector: 'basic-sidenav-a11y',
7+
templateUrl: 'basic-sidenav-a11y.html',
8+
styleUrls: ['shared.css'],
9+
host: {'class': 'a11y-demo-sidenav-app'},
10+
encapsulation: ViewEncapsulation.None,
11+
preserveWhitespaces: false,
12+
})
13+
export class SidenavBasicAccessibilityDemo {}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<mat-toolbar color="primary" role="header" aria-label="Dual sidenav app">
2+
<button mat-icon-button aria-label="Navigation menu" (click)="startSnav.toggle()">
3+
<mat-icon aria-hidden="true">menu</mat-icon>
4+
</button>
5+
<h1 class="a11y-demo-sidenav-app-name">Dual Sidenav App</h1>
6+
<span class="a11y-demo-sidenav-spacer" aria-hidden="true"></span>
7+
<button mat-icon-button aria-label="Playlist menu" (click)="endSnav.toggle()">
8+
<mat-icon aria-hidden="true">playlist_play</mat-icon>
9+
</button>
10+
</mat-toolbar>
11+
12+
<mat-sidenav-container class="a11y-demo-sidenav-container">
13+
<mat-sidenav #startSnav mode="side" role="navigation">
14+
<mat-nav-list>
15+
<a mat-list-item routerLink="..">Home</a>
16+
<a mat-list-item routerLink="../basic">Basic sidenav example</a>
17+
<a mat-list-item routerLink="../mobile">Responsive sidenav example</a>
18+
<a mat-list-item routerLink="../dual">Dual sidenavs example</a>
19+
</mat-nav-list>
20+
</mat-sidenav>
21+
22+
<mat-sidenav-content role="region">
23+
<button mat-raised-button aria-label="Close dual sidenav example" color="primary"
24+
class="a11y-demo-sidenav-close" routerLink="..">
25+
Close example
26+
</button>
27+
</mat-sidenav-content>
28+
29+
<mat-sidenav #endSnav mode="side" position="end" class="a11y-demo-sidenav-playlist" role="region"
30+
(open)="playlist1.focus()">
31+
<h2 class="a11y-demo-sidenav-playlist-header">Playlists</h2>
32+
<button #playlist1 mat-button aria-label="Thumbs up playlist" (click)="play('Thumbs up')">
33+
Thumbs up
34+
</button>
35+
<button mat-button aria-label="Last added playlist" (click)="play('Last added')">
36+
Last added
37+
</button>
38+
<button mat-button aria-label="Free and purchased playlist"
39+
(click)="play('Free and purchased')">
40+
Free and purchased
41+
</button>
42+
</mat-sidenav>
43+
</mat-sidenav-container>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.a11y-demo-sidenav-spacer {
2+
flex: 1;
3+
}
4+
5+
mat-sidenav.a11y-demo-sidenav-playlist {
6+
display: flex;
7+
flex-direction: column;
8+
width: 200px;
9+
}
10+
11+
.a11y-demo-sidenav-playlist-header {
12+
text-align: center;
13+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {Component, ViewEncapsulation} from '@angular/core';
2+
import {MatSnackBar} from '@angular/material/snack-bar';
3+
4+
5+
@Component({
6+
moduleId: module.id,
7+
selector: 'dual-sidenav-a11y',
8+
templateUrl: 'dual-sidenav-a11y.html',
9+
styleUrls: ['shared.css', 'dual-sidenav-a11y.css'],
10+
host: {'class': 'a11y-demo-sidenav-app'},
11+
encapsulation: ViewEncapsulation.None,
12+
preserveWhitespaces: false,
13+
})
14+
export class SidenavDualAccessibilityDemo {
15+
constructor(private _snackbar: MatSnackBar) {}
16+
17+
play(list: string) {
18+
this._snackbar.open(`Playing "${list}"`, '', {duration: 1000});
19+
}
20+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<mat-toolbar color="primary" role="header" aria-label="Responsive sidenav app"
2+
[class.a11y-demo-sidenav-header-fixed]="mobileQuery.matches">
3+
<button mat-icon-button aria-label="Navigation menu" (click)="snav.toggle()">
4+
<mat-icon aria-hidden="true">menu</mat-icon>
5+
</button>
6+
<h1 class="a11y-demo-sidenav-app-name">Responsive Sidenav App</h1>
7+
</mat-toolbar>
8+
9+
<mat-sidenav-container
10+
class="a11y-demo-sidenav-container"
11+
[class.a11y-demo-sidenav-container-fixed]="mobileQuery.matches"
12+
[style.marginTop.px]="mobileQuery.matches ? 56 : 0">
13+
<mat-sidenav
14+
#snav
15+
role="navigation"
16+
[mode]="mobileQuery.matches ? 'over' : 'side'"
17+
[fixedInViewport]="mobileQuery.matches">
18+
<mat-nav-list>
19+
<a mat-list-item routerLink="..">Home</a>
20+
<a mat-list-item routerLink="../basic">Basic sidenav example</a>
21+
<a mat-list-item routerLink="../mobile">Responsive sidenav example</a>
22+
<a mat-list-item routerLink="../dual">Dual sidenavs example</a>
23+
</mat-nav-list>
24+
<p class="a11y-demo-sidenav-filler" *ngFor="let f of filler">Filler content</p>
25+
</mat-sidenav>
26+
27+
<mat-sidenav-content role="region">
28+
<button mat-raised-button aria-label="Close responsive sidenav example" color="primary"
29+
class="a11y-demo-sidenav-close" routerLink="..">
30+
Close example
31+
</button>
32+
<p class="a11y-demo-sidenav-filler" *ngFor="let f of filler">Filler content</p>
33+
</mat-sidenav-content>
34+
</mat-sidenav-container>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.a11y-demo-sidenav-header-fixed {
2+
position: fixed;
3+
z-index: 2;
4+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {ChangeDetectorRef, Component, OnDestroy, ViewEncapsulation} from '@angular/core';
2+
3+
4+
@Component({
5+
moduleId: module.id,
6+
selector: 'mobile-sidenav-a11y',
7+
templateUrl: 'mobile-sidenav-a11y.html',
8+
styleUrls: ['shared.css', 'mobile-sidenav-a11y.css'],
9+
host: {'class': 'a11y-demo-sidenav-app'},
10+
encapsulation: ViewEncapsulation.None,
11+
preserveWhitespaces: false,
12+
})
13+
export class SidenavMobileAccessibilityDemo implements OnDestroy {
14+
mobileQuery = matchMedia('(max-width: 600px)');
15+
16+
filler = Array(20).fill(0);
17+
18+
_mobileQueryListener: () => void;
19+
20+
constructor(changeDetectorRef: ChangeDetectorRef) {
21+
this._mobileQueryListener = () => changeDetectorRef.detectChanges();
22+
this.mobileQuery.addListener(this._mobileQueryListener);
23+
}
24+
25+
ngOnDestroy(): void {
26+
this.mobileQuery.removeListener(this._mobileQueryListener);
27+
}
28+
}

0 commit comments

Comments
 (0)