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

Commit

Permalink
feat(ngAria): add support for aria-readonly based on ngReadonly
Browse files Browse the repository at this point in the history
Closes #14140
Closes #14077
  • Loading branch information
m-amr authored and Narretz committed Apr 8, 2016
1 parent 9147d5c commit ec0baad
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 4 deletions.
23 changes: 22 additions & 1 deletion docs/content/guide/accessibility.ngdoc
Expand Up @@ -28,12 +28,13 @@ added it as a dependency, you can test a few things:
* Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support.
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)

##Supported directives
## Supported directives
Currently, ngAria interfaces with the following directives:

* {@link guide/accessibility#ngmodel ngModel}
* {@link guide/accessibility#ngdisabled ngDisabled}
* {@link guide/accessibility#ngrequired ngRequired}
* {@link guide/accessibility#ngreadonly ngReadonly}
* {@link guide/accessibility#ngvaluechecked ngChecked}
* {@link guide/accessibility#ngvaluechecked ngValue}
* {@link guide/accessibility#ngshow ngShow}
Expand All @@ -57,6 +58,7 @@ attributes (if they have not been explicitly specified by the developer):
* aria-valuenow
* aria-invalid
* aria-required
* aria-readonly

###Example

Expand Down Expand Up @@ -203,6 +205,25 @@ Becomes:
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
```

<h2 id="ngreadonly">ngReadonly</h2>

The boolean `readonly` attribute is only valid for native form controls such as `input` and
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
as required, using ngAria with {@link ng.ngReadonly ngReadonly} will also add
`aria-readonly`. This tells accessibility APIs when a custom control is read-only.

###Example

```html
<md-checkbox ng-readonly="val"></md-checkbox>
```

Becomes:

```html
<md-checkbox ng-readonly="val" aria-readonly="true"></md-checkbox>
```

<h2 id="ngshow">ngShow</h2>

>The {@link ng.ngShow ngShow} directive shows or hides the
Expand Down
13 changes: 10 additions & 3 deletions src/ngAria/aria.js
Expand Up @@ -16,7 +16,7 @@
*
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
* directives are supported:
* `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
* `ngDblClick`, and `ngMessages`.
*
* Below is a more detailed breakdown of the attributes handled by ngAria:
Expand All @@ -25,8 +25,9 @@
* |---------------------------------------------|----------------------------------------------------------------------------------------|
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
* | {@link ng.directive:ngRequired ngRequired} | aria-required |
* | {@link ng.directive:ngChecked ngChecked} | aria-checked |
* | {@link ng.directive:ngRequired ngRequired} | aria-required
* | {@link ng.directive:ngChecked ngChecked} | aria-checked
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly ||
* | {@link ng.directive:ngValue ngValue} | aria-checked |
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
Expand Down Expand Up @@ -91,6 +92,7 @@ function $AriaProvider() {
var config = {
ariaHidden: true,
ariaChecked: true,
ariaReadonly: true,
ariaDisabled: true,
ariaRequired: true,
ariaInvalid: true,
Expand All @@ -108,6 +110,7 @@ function $AriaProvider() {
*
* - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
* - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
* - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
Expand Down Expand Up @@ -170,6 +173,7 @@ function $AriaProvider() {
* The full list of directives that interface with ngAria:
* * **ngModel**
* * **ngChecked**
* * **ngReadonly**
* * **ngRequired**
* * **ngDisabled**
* * **ngValue**
Expand Down Expand Up @@ -209,6 +213,9 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
.directive('ngChecked', ['$aria', function($aria) {
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
}])
.directive('ngReadonly', ['$aria', function($aria) {
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
}])
.directive('ngRequired', ['$aria', function($aria) {
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
}])
Expand Down
60 changes: 60 additions & 0 deletions test/ngAria/ariaSpec.js
Expand Up @@ -396,6 +396,66 @@ describe('$aria', function() {
});
});

describe('aria-readonly', function() {
beforeEach(injectScopeAndCompiler);

they('should not attach itself to native $prop controls', {
input: '<input ng-readonly="val">',
textarea: '<textarea ng-readonly="val"></textarea>',
select: '<select ng-readonly="val"></select>',
button: '<button ng-readonly="val"></button>'
}, function(tmpl) {
var element = $compile(tmpl)(scope);
scope.$apply('val = true');

expect(element.attr('readonly')).toBeDefined();
expect(element.attr('aria-readonly')).toBeUndefined();
});

it('should attach itself to custom controls', function() {
compileElement('<div ng-readonly="val"></div>');
expect(element.attr('aria-readonly')).toBe('false');

scope.$apply('val = true');
expect(element.attr('aria-readonly')).toBe('true');

});

it('should not attach itself if an aria-readonly attribute is already present', function() {
compileElement('<div ng-readonly="val" aria-readonly="userSetValue"></div>');

expect(element.attr('aria-readonly')).toBe('userSetValue');
});

it('should always set aria-readonly to a boolean value', function() {
compileElement('<div ng-readonly="val"></div>');

scope.$apply('val = "test angular"');
expect(element.attr('aria-readonly')).toBe('true');

scope.$apply('val = null');
expect(element.attr('aria-readonly')).toBe('false');

scope.$apply('val = {}');
expect(element.attr('aria-readonly')).toBe('true');
});
});

describe('aria-readonly when disabled', function() {
beforeEach(configAriaProvider({
ariaReadonly: false
}));
beforeEach(injectScopeAndCompiler);

it('should not add the aria-readonly attribute', function() {
compileElement("<input ng-model='val' readonly>");
expect(element.attr('aria-readonly')).toBeUndefined();

compileElement("<div ng-model='val' ng-readonly='true'></div>");
expect(element.attr('aria-readonly')).toBeUndefined();
});
});

describe('aria-required', function() {
beforeEach(injectScopeAndCompiler);

Expand Down

0 comments on commit ec0baad

Please sign in to comment.