docs: imrpove accessibility of lifecycle hooks example (#41071)

PR Close #41071
This commit is contained in:
Kapunahele Wong 2021-03-03 10:30:47 -05:00 committed by Jessica Janiuk
parent 3ccad85b01
commit 695b72a972
21 changed files with 157 additions and 116 deletions

View File

@ -86,7 +86,7 @@ describe('Lifecycle hooks', () => {
const parentEle = element(by.tagName('after-view-parent')); const parentEle = element(by.tagName('after-view-parent'));
const buttonEle = parentEle.element(by.tagName('button')); // Reset const buttonEle = parentEle.element(by.tagName('button')); // Reset
const commentEle = parentEle.element(by.className('comment')); const commentEle = parentEle.element(by.className('comment'));
const logEles = parentEle.all(by.css('h4 ~ div')); const logEles = parentEle.all(by.css('h3 ~ div'));
const childViewInputEle = parentEle.element(by.css('app-child-view input')); const childViewInputEle = parentEle.element(by.css('app-child-view input'));
let logCount: number; let logCount: number;
@ -113,7 +113,7 @@ describe('Lifecycle hooks', () => {
const parentEle = element(by.tagName('after-content-parent')); const parentEle = element(by.tagName('after-content-parent'));
const buttonEle = parentEle.element(by.tagName('button')); // Reset const buttonEle = parentEle.element(by.tagName('button')); // Reset
const commentEle = parentEle.element(by.className('comment')); const commentEle = parentEle.element(by.className('comment'));
const logEles = parentEle.all(by.css('h4 ~ div')); const logEles = parentEle.all(by.css('h3 ~ div'));
const childViewInputEle = parentEle.element(by.css('app-child input')); const childViewInputEle = parentEle.element(by.css('app-child input'));
let logCount = await logEles.count(); let logCount = await logEles.count();
@ -136,8 +136,8 @@ describe('Lifecycle hooks', () => {
const inputEle = element(by.css('spy-parent input')); const inputEle = element(by.css('spy-parent input'));
const addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero')); const addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero'));
const resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes')); const resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes'));
const heroEles = element.all(by.css('spy-parent div[appSpy')); const heroEles = element.all(by.css('spy-parent div p'));
const logEles = element.all(by.css('spy-parent h4 ~ div')); const logEles = element.all(by.css('spy-parent h3 ~ div'));
expect(await heroEles.count()).toBe(2, 'should have two heroes displayed'); expect(await heroEles.count()).toBe(2, 'should have two heroes displayed');
expect(await logEles.count()).toBe(2, 'should have two log entries'); expect(await logEles.count()).toBe(2, 'should have two log entries');
@ -156,18 +156,19 @@ describe('Lifecycle hooks', () => {
it('should support "spy counter"', async () => { it('should support "spy counter"', async () => {
const updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update')); const updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update'));
const resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset')); const resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset'));
const textEle = element(by.css('counter-parent app-counter > div')); const textEle = element(by.css('counter-parent app-counter p'));
const logEles = element.all(by.css('counter-parent h4 ~ div')); const logEles = element.all(by.css('counter-parent .info .log'));
expect(await textEle.getText()).toContain('Counter = 0'); expect(await textEle.getText()).toContain('Counter = 0');
expect(await logEles.count()).toBe(2, 'should start with two log entries'); expect(await logEles.count()).toBe(3, 'should start with one change log and two lifecycle log entries, including reset');
await updateCounterButtonEle.click(); await updateCounterButtonEle.click();
expect(await textEle.getText()).toContain('Counter = 1'); expect(await textEle.getText()).toContain('Counter = 1');
expect(await logEles.count()).toBe(3, 'should now have 3 log entries'); expect(await logEles.count()).toBe(5, 'should now have 2 change log entries and 3 lifecycle log entries, including reset');
await resetCounterButtonEle.click(); await resetCounterButtonEle.click();
expect(await textEle.getText()).toContain('Counter = 0'); expect(await textEle.getText()).toContain('Counter = 0');
expect(await logEles.count()).toBe(7, 'should now have 7 log entries - 3 prev + 1 reset + 2 destroy + 1 init'); expect(await logEles.count()).toBe(8, 'should now have 8 log entries - 1 change log + 2 reset + 2 destroy + 3 init');
}); });
}); });

View File

@ -16,12 +16,13 @@ import { LoggerService } from './logger.service';
// #enddocregion parent-template // #enddocregion parent-template
+ `</div> + `</div>
<h4>-- AfterContent Logs --</h4> <div class="info">
<p><button (click)="reset()">Reset</button></p> <h3>AfterContent Logs</h3>
<div *ngFor="let msg of logger.logs">{{msg}}</div> <button (click)="reset()">Reset</button>
<div *ngFor="let msg of logger.logs" class="log">{{msg}}</div>
</div>
</div> </div>
`, `,
styles: ['.parent {background: burlywood}'],
providers: [LoggerService] providers: [LoggerService]
}) })
export class AfterContentParentComponent { export class AfterContentParentComponent {

View File

@ -9,9 +9,9 @@ import { LoggerService } from './logger.service';
selector: 'after-content', selector: 'after-content',
// #docregion template // #docregion template
template: ` template: `
<div>-- projected content begins --</div> <div>projected content begins</div>
<ng-content></ng-content> <ng-content></ng-content>
<div>-- projected content ends --</div>` <div>projected content ends</div>`
// #enddocregion template // #enddocregion template
+ ` + `
<p *ngIf="comment" class="comment"> <p *ngIf="comment" class="comment">

View File

@ -5,17 +5,16 @@ import { LoggerService } from './logger.service';
@Component({ @Component({
selector: 'after-view-parent', selector: 'after-view-parent',
template: ` template: `
<div class="parent"> <h2>AfterView</h2>
<h2>AfterView</h2>
<after-view *ngIf="show"></after-view> <after-view *ngIf="show"></after-view>
<h4>-- AfterView Logs --</h4> <div class="info">
<p><button (click)="reset()">Reset</button></p> <h3>AfterView Logs</h3>
<div *ngFor="let msg of logger.logs">{{msg}}</div> <button (click)="reset()">Reset</button>
<div *ngFor="let msg of logger.logs" class="log">{{msg}}</div>
</div> </div>
`, `,
styles: ['.parent {background: burlywood}'],
providers: [LoggerService] providers: [LoggerService]
}) })
export class AfterViewParentComponent { export class AfterViewParentComponent {

View File

@ -9,9 +9,9 @@ import { LoggerService } from './logger.service';
selector: 'after-view', selector: 'after-view',
// #docregion template // #docregion template
template: ` template: `
<div>-- child view begins --</div> <div>child view begins</div>
<app-child-view></app-child-view> <app-child-view></app-child-view>
<div>-- child view ends --</div>` <div>child view ends</div>`
// #enddocregion template // #enddocregion template
+ ` + `
<p *ngIf="comment" class="comment"> <p *ngIf="comment" class="comment">

View File

@ -1,36 +1,42 @@
<a id="top"></a> <a id="top"></a>
<h1>Component Lifecycle Hooks</h1> <h1>Lifecycle Hooks</h1>
<a href="#hooks">Peek-a-boo: (most) lifecycle hooks</a><br> <a href="#hooks">Peek-a-boo: (most) lifecycle hooks</a>
<a href="#spy">Spy: directive with OnInit & OnDestroy</a><br> <a href="#spy">Spy: directive with OnInit & OnDestroy</a>
<a href="#onchanges">OnChanges</a><br> <a href="#onchanges">OnChanges</a>
<a href="#docheck">DoCheck</a><br> <a href="#docheck">DoCheck</a>
<a href="#after-view">AfterViewInit & AfterViewChecked</a><br> <a href="#after-view">AfterViewInit & AfterViewChecked</a>
<a href="#after-content">AfterContentInit & AfterContentChecked</a><br> <a href="#after-content">AfterContentInit & AfterContentChecked</a>
<a href="#counter">Counter: OnChanges + Spy directive</a><br> <a href="#counter">Counter: OnChanges + Spy directive</a>
<a id="hooks"></a> <a id="hooks"></a>
<peek-a-boo-parent></peek-a-boo-parent> <peek-a-boo-parent></peek-a-boo-parent>
<a href="#top">back to top</a> <a href="#top">back to top</a>
<hr />
<a id="spy"></a> <a id="spy"></a>
<spy-parent></spy-parent> <spy-parent></spy-parent>
<a href="#top">back to top</a> <a href="#top">back to top</a>
<hr />
<a id="onchanges"></a> <a id="onchanges"></a>
<on-changes-parent></on-changes-parent> <on-changes-parent></on-changes-parent>
<a href="#top">back to top</a> <a href="#top">back to top</a>
<hr />
<a id="docheck"></a> <a id="docheck"></a>
<do-check-parent></do-check-parent> <do-check-parent></do-check-parent>
<a href="#top">back to top</a> <a href="#top">back to top</a>
<hr />
<a id="after-view"></a> <a id="after-view"></a>
<after-view-parent></after-view-parent> <after-view-parent></after-view-parent>
<a href="#top">back to top</a> <a href="#top">back to top</a>
<hr />
<a id="after-content"></a> <a id="after-content"></a>
<after-content-parent></after-content-parent> <after-content-parent></after-content-parent>
<a href="#top">back to top</a> <a href="#top">back to top</a>
<hr />
<a id="counter"></a> <a id="counter"></a>
<counter-parent></counter-parent> <counter-parent></counter-parent>

View File

@ -3,7 +3,8 @@ import { Component } from '@angular/core';
// #docregion child-view // #docregion child-view
@Component({ @Component({
selector: 'app-child-view', selector: 'app-child-view',
template: '<input [(ngModel)]="hero">' template: `<label for="hero-name">Hero name: </label>
<input type="text" id="hero-name" [(ngModel)]="hero">`
}) })
export class ChildViewComponent { export class ChildViewComponent {
hero = 'Magneta'; hero = 'Magneta';

View File

@ -2,7 +2,8 @@ import { Component } from '@angular/core';
@Component({ @Component({
selector: 'app-child', selector: 'app-child',
template: '<input [(ngModel)]="hero">' template: `<label for="hero-name">Hero name: </label>
<input type="text" id="hero-name" [(ngModel)]="hero">`
}) })
export class ChildComponent { export class ChildComponent {
hero = 'Magneta'; hero = 'Magneta';

View File

@ -5,19 +5,18 @@ import { LoggerService } from './logger.service';
@Component({ @Component({
selector: 'counter-parent', selector: 'counter-parent',
template: ` template: `
<div class="parent"> <h2>Counter Spy</h2>
<h2>Counter Spy</h2>
<button (click)="updateCounter()">Update counter</button> <button (click)="updateCounter()">Update counter</button>
<button (click)="reset()">Reset Counter</button> <button (click)="reset()">Reset Counter</button>
<app-counter [counter]="value"></app-counter> <app-counter [counter]="value"></app-counter>
<h4>-- Spy Lifecycle Hook Log --</h4> <div class="info">
<div *ngFor="let msg of spyLog">{{msg}}</div> <h3>Spy Lifecycle Hook Log</h3>
<div *ngFor="let msg of spyLog" class="log">{{msg}}</div>
</div> </div>
`, `,
styles: ['.parent {background: gold;}'],
providers: [LoggerService] providers: [LoggerService]
}) })
export class CounterParentComponent { export class CounterParentComponent {
@ -38,7 +37,7 @@ export class CounterParentComponent {
} }
reset() { reset() {
this.logger.log('-- reset --'); this.logger.log('reset');
this.value = 0; this.value = 0;
this.logger.tick(); this.logger.tick();
} }

View File

@ -7,14 +7,13 @@ import {
@Component({ @Component({
selector: 'app-counter', selector: 'app-counter',
template: ` template: `
<div class="counter"> <p>Counter = {{counter}}</p>
Counter = {{counter}}
<h5>-- Counter Change Log --</h5> <div class="info">
<div *ngFor="let chg of changeLog" appSpy>{{chg}}</div> <h3>Counter Change Log</h3>
</div> <div *ngFor="let chg of changeLog" appSpy class="log">{{chg}}</div>
`, </div>
styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'] `
}) })
export class MyCounterComponent implements OnChanges { export class MyCounterComponent implements OnChanges {
@Input() counter: number; @Input() counter: number;

View File

@ -1,11 +1,9 @@
<div class="parent"> <h2>{{title}}</h2>
<h2>{{title}}</h2> <label for="power-input"><label>Power: </label>
<input type="text" id="power-input" [(ngModel)]="power">
<label for="hero-name">Hero.name: </label>
<input type="text" id="hero-name" [(ngModel)]="hero.name">
<button (click)="reset()">Reset Log</button>
<table> <do-check [hero]="hero" [power]="power"></do-check>
<tr><td>Power: </td><td><input [(ngModel)]="power"></td></tr>
<tr><td>Hero.name: </td><td><input [(ngModel)]="hero.name"></td></tr>
</table>
<p><button (click)="reset()">Reset Log</button></p>
<do-check [hero]="hero" [power]="power"></do-check>
</div>

View File

@ -5,8 +5,7 @@ import { Hero } from './hero';
@Component({ @Component({
selector: 'do-check-parent', selector: 'do-check-parent',
templateUrl: './do-check-parent.component.html', templateUrl: './do-check-parent.component.html'
styles: ['.parent {background: Lavender}']
}) })
export class DoCheckParentComponent { export class DoCheckParentComponent {
hero: Hero; hero: Hero;

View File

@ -7,17 +7,13 @@ import { Hero } from './hero';
@Component({ @Component({
selector: 'do-check', selector: 'do-check',
template: ` template: `
<div class="hero"> <div class="info">
<p>{{hero.name}} can {{power}}</p> <p>{{hero.name}} can {{power}}</p>
<h4>-- Change Log --</h4> <h3>Change Log</h3>
<div *ngFor="let chg of changeLog">{{chg}}</div> <div *ngFor="let chg of changeLog" class="log">{{chg}}</div>
</div> </div>
`, `
styles: [
'.hero {background: LightYellow; padding: 8px; margin-top: 8px}',
'p {background: Yellow; padding: 8px; margin-top: 8px}'
]
}) })
export class DoCheckComponent implements DoCheck { export class DoCheckComponent implements DoCheck {
@Input() hero: Hero; @Input() hero: Hero;

View File

@ -1,13 +1,11 @@
<div class="parent"> <h2>{{title}}</h2>
<h2>{{title}}</h2> <label for="power-input">Power: </label>
<input type="text" id="power-input" [(ngModel)]="power">
<label for="hero-name"> Hero.name: </label>
<input type="text" id="hero-name" [(ngModel)]="hero.name">
<table> <button (click)="reset()">Reset Log</button>
<tr><td>Power: </td><td><input [(ngModel)]="power"></td></tr>
<tr><td>Hero.name: </td><td><input [(ngModel)]="hero.name"></td></tr>
</table>
<p><button (click)="reset()">Reset Log</button></p>
<!-- #docregion on-changes --> <!-- #docregion on-changes -->
<on-changes [hero]="hero" [power]="power"></on-changes> <on-changes [hero]="hero" [power]="power"></on-changes>
<!-- #enddocregion on-changes --> <!-- #enddocregion on-changes -->
</div>

View File

@ -6,7 +6,7 @@ import { OnChangesComponent } from './on-changes.component';
@Component({ @Component({
selector: 'on-changes-parent', selector: 'on-changes-parent',
templateUrl: './on-changes-parent.component.html', templateUrl: './on-changes-parent.component.html',
styles: ['.parent {background: Lavender;}'] styles: ['']
}) })
export class OnChangesParentComponent { export class OnChangesParentComponent {
hero: Hero; hero: Hero;

View File

@ -7,17 +7,13 @@ import { Hero } from './hero';
@Component({ @Component({
selector: 'on-changes', selector: 'on-changes',
template: ` template: `
<div class="hero"> <div class="info">
<p>{{hero.name}} can {{power}}</p> <p>{{hero.name}} can {{power}}</p>
<h4>-- Change Log --</h4> <h3>Change Log</h3>
<div *ngFor="let chg of changeLog">{{chg}}</div> <div *ngFor="let chg of changeLog" class="log">{{chg}}</div>
</div> </div>
`, `
styles: [
'.hero {background: LightYellow; padding: 8px; margin-top: 8px}',
'p {background: Yellow; padding: 8px; margin-top: 8px}'
]
}) })
export class OnChangesComponent implements OnChanges { export class OnChangesComponent implements OnChanges {
// #docregion inputs // #docregion inputs

View File

@ -6,6 +6,7 @@ import { LoggerService } from './logger.service';
@Component({ @Component({
selector: 'peek-a-boo-parent', selector: 'peek-a-boo-parent',
template: ` template: `
<hr />
<div class="parent"> <div class="parent">
<h2>Peek-A-Boo</h2> <h2>Peek-A-Boo</h2>
@ -14,14 +15,14 @@ import { LoggerService } from './logger.service';
</button> </button>
<button (click)="updateHero()" [hidden]="!hasChild">Update Hero</button> <button (click)="updateHero()" [hidden]="!hasChild">Update Hero</button>
<peek-a-boo *ngIf="hasChild" [name]="heroName"> <div class="info">
</peek-a-boo> <peek-a-boo *ngIf="hasChild" [name]="heroName"></peek-a-boo>
<h4>-- Lifecycle Hook Log --</h4> <h3>Lifecycle Hook Log</h3>
<div *ngFor="let msg of hookLog">{{msg}}</div> <div *ngFor="let msg of hookLog" class="log">{{msg}}</div>
</div>
</div> </div>
`, `,
styles: ['.parent {background: moccasin}'],
providers: [ LoggerService ] providers: [ LoggerService ]
}) })
export class PeekABooParentComponent { export class PeekABooParentComponent {

View File

@ -19,8 +19,7 @@ import { PeekABooDirective } from './peek-a-boo.directive';
@Component({ @Component({
selector: 'peek-a-boo', selector: 'peek-a-boo',
template: '<p>Now you see my hero, {{name}}</p>', template: '<p>Now you see my hero, {{name}}</p>'
styles: ['p {background: LightYellow; padding: 8px}']
}) })
// Don't HAVE to mention the Lifecycle Hook interfaces // Don't HAVE to mention the Lifecycle Hook interfaces
// unless we want typing and tool support. // unless we want typing and tool support.

View File

@ -1,16 +1,19 @@
<div class="parent"> <div class="parent">
<h2>Spy Directive</h2> <h2>Spy Directive</h2>
<input [(ngModel)]="newName" (keyup.enter)="addHero()"> <label for="hero-name">Hero name: </label>
<input type="text" id="hero-name" [(ngModel)]="newName" (keyup.enter)="addHero()">
<button (click)="addHero()">Add Hero</button> <button (click)="addHero()">Add Hero</button>
<button (click)="reset()">Reset Heroes</button> <button (click)="reset()">Reset Heroes</button>
<p></p> <div class="info">
<!-- #docregion template --> <!-- #docregion template -->
<div *ngFor="let hero of heroes" appSpy class="heroes"> <p *ngFor="let hero of heroes" appSpy>
{{hero}} {{hero}}
</p>
<!-- #enddocregion template -->
<h3>Spy Lifecycle Hook Log</h3>
<div *ngFor="let msg of logger.logs" class="log">{{msg}}</div>
</div> </div>
<!-- #enddocregion template -->
<h4>-- Spy Lifecycle Hook Log --</h4>
<div *ngFor="let msg of logger.logs">{{msg}}</div>
</div> </div>

View File

@ -6,10 +6,6 @@ import { LoggerService } from './logger.service';
@Component({ @Component({
selector: 'spy-parent', selector: 'spy-parent',
templateUrl: './spy.component.html', templateUrl: './spy.component.html',
styles: [
'.parent {background: khaki;}',
'.heroes {background: LightYellow; padding: 0 8px}'
],
providers: [LoggerService] providers: [LoggerService]
}) })
export class SpyParentComponent { export class SpyParentComponent {
@ -31,7 +27,7 @@ export class SpyParentComponent {
this.logger.tick(); this.logger.tick();
} }
reset() { reset() {
this.logger.log('-- reset --'); this.logger.log('reset');
this.heroes = []; this.heroes = [];
this.logger.tick(); this.logger.tick();
} }

View File

@ -1,13 +1,61 @@
.parent { .parent {
color: #666; padding: 2rem 0;
margin: 14px 0;
padding: 8px;
} }
input {
margin: 4px; h2 {
padding: 4px; text-align: center;
} }
.comment { .comment {
color: red; color: #890000;
font-style: italic; font-style: italic;
} }
.log {
background-color: #e4e4e4;
padding: .25rem;
}
.info {
background: #fff4f4;
padding: .75rem;
margin-top: 1rem;
}
p {
padding: .25rem;
}
a {
display: block;
padding-bottom: .5rem;
font-size: 1.1rem;
color: #264d73
}
a:hover {
text-decoration: none;
}
a[href="#top"] {
margin: 1rem 0;
padding: .5rem;
background-color: #264d73;
color: #fff;
}
a[href="#top"]:hover {
background-color:#eaeaea;
color: black;
}
a[href="#top"]:active {
background-color: black;
color: #eaeaea;
}
input {
margin-bottom: 1rem;
margin-top: .5rem;
font-size: 1rem;
}