From e78bdf91ee1e2a52d0d36ecb915b837046c40ec8 Mon Sep 17 00:00:00 2001 From: btroncone Date: Wed, 13 Dec 2017 19:54:42 -0500 Subject: [PATCH 1/4] fix(a11y): open operator page menu when main nav clicked --- src/app/app.component.html | 10 ++++- src/app/app.component.ts | 11 ++++- src/app/core/core.module.ts | 3 +- .../core/services/operator-menu.service.ts | 19 +++++++++ src/app/operators/operators.component.html | 42 +++++++++---------- src/app/operators/operators.component.ts | 18 +++++++- 6 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 src/app/core/services/operator-menu.service.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 92854828..f736e212 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -4,9 +4,15 @@
- + - + {{menu.title}} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 42f17e39..c25ca087 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -7,6 +7,7 @@ import { } from '@angular/router'; import { filter, map, mergeMap } from 'rxjs/operators'; import { SeoService, SeoData } from './core/services/seo.service'; +import { OperatorMenuService } from './core/services/operator-menu.service'; interface Menu { title: string; @@ -46,7 +47,8 @@ export class AppComponent implements OnInit { constructor( private _router: Router, private _activatedRoute: ActivatedRoute, - private _seo: SeoService + private _seo: SeoService, + private _operatorMenuService: OperatorMenuService ) {} ngOnInit() { @@ -66,4 +68,11 @@ export class AppComponent implements OnInit { ) .subscribe((data: SeoData) => this._seo.setHeaders(data)); } + + shouldOpenChildMenu(title: string) { + // for accessibility we need to ensure child menu is open when clicked + if (title === 'Operators') { + this._operatorMenuService.openOperatorMenu(); + } + } } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 5b4767f3..aed5392f 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -5,6 +5,7 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { CopierService } from './services/copier.service'; import { SeoService } from './services/seo.service'; +import { OperatorMenuService } from './services/operator-menu.service'; import { ToolbarComponent } from './components/toolbar/toolbar.component'; import { MaterialModule } from '../material/material.module'; @@ -17,7 +18,7 @@ export class CoreModule { static forRoot() { return { ngModule: CoreModule, - providers: [CopierService, SeoService] + providers: [CopierService, SeoService, OperatorMenuService] }; } } diff --git a/src/app/core/services/operator-menu.service.ts b/src/app/core/services/operator-menu.service.ts new file mode 100644 index 00000000..74c8399d --- /dev/null +++ b/src/app/core/services/operator-menu.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +@Injectable() +export class OperatorMenuService { + private _operatorMenuStatus = new Subject(); + + openOperatorMenu() { + this._operatorMenuStatus.next(true); + } + + closeOperatorMenu() { + this._operatorMenuStatus.next(false); + } + + menuStatus() { + return this._operatorMenuStatus.asObservable(); + } +} diff --git a/src/app/operators/operators.component.html b/src/app/operators/operators.component.html index 91046a0e..1d26741d 100644 --- a/src/app/operators/operators.component.html +++ b/src/app/operators/operators.component.html @@ -1,31 +1,29 @@ - - - -

{{ category }}

+ + + +

{{ category }}

+ *ngFor="let operator of groupedOperators[category]" + [routerLink]="['/operators', operator.name]" + routerLinkActive="active"> {{ operator.name }}
- - diff --git a/src/app/operators/operators.component.ts b/src/app/operators/operators.component.ts index 8e3da660..0cdff315 100644 --- a/src/app/operators/operators.component.ts +++ b/src/app/operators/operators.component.ts @@ -3,7 +3,9 @@ import { Inject, InjectionToken, OnInit, - ChangeDetectionStrategy + AfterContentInit, + ChangeDetectionStrategy, + ViewChild } from '@angular/core'; import { trigger, @@ -14,9 +16,12 @@ import { } from '@angular/animations'; import { Router, ActivatedRoute } from '@angular/router'; import { BreakpointObserver } from '@angular/cdk/layout'; +import { MatSidenav } from '@angular/material'; import { Subscription } from 'rxjs/Subscription'; import { Observable } from 'rxjs/Observable'; +import { filter } from 'rxjs/operators'; import { OperatorDoc } from '../../operator-docs/operator.model'; +import { OperatorMenuService } from '../core/services/operator-menu.service'; const OPERATOR_MENU_GAP_LARGE = 64; const OPERATOR_MENU_GAP_SMALL = 54; @@ -53,12 +58,14 @@ interface OperatorDocMap { ]) ] }) -export class OperatorsComponent implements OnInit { +export class OperatorsComponent implements OnInit, AfterContentInit { + @ViewChild(MatSidenav) _sidenav: MatSidenav; public groupedOperators: OperatorDocMap; public categories: string[]; constructor( private _breakpointObserver: BreakpointObserver, + private _operatorMenuService: OperatorMenuService, @Inject(OPERATORS_TOKEN) public operators: OperatorDoc[] ) {} @@ -67,6 +74,13 @@ export class OperatorsComponent implements OnInit { this.categories = Object.keys(this.groupedOperators); } + ngAfterContentInit() { + this._operatorMenuService + .menuStatus() + .pipe(filter(s => !!s)) + .subscribe(_ => this._sidenav.open()); + } + get extraSmallScreen() { return this._breakpointObserver.isMatched('(max-width: 601px)'); } From c86061f1f9e8dac2e1039e716057f743b57a0847 Mon Sep 17 00:00:00 2001 From: btroncone Date: Wed, 13 Dec 2017 19:58:27 -0500 Subject: [PATCH 2/4] style(operators): remove unneeded check in operator menu --- src/app/operators/operators.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/operators/operators.component.html b/src/app/operators/operators.component.html index 1d26741d..98470d77 100644 --- a/src/app/operators/operators.component.html +++ b/src/app/operators/operators.component.html @@ -1,6 +1,6 @@ Date: Wed, 13 Dec 2017 20:04:07 -0500 Subject: [PATCH 3/4] fix(operators): clean up menu subscription onDestroy --- src/app/operators/operators.component.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/operators/operators.component.ts b/src/app/operators/operators.component.ts index 0cdff315..2b590937 100644 --- a/src/app/operators/operators.component.ts +++ b/src/app/operators/operators.component.ts @@ -3,6 +3,7 @@ import { Inject, InjectionToken, OnInit, + OnDestroy, AfterContentInit, ChangeDetectionStrategy, ViewChild @@ -18,8 +19,9 @@ import { Router, ActivatedRoute } from '@angular/router'; import { BreakpointObserver } from '@angular/cdk/layout'; import { MatSidenav } from '@angular/material'; import { Subscription } from 'rxjs/Subscription'; +import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; -import { filter } from 'rxjs/operators'; +import { filter, takeUntil } from 'rxjs/operators'; import { OperatorDoc } from '../../operator-docs/operator.model'; import { OperatorMenuService } from '../core/services/operator-menu.service'; @@ -58,10 +60,11 @@ interface OperatorDocMap { ]) ] }) -export class OperatorsComponent implements OnInit, AfterContentInit { +export class OperatorsComponent implements OnInit, AfterContentInit, OnDestroy { @ViewChild(MatSidenav) _sidenav: MatSidenav; public groupedOperators: OperatorDocMap; public categories: string[]; + private _onDestroy = new Subject(); constructor( private _breakpointObserver: BreakpointObserver, @@ -77,7 +80,7 @@ export class OperatorsComponent implements OnInit, AfterContentInit { ngAfterContentInit() { this._operatorMenuService .menuStatus() - .pipe(filter(s => !!s)) + .pipe(filter(s => !!s), takeUntil(this._onDestroy)) .subscribe(_ => this._sidenav.open()); } @@ -98,6 +101,10 @@ export class OperatorsComponent implements OnInit, AfterContentInit { get sidenavMode() { return this.smallScreen ? 'over' : 'side'; } + + ngOnDestroy() { + this._onDestroy.next(); + } } export function groupOperatorsByType(operators: OperatorDoc[]): OperatorDocMap { From 908c2115735dbf80e987e79021d2f252a4250df5 Mon Sep 17 00:00:00 2001 From: btroncone Date: Thu, 14 Dec 2017 09:19:18 -0500 Subject: [PATCH 4/4] test(operators): fix missing provider on failing test --- src/app/operators/specs/operators.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/operators/specs/operators.spec.ts b/src/app/operators/specs/operators.spec.ts index fcf0629a..0cac9d81 100644 --- a/src/app/operators/specs/operators.spec.ts +++ b/src/app/operators/specs/operators.spec.ts @@ -10,6 +10,7 @@ import { groupOperatorsByType } from '../operators.component'; import { OperatorDoc } from '../../../operator-docs'; +import { OperatorMenuService } from '../../core/services/operator-menu.service'; const mockActivatedRoute = { snapshot: {}, @@ -41,6 +42,7 @@ describe('Operators', () => { imports: [RouterTestingModule, LayoutModule], declarations: [OperatorsComponent], providers: [ + OperatorMenuService, { provide: OPERATORS_TOKEN, useValue: mockOperators }, { provide: ActivatedRoute, useValue: mockActivatedRoute } ],