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

Commit

Permalink
fix(material-radio): Radio button a11y
Browse files Browse the repository at this point in the history
Closes #310
  • Loading branch information
Marcy Sutton committed Sep 24, 2014
1 parent 664ab99 commit 05ed42d
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 14 deletions.
44 changes: 35 additions & 9 deletions src/components/radioButton/radioButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function materialRadioGroupDirective() {

return {
restrict: 'E',
controller: RadioGroupController,
controller: ['$element', RadioGroupController],
require: ['materialRadioGroup', '?ngModel'],
link: link
};
Expand All @@ -65,12 +65,11 @@ function materialRadioGroupDirective() {
};

function keydownListener(ev) {

if (ev.which === Constant.KEY_CODE.LEFT_ARROW) {
if (ev.which === Constant.KEY_CODE.LEFT_ARROW || ev.which === Constant.KEY_CODE.UP_ARROW) {
ev.preventDefault();
rgCtrl.selectPrevious(element);
}
else if (ev.which === Constant.KEY_CODE.RIGHT_ARROW) {
else if (ev.which === Constant.KEY_CODE.RIGHT_ARROW || ev.which === Constant.KEY_CODE.DOWN_ARROW) {
ev.preventDefault();
rgCtrl.selectNext(element);
}
Expand All @@ -85,8 +84,9 @@ function materialRadioGroupDirective() {
.on('keydown', keydownListener);
}

function RadioGroupController() {
function RadioGroupController($element) {
this._radioButtonRenderFns = [];
this.$element = $element;
}

function createRadioGroupControllerProto() {
Expand Down Expand Up @@ -122,6 +122,9 @@ function materialRadioGroupDirective() {
},
selectPrevious : function(element) {
return selectButton('previous', element);
},
setActiveDescendant: function (radioId) {
this.$element.attr('aria-activedescendant', radioId);
}
};
}
Expand Down Expand Up @@ -221,17 +224,16 @@ function materialRadioButtonDirective($aria) {
function link(scope, element, attr, rgCtrl) {
var lastChecked;

configureAria(element, scope);

rgCtrl.add(render);
attr.$observe('value', render);

element
.on('click', listener)
.on('$destroy', function() {
rgCtrl.remove(render);
})
.attr('role', 'radio');

$aria.expect(element, 'aria-label', element.text());
});

function listener(ev) {
if (element[0].hasAttribute('disabled')) return;
Expand All @@ -250,10 +252,34 @@ function materialRadioButtonDirective($aria) {
element.attr('aria-checked', checked);
if (checked) {
element.addClass(CHECKED_CSS);
rgCtrl.setActiveDescendant(element.attr('id'));
} else {
element.removeClass(CHECKED_CSS);
}
}
/**
* Inject ARIA-specific attributes appropriate for each radio button
*/
function configureAria( element, scope ){
scope.ariaId = buildAriaID();

element.attr({
'id' : scope.ariaId,
'role' : 'radio',
'aria-checked' : 'false'
});

$aria.expect(element, 'aria-label', element.text());

/**
* Build a unique ID for each radio button that will be used with aria-activedescendant.
* Preserve existing ID if already specified.
* @returns {*|string}
*/
function buildAriaID() {
return attr.id || ( 'radio' + "_" + Util.nextUid() );
}
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/components/radioButton/radioButton.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('radioButton', function() {
expect(rbElements.eq(1).hasClass(CHECKED_CSS)).toEqual(true);
}));

it('should set aria roles', inject(function($compile, $rootScope) {
it('should set roles', inject(function($compile, $rootScope) {

var element = $compile('<material-radio-group ng-model="color">' +
'<material-radio-button value="blue"></material-radio-button>' +
Expand All @@ -31,7 +31,7 @@ describe('radioButton', function() {
expect(rbGroupElement.find('material-radio-button').eq(0).attr('role')).toEqual('radio');
}));

it('should set aria-check attributes', inject(function($compile, $rootScope) {
it('should set aria attributes', inject(function($compile, $rootScope) {
var element = $compile('<material-radio-group ng-model="color">' +
'<material-radio-button value="blue"></material-radio-button>' +
'<material-radio-button value="green"></material-radio-button>' +
Expand All @@ -45,6 +45,9 @@ describe('radioButton', function() {

expect(rbElements.eq(0).attr('aria-checked')).toEqual('false');
expect(rbElements.eq(1).attr('aria-checked')).toEqual('true');

expect(element.attr('aria-activedescendant')).toEqual(rbElements.eq(1).attr('id'));
expect(element.attr('aria-activedescendant')).not.toEqual(rbElements.eq(0).attr('id'));
}));

it('should be operable via arrow keys', inject(function($compile, $rootScope) {
Expand Down
8 changes: 5 additions & 3 deletions src/core/constant.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
var Constant = {
KEY_CODE: {
ENTER: 13,
ESCAPE: 27,
SPACE: 32,
LEFT_ARROW: 37,
RIGHT_ARROW: 39,
ENTER: 13
LEFT_ARROW : 37,
UP_ARROW : 38,
RIGHT_ARROW : 39,
DOWN_ARROW : 40
}
};

0 comments on commit 05ed42d

Please sign in to comment.