Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 2268c24

Browse files
Splaktarandrewseguin
authored andcommitted
fix(chips): improve screen reader support (#11422)
make static chips accessible add support for specifying input-aria-labelledby add support for specifying input-aria-describedby add a container-empty-hint API for use when there are no chips add support for screen readers announcing chip removal and addition add a md-removed-message API to customize the chip removal announcement add a md-added-message API to customize the chip creation announcement move chips-container-hint and delete-button-label to aria-labels fixed use of aria-posinset which is 1 indexed, not 0 indexed when a chip is removable, the label will include the delete-hint add the same screen reader support to md-contact-chips as well change autocomplete's aria-described by to input-aria-describedby - fixes a conflict with duplicating messages in screen readers fix a11y issues with demos Fixes #2618
1 parent 6815faf commit 2268c24

File tree

16 files changed

+363
-89
lines changed

16 files changed

+363
-89
lines changed

src/components/autocomplete/js/autocompleteController.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
9393
if ($scope.autofocus) {
9494
$element.on('focus', focusInputElement);
9595
}
96-
if ($scope.ariaDescribedBy) {
97-
elements.input.setAttribute('aria-describedby', $scope.ariaDescribedBy);
96+
if ($scope.inputAriaDescribedBy) {
97+
elements.input.setAttribute('aria-describedby', $scope.inputAriaDescribedBy);
9898
}
9999
});
100100
}
@@ -491,7 +491,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
491491
ctrl.matches = [];
492492

493493
setLoading(false);
494-
reportMessages(false, ReportType.Count);
494+
reportMessages(true, ReportType.Count);
495495

496496
} else {
497497
handleQuery();

src/components/autocomplete/js/autocompleteDirective.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,11 @@ angular
130130
* as much as possible.
131131
* @param {string=} md-dropdown-position Overrides the default dropdown position. Options: `top`,
132132
* `bottom`.
133-
* @param {string=} aria-describedby A space-separated list of element IDs. This should contain the
134-
* IDs of any elements that describe this autocomplete. Screen readers will read the content of
135-
* these elements at the end of announcing that the autocomplete has been selected and
136-
* describing its current state. The descriptive elements do not need to be visible on the page.
133+
* @param {string=} input-aria-describedby A space-separated list of element IDs. This should
134+
* contain the IDs of any elements that describe this autocomplete. Screen readers will read the
135+
* content of these elements at the end of announcing that the autocomplete has been selected
136+
* and describing its current state. The descriptive elements do not need to be visible on the
137+
* page.
137138
* @param {string=} md-selected-message Attribute to specify the text that the screen reader will
138139
* announce after a value is selected. Default is: "selected". If `Alaska` is selected in the
139140
* options panel, it will read "Alaska selected". You will want to override this when your app
@@ -263,7 +264,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
263264
itemsExpr: '@mdItems',
264265
itemText: '&mdItemText',
265266
placeholder: '@placeholder',
266-
ariaDescribedBy: '@?ariaDescribedby',
267+
inputAriaDescribedBy: '@?inputAriaDescribedby',
267268
noCache: '=?mdNoCache',
268269
requireMatch: '=?mdRequireMatch',
269270
selectOnMatch: '=?mdSelectOnMatch',

src/components/chips/demoBasicUsage/index.html

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ <h2 class="md-title">Use a custom chip template.</h2>
55

66
<form name="fruitForm">
77
<md-chips ng-model="ctrl.roFruitNames" name="fruitName" readonly="ctrl.readonly"
8-
md-removable="ctrl.removable" md-max-chips="5" placeholder="Enter a fruit...">
8+
md-removable="ctrl.removable" md-max-chips="5" placeholder="Enter a fruit..."
9+
input-aria-label="Fruit names">
910
<md-chip-template>
1011
<strong>{{$chip}}</strong>
1112
<em>(fruit)</em>
@@ -26,15 +27,16 @@ <h2 class="md-title">Use the default chip template.</h2>
2627
<br/>
2728
<h2 class="md-title">Use ng-change and add-on-blur</h2>
2829

29-
<md-chips ng-model="ctrl.ngChangeFruitNames" md-add-on-blur="true"
30-
ng-change="ctrl.onModelChange(ctrl.ngChangeFruitNames)"
30+
<md-chips ng-model="ctrl.ngChangeFruitNames" md-add-on-blur="true" readonly="ctrl.readonly"
31+
ng-change="ctrl.onModelChange(ctrl.ngChangeFruitNames)" input-aria-label="Fruit names"
3132
md-removable="ctrl.removable"></md-chips>
3233

3334
<br/>
3435
<h2 class="md-title">Make chips editable.</h2>
3536

3637
<md-chips ng-model="ctrl.editableFruitNames" readonly="ctrl.readonly"
37-
md-removable="ctrl.removable" md-enable-chip-edit="true"></md-chips>
38+
md-removable="ctrl.removable" md-enable-chip-edit="true"
39+
input-aria-label="Fruit names"></md-chips>
3840

3941
<br/>
4042
<h2 class="md-title">Use Placeholders and override hint texts.</h2>
@@ -46,14 +48,21 @@ <h2 class="md-title">Use Placeholders and override hint texts.</h2>
4648
placeholder="Enter a tag"
4749
delete-button-label="Remove Tag"
4850
delete-hint="Press delete to remove tag"
49-
secondary-placeholder="+Tag"></md-chips>
51+
md-added-message="was created"
52+
md-removed-message="was deleted"
53+
secondary-placeholder="+Tag"
54+
input-aria-label="Tags"
55+
container-hint="Chips container. Press the right and left arrow keys to change tag selection."
56+
container-empty-hint="Chips container. Enter the text area, start typing, and then press enter when done to add a tag.">
57+
</md-chips>
5058

5159
<br/>
5260
<h2 class="md-title">Display an ordered set of objects as chips (with custom template).</h2>
5361
<p>Note: the variables <code>$chip</code> and <code>$index</code> are available in custom chip templates.</p>
5462

5563
<md-chips class="custom-chips" ng-model="ctrl.vegObjs" readonly="ctrl.readonly"
56-
md-transform-chip="ctrl.newVeg($chip)" md-removable="ctrl.removable">
64+
md-transform-chip="ctrl.newVeg($chip)" md-removable="ctrl.removable"
65+
input-aria-label="Vegetables">
5766
<md-chip-template>
5867
<span>
5968
<strong>[{{$index}}] {{$chip.name}}</strong>

src/components/chips/demoContactChips/index.html

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<div ng-controller="ContactChipDemoCtrl as ctrl" layout="column" ng-cloak>
22

33
<md-content class="md-padding autocomplete" layout="column">
4+
<label id="toLabel">To:</label>
45
<md-contact-chips
56
ng-model="ctrl.contacts"
67
ng-change="ctrl.onModelChange(ctrl.contacts)"
@@ -11,7 +12,9 @@
1112
md-require-match="true"
1213
md-separator-keys="ctrl.keys"
1314
md-highlight-flags="i"
14-
placeholder="To">
15+
placeholder="Type name to see matches"
16+
secondary-placeholder="Add another contact"
17+
input-aria-label="Intended Recipients">
1518
</md-contact-chips>
1619

1720
<md-list class="fixedRows">
@@ -24,7 +27,8 @@ <h3>{{contact.name}}</h3>
2427
<p>{{contact.email}}</p>
2528
</div>
2629
</md-list-item>
27-
<md-list-item class="md-2-line contact-item selected" ng-repeat="(index, contact) in ctrl.contacts">
30+
<md-list-item class="md-2-line contact-item selected"
31+
ng-repeat="(index, contact) in ctrl.contacts">
2832
<img ng-src="{{contact.image}}" class="md-avatar" alt="{{contact.name}}" />
2933
<div class="md-list-item-text compact">
3034
<h3>{{contact.name}}</h3>
@@ -35,6 +39,7 @@ <h3>{{contact.name}}</h3>
3539

3640
<br>
3741
<h2 class="md-title">Searching asynchronously.</h2>
42+
<label id="fromLabel">From:</label>
3843
<md-contact-chips
3944
ng-model="ctrl.asyncContacts"
4045
md-contacts="ctrl.delayedQuerySearch($query)"
@@ -43,7 +48,9 @@ <h2 class="md-title">Searching asynchronously.</h2>
4348
md-contact-email="email"
4449
md-require-match="true"
4550
md-highlight-flags="i"
46-
placeholder="To">
51+
placeholder="Type name to see matches"
52+
secondary-placeholder="Add another contact"
53+
input-aria-label="Senders">
4754
</md-contact-chips>
4855
</md-content>
4956
</div>

src/components/chips/demoContactChips/style.scss

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ md-content.autocomplete {
1414
.contact-item {
1515
box-sizing: border-box;
1616
&.selected {
17-
opacity: 0.5;
18-
h3 {
19-
opacity: 0.5;
17+
background-color: #E3ECF5;
18+
p {
19+
color: rgba(0,0,0,0.87);
20+
font-weight: 400;
2021
}
2122
}
2223
.md-list-item-text {

src/components/chips/demoCustomInputs/index.html

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
11
<div ng-controller="CustomInputDemoCtrl as ctrl" layout="column" ng-cloak>
22

33
<md-content class="md-padding" layout="column">
4-
<h2 class="md-title">Use an <code>input</code> element with no model to build an ordered set of chips.</h2>
5-
<md-chips ng-model="ctrl.numberChips">
6-
<input type="number" placeholder="Enter a number">
4+
<h2 id="chipsNoModelTitle" class="md-title">
5+
Use an <code>input</code> element with no model to build an ordered set of chips.
6+
</h2>
7+
<md-chips ng-model="ctrl.numberChips" input-aria-label="Numbers with no model"
8+
input-aria-describedby="chipsNoModelTitle"
9+
container-empty-hint="Chips container. Enter the text area, type a number, and then press enter to add a chip.">
10+
<input type="number" placeholder="Enter a number" aria-label="Number chips with no model">
711
</md-chips>
812

913
<br/>
10-
<h2 class="md-title">Use an <code>input</code> element to build an ordered set of chips.</h2>
11-
<md-chips ng-model="ctrl.numberChips2">
12-
<input type="number" ng-model="ctrl.numberBuffer" placeholder="Enter a number">
14+
<h2 id="chipsWithModelTitle" class="md-title">
15+
Use an <code>input</code> element to build an ordered set of chips.
16+
</h2>
17+
<md-chips ng-model="ctrl.numberChips2" input-aria-label="Numbers with a model"
18+
input-aria-describedby="chipsWithModelTitle"
19+
container-empty-hint="Chips container. Enter the text area, type a number, and then press enter to add a chip.">
20+
<input type="number" ng-model="ctrl.numberBuffer" placeholder="Enter a number"
21+
aria-label="Number chips">
1322
</md-chips>
1423

1524
<br/>
16-
<h2 class="md-title">Use <code>md-autocomplete</code> to build an ordered set of chips.</h2>
17-
25+
<h2 id="autocompleteTitle" class="md-title">
26+
Use <code>md-autocomplete</code> to build an ordered set of chips.
27+
</h2>
1828
<md-chips ng-model="ctrl.selectedVegetables" md-autocomplete-snap
1929
md-transform-chip="ctrl.transformChip($chip)"
20-
md-require-match="ctrl.autocompleteDemoRequireMatch">
30+
md-require-match="ctrl.autocompleteDemoRequireMatch"
31+
input-aria-label="Favorite Vegetables">
2132
<md-autocomplete
2233
md-selected-item="ctrl.selectedItem"
2334
md-search-text="ctrl.searchText"
2435
md-items="item in ctrl.querySearch(ctrl.searchText)"
2536
md-item-text="item.name"
37+
input-aria-describedby="autocompleteTitle"
2638
placeholder="Search for a vegetable">
2739
<span md-highlight-text="ctrl.searchText">{{item.name}} :: {{item.type}}</span>
2840
</md-autocomplete>
Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
<div ng-controller="CustomSeparatorCtrl as ctrl" layout="column" ng-cloak>
22
<md-content class="md-padding" layout="column">
33

4-
<h2 class="md-title">Use <code>md-separator-keys</code> to customize the key codes which trigger chip creation.</h2>
4+
<h2 class="md-title">
5+
Use <code>md-separator-keys</code> to customize the key codes which trigger chip creation.
6+
</h2>
7+
<md-subheader id="commaSeparatorKeyDescription">
8+
You can use either the Enter or Comma keys to trigger chip creation.
9+
</md-subheader>
510
<md-chips
6-
ng-model="ctrl.tags"
11+
ng-model="ctrl.tags" input-aria-label="Tags"
712
md-separator-keys="ctrl.keys"
8-
placeholder="Enter a tag"
9-
secondary-placeholder="Comma separated tags"></md-chips>
13+
placeholder="Ex. angularjs-material"
14+
secondary-placeholder="Add another tag"
15+
input-aria-describedby="commaSeparatorKeyDescription">
16+
</md-chips>
1017
<br/>
1118

1219
<h2 class="md-title">Add custom separator key codes such as semicolon for e-mails.</h2>
20+
<md-subheader id="customSeparatorKeyDescription">
21+
You can use the Semicolon, Enter, or Comma keys to trigger chip creation.
22+
</md-subheader>
1323
<md-chips
14-
ng-model="ctrl.contacts"
15-
md-separator-keys="ctrl.customKeys"></md-chips>
24+
ng-model="ctrl.contacts" input-aria-label="Emails"
25+
md-separator-keys="ctrl.customKeys" placeholder="Ex. myemail@example.com"
26+
secondary-placeholder="Add another email address"
27+
input-aria-describedby="customSeparatorKeyDescription">
28+
</md-chips>
1629

1730
</md-content>
1831
</div>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
h2 {
2+
margin-bottom: 0;
3+
}

src/components/chips/demoStaticChips/index.html

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
<div ng-controller="DemoCtrl as ctrl" layout="column" ng-cloak>
2-
32
<md-content class="md-padding" layout="column">
4-
<h2 class="md-title">Display a set of items as chips.</h2>
3+
<h2 id="staticChipsTitle" class="md-title">Display a set of items as chips.</h2>
54

6-
<md-chips>
5+
<md-chips input-aria-label="Favorite Sports" input-aria-describedby="staticChipsNote">
6+
<md-chip>Baseball</md-chip>
77
<md-chip>Hockey</md-chip>
88
<md-chip>Lacrosse</md-chip>
9-
<md-chip>Baseball</md-chip>
10-
119
<!-- Static chips are transcluded -->
1210
<md-chip>{{ctrl.chipText}}</md-chip>
1311
</md-chips>
14-
<p class="note">
12+
<p id="staticChipsNote" class="note">
1513
Static chips cannot be selected, removed or edited, and are not part of any model.<br/>
1614
If no <code>ng-model</code> is provided, there are no input elements in <code>md-chips</code>.
1715
</p>

src/components/chips/demoValidation/index.html

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,43 @@
11
<div ng-controller="ChipsValidationCtrl as ctrl" layout="column" ng-cloak>
22
<md-content class="md-padding" layout="column">
33

4-
<h2 class="md-title">Required</h2>
5-
<form name="fruitForm">
6-
<md-chips name="fruits"
4+
<h2 class="md-title">Example: Required</h2>
5+
<form name="form">
6+
<label id="fruitLabel">Favorite Fruits</label>
7+
<md-chips name="fruits" input-aria-labelledby="fruitLabel"
78
ng-model="ctrl.selectedFruit"
89
ng-required="true"
9-
placeholder="Add a fruit">
10+
placeholder="Ex. Apple"
11+
secondary-placeholder="Add another fruit">
1012
</md-chips>
1113
<div class="md-chips-messages"
12-
ng-show="fruitForm.fruits.$dirty || fruitForm.$submitted"
13-
ng-messages="fruitForm.fruits.$error">
14+
ng-show="form.fruits.$touched || form.$submitted"
15+
ng-messages="form.fruits.$error">
1416
<div ng-message="required">At least one fruit is required</div>
1517
</div>
16-
</form>
17-
1818

19-
<h2 class="md-title">Max Chips</h2>
20-
<form name="vegetableForm">
21-
<md-chips name="vegetables"
19+
<h2 class="md-title">Example: Max Chips</h2>
20+
<label id="vegetableLabel">Favorite Vegetables</label>
21+
<md-chips name="vegetables" input-aria-labelledby="vegetableLabel"
2222
ng-model="ctrl.selectedVegetables"
23-
placeholder="Add a vegetable"
24-
md-max-chips="5">
23+
md-max-chips="5"
24+
placeholder="Ex. Carrot"
25+
secondary-placeholder="Add another vegetable">
2526
</md-chips>
2627
<div class="md-chips-messages"
27-
ng-show="vegetableForm.vegetables.$dirty || vegetableForm.$submitted"
28-
ng-messages="vegetableForm.vegetables.$error">
28+
ng-show="form.vegetables.$touched || form.$submitted"
29+
ng-messages="form.vegetables.$error">
2930
<div ng-message="md-max-chips">You reached the maximum number of vegetables</div>
3031
</div>
32+
<br>
33+
<md-button class="md-primary" type="submit" ng-click="ctrl.onSubmit(form)">
34+
Submit Form
35+
</md-button>
3136
</form>
3237

3338
<p class="note">
34-
Be aware that error messages for chips are not styled by default since they are not part of <code>md-input-container</code>.
39+
Be aware that error messages for chips are not styled by default since they are not part of
40+
<code>md-input-container</code>.
3541
</p>
3642

3743
</md-content>

0 commit comments

Comments
 (0)