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

Commit

Permalink
fix(menubar): fix broken menubar accessability with checkbox and radi…
Browse files Browse the repository at this point in the history
…o menuitems
  • Loading branch information
rschmukler committed Dec 8, 2015
1 parent cd75b22 commit 9fe3a57
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 11 deletions.
3 changes: 3 additions & 0 deletions src/components/menu/js/menuController.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
menuContainer = setMenuContainer;
// Default element for ARIA attributes has the ngClick or ngMouseenter expression
triggerElement = $element[0].querySelector('[ng-click],[ng-mouseenter]');
triggerElement.setAttribute('aria-expanded', 'false');

this.isInMenuBar = opts.isInMenuBar;
this.nestedMenus = $mdUtil.nodesToArray(menuContainer[0].querySelectorAll('.md-nested-menu'));
Expand Down Expand Up @@ -110,6 +111,7 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
self.enableHoverListener();
self.isOpen = true;
triggerElement = triggerElement || (ev ? ev.target : $element[0]);
triggerElement.setAttribute('aria-expanded', 'true');
$scope.$emit('$mdMenuOpen', $element);
$mdMenu.show({
scope: $scope,
Expand All @@ -120,6 +122,7 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
preserveElement: true,
parent: 'body'
}).finally(function() {
triggerElement.setAttribute('aria-expanded', 'false');
self.disableHoverListener();
});
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/menu/js/menuServiceProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,13 @@ function MenuProvider($$interimElementProvider) {
handled = true;
break;
case $mdConstant.KEY_CODE.UP_ARROW:
if (!focusMenuItem(ev, opts.menuContentEl, opts, -1)) {
if (!focusMenuItem(ev, opts.menuContentEl, opts, -1) && !opts.nestLevel) {
opts.mdMenuCtrl.triggerContainerProxy(ev);
}
handled = true;
break;
case $mdConstant.KEY_CODE.DOWN_ARROW:
if (!focusMenuItem(ev, opts.menuContentEl, opts, 1)) {
if (!focusMenuItem(ev, opts.menuContentEl, opts, 1) && !opts.nestLevel) {
opts.mdMenuCtrl.triggerContainerProxy(ev);
}
handled = true;
Expand Down
2 changes: 1 addition & 1 deletion src/components/menu/menu.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ddescribe('material.components.menu', function() {
describe('material.components.menu', function() {
var attachedElements = [];
var $mdMenu, $timeout, menuActionPerformed, $mdUtil;

Expand Down
21 changes: 17 additions & 4 deletions src/components/menuBar/js/menuItemController.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,23 @@ MenuItemController.prototype.init = function(ngModel) {
this.mode = $attrs.type;
this.iconEl = $element[0].children[0];
this.buttonEl = $element[0].children[1];
if (ngModel) this.initClickListeners();
if (ngModel) {
// Clear ngAria set attributes
this.initClickListeners();
}
}
};

MenuItemController.prototype.clearNgAria = function() {
var el = this.$element[0];
var clearAttrs = ['role', 'tabindex', 'aria-invalid', 'aria-checked'];
angular.forEach(clearAttrs, function(attr) {
el.removeAttribute(attr);
});
};

MenuItemController.prototype.initClickListeners = function() {
var self = this;
var ngModel = this.ngModel;
var $scope = this.$scope;
var $attrs = this.$attrs;
Expand All @@ -35,20 +47,21 @@ MenuItemController.prototype.initClickListeners = function() {

this.handleClick = angular.bind(this, this.handleClick);

var icon = this.iconEl
var icon = this.iconEl;
var button = angular.element(this.buttonEl);
var handleClick = this.handleClick;

$attrs.$observe('disabled', setDisabled);
setDisabled($attrs.disabled);

ngModel.$render = function render() {
self.clearNgAria();
if (isSelected()) {
icon.style.display = '';
$element.attr('aria-checked', 'true');
button.attr('aria-checked', 'true');
} else {
icon.style.display = 'none';
$element.attr('aria-checked', 'false');
button.attr('aria-checked', 'false');
}
};

Expand Down
1 change: 1 addition & 0 deletions src/components/menuBar/js/menuItemDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ angular
function MenuItemDirective() {
return {
require: ['mdMenuItem', '?ngModel'],
priority: 210,
compile: function(templateEl, templateAttrs) {
if (templateAttrs.type == 'checkbox' || templateAttrs.type == 'radio') {
var text = templateEl[0].textContent;
Expand Down
10 changes: 6 additions & 4 deletions src/components/menuBar/menu-bar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,13 @@ describe('material.components.menuBar', function() {
});
it('reflects the ng-model value', inject(function($rootScope) {
var menuItem = setup('ng-model="test"')[0];
expect(menuItem.getAttribute('aria-checked')).toBe('false');
var button = menuItem.querySelector('md-button');
expect(button.getAttribute('aria-checked')).toBe('false');
expect(menuItem.children[0].style.display).toBe('none');
$rootScope.test = true;
$rootScope.$digest();
expect(menuItem.children[0].style.display).toBe('');
expect(menuItem.getAttribute('aria-checked')).toBe('true');
expect(button.getAttribute('aria-checked')).toBe('true');
}));

function setup(attrs) {
Expand Down Expand Up @@ -283,12 +284,13 @@ describe('material.components.menuBar', function() {
it('reflects the ng-model value', inject(function($rootScope) {
$rootScope.test = 'apple';
var menuItem = setup('ng-model="test" value="hello"')[0];
expect(menuItem.getAttribute('aria-checked')).toBe('false');
var button = menuItem.querySelector('md-button');
expect(button.getAttribute('aria-checked')).toBe('false');
expect(menuItem.children[0].style.display).toBe('none');
$rootScope.test = 'hello';
$rootScope.$digest();
expect(menuItem.children[0].style.display).toBeFalsy();
expect(menuItem.getAttribute('aria-checked')).toBe('true');
expect(button.getAttribute('aria-checked')).toBe('true');
}));

function setup(attrs) {
Expand Down

0 comments on commit 9fe3a57

Please sign in to comment.