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

WIP: Refactor to use ngAria #316

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 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
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"dependencies": {
"angular": "1.3.0-rc.2",
"angular-animate": "1.3.0-rc.2",
"hammerjs": "~2.0.2"
"hammerjs": "~2.0.2",
"angular-aria": "~1.3.0-build.3289+sha.d8c8b2e"
},
"devDependencies": {
"angular-mocks": "1.3.0-rc.2",
Expand Down
1 change: 1 addition & 0 deletions config/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'bower_components/jquery/dist/jquery.js',
'bower_components/angular/angular.js',
'bower_components/angular-animate/angular-animate.js',
'bower_components/angular-aria/angular-aria.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/hammerjs/hammer.js',
'config/test-utils.js',
Expand Down
6 changes: 3 additions & 3 deletions src/components/buttons/buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ angular.module('material.components.button', [
.directive('materialButton', [
'ngHrefDirective',
'$materialInkRipple',
'$aria',
'$materialAria',
MaterialButtonDirective
]);

Expand Down Expand Up @@ -46,7 +46,7 @@ angular.module('material.components.button', [
* </material-button>
* </hljs>
*/
function MaterialButtonDirective(ngHrefDirectives, $materialInkRipple, $aria ) {
function MaterialButtonDirective(ngHrefDirectives, $materialInkRipple, $materialAria ) {
var ngHrefDirective = ngHrefDirectives[0];

return {
Expand Down Expand Up @@ -95,7 +95,7 @@ function MaterialButtonDirective(ngHrefDirectives, $materialInkRipple, $aria ) {
});

return function postLink(scope, element, attr) {
$aria.expect(element, 'aria-label', element.text());
$materialAria.expect(element, 'aria-label', element.text());
$materialInkRipple.attachButtonBehavior(element);
};
}
Expand Down
118 changes: 60 additions & 58 deletions src/components/checkbox/checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ angular.module('material.components.checkbox', [
'material.animations',
'material.services.aria'
])
.directive('materialCheckbox', [
.directive('materialCheckbox', [
'inputDirective',
'$materialInkRipple',
'$aria',
'$materialAria',
MaterialCheckboxDirective
]);

Expand Down Expand Up @@ -49,7 +49,7 @@ angular.module('material.components.checkbox', [
* </hljs>
*
*/
function MaterialCheckboxDirective(inputDirectives, $materialInkRipple, $aria) {
function MaterialCheckboxDirective(inputDirectives, $materialInkRipple, $materialAria) {
var inputDirective = inputDirectives[0];

var CHECKED_CSS = 'material-checked';
Expand All @@ -58,75 +58,77 @@ function MaterialCheckboxDirective(inputDirectives, $materialInkRipple, $aria) {
restrict: 'E',
transclude: true,
require: '?ngModel',
template:
template:
'<div class="material-container" ink-ripple="checkbox">' +
'<div class="material-icon"></div>' +
'</div>' +
'<div ng-transclude class="material-label"></div>',
link: postLink
compile: compile
};

// **********************************************************
// Private Methods
// **********************************************************

function postLink(scope, element, attr, ngModelCtrl) {
var checked = false;
function compile (tElement, tAttrs) {

tAttrs.type = 'checkbox';
tAttrs.tabIndex = 0;
tElement.attr('role', tAttrs.type);

$materialAria.expect(tElement, Constant.ARIA.PROPERTY.LABEL, tElement.text());

return function postLink(scope, element, attr, ngModelCtrl) {
var checked = false;

// Create a mock ngModel if the user doesn't provide one
ngModelCtrl = ngModelCtrl || {
$setViewValue: function(value) {
this.$viewValue = value;
},
$parsers: [],
$formatters: []
};

// Reuse the original input[type=checkbox] directive from Angular core.
// This is a bit hacky as we need our own event listener and own render
// function.
inputDirective.link(scope, {
on: angular.noop,
0: {}
}, attr, [ngModelCtrl]);

// Create a mock ngModel if the user doesn't provide one
ngModelCtrl = ngModelCtrl || {
$setViewValue: function(value) {
this.$viewValue = value;
},
$parsers: [],
$formatters: []
};

// Reuse the original input[type=checkbox] directive from Angular core.
// This is a bit hacky as we need our own event listener and own render
// function.
attr.type = 'checkbox';
attr.tabIndex = 0;
inputDirective.link(scope, {
on: angular.noop,
0: {}
}, attr, [ngModelCtrl]);

// We can't chain element.attr here because of a bug with jqLite
element.attr(Constant.ARIA.PROPERTY.CHECKED, checked);
element.attr('role', attr.type);
element.attr('tabIndex', attr.tabIndex);
element.on('click', listener);
element.on('keypress', keypressHandler);
ngModelCtrl.$render = render;

$aria.expect(element, Constant.ARIA.PROPERTY.LABEL, element.text());

function keypressHandler(ev) {
if(ev.which === Constant.KEY_CODE.SPACE) {
ev.preventDefault();
listener(ev);
//element.attr('tabIndex', attr.tabIndex);
element.on('click', listener);
element.on('keypress', keypressHandler);
ngModelCtrl.$render = render;

function keypressHandler(ev) {
if(ev.which === Constant.KEY_CODE.SPACE) {
ev.preventDefault();
listener(ev);
}
}
}
function listener(ev) {
if (element[0].hasAttribute('disabled')) return;

scope.$apply(function() {
checked = !checked;
ngModelCtrl.$setViewValue(checked, ev && ev.type);
ngModelCtrl.$render();
});
}

function render() {
checked = ngModelCtrl.$viewValue;
element.attr(Constant.ARIA.PROPERTY.CHECKED, checked);
if(checked) {
element.addClass(CHECKED_CSS);
} else {
element.removeClass(CHECKED_CSS);
function listener(ev) {
if (element[0].hasAttribute('disabled')) return;

scope.$apply(function() {
checked = !checked;
ngModelCtrl.$setViewValue(checked, ev && ev.type);
ngModelCtrl.$render();
});
}
}

function render() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With removal of this line, aria-checked isn't being added even though ngAria is in place. Some bugs to iron out there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, not sure how to go about this. also thinking if this model of composition for directives really makes sense. might be better to have a few different linking-like fns and mix them in to each individual directive.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There does seem to be repetition amongst some of the Material directives. But the interaction models and required ARIA attributes amongst components are quite varied. What behaviors would you move into link functions?

checked = ngModelCtrl.$viewValue;
if(checked) {
element.addClass(CHECKED_CSS);
} else {
element.removeClass(CHECKED_CSS);
}
}
};
}

}
Expand Down
1 change: 1 addition & 0 deletions src/components/checkbox/checkbox.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ describe('materialCheckbox', function() {
var CHECKED_CSS = 'material-checked';

beforeEach(module('material.components.checkbox'));
beforeEach(module('ngAria'));

it('should set checked css class and aria-checked attributes', inject(function($compile, $rootScope) {
var element = $compile('<div>' +
Expand Down
8 changes: 4 additions & 4 deletions src/components/dialog/dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ angular.module('material.components.dialog', [
'$rootScope',
'$materialEffects',
'$animate',
'$aria',
'$materialAria',
MaterialDialogService
]);

Expand Down Expand Up @@ -104,7 +104,7 @@ function MaterialDialogDirective($$rAF) {
* @param {element=} appendTo The element to append the dialog to. Defaults to appending
* to the root element of the application.
*/
function MaterialDialogService($timeout, $materialCompiler, $rootElement, $rootScope, $materialEffects, $animate, $aria) {
function MaterialDialogService($timeout, $materialCompiler, $rootElement, $rootScope, $materialEffects, $animate, $materialAria) {
var recentDialog;
var dialogParent = $rootElement.find('body');
if ( !dialogParent.length ) {
Expand Down Expand Up @@ -221,7 +221,7 @@ function MaterialDialogService($timeout, $materialCompiler, $rootElement, $rootS
function configureAria(element) {
var ROLE = Constant.ARIA.ROLE;

$aria.update(element, {
$materialAria.update(element, {
'role': ROLE.DIALOG
});

Expand All @@ -230,7 +230,7 @@ function MaterialDialogService($timeout, $materialCompiler, $rootElement, $rootS
dialogContent = element;
}
var defaultText = Util.stringFromTextBody(dialogContent.text(), 3);
$aria.expect(element, 'aria-label', defaultText);
$materialAria.expect(element, 'aria-label', defaultText);
}

return recentDialog;
Expand Down
6 changes: 3 additions & 3 deletions src/components/radioButton/radioButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ angular.module('material.components.radioButton', [
materialRadioGroupDirective
])
.directive('materialRadioButton', [
'$aria',
'$materialAria',
materialRadioButtonDirective
]);

Expand Down Expand Up @@ -202,7 +202,7 @@ function materialRadioGroupDirective() {
* </hljs>
*
*/
function materialRadioButtonDirective($aria) {
function materialRadioButtonDirective($materialAria) {

var CHECKED_CSS = 'material-checked';

Expand Down Expand Up @@ -231,7 +231,7 @@ function materialRadioButtonDirective($aria) {
})
.attr('role', Constant.ARIA.ROLE.RADIO);

$aria.expect(element, Constant.ARIA.PROPERTY.LABEL, element.text());
$materialAria.expect(element, Constant.ARIA.PROPERTY.LABEL, element.text());

function listener(ev) {
if (element[0].hasAttribute('disabled')) return;
Expand Down
6 changes: 3 additions & 3 deletions src/components/slider/slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function SliderDirective() {
'$timeout',
'$window',
'$materialEffects',
'$aria',
'$materialAria',
SliderController
],
template:
Expand Down Expand Up @@ -99,7 +99,7 @@ function SliderDirective() {
* We use a controller for all the logic so that we can expose a few
* things to unit tests
*/
function SliderController(scope, element, attr, $$rAF, $timeout, $window, $materialEffects, $aria) {
function SliderController(scope, element, attr, $$rAF, $timeout, $window, $materialEffects, $materialAria) {

this.init = function init(ngModelCtrl) {
var thumb = angular.element(element[0].querySelector('.slider-thumb'));
Expand All @@ -123,7 +123,7 @@ function SliderController(scope, element, attr, $$rAF, $timeout, $window, $mater
updateAriaDisabled(!!attr.disabled);
}

$aria.expect(element, 'aria-label');
$materialAria.expect(element, 'aria-label');
element.attr('tabIndex', 0);
element.attr('role', Constant.ARIA.ROLE.SLIDER);
element.on('keydown', keydownListener);
Expand Down
9 changes: 6 additions & 3 deletions src/components/switch/switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,17 @@ function MaterialSwitch(checkboxDirectives, radioButtonDirectives) {
};

function compile(element, attr) {

var thumb = angular.element(element[0].querySelector('.material-switch-thumb'));
//Copy down disabled attributes for checkboxDirective to use
thumb.attr('disabled', attr.disabled);
thumb.attr('ngDisabled', attr.ngDisabled);

return function postLink(scope, element, attr, ngModelCtrl) {
checkboxDirective.link(scope, thumb, attr, ngModelCtrl);
var link = checkboxDirective.compile(thumb, attr);

return function (scope, element, attr, ngModelCtrl) {
var thumb = angular.element(element[0].querySelector('.material-switch-thumb'));
return link(scope, thumb, attr, ngModelCtrl)
};
}
}
1 change: 1 addition & 0 deletions src/components/switch/switch.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
describe('<material-switch>', function() {
var CHECKED_CSS = 'material-checked';

beforeEach(module('ngAria'));
beforeEach(module('material.components.switch'));

it('should set checked css class and aria-checked attributes', inject(function($compile, $rootScope) {
Expand Down
10 changes: 5 additions & 5 deletions src/components/tabs/js/tabItemDirective.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
angular.module('material.components.tabs')
.directive('materialTab', [
'$attrBind',
'$aria',
'$materialAria',
'$materialInkRipple',
TabDirective
TabDirective
]);

/**
Expand Down Expand Up @@ -56,7 +56,7 @@ angular.module('material.components.tabs')
* </hljs>
*
*/
function TabDirective( $attrBind, $aria, $materialInkRipple) {
function TabDirective( $attrBind, $materialAria, $materialInkRipple) {
var noop = angular.noop;

return {
Expand Down Expand Up @@ -125,7 +125,7 @@ function TabDirective( $attrBind, $aria, $materialInkRipple) {
var ROLE = Constant.ARIA.ROLE;

scope.ariaId = buildAriaID();
$aria.update( element, {
$materialAria.update( element, {
'id' : scope.ariaId,
'role' : ROLE.TAB,
'aria-selected' : false,
Expand Down Expand Up @@ -159,7 +159,7 @@ function TabDirective( $attrBind, $aria, $materialInkRipple) {

scope.$watch('active', function (isActive) {

$aria.update( element, {
$materialAria.update( element, {
'aria-selected' : isActive,
'tabIndex' : isActive === true ? 0 : -1
});
Expand Down
Loading