mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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:
@@ -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 {}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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 {}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user