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

Commit 5b23bc9

Browse files
Marcy Suttoncaitp
Marcy Sutton
authored andcommitted
docs(ngAria): Add Usage Details and Examples
Closes #10031
1 parent 9d1e87a commit 5b23bc9

File tree

3 files changed

+317
-48
lines changed

3 files changed

+317
-48
lines changed

docs/content/api/index.ngdoc

+21
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,27 @@ or JavaScript callbacks.
161161
</tr>
162162
</table>
163163

164+
## {@link ngAria ngAria}
165+
166+
Use ngAria to inject common accessibility attributes into directives and improve the experience for users with disabilities.
167+
168+
<div class="alert alert-info">Include the **angular-aria.js** file and set ngAria as a dependency for this to work in your application.</div>
169+
170+
<table class="definition-table spaced">
171+
<tr>
172+
<td>
173+
{@link ngAria#service Services}
174+
</td>
175+
<td>
176+
<p>
177+
The {@link ngAria.$aria $aria} service contains helper methods for applying ARIA attributes to HTML.
178+
<p>
179+
<p>
180+
{@link ngAria.$ariaProvider $ariaProvider} is used for configuring ARIA attributes.
181+
</p>
182+
</td>
183+
</tr>
184+
</table>
164185

165186
## {@link ngResource ngResource}
166187

docs/content/guide/accessibility.ngdoc

+214-26
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,23 @@ assistive technologies used by persons with disabilities.
1212

1313
##Including ngAria
1414

15-
Using ngAria is as simple as requiring the ngAria module in your application. ngAria hooks into
15+
Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into
1616
standard AngularJS directives and quietly injects accessibility support into your application
1717
at runtime.
1818

1919
```js
2020
angular.module('myApp', ['ngAria'])...
2121
```
2222

23+
###Using ngAria
24+
Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
25+
added it as a dependency, you can test a few things:
26+
* Using your favorite element inspector, look for ngAria attributes in your own code.
27+
* Test using your keyboard to ensure `tabindex` is used correctly.
28+
* Fire up a screen reader such as VoiceOver to listen for ARIA support.
29+
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
30+
31+
##Supported directives
2332
Currently, ngAria interfaces with the following directives:
2433

2534
* <a href="#ngmodel">ngModel</a>
@@ -31,7 +40,7 @@ Currently, ngAria interfaces with the following directives:
3140

3241
<h2 id="ngmodel">ngModel</h2>
3342

34-
Most of ngAria's heavy lifting happens in the [ngModel](https://docs.angularjs.org/api/ng/directive/ngModel)
43+
Most of ngAria's heavy lifting happens in the {@link ngModel ngModel}
3544
directive. For elements using ngModel, special attention is paid by ngAria if that element also
3645
has a a role or type of `checkbox`, `radio`, `range` or `textbox`.
3746

@@ -47,15 +56,80 @@ attributes (if they have not been explicitly specified by the developer):
4756

4857
###Example
4958

50-
```html
51-
<md-checkbox ng-model="val" required>
52-
```
53-
54-
Becomes:
55-
56-
```html
57-
<md-checkbox ng-model="val" required aria-required="true" tabIndex="0">
58-
```
59+
<example module="ngAria_ngModelExample" deps="angular-aria.js">
60+
<file name="index.html">
61+
<style>
62+
[role=checkbox] {
63+
cursor: pointer;
64+
display: inline-block;
65+
}
66+
[role=checkbox] .icon:before {
67+
content: '\2610';
68+
display: inline-block;
69+
font-size: 2em;
70+
line-height: 1;
71+
vertical-align: middle;
72+
speak: none;
73+
}
74+
[role=checkbox].active .icon:before {
75+
content: '\2611';
76+
}
77+
pre {
78+
white-space: pre-wrap;
79+
}
80+
</style>
81+
<div>
82+
<form ng-controller="formsController">
83+
<some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
84+
ng-disabled="isDisabled" ng-click="toggleCheckbox()"
85+
aria-label="Custom Checkbox" show-attrs>
86+
<span class="icon" aria-hidden="true"></span>
87+
Custom Checkbox
88+
</some-checkbox>
89+
</form>
90+
</div>
91+
<script>
92+
var app = angular.module('ngAria_ngModelExample', ['ngAria'])
93+
.controller('formsController', function($scope){
94+
$scope.checked = false;
95+
$scope.toggleCheckbox = function(){
96+
$scope.checked = !$scope.checked;
97+
}
98+
})
99+
.directive('someCheckbox', function(){
100+
return {
101+
restrict: 'E',
102+
link: function($scope, $el, $attrs) {
103+
$el.on('keypress', function(event){
104+
event.preventDefault();
105+
if(event.keyCode === 32 || event.keyCode === 13){
106+
$scope.toggleCheckbox();
107+
$scope.$apply();
108+
}
109+
});
110+
}
111+
}
112+
})
113+
.directive('showAttrs', function() {
114+
return function($scope, $el, $attrs) {
115+
var pre = document.createElement('pre');
116+
$el.after(pre);
117+
$scope.$watch(function() {
118+
var $attrs = {};
119+
Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
120+
if (item.name !== 'show-$attrs') {
121+
$attrs[item.name] = item.value;
122+
}
123+
});
124+
return $attrs;
125+
}, function(newAttrs, oldAttrs) {
126+
pre.textContent = JSON.stringify(newAttrs, null, 2);
127+
}, true);
128+
}
129+
});
130+
</script>
131+
</file>
132+
</example>
59133

60134
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
61135
the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
@@ -106,6 +180,24 @@ screen reader users won't accidentally focus on "mystery elements". Managing tab
106180
child control can be complex and affect performance, so it’s best to just stick with the default
107181
`display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use).
108182

183+
###Example
184+
```css
185+
.ng-hide {
186+
display: block;
187+
opacity: 0;
188+
}
189+
```
190+
```html
191+
<div ng-show="false" class="ng-hide" aria-hidden="true"></div>
192+
```
193+
194+
Becomes:
195+
196+
```html
197+
<div ng-show="true" aria-hidden="false"></div>
198+
```
199+
*Note: Child links, buttons or other interactive controls must also be removed from the tab order.*
200+
109201
<h2 id="nghide">ngHide</h2>
110202

111203
>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
@@ -116,33 +208,128 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
116208
`aria-hidden` on the directive when it is hidden or shown, but the content is already hidden with
117209
`display: none`. See explanation for <a href="#ngshow">ngShow</a> when overriding the default CSS.
118210

119-
<h2 id="ngclick">ngClick and ngDblClick</h2>
120-
If `ngClick` or `ngDblClick` is encountered, ngAria will add `tabIndex` if it isn't there already.
121-
Even with this, you must currently still add `ng-keypress` to non-interactive elements such as
122-
`<div>` or `<taco-button>` to enable keyboard access. Conversation is
123-
[currently ongoing](https://github.com/angular/angular.js/issues/9254) about whether ngAria
124-
should also bind `ng-keypress` to be more useful.
211+
<h2 id="ngclick-and-ngdblclick">ngClick and ngDblclick</h2>
212+
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex` if it isn't there already.
213+
Even with this, you must currently still add `ng-keypress` to non-interactive elements such as `div`
214+
or `taco-button` to enable keyboard access. Conversation is currently ongoing about whether ngAria
215+
should also bind `ng-keypress`.
216+
217+
<h3>Example</h3>
218+
```html
219+
<div ng-click="toggleMenu()"></div>
220+
```
221+
222+
Becomes:
223+
```html
224+
<div ng-click="toggleMenu()" tabindex="0"></div>
225+
```
226+
*Note: ngAria still requires `ng-keypress` to be added manually to non-native controls like divs.*
125227

126228
<h2 id="ngmessages">ngMessages</h2>
127229

128230
The new ngMessages module makes it easy to display form validation or other messages with priority
129231
sequencing and animation. To expose these visual messages to screen readers,
130232
ngAria injects `aria-live="polite"`, causing them to be read aloud any time a message is shown,
131233
regardless of the user's focus location.
234+
###Example
235+
236+
```html
237+
<div ng-messages="myForm.myName.$error">
238+
<div ng-message="required">You did not enter a field</div>
239+
<div ng-message="maxlength">Your field is too long</div>
240+
</div>
241+
```
132242

133-
* * *
243+
Becomes:
244+
245+
```html
246+
<div ng-messages="myForm.myName.$error" aria-live="polite">
247+
<div ng-message="required">You did not enter a field</div>
248+
<div ng-message="maxlength">Your field is too long</div>
249+
</div>
250+
```
134251

135252
##Disabling attributes
136253
The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
137-
you can use the {@link ngAria.$ariaProvider#config config} method:
138-
139-
```
140-
angular.module('myApp', ['ngAria'], function config($ariaProvider) {
141-
$ariaProvider.config({
142-
tabindex: false
254+
you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
255+
tell ngAria to ignore the attribute globally.
256+
257+
<example module="ngAria_ngDisabledExample" deps="angular-aria.js">
258+
<file name="index.html">
259+
<style>
260+
[role=checkbox] {
261+
cursor: pointer;
262+
display: inline-block;
263+
}
264+
[role=checkbox] .icon:before {
265+
content: '\2610';
266+
display: inline-block;
267+
font-size: 2em;
268+
line-height: 1;
269+
vertical-align: middle;
270+
speak: none;
271+
}
272+
[role=checkbox].active .icon:before {
273+
content: '\2611';
274+
}
275+
</style>
276+
<form ng-controller="formsController">
277+
<div ng-model="someModel" show-attrs>
278+
Div with ngModel and aria-invalid disabled
279+
</div>
280+
<div role="checkbox" ng-model="checked" ng-class="{active: checked}"
281+
aria-label="Custom Checkbox" ng-click="toggleCheckbox()" some-checkbox show-attrs>
282+
<span class="icon" aria-hidden="true"></span>
283+
Custom Checkbox for comparison
284+
</div>
285+
</form>
286+
<script>
287+
angular.module('ngAria_ngDisabledExample', ['ngAria'], function config($ariaProvider) {
288+
$ariaProvider.config({
289+
ariaInvalid: false,
290+
tabindex: true
291+
});
292+
})
293+
.controller('formsController', function($scope){
294+
$scope.checked = false;
295+
$scope.toggleCheckbox = function(){
296+
$scope.checked = !$scope.checked;
297+
}
298+
})
299+
.directive('someCheckbox', function(){
300+
return {
301+
restrict: 'A',
302+
link: function($scope, $el, $attrs) {
303+
$el.on('keypress', function(event){
304+
event.preventDefault();
305+
if(event.keyCode === 32 || event.keyCode === 13){
306+
$scope.toggleCheckbox();
307+
$scope.$apply();
308+
}
309+
});
310+
}
311+
}
312+
})
313+
.directive('showAttrs', function() {
314+
return function(scope, el, attrs) {
315+
var pre = document.createElement('pre');
316+
el.after(pre);
317+
scope.$watch(function() {
318+
var attrs = {};
319+
Array.prototype.slice.call(el[0].attributes, 0).forEach(function(item) {
320+
if (item.name !== 'show-attrs') {
321+
attrs[item.name] = item.value;
322+
}
323+
});
324+
return attrs;
325+
}, function(newAttrs, oldAttrs) {
326+
pre.textContent = JSON.stringify(newAttrs, null, 2);
327+
}, true);
328+
}
143329
});
144-
});
145-
```
330+
</script>
331+
</file>
332+
</example>
146333

147334
##Common Accessibility Patterns
148335

@@ -171,6 +358,7 @@ Accessibility best practices that apply to web apps in general also apply to Ang
171358

172359
* [Using ARIA in HTML](http://www.w3.org/TR/aria-in-html/)
173360
* [AngularJS Accessibility at ngEurope](https://www.youtube.com/watch?v=dmYDggEgU-s&list=UUEGUP3TJJfMsEM_1y8iviSQ)
361+
* [Testing with Screen Readers](http://webaim.org/articles/screenreader_testing/)
174362
* [Chrome Accessibility Developer Tools](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en)
175363
* [W3C Accessibility Testing](http://www.w3.org/wiki/Accessibility_testing)
176364
* [WebAIM](http://webaim.org)

0 commit comments

Comments
 (0)