diff --git a/src/components/select/demoBasicUsage/index.html b/src/components/select/demoBasicUsage/index.html index 1ef01d4cb1d..972f93b99f7 100755 --- a/src/components/select/demoBasicUsage/index.html +++ b/src/components/select/demoBasicUsage/index.html @@ -2,18 +2,23 @@

Enter an address

+ - + + - + + - {{state.abbrev}} + + {{state.abbrev}} +
diff --git a/src/components/select/select.js b/src/components/select/select.js index 82126cd9a9b..16631018194 100755 --- a/src/components/select/select.js +++ b/src/components/select/select.js @@ -70,7 +70,7 @@ angular.module('material.components.select', [ * * */ -function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $rootElement, $compile, $parse) { +function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $compile, $parse) { return { restrict: 'E', require: ['^?mdInputContainer', 'mdSelect', 'ngModel', '?^form'], @@ -281,7 +281,7 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $rootElement, }); attr.$observe('disabled', function(disabled) { - if (typeof disabled == "string") { + if (angular.isString(disabled)) { disabled = true; } // Prevent click event being registered twice diff --git a/src/components/slider/slider.js b/src/components/slider/slider.js index 20e1e95a5d3..8adf868fa4d 100644 --- a/src/components/slider/slider.js +++ b/src/components/slider/slider.js @@ -50,22 +50,22 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi scope: {}, require: '?ngModel', template: - '
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ - \ -
\ -
\ -
\ -
', + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
', compile: compile }; @@ -98,10 +98,13 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi $viewChangeListeners: [] }; - var isDisabledParsed = attr.ngDisabled && $parse(attr.ngDisabled); - var isDisabledGetter = isDisabledParsed ? - function() { return isDisabledParsed(scope.$parent); } : - angular.noop; + var isDisabledGetter = angular.noop; + if (attr.disabled != null) { + isDisabledGetter = function() { return true; }; + } else if (attr.ngDisabled) { + isDisabledGetter = angular.bind(null, $parse(attr.ngDisabled), scope.$parent); + } + var thumb = angular.element(element[0].querySelector('.md-thumb')); var thumbText = angular.element(element[0].querySelector('.md-thumb-text')); var thumbContainer = thumb.parent(); @@ -139,7 +142,7 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi ngModelRender(); redrawTicks(); } - setTimeout(updateAll); + setTimeout(updateAll, 0); var debouncedUpdateAll = $$rAF.throttle(updateAll); angular.element($window).on('resize', debouncedUpdateAll); @@ -306,7 +309,7 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi function onPressDown(ev) { if (isDisabledGetter()) return; - element.addClass('active'); + element.addClass('md-active'); element[0].focus(); refreshSliderDimensions(); @@ -320,7 +323,7 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi function onPressUp(ev) { if (isDisabledGetter()) return; - element.removeClass('dragging active'); + element.removeClass('md-dragging md-active'); var exactVal = percentToValue( positionToPercent( ev.pointer.x )); var closestVal = minMaxValidator( stepValidator(exactVal) ); @@ -334,7 +337,7 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi isDragging = true; ev.stopPropagation(); - element.addClass('dragging'); + element.addClass('md-dragging'); setSliderFromEvent(ev); } function onDrag(ev) { diff --git a/src/components/slider/slider.scss b/src/components/slider/slider.scss index c1707933843..05c43cd5525 100644 --- a/src/components/slider/slider.scss +++ b/src/components/slider/slider.scss @@ -214,7 +214,7 @@ md-slider { } /* Don't animate left/right while panning */ - &.dragging { + &.md-dragging { .md-thumb-container, .md-track-fill { transition: none; @@ -236,7 +236,7 @@ md-slider { } &:focus, - &.active { + &.md-active { .md-focus-thumb { display: block; } @@ -259,7 +259,7 @@ md-slider { &:not([disabled]) { &:focus, - &.active { + &.md-active { .md-sign, .md-sign:after { opacity: 1; diff --git a/src/components/slider/slider.spec.js b/src/components/slider/slider.spec.js index dcdc19022fd..619e7f840fa 100644 --- a/src/components/slider/slider.spec.js +++ b/src/components/slider/slider.spec.js @@ -1,101 +1,112 @@ describe('md-slider', function() { + var $compile, $timeout, $log, $mdConstant, pageScope; beforeEach(module('ngAria')); beforeEach(module('material.components.slider')); + beforeEach(inject(function($injector) { + var $rootScope = $injector.get('$rootScope'); + pageScope = $rootScope.$new(); + + $compile = $injector.get('$compile'); + $timeout = $injector.get('$timeout'); + $mdConstant = $injector.get('$mdConstant'); + $log = $injector.get('$log'); + })); + function setup(attrs, dimensions) { var slider; - inject(function($compile, $rootScope) { - slider = $compile('')($rootScope); - spyOn( - slider[0].querySelector('.md-track-container'), - 'getBoundingClientRect' - ).and.returnValue(angular.extend({ - width: 100, - left: 0, - right: 0 - }, dimensions || {})); - }); + + slider = $compile('')(pageScope); + spyOn( + slider[0].querySelector('.md-track-container'), + 'getBoundingClientRect' + ).and.returnValue(angular.extend({ + width: 100, + left: 0, + right: 0 + }, dimensions || {})); + return slider; } - it('should set model on press', inject(function($compile, $rootScope, $timeout) { + it('should set model on press', function() { var slider = setup('ng-model="value" min="0" max="100"'); - $rootScope.$apply('value = 50'); + pageScope.$apply('value = 50'); slider.triggerHandler({type: '$md.pressdown', pointer: { x: 30 }}); slider.triggerHandler({type: '$md.dragstart', pointer: { x: 30 }}); $timeout.flush(); - expect($rootScope.value).toBe(30); + expect(pageScope.value).toBe(30); - //When going past max, it should clamp to max + // When going past max, it should clamp to max. slider.triggerHandler({type: '$md.drag', pointer: { x: 150 }}); $timeout.flush(); - expect($rootScope.value).toBe(100); + expect(pageScope.value).toBe(100); slider.triggerHandler({type: '$md.drag', pointer: { x: 50 }}); $timeout.flush(); - expect($rootScope.value).toBe(50); - })); + expect(pageScope.value).toBe(50); + }); - it('should increment model on right arrow', inject(function($compile, $rootScope, $timeout, $mdConstant) { + it('should increment model on right arrow', function() { var slider = setup('min="100" max="104" step="2" ng-model="model"'); - $rootScope.$apply('model = 100'); + pageScope.$apply('model = 100'); slider.triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW }); $timeout.flush(); - expect($rootScope.model).toBe(102); + expect(pageScope.model).toBe(102); slider.triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW }); $timeout.flush(); - expect($rootScope.model).toBe(104); + expect(pageScope.model).toBe(104); - // Stays at max + // Stays at max. slider.triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW }); $timeout.flush(); - expect($rootScope.model).toBe(104); - })); + expect(pageScope.model).toBe(104); + }); - it('should decrement model on left arrow', inject(function($compile, $rootScope, $timeout, $mdConstant) { + it('should decrement model on left arrow', function() { var slider = setup('min="100" max="104" step="2" ng-model="model"'); - $rootScope.$apply('model = 104'); + pageScope.$apply('model = 104'); slider.triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.LEFT_ARROW }); $timeout.flush(); - expect($rootScope.model).toBe(102); + expect(pageScope.model).toBe(102); slider.triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.LEFT_ARROW }); $timeout.flush(); - expect($rootScope.model).toBe(100); + expect(pageScope.model).toBe(100); - // Stays at min + // Stays at min. slider.triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.LEFT_ARROW }); $timeout.flush(); - expect($rootScope.model).toBe(100); - })); + expect(pageScope.model).toBe(100); + }); - it('should update the thumb text', inject(function($compile, $rootScope, $timeout, $mdConstant) { + it('should update the thumb text', function() { var slider = setup('ng-model="value" md-discrete min="0" max="100" step="1"'); - $rootScope.$apply('value = 30'); + pageScope.$apply('value = 30'); expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('30'); slider.triggerHandler({ @@ -111,38 +122,37 @@ describe('md-slider', function() { slider.triggerHandler({type: '$md.dragstart', pointer: { x: 31 }}); slider.triggerHandler({type: '$md.drag', pointer: { x: 31 }}); expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('31'); - })); + }); - it('should update the thumb text with the model value when using ng-change', inject(function($compile, $rootScope, $timeout) { - $rootScope.stayAt50 = function () { - $rootScope.value = 50; + it('should update the thumb text with the model value when using ng-change', function() { + pageScope.stayAt50 = function () { + pageScope.value = 50; }; var slider = setup('ng-model="value" min="0" max="100" ng-change="stayAt50()"'); - var sliderCtrl = slider.controller('mdSlider'); slider.triggerHandler({type: '$md.pressdown', pointer: { x: 30 }}); $timeout.flush(); - expect($rootScope.value).toBe(50); + expect(pageScope.value).toBe(50); expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('50'); - })); + }); - it('should call $log.warn if aria-label isnt provided', inject(function($compile, $rootScope, $timeout, $log) { + it('should call $log.warn if aria-label isnt provided', function() { spyOn($log, "warn"); - var element = setup('min="100" max="104" step="2" ng-model="model"'); + setup('min="100" max="104" step="2" ng-model="model"'); expect($log.warn).toHaveBeenCalled(); - })); + }); - it('should not call $log.warn if aria-label is provided', inject(function($compile, $rootScope, $timeout, $log) { + it('should not call $log.warn if aria-label is provided', function() { spyOn($log, "warn"); - var element = setup('aria-label="banana" min="100" max="104" step="2" ng-model="model"'); + setup('aria-label="banana" min="100" max="104" step="2" ng-model="model"'); expect($log.warn).not.toHaveBeenCalled(); - })); + }); - it('should add aria attributes', inject(function($compile, $rootScope, $timeout, $mdConstant){ + it('should add aria attributes', function() { var slider = setup('min="100" max="104" step="2" ng-model="model"'); - $rootScope.$apply('model = 102'); + pageScope.$apply('model = 102'); expect(slider.attr('role')).toEqual('slider'); expect(slider.attr('aria-valuemin')).toEqual('100'); @@ -155,10 +165,10 @@ describe('md-slider', function() { }); $timeout.flush(); expect(slider.attr('aria-valuenow')).toEqual('100'); - })); + }); - it('should ignore pressdown events when disabled', inject(function($compile, $rootScope, $timeout) { - $rootScope.isDisabled = true; + it('should ignore pressdown events when disabled', function() { + pageScope.isDisabled = true; var slider = setup('ng-disabled="isDisabled"'); // Doesn't add active class on pressdown when disabled @@ -166,52 +176,65 @@ describe('md-slider', function() { type: '$md.pressdown', pointer: {} }); - expect(slider).not.toHaveClass('active'); + expect(slider).not.toHaveClass('md-active'); // Doesn't remove active class up on pressup when disabled - slider.addClass('active'); + slider.addClass('md-active'); slider.triggerHandler({ type: '$md.pressup', pointer: {} }); - expect(slider).toHaveClass('active'); - })); + expect(slider).toHaveClass('md-active'); + }); + + it('should disable via the `disabled` attribute', function() { + var slider = setup('disabled'); + + + // Check for disabled state by triggering the pressdown handler and asserting that + // the slider is not active. + slider.triggerHandler({ + type: '$md.pressdown', + pointer: {} + }); + expect(slider).not.toHaveClass('md-active'); + }); - it('should add active class on pressdown and remove on pressup', inject(function($rootScope) { + it('should add active class on pressdown and remove on pressup', function() { var slider = setup(); - expect(slider).not.toHaveClass('active'); + expect(slider).not.toHaveClass('md-active'); slider.triggerHandler({ type: '$md.pressdown', pointer: {} }); - expect(slider).toHaveClass('active'); + expect(slider).toHaveClass('md-active'); slider.triggerHandler({ type: '$md.pressup', pointer: {} }); - expect(slider).not.toHaveClass('active'); - })); + expect(slider).not.toHaveClass('md-active'); + }); - it('should increment at a predictable step', inject(function($rootScope, $timeout) { + it('should increment at a predictable step', function() { buildSlider(0.1, 1).drag({x:70}); - expect($rootScope.value).toBe(0.7); + expect(pageScope.value).toBe(0.7); buildSlider(0.25, 1).drag({x:45}); - expect($rootScope.value).toBe(0.5); + expect(pageScope.value).toBe(0.5); buildSlider(0.25, 1).drag({x:35}); - expect($rootScope.value).toBe(0.25); + expect(pageScope.value).toBe(0.25); buildSlider(1, 100).drag({x:90}); - expect($rootScope.value).toBe(90); + expect(pageScope.value).toBe(90); function buildSlider(step, max) { var slider = setup('ng-model="value" min="0" max="' + max + '" step="' + step + '"'); - $rootScope.$apply('value = 0.5'); + pageScope.$apply('value = 0.5'); return { drag : function simulateDrag(drag) { @@ -224,6 +247,6 @@ describe('md-slider', function() { }; } - })); + }); }); diff --git a/src/components/switch/switch.js b/src/components/switch/switch.js index 4162dbc083f..fef618abcb0 100644 --- a/src/components/switch/switch.js +++ b/src/components/switch/switch.js @@ -47,12 +47,12 @@ angular.module('material.components.switch', [ * * */ -function MdSwitch(mdCheckboxDirective, $mdTheming, $mdUtil, $document, $mdConstant, $parse, $$rAF, $mdGesture) { +function MdSwitch(mdCheckboxDirective, $mdUtil, $mdConstant, $parse, $$rAF, $mdGesture) { var checkboxDirective = mdCheckboxDirective[0]; return { restrict: 'E', - priority:210, // Run before ngAria + priority: 210, // Run before ngAria transclude: true, template: '
' + @@ -61,20 +61,26 @@ function MdSwitch(mdCheckboxDirective, $mdTheming, $mdUtil, $document, $mdConsta '
' + '
'+ '
' + - '
' + - '
', + '
', require: '?ngModel', - compile: compile + compile: mdSwitchCompile }; - function compile(element, attr) { + function mdSwitchCompile(element, attr) { var checkboxLink = checkboxDirective.compile(element, attr); - // no transition on initial load + // No transition on initial load. element.addClass('md-dragging'); return function (scope, element, attr, ngModel) { ngModel = ngModel || $mdUtil.fakeNgModel(); - var disabledGetter = $parse(attr.ngDisabled); + + var disabledGetter = null; + if (attr.disabled != null) { + disabledGetter = function() { return true; }; + } else if (attr.ngDisabled) { + disabledGetter = $parse(attr.ngDisabled); + } + var thumbContainer = angular.element(element[0].querySelector('.md-thumb-container')); var switchContainer = angular.element(element[0].querySelector('.md-container')); @@ -85,7 +91,7 @@ function MdSwitch(mdCheckboxDirective, $mdTheming, $mdUtil, $document, $mdConsta checkboxLink(scope, element, attr, ngModel); - if (angular.isDefined(attr.ngDisabled)) { + if (disabledGetter) { scope.$watch(disabledGetter, function(isDisabled) { element.attr('tabindex', isDisabled ? -1 : 0); }); @@ -100,14 +106,12 @@ function MdSwitch(mdCheckboxDirective, $mdTheming, $mdUtil, $document, $mdConsta var drag; function onDragStart(ev) { - // Don't go if ng-disabled===true - if (disabledGetter(scope)) return; + // Don't go if the switch is disabled. + if (disabledGetter && disabledGetter(scope)) return; ev.stopPropagation(); element.addClass('md-dragging'); - drag = { - width: thumbContainer.prop('offsetWidth') - }; + drag = {width: thumbContainer.prop('offsetWidth')}; element.removeClass('transition'); } diff --git a/src/components/switch/switch.spec.js b/src/components/switch/switch.spec.js index eb39b0b43c2..83c2a83ec7f 100644 --- a/src/components/switch/switch.spec.js +++ b/src/components/switch/switch.spec.js @@ -1,19 +1,28 @@ describe('', function() { var CHECKED_CSS = 'md-checked'; + var $compile, parentScope; beforeEach(module('ngAria', 'material.components.switch')); - it('should set checked css class and aria-checked attributes', inject(function($compile, $rootScope) { - var element = $compile('
' + - '' + - '' + - '' + - '' + - '
')($rootScope); - - $rootScope.$apply(function(){ - $rootScope.blue = false; - $rootScope.green = true; + beforeEach(inject(function($injector) { + var $rootScope = $injector.get('$rootScope'); + parentScope = $rootScope.$new(); + + $compile = $injector.get('$compile'); + })); + + it('should set checked css class and aria-checked attributes', function() { + var template = + '
' + + '' + + '' + + '
'; + + var element = $compile(template)(parentScope); + + parentScope.$apply(function(){ + parentScope.blue = false; + parentScope.green = true; }); var switches = angular.element(element[0].querySelectorAll('md-switch')); @@ -26,9 +35,9 @@ describe('', function() { expect(switches.eq(1).attr('aria-checked')).toEqual('true'); expect(switches.eq(1).attr('role')).toEqual('checkbox'); - $rootScope.$apply(function(){ - $rootScope.blue = true; - $rootScope.green = false; + parentScope.$apply(function(){ + parentScope.blue = true; + parentScope.green = false; }); expect(switches.eq(1).hasClass(CHECKED_CSS)).toEqual(false); @@ -36,17 +45,24 @@ describe('', function() { expect(switches.eq(1).attr('aria-checked')).toEqual('false'); expect(switches.eq(0).attr('aria-checked')).toEqual('true'); expect(switches.eq(1).attr('role')).toEqual('checkbox'); - })); + }); - it('should have tabindex -1 while disabled', inject(function($rootScope, $compile) { - $rootScope.value = false; - var el = $compile('')($rootScope); + it('should have tabindex -1 while disabled', function() { + parentScope.value = false; + var el = $compile('')(parentScope); - $rootScope.$apply(); + parentScope.$apply(); expect(el.attr('tabindex')).not.toEqual('-1'); - $rootScope.$apply('value = true'); + parentScope.$apply('value = true'); expect(el.attr('tabindex')).toEqual('-1'); - })); + }); + + it('should disable via `disabled` attribute', function() { + parentScope.value = false; + var element = $compile('')(parentScope); + parentScope.$apply(); + expect(element.attr('tabindex')).toEqual('-1'); + }); });