docs: improve accessibility of toh-pt6 (#40805)

PR Close #40805
This commit is contained in:
Kapunahele Wong 2021-02-08 10:05:25 -05:00 committed by atscott
parent 596dfb88a6
commit edd26a2b79
15 changed files with 167 additions and 162 deletions

View File

@ -53,7 +53,7 @@ describe('Tutorial part 6', () => {
appDashboardHref: navElts.get(0), appDashboardHref: navElts.get(0),
appDashboard: element(by.css('app-root app-dashboard')), appDashboard: element(by.css('app-root app-dashboard')),
topHeroes: element.all(by.css('app-root app-dashboard > div h4')), topHeroes: element.all(by.css('app-root app-dashboard > div a')),
appHeroesHref: navElts.get(1), appHeroesHref: navElts.get(1),
appHeroes: element(by.css('app-root app-heroes')), appHeroes: element(by.css('app-root app-heroes')),
@ -176,7 +176,7 @@ describe('Tutorial part 6', () => {
const numHeroes = heroesBefore.length; const numHeroes = heroesBefore.length;
await element(by.css('input')).sendKeys(addedHeroName); await element(by.css('input')).sendKeys(addedHeroName);
await element(by.buttonText('add')).click(); await element(by.buttonText('Add hero')).click();
const page = getPageElts(); const page = getPageElts();
const heroesAfter = await toHeroArray(page.allHeroes); const heroesAfter = await toHeroArray(page.allHeroes);
@ -195,18 +195,18 @@ describe('Tutorial part 6', () => {
// Inherited styles from styles.css // Inherited styles from styles.css
expect(await button.getCssValue('font-family')).toBe('Arial, sans-serif'); expect(await button.getCssValue('font-family')).toBe('Arial, sans-serif');
expect(await button.getCssValue('border')).toContain('none'); expect(await button.getCssValue('border')).toContain('none');
expect(await button.getCssValue('padding')).toBe('5px 10px'); expect(await button.getCssValue('padding')).toBe('1px 10px 3px');
expect(await button.getCssValue('border-radius')).toBe('4px'); expect(await button.getCssValue('border-radius')).toBe('4px');
// Styles defined in heroes.component.css // Styles defined in heroes.component.css
expect(await button.getCssValue('left')).toBe('194px'); expect(await button.getCssValue('left')).toBe('210px');
expect(await button.getCssValue('top')).toBe('-32px'); expect(await button.getCssValue('top')).toBe('5px');
} }
const addButton = element(by.buttonText('add')); const addButton = element(by.buttonText('Add hero'));
// Inherited styles from styles.css // Inherited styles from styles.css
expect(await addButton.getCssValue('font-family')).toBe('Arial, sans-serif'); expect(await addButton.getCssValue('font-family')).toBe('Arial, sans-serif');
expect(await addButton.getCssValue('border')).toContain('none'); expect(await addButton.getCssValue('border')).toContain('none');
expect(await addButton.getCssValue('padding')).toBe('5px 10px'); expect(await addButton.getCssValue('padding')).toBe('8px 24px');
expect(await addButton.getCssValue('border-radius')).toBe('4px'); expect(await addButton.getCssValue('border-radius')).toBe('4px');
}); });

View File

@ -1,23 +1,20 @@
/* AppComponent's private CSS styles */ /* AppComponent's private CSS styles */
h1 { h1 {
font-size: 1.2em;
margin-bottom: 0; margin-bottom: 0;
} }
nav a { nav a {
padding: 5px 10px; padding: 1rem;
text-decoration: none; text-decoration: none;
margin-top: 10px; margin-top: 10px;
display: inline-block; display: inline-block;
background-color: #eee; background-color: #e8e8e8;
color: #3d3d3d;
border-radius: 4px; border-radius: 4px;
} }
nav a:visited, a:link {
color: #334953;
}
nav a:hover { nav a:hover {
color: #039be5; color: white;
background-color: #CFD8DC; background-color: #42545C;
} }
nav a.active { nav a.active {
color: #039be5; background-color: black;
} }

View File

@ -1,61 +1,54 @@
/* DashboardComponent's private CSS styles */ /* DashboardComponent's private CSS styles */
[class*='col-'] {
float: left; h2 {
padding-right: 20px; text-align: center;
padding-bottom: 20px;
} }
[class*='col-']:last-of-type {
padding-right: 0; .heroes-menu {
padding: 0;
margin: auto;
max-width: 1000px;
/* flexbox */
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: flex-start;
align-items: flex-start;
} }
a { a {
text-decoration: none;
}
*, *::after, *::before {
box-sizing: border-box;
}
h3 {
text-align: center;
margin-bottom: 0;
}
h4 {
position: relative;
}
.grid {
margin: 0;
}
.col-1-4 {
width: 25%;
}
.module {
padding: 20px;
text-align: center;
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #3f525c; background-color: #3f525c;
border-radius: 2px; border-radius: 2px;
padding: 1rem;
font-size: 1.2rem;
text-decoration: none;
display: inline-block;
color: #fff;
text-align: center;
width: 100%;
min-width: 70px;
margin: .5rem auto;
box-sizing: border-box;
/* flexbox */
order: 0;
flex: 0 1 auto;
align-self: auto;
} }
.module:hover {
background-color: #EEE; @media (min-width: 600px) {
cursor: pointer; a {
color: #607d8b; width: 18%;
} box-sizing: content-box;
.grid-pad {
padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
padding-right: 20px;
}
@media (max-width: 600px) {
.module {
font-size: 10px;
max-height: 75px; }
}
@media (max-width: 1024px) {
.grid {
margin: 0;
}
.module {
min-width: 60px;
} }
} }
a:hover {
background-color: black;
}

View File

@ -1,10 +1,8 @@
<h3>Top Heroes</h3> <h2>Top Heroes</h2>
<div class="grid grid-pad"> <div class="heroes-menu">
<a *ngFor="let hero of heroes" class="col-1-4" <a *ngFor="let hero of heroes"
routerLink="/detail/{{hero.id}}"> routerLink="/detail/{{hero.id}}">
<div class="module hero"> {{hero.name}}
<h4>{{hero.name}}</h4>
</div>
</a> </a>
</div> </div>

View File

@ -37,7 +37,7 @@ describe('DashboardComponent', () => {
}); });
it('should display "Top Heroes" as headline', () => { it('should display "Top Heroes" as headline', () => {
expect(fixture.nativeElement.querySelector('h3').textContent).toEqual('Top Heroes'); expect(fixture.nativeElement.querySelector('h2').textContent).toEqual('Top Heroes');
}); });
it('should call heroService', waitForAsync(() => { it('should call heroService', waitForAsync(() => {

View File

@ -1,24 +1,19 @@
/* HeroDetailComponent's private CSS styles */ /* HeroDetailComponent's private CSS styles */
label { label {
display: inline-block; color: #435960;
width: 3em;
margin: .5em 0;
color: #607D8B;
font-weight: bold; font-weight: bold;
} }
input { input {
height: 2em;
font-size: 1em; font-size: 1em;
padding-left: .4em; padding: .5rem;
} }
button { button {
margin-top: 20px; margin-top: 20px;
font-family: Arial, sans-serif; margin-right: .5rem;
background-color: #eee; background-color: #eee;
border: none; padding: 1rem;
padding: 5px 10px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; font-size: 1rem;
} }
button:hover { button:hover {
background-color: #cfd8dc; background-color: #cfd8dc;

View File

@ -2,9 +2,8 @@
<h2>{{hero.name | uppercase}} Details</h2> <h2>{{hero.name | uppercase}} Details</h2>
<div><span>id: </span>{{hero.id}}</div> <div><span>id: </span>{{hero.id}}</div>
<div> <div>
<label>name: <label for="hero-name">Hero name: </label>
<input [(ngModel)]="hero.name" placeholder="name"/> <input id="hero-name" [(ngModel)]="hero.name" placeholder="Hero name"/>
</label>
</div> </div>
<button (click)="goBack()">go back</button> <button (click)="goBack()">go back</button>
<!-- #docregion save --> <!-- #docregion save -->

View File

@ -1,37 +1,45 @@
/* HeroSearch private styles */ /* HeroSearch private styles */
.search-result li {
label {
display: block;
font-weight: bold;
font-size: 1.2rem;
margin-top: 1rem;
margin-bottom: .5rem;
}
input {
padding: .5rem;
width: 100%;
max-width: 600px;
box-sizing: border-box;
display: block;
}
input:focus {
outline: #336699 auto 1px;
}
li {
list-style-type: none;
}
.search-result li a {
border-bottom: 1px solid gray; border-bottom: 1px solid gray;
border-left: 1px solid gray; border-left: 1px solid gray;
border-right: 1px solid gray; border-right: 1px solid gray;
width: 195px; display: inline-block;
height: 16px; width: 100%;
padding: 5px; max-width: 600px;
background-color: white; padding: .5rem;
cursor: pointer; box-sizing: border-box;
list-style-type: none;
}
.search-result li:hover {
background-color: #607D8B;
}
.search-result li a {
color: #888;
display: block;
text-decoration: none; text-decoration: none;
color: black;
} }
.search-result li a:hover { .search-result li a:hover {
background-color: #435A60;
color: white; color: white;
} }
.search-result li a:active {
color: white;
}
#search-box {
width: 200px;
height: 20px;
}
ul.search-result { ul.search-result {
margin-top: 0; margin-top: 0;

View File

@ -1,6 +1,5 @@
<div id="search-component"> <div id="search-component">
<h4><label for="search-box">Hero Search</label></h4> <label for="search-box">Hero Search</label>
<!-- #docregion input --> <!-- #docregion input -->
<input #searchBox id="search-box" (input)="search(searchBox.value)" /> <input #searchBox id="search-box" (input)="search(searchBox.value)" />
<!-- #enddocregion input --> <!-- #enddocregion input -->

View File

@ -5,32 +5,44 @@
padding: 0; padding: 0;
width: 15em; width: 15em;
} }
input {
display: block;
width: 100%;
padding: .5rem;
margin: 1rem 0;
box-sizing: border-box;
}
.heroes li { .heroes li {
position: relative; position: relative;
cursor: pointer; cursor: pointer;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
} }
.heroes li:hover { .heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em; left: .1em;
} }
.heroes a { .heroes a {
color: #333; color: #333;
text-decoration: none; text-decoration: none;
position: relative; background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
display: block; display: block;
width: 250px; width: 100%;
} }
.heroes a:hover { .heroes a:hover {
color: #607D8B; color: #2c3a41;
background-color: #e6e6e6;
}
.heroes a:active {
background-color: #525252;
color: #fafafa;
} }
.heroes .badge { .heroes .badge {
@ -38,7 +50,7 @@
font-size: small; font-size: small;
color: white; color: white;
padding: 0.8em 0.7em 0 0.7em; padding: 0.8em 0.7em 0 0.7em;
background-color: #405061; background-color:#405061;
line-height: 1em; line-height: 1em;
position: relative; position: relative;
left: -1px; left: -1px;
@ -50,23 +62,28 @@
border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px;
} }
button { .add-button {
background-color: #eee; padding: .5rem 1.5rem;
border: none; font-size: 1rem;
padding: 5px 10px; margin-bottom: 2rem;
border-radius: 4px;
cursor: pointer;
font-family: Arial, sans-serif;
} }
button:hover { .add-button:hover {
background-color: #cfd8dc; color: white;
background-color: #42545C;
} }
button.delete { button.delete {
position: relative; position: absolute;
left: 194px; left: 210px;
top: -32px; top: 5px;
background-color: gray !important; background-color: white;
color: #525252;
font-size: 1.1rem;
padding: 1px 10px 3px 10px;
}
button.delete:hover {
background-color: #525252;
color: white; color: white;
} }

View File

@ -2,12 +2,12 @@
<!-- #docregion add --> <!-- #docregion add -->
<div> <div>
<label>Hero name: <label id="new-hero">Hero name: </label>
<input #heroName /> <input for="new-hero" #heroName />
</label>
<!-- (click) passes input value to add() and then clears the input --> <!-- (click) passes input value to add() and then clears the input -->
<button (click)="add(heroName.value); heroName.value=''"> <button class="add-button" (click)="add(heroName.value); heroName.value=''">
add Add hero
</button> </button>
</div> </div>
<!-- #enddocregion add --> <!-- #enddocregion add -->

View File

@ -1,20 +1,19 @@
/* MessagesComponent's private CSS styles */ /* MessagesComponent's private CSS styles */
h2 { h2 {
color: red; color: #A80000;
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
font-weight: lighter; font-weight: lighter;
} }
button.clear { .clear {
font-family: Arial, sans-serif;
color: #333; color: #333;
background-color: #eee; background-color: #eee;
margin-bottom: 12px; margin-bottom: 12px;
border: none; padding: 1rem;
padding: 5px 10px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; font-size: 1rem;
} }
button:hover { .clear:hover {
background-color: #cfd8dc; color: #fff;
background-color: #42545C;
} }

View File

@ -2,7 +2,7 @@
<h2>Messages</h2> <h2>Messages</h2>
<button class="clear" <button class="clear"
(click)="messageService.clear()">clear</button> (click)="messageService.clear()">Clear messages</button>
<div *ngFor='let message of messageService.messages'> {{message}} </div> <div *ngFor='let message of messageService.messages'> {{message}} </div>
</div> </div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -9,7 +9,7 @@ Angular's `HttpClient`.
<div class="alert is-helpful"> <div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>. For the sample application that this page describes, see the <live-example></live-example>.
</div> </div>
@ -17,7 +17,7 @@ Angular's `HttpClient`.
`HttpClient` is Angular's mechanism for communicating with a remote server over HTTP. `HttpClient` is Angular's mechanism for communicating with a remote server over HTTP.
Make `HttpClient` available everywhere in the app in two steps. First, add it to the root `AppModule` by importing it: Make `HttpClient` available everywhere in the application in two steps. First, add it to the root `AppModule` by importing it:
<code-example path="toh-pt6/src/app/app.module.ts" region="import-http-client" header="src/app/app.module.ts (HttpClientModule import)"> <code-example path="toh-pt6/src/app/app.module.ts" region="import-http-client" header="src/app/app.module.ts (HttpClientModule import)">
</code-example> </code-example>
@ -33,7 +33,7 @@ Next, still in the `AppModule`, add `HttpClient` to the `imports` array:
This tutorial sample mimics communication with a remote data server by using the This tutorial sample mimics communication with a remote data server by using the
[In-memory Web API](https://github.com/angular/angular/tree/master/packages/misc/angular-in-memory-web-api "In-memory Web API") module. [In-memory Web API](https://github.com/angular/angular/tree/master/packages/misc/angular-in-memory-web-api "In-memory Web API") module.
After installing the module, the app will make requests to and receive responses from the `HttpClient` After installing the module, the application will make requests to and receive responses from the `HttpClient`
without knowing that the *In-memory Web API* is intercepting those requests, without knowing that the *In-memory Web API* is intercepting those requests,
applying them to an in-memory data store, and returning simulated responses. applying them to an in-memory data store, and returning simulated responses.
@ -127,7 +127,7 @@ Convert that method to use `HttpClient` as follows:
Refresh the browser. The hero data should successfully load from the Refresh the browser. The hero data should successfully load from the
mock server. mock server.
You've swapped `of()` for `http.get()` and the app keeps working without any other changes You've swapped `of()` for `http.get()` and the application keeps working without any other changes
because both functions return an `Observable<Hero[]>`. because both functions return an `Observable<Hero[]>`.
### `HttpClient` methods return one value ### `HttpClient` methods return one value
@ -197,10 +197,10 @@ has configured with both the name of the operation that failed and a safe return
</code-example> </code-example>
After reporting the error to the console, the handler constructs After reporting the error to the console, the handler constructs
a user friendly message and returns a safe value to the app so the app can keep working. a user friendly message and returns a safe value to the application so the application can keep working.
Because each service method returns a different kind of `Observable` result, Because each service method returns a different kind of `Observable` result,
`handleError()` takes a type parameter so it can return the safe value as the type that the app expects. `handleError()` takes a type parameter so it can return the safe value as the type that the application expects.
### Tap into the Observable ### Tap into the Observable
@ -283,7 +283,7 @@ The hero now appears in the list with the changed name.
## Add a new hero ## Add a new hero
To add a hero, this app only needs the hero's name. You can use an `<input>` To add a hero, this application only needs the hero's name. You can use an `<input>`
element paired with an add button. element paired with an add button.
Insert the following into the `HeroesComponent` template, just after Insert the following into the `HeroesComponent` template, just after
@ -506,7 +506,7 @@ It cancels and discards previous search observables, returning only the latest s
Note that canceling a previous `searchHeroes()` Observable Note that canceling a previous `searchHeroes()` Observable
doesn't actually abort a pending HTTP request. doesn't actually abort a pending HTTP request.
Unwanted results are simply discarded before they reach your application code. Unwanted results are discarded before they reach your application code.
</div> </div>
@ -515,11 +515,11 @@ That's the job of the [`AsyncPipe`](#asyncpipe) in the template.
#### Try it #### Try it
Run the app again. In the *Dashboard*, enter some text in the search box. Run the application again. In the *Dashboard*, enter some text in the search box.
If you enter characters that match any existing hero names, you'll see something like this. If you enter characters that match any existing hero names, you'll see something like this.
<div class="lightbox"> <div class="lightbox">
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component"> <img src='generated/images/guide/toh/toh-hero-search.gif' alt="Hero Search field with the letters 'm' and 'a' along with four search results that match the query displayed in a list beneath the search input">
</div> </div>
## Final code review ## Final code review