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

Roles dynamically added with ngAria #10318

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/content/guide/accessibility.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,13 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` if it isn't there
already.

For `ng-click`, keypress will also be bound to `div` and `li` elements. You can turn this
functionality on or off with the `bindKeypress` configuration option.
To fix widespread accessibility problems with `ng-click` on div elements, ngAria will dynamically
bind keypress by default as long as the element isn't an anchor, button, input or textarea.
You can turn this functionality on or off with the `bindKeypress` configuration option. ngAria
will also add the `button` role to communicate to users of assistive technologies.

For `ng-dblclick`, you must manually add `ng-keypress` to non-interactive elements such as `div`
or `taco-button` to enable keyboard access.
For `ng-dblclick`, you must still manually add `ng-keypress` and role to non-interactive elements such
as `div` or `taco-button` to enable keyboard access.

<h3>Example</h3>
```html
Expand Down
21 changes: 18 additions & 3 deletions src/ngAria/aria.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
*
* | Directive | Supported Attributes |
* |---------------------------------------------|----------------------------------------------------------------------------------------|
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required |
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event |
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
* | {@link module:ngMessages ngMessages} | aria-live |
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
*
* Find out more information about each directive by reading the
* {@link guide/accessibility ngAria Developer Guide}.
Expand Down Expand Up @@ -193,6 +193,10 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
return $aria.config(normalizedAttr) && !elem.attr(attr);
}

function shouldAttachRole(role, elem) {
return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
}

function getShape(attr, elem) {
var type = attr.type,
role = attr.role;
Expand Down Expand Up @@ -237,12 +241,18 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
switch (shape) {
case 'radio':
case 'checkbox':
if (shouldAttachRole(shape, elem)) {
elem.attr('role', shape);
}
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem)) {
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
getRadioReaction() : ngAriaCheckboxReaction);
}
break;
case 'range':
if (shouldAttachRole(shape, elem)) {
elem.attr('role', 'slider');
}
if ($aria.config('ariaValue')) {
if (attr.min && !elem.attr('aria-valuemin')) {
elem.attr('aria-valuemin', attr.min);
Expand Down Expand Up @@ -307,17 +317,22 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
return function(scope, elem, attr) {

var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA'];

function isNodeOneOf(elem, nodeTypeArray) {
if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
return true;
}
}
if (!elem.attr('role') && !isNodeOneOf(elem, nodeBlackList)) {
elem.attr('role', 'button');
}

if ($aria.config('tabindex') && !elem.attr('tabindex')) {
elem.attr('tabindex', 0);
}

if ($aria.config('bindKeypress') && !attr.ngKeypress && isNodeOneOf(elem, ['DIV', 'LI'])) {
if ($aria.config('bindKeypress') && !attr.ngKeypress && !isNodeOneOf(elem, nodeBlackList)) {
elem.on('keypress', function(event) {
if (event.keyCode === 32 || event.keyCode === 13) {
scope.$apply(callback);
Expand Down
Loading