MODULE-TYPO

- Sass set-up
- md-list
- md-theming (install)
This commit is contained in:
iRouge
2015-01-16 07:52:29 -05:00
parent ec1b4b9b0c
commit f1d2b8cb75
312 changed files with 26839 additions and 1309 deletions
@@ -0,0 +1,55 @@
<div ng-controller="AppCtrl">
<md-content class="md-padding">
<h3>
RGB <span ng-attr-style="border: 1px solid #333; background: rgb({{color.red}},{{color.green}},{{color.blue}})">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
</h3>
<div layout>
<div flex="10" layout layout-align="center center">
<span>R</span>
</div>
<md-slider flex min="0" max="255" ng-model="color.red" aria-label="red" id="red-slider" class="md-warn">
</md-slider>
<div flex="20" layout layout-align="center center">
<input type="number" ng-model="color.red" aria-label="red" aria-controls="red-slider">
</div>
</div>
<div layout>
<div flex="10" layout layout-align="center center">
<span>G</span>
</div>
<md-slider flex ng-model="color.green" min="0" max="255" aria-label="green" id="green-slider" class="md-accent">
</md-slider>
<div flex="20" layout layout-align="center center">
<input type="number" ng-model="color.green" aria-label="green" aria-controls="green-slider">
</div>
</div>
<div layout>
<div flex="10" layout layout-align="center center">
<span>B</span>
</div>
<md-slider flex ng-model="color.blue" min="0" max="255" aria-label="blue" id="blue-slider">
</md-slider>
<div flex="20" layout layout-align="center center">
<input type="number" ng-model="color.blue" aria-label="blue" aria-controls="blue-slider">
</div>
</div>
<h3>Rating: {{rating}}/5</h3>
<md-slider md-discrete ng-model="rating" step="1" min="1" max="5" aria-label="rating">
</md-slider>
<h3>Disabled</h3>
<md-slider ng-model="disabled1" ng-disabled="true" aria-label="Disabled 1"></md-slider>
<md-slider ng-model="disabled2" ng-disabled="true" aria-label="Disabled 2"></md-slider>
<h3>Disabled, Discrete</h3>
<md-slider ng-model="disabled1" ng-disabled="true" step="3" md-discrete min="0" max="10" aria-label="Disabled discrete 1"></md-slider>
<md-slider ng-model="disabled2" ng-disabled="true" step="10" md-discrete aria-label="Disabled discrete 2"></md-slider>
</md-content>
</div>
@@ -0,0 +1,16 @@
angular.module('sliderDemo1', ['ngMaterial'])
.controller('AppCtrl', function($scope) {
$scope.color = {
red: Math.floor(Math.random() * 255),
green: Math.floor(Math.random() * 255),
blue: Math.floor(Math.random() * 255)
};
$scope.rating = 3;
$scope.disabled1 = 0;
$scope.disabled2 = 70;
});
@@ -0,0 +1,4 @@
input[type="number"] {
text-align: center;
}
@@ -0,0 +1,87 @@
md-slider.md-THEME_NAME-theme {
.md-track {
background-color: '{{foreground-3}}';
}
.md-track-ticks {
background-color: '{{foreground-4}}';
}
.md-focus-thumb {
background-color: '{{foreground-2}}';
}
.md-focus-ring {
border-color: '{{foreground-4}}';
}
.md-disabled-thumb {
border-color: '{{background-hue-3}}';
}
&.md-min .md-thumb:after {
background-color: '{{background-hue-3}}';
}
.md-track.md-track-fill {
background-color: '{{primary-color}}';
}
.md-thumb:after {
border-color: '{{primary-color}}';
background-color: '{{primary-color}}';
}
.md-sign {
background-color: '{{primary-color}}';
&:after {
border-top-color: '{{primary-color}}';
}
}
.md-thumb-text {
color: '{{primary-contrast}}';
}
&.md-warn {
.md-track-fill {
background-color: '{{warn-color}}';
}
.md-thumb:after {
border-color: '{{warn-color}}';
background-color: '{{warn-color}}';
}
.md-sign {
background-color: '{{warn-color}}';
&:after {
border-top-color: '{{warn-color}}';
}
}
.md-thumb-text {
color: '{{warn-contrast}}';
}
}
&.md-accent {
.md-track-fill {
background-color: '{{accent-color}}';
}
.md-thumb:after {
border-color: '{{accent-color}}';
background-color: '{{accent-color}}';
}
.md-sign {
background-color: '{{accent-color}}';
&:after {
border-top-color: '{{accent-color}}';
}
}
.md-thumb-text {
color: '{{accent-contrast}}';
}
}
&[disabled] {
.md-thumb:after {
border-color: '{{foreground-3}}';
}
&:not(.md-min) .md-thumb:after {
background-color: '{{foreground-3}}';
}
}
}
@@ -0,0 +1,403 @@
(function() {
'use strict';
/**
* @ngdoc module
* @name material.components.slider
*/
angular.module('material.components.slider', [
'material.core'
])
.directive('mdSlider', SliderDirective);
/**
* @ngdoc directive
* @name mdSlider
* @module material.components.slider
* @restrict E
* @description
* The `<md-slider>` component allows the user to choose from a range of
* values.
*
* It has two modes: 'normal' mode, where the user slides between a wide range
* of values, and 'discrete' mode, where the user slides between only a few
* select values.
*
* To enable discrete mode, add the `md-discrete` attribute to a slider,
* and use the `step` attribute to change the distance between
* values the user is allowed to pick.
*
* @usage
* <h4>Normal Mode</h4>
* <hljs lang="html">
* <md-slider ng-model="myValue" min="5" max="500">
* </md-slider>
* </hljs>
* <h4>Discrete Mode</h4>
* <hljs lang="html">
* <md-slider md-discrete ng-model="myDiscreteValue" step="10" min="10" max="130">
* </md-slider>
* </hljs>
*
* @param {boolean=} md-discrete Whether to enable discrete mode.
* @param {number=} step The distance between values the user is allowed to pick. Default 1.
* @param {number=} min The minimum value the user is allowed to pick. Default 0.
* @param {number=} max The maximum value the user is allowed to pick. Default 100.
*/
function SliderDirective($mdTheming) {
return {
scope: {},
require: ['?ngModel', 'mdSlider'],
controller: SliderController,
template:
'<div class="md-track-container">' +
'<div class="md-track"></div>' +
'<div class="md-track md-track-fill"></div>' +
'<div class="md-track-ticks"></div>' +
'</div>' +
'<div class="md-thumb-container">' +
'<div class="md-thumb"></div>' +
'<div class="md-focus-thumb"></div>' +
'<div class="md-focus-ring"></div>' +
'<div class="md-sign">' +
'<span class="md-thumb-text"></span>' +
'</div>' +
'<div class="md-disabled-thumb"></div>' +
'</div>',
link: postLink
};
function postLink(scope, element, attr, ctrls) {
$mdTheming(element);
var ngModelCtrl = ctrls[0] || {
// Mock ngModelController if it doesn't exist to give us
// the minimum functionality needed
$setViewValue: function(val) {
this.$viewValue = val;
this.$viewChangeListeners.forEach(function(cb) { cb(); });
},
$parsers: [],
$formatters: [],
$viewChangeListeners: []
};
var sliderCtrl = ctrls[1];
sliderCtrl.init(ngModelCtrl);
}
}
/**
* We use a controller for all the logic so that we can expose a few
* things to unit tests
*/
function SliderController($scope, $element, $attrs, $$rAF, $window, $mdAria, $mdUtil, $mdConstant) {
this.init = function init(ngModelCtrl) {
var thumb = angular.element($element[0].querySelector('.md-thumb'));
var thumbText = angular.element($element[0].querySelector('.md-thumb-text'));
var thumbContainer = thumb.parent();
var trackContainer = angular.element($element[0].querySelector('.md-track-container'));
var activeTrack = angular.element($element[0].querySelector('.md-track-fill'));
var tickContainer = angular.element($element[0].querySelector('.md-track-ticks'));
var throttledRefreshDimensions = $mdUtil.throttle(refreshSliderDimensions, 5000);
// Default values, overridable by $attrss
$attrs.min ? $attrs.$observe('min', updateMin) : updateMin(0);
$attrs.max ? $attrs.$observe('max', updateMax) : updateMax(100);
$attrs.step ? $attrs.$observe('step', updateStep) : updateStep(1);
// We have to manually stop the $watch on ngDisabled because it exists
// on the parent $scope, and won't be automatically destroyed when
// the component is destroyed.
var stopDisabledWatch = angular.noop;
if ($attrs.ngDisabled) {
stopDisabledWatch = $scope.$parent.$watch($attrs.ngDisabled, updateAriaDisabled);
}
$mdAria.expect($element, 'aria-label');
$element.attr('tabIndex', 0);
$element.attr('role', 'slider');
$element.on('keydown', keydownListener);
var hammertime = new Hammer($element[0], {
recognizers: [
[Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
});
hammertime.on('hammer.input', onInput);
hammertime.on('panstart', onPanStart);
hammertime.on('pan', onPan);
hammertime.on('panend', onPanEnd);
// On resize, recalculate the slider's dimensions and re-render
function updateAll() {
refreshSliderDimensions();
ngModelRender();
redrawTicks();
}
setTimeout(updateAll);
var debouncedUpdateAll = $$rAF.debounce(updateAll);
angular.element($window).on('resize', debouncedUpdateAll);
$scope.$on('$destroy', function() {
angular.element($window).off('resize', debouncedUpdateAll);
hammertime.destroy();
stopDisabledWatch();
});
ngModelCtrl.$render = ngModelRender;
ngModelCtrl.$viewChangeListeners.push(ngModelRender);
ngModelCtrl.$formatters.push(minMaxValidator);
ngModelCtrl.$formatters.push(stepValidator);
/**
* Attributes
*/
var min;
var max;
var step;
function updateMin(value) {
min = parseFloat(value);
$element.attr('aria-valuemin', value);
updateAll();
}
function updateMax(value) {
max = parseFloat(value);
$element.attr('aria-valuemax', value);
updateAll();
}
function updateStep(value) {
step = parseFloat(value);
redrawTicks();
}
function updateAriaDisabled(isDisabled) {
$element.attr('aria-disabled', !!isDisabled);
}
// Draw the ticks with canvas.
// The alternative to drawing ticks with canvas is to draw one $element for each tick,
// which could quickly become a performance bottleneck.
var tickCanvas, tickCtx;
function redrawTicks() {
if (!angular.isDefined($attrs.mdDiscrete)) return;
var numSteps = Math.floor( (max - min) / step );
if (!tickCanvas) {
var trackTicksStyle = $window.getComputedStyle(tickContainer[0]);
tickCanvas = angular.element('<canvas style="position:absolute;">');
tickCtx = tickCanvas[0].getContext('2d');
tickCtx.fillStyle = trackTicksStyle.backgroundColor || 'black';
tickContainer.append(tickCanvas);
}
var dimensions = getSliderDimensions();
tickCanvas[0].width = dimensions.width;
tickCanvas[0].height = dimensions.height;
var distance;
for (var i = 0; i <= numSteps; i++) {
distance = Math.floor(dimensions.width * (i / numSteps));
tickCtx.fillRect(distance - 1, 0, 2, dimensions.height);
}
}
/**
* Refreshing Dimensions
*/
var sliderDimensions = {};
refreshSliderDimensions();
function refreshSliderDimensions() {
sliderDimensions = trackContainer[0].getBoundingClientRect();
}
function getSliderDimensions() {
throttledRefreshDimensions();
return sliderDimensions;
}
/**
* left/right arrow listener
*/
function keydownListener(ev) {
if($element[0].hasAttribute('disabled')) {
return;
}
var changeAmount;
if (ev.keyCode === $mdConstant.KEY_CODE.LEFT_ARROW) {
changeAmount = -step;
} else if (ev.keyCode === $mdConstant.KEY_CODE.RIGHT_ARROW) {
changeAmount = step;
}
if (changeAmount) {
if (ev.metaKey || ev.ctrlKey || ev.altKey) {
changeAmount *= 4;
}
ev.preventDefault();
ev.stopPropagation();
$scope.$evalAsync(function() {
setModelValue(ngModelCtrl.$viewValue + changeAmount);
});
}
}
/**
* ngModel setters and validators
*/
function setModelValue(value) {
ngModelCtrl.$setViewValue( minMaxValidator(stepValidator(value)) );
}
function ngModelRender() {
if (isNaN(ngModelCtrl.$viewValue)) {
ngModelCtrl.$viewValue = ngModelCtrl.$modelValue;
}
var percent = (ngModelCtrl.$viewValue - min) / (max - min);
$scope.modelValue = ngModelCtrl.$viewValue;
$element.attr('aria-valuenow', ngModelCtrl.$viewValue);
setSliderPercent(percent);
thumbText.text( ngModelCtrl.$viewValue );
}
function minMaxValidator(value) {
if (angular.isNumber(value)) {
return Math.max(min, Math.min(max, value));
}
}
function stepValidator(value) {
if (angular.isNumber(value)) {
return Math.round(value / step) * step;
}
}
/**
* @param percent 0-1
*/
function setSliderPercent(percent) {
activeTrack.css('width', (percent * 100) + '%');
thumbContainer.css(
$mdConstant.CSS.TRANSFORM,
'translate3d(' + getSliderDimensions().width * percent + 'px,0,0)'
);
$element.toggleClass('md-min', percent === 0);
}
/**
* Slide listeners
*/
var isSliding = false;
var isDiscrete = angular.isDefined($attrs.mdDiscrete);
function onInput(ev) {
if (!isSliding && ev.eventType === Hammer.INPUT_START &&
!$element[0].hasAttribute('disabled')) {
isSliding = true;
$element.addClass('active');
$element[0].focus();
refreshSliderDimensions();
onPan(ev);
ev.srcEvent.stopPropagation();
} else if (isSliding && ev.eventType === Hammer.INPUT_END) {
if ( isSliding && isDiscrete ) onPanEnd(ev);
isSliding = false;
$element.removeClass('panning active');
}
}
function onPanStart() {
if (!isSliding) return;
$element.addClass('panning');
}
function onPan(ev) {
if (!isSliding) return;
// While panning discrete, update only the
// visual positioning but not the model value.
if ( isDiscrete ) adjustThumbPosition( ev.center.x );
else doSlide( ev.center.x );
ev.preventDefault();
ev.srcEvent.stopPropagation();
}
function onPanEnd(ev) {
if ( isDiscrete && !$element[0].hasAttribute('disabled') ) {
// Convert exact to closest discrete value.
// Slide animate the thumb... and then update the model value.
var exactVal = percentToValue( positionToPercent( ev.center.x ));
var closestVal = minMaxValidator( stepValidator(exactVal) );
setSliderPercent( valueToPercent(closestVal));
$$rAF(function(){
setModelValue( closestVal );
});
ev.preventDefault();
ev.srcEvent.stopPropagation();
}
}
/**
* Expose for testing
*/
this._onInput = onInput;
this._onPanStart = onPanStart;
this._onPan = onPan;
/**
* Slide the UI by changing the model value
* @param x
*/
function doSlide( x ) {
$scope.$evalAsync( function() {
setModelValue( percentToValue( positionToPercent(x) ));
});
}
/**
* Slide the UI without changing the model (while dragging/panning)
* @param x
*/
function adjustThumbPosition( x ) {
var exactVal = percentToValue( positionToPercent( x ));
var closestVal = minMaxValidator( stepValidator(exactVal) );
setSliderPercent( positionToPercent(x) );
thumbText.text( closestVal );
}
/**
* Convert horizontal position on slider to percentage value of offset from beginning...
* @param x
* @returns {number}
*/
function positionToPercent( x ) {
return Math.max(0, Math.min(1, (x - sliderDimensions.left) / (sliderDimensions.width)));
}
/**
* Convert percentage offset on slide to equivalent model value
* @param percent
* @returns {*}
*/
function percentToValue( percent ) {
return (min + percent * (max - min));
}
function valueToPercent( val ) {
return (val - min)/(max - min);
}
};
}
})();
@@ -0,0 +1,269 @@
$slider-background-color: rgb(200, 200, 200) !default;
$slider-height: 48px !default;
$slider-track-height: 2px !default;
$slider-thumb-width: 32px !default;
$slider-thumb-height: $slider-thumb-width !default;
$slider-thumb-default-scale: 0.5 !default;
$slider-thumb-hover-scale: 0.6 !default;
$slider-thumb-focus-scale: 0.85 !default;
$slider-thumb-disabled-scale: 0.35 !default;
$slider-thumb-disabled-border: 6px !default;
$slider-focus-thumb-width: 48px !default;
$slider-focus-thumb-height: $slider-focus-thumb-width !default;
$slider-focus-ring-border-width: 3px !default;
$slider-arrow-height: 16px !default;
$slider-arrow-width: 28px !default;
$slider-sign-height: 28px !default;
$slider-sign-width: 28px !default;
$slider-sign-top: ($slider-height / 2) - ($slider-thumb-default-scale * $slider-thumb-height / 2) - ($slider-sign-height) - ($slider-arrow-height) + 8px !default;
@keyframes sliderFocusThumb {
0% {
opacity: 0;
transform: scale(0.0);
}
50% {
transform: scale(1.0);
opacity: 1;
}
100% {
opacity: 0;
}
}
@mixin slider-thumb-position($width: $slider-thumb-width, $height: $slider-thumb-height) {
position: absolute;
left: -$width / 2;
top: ($slider-height / 2) - ($height / 2);
width: $width;
height: $height;
border-radius: max($width, $height);
}
md-slider {
height: $slider-height;
position: relative;
display: block;
margin-left: 4px;
margin-right: 4px;
/**
* Track
*/
.md-track-container {
width: 100%;
position: absolute;
top: ($slider-height / 2) - ($slider-track-height) / 2;
height: $slider-track-height;
}
.md-track {
position: absolute;
left: 0;
right: 0;
height: 100%;
}
.md-track-fill {
transition: width 0.05s linear;
}
.md-track-ticks {
position: absolute;
left: 0;
right: 0;
height: 100%;
}
/**
* Slider thumb
*/
.md-thumb-container {
position: absolute;
left: 0;
top: 0;
transform: translate3d(0,0,0);
transition: transform 0.1s linear;
}
.md-thumb {
z-index: 1;
// Positioning the outer area of the thumb 6px bigger than it needs to be keeps
// the :after area being clipped by the background of the focus-thumb animation.
@include slider-thumb-position($slider-thumb-width + 6, $slider-thumb-height + 6);
// We render thumb in an :after selector to fix an obscure problem with the
// thumb being clipped by the focus-ring and focus-thumb while running the focus
// animation.
&:after {
content: '';
position: absolute;
left: 3px;
top: 3px;
width: $slider-thumb-width;
height: $slider-thumb-height;
border-radius: max($slider-thumb-width, $slider-thumb-height);
border: 3px solid;
}
transform: scale($slider-thumb-default-scale);
transition: all 0.1s linear;
}
/* The sign that's focused in discrete mode */
.md-sign {
/* Center the children (slider-thumb-text) */
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: -($slider-sign-height / 2);
top: $slider-sign-top;
width: $slider-sign-width;
height: $slider-sign-height;
border-radius: max($slider-sign-height, $slider-sign-width);
transform: scale(0.4) translate3d(0,(-$slider-sign-top + 8) / 0.4,0);
transition: all 0.2s ease-in-out;
/* The arrow pointing down under the sign */
&:after {
position: absolute;
content: '';
left: -($slider-sign-width / 2 - $slider-arrow-width / 2);
border-radius: $slider-arrow-height;
top: 19px;
border-left: $slider-arrow-width / 2 solid transparent;
border-right: $slider-arrow-width / 2 solid transparent;
border-top: $slider-arrow-height solid;
opacity: 0;
transform: translate3d(0,-8px,0);
transition: all 0.2s ease-in-out;
}
.md-thumb-text {
z-index: 1;
font-size: 12px;
font-weight: bold;
}
}
/**
* The border/background that comes in when focused in non-discrete mode
*/
.md-focus-thumb {
@include slider-thumb-position($slider-focus-thumb-width, $slider-focus-thumb-height);
display: none;
opacity: 0;
background-color: #C0C0C0;
animation: sliderFocusThumb 0.4s linear;
}
.md-focus-ring {
@include slider-thumb-position($slider-focus-thumb-width, $slider-focus-thumb-height);
border: 2px solid #D6D6D6;
background-color: transparent;
transform: scale(0);
transition: all 0.2s linear;
}
.md-disabled-thumb {
@include slider-thumb-position(
$slider-thumb-width + $slider-thumb-disabled-border * 2,
$slider-thumb-height + $slider-thumb-disabled-border * 2
);
transform: scale($slider-thumb-disabled-scale);
border: $slider-thumb-disabled-border solid;
display: none;
}
&.md-min {
.md-thumb {
&:after {
background-color: white;
}
}
.md-sign {
opacity: 0;
}
}
&:focus {
outline: none;
}
/* Don't animate left/right while panning */
&.panning {
.md-thumb-container,
.md-track-fill {
transition: none;
}
}
&:not([md-discrete]) {
/* Hide the sign and ticks in non-discrete mode */
.md-track-ticks,
.md-sign {
display: none;
}
&:not([disabled]) {
&:hover {
.md-thumb {
transform: scale($slider-thumb-hover-scale);
}
}
&:focus,
&.active {
.md-focus-thumb {
display: block;
}
.md-focus-ring {
transform: scale(1);
}
.md-thumb {
transform: scale($slider-thumb-focus-scale);
}
}
}
}
&[md-discrete] {
/* Hide the focus thumb in discrete mode */
.md-focus-thumb,
.md-focus-ring {
display: none;
}
&:not([disabled]) {
&:focus,
&.active {
.md-sign,
.md-sign:after {
opacity: 1;
transform: translate3d(0,0,0) scale(1.0);
}
}
}
}
&[disabled] {
.md-track-fill {
display: none;
}
.md-sign {
display: none;
}
.md-thumb {
transform: scale($slider-thumb-disabled-scale);
}
.md-disabled-thumb {
display: block;
}
}
}
@@ -0,0 +1,205 @@
describe('md-slider', function() {
function simulateEventAt( centerX, eventType ) {
return {
eventType: eventType,
center: { x: centerX },
preventDefault: angular.noop,
srcEvent : {
stopPropagation : angular.noop
}
};
}
beforeEach(TestUtil.mockRaf);
beforeEach(module('ngAria'));
beforeEach(module('material.components.slider'));
it('should set model on press', inject(function($compile, $rootScope, $timeout) {
var slider = $compile('<md-slider ng-model="value" min="0" max="100">')($rootScope);
$rootScope.$apply('value = 50');
var sliderCtrl = slider.controller('mdSlider');
spyOn(slider[0].querySelector('.md-track-container'), 'getBoundingClientRect').andReturn({
width: 100,
left: 0,
right: 0
});
sliderCtrl._onInput( simulateEventAt( 30, Hammer.INPUT_START ));
$timeout.flush();
expect($rootScope.value).toBe(30);
//When going past max, it should clamp to max
sliderCtrl._onPan( simulateEventAt( 500 ));
$timeout.flush();
expect($rootScope.value).toBe(100);
sliderCtrl._onPan( simulateEventAt( 50 ));
$timeout.flush();
expect($rootScope.value).toBe(50);
}));
it('should set model on drag', inject(function($compile, $rootScope, $timeout) {
var slider = $compile('<md-slider ng-model="value" min="0" max="100" md-discrete>')($rootScope);
$rootScope.$apply('value = 50');
var sliderCtrl = slider.controller('mdSlider');
spyOn(slider[0].querySelector('.md-track-container'), 'getBoundingClientRect').andReturn({
width: 100,
left: 0,
right: 0
});
sliderCtrl._onInput( simulateEventAt( 30, Hammer.INPUT_START ));
$timeout.flush();
sliderCtrl._onPan( simulateEventAt( 80 ));
expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('80');
}));
it('should increment model on right arrow', inject(function($compile, $rootScope, $timeout, $mdConstant) {
var slider = $compile(
'<md-slider min="100" max="104" step="2" ng-model="model">'
)($rootScope);
$rootScope.$apply('model = 100');
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW
});
$timeout.flush();
expect($rootScope.model).toBe(102);
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW
});
$timeout.flush();
expect($rootScope.model).toBe(104);
// Stays at max
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW
});
$timeout.flush();
expect($rootScope.model).toBe(104);
}));
it('should decrement model on left arrow', inject(function($compile, $rootScope, $timeout, $mdConstant) {
var slider = $compile(
'<md-slider min="100" max="104" step="2" ng-model="model">'
)($rootScope);
$rootScope.$apply('model = 104');
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.LEFT_ARROW
});
$timeout.flush();
expect($rootScope.model).toBe(102);
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.LEFT_ARROW
});
$timeout.flush();
expect($rootScope.model).toBe(100);
// Stays at min
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.LEFT_ARROW
});
$timeout.flush();
expect($rootScope.model).toBe(100);
}));
it('should update the thumb text', inject(function($compile, $rootScope, $timeout, $mdConstant) {
var slider = $compile('<md-slider ng-model="value" min="0" max="100">')($rootScope);
var sliderCtrl = slider.controller('mdSlider');
spyOn(slider[0].querySelector('.md-track-container'), 'getBoundingClientRect').andReturn({
width: 100,
left: 0,
right: 0
});
sliderCtrl._onInput( simulateEventAt( 30, Hammer.INPUT_START ));
$timeout.flush();
expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('30');
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.LEFT_ARROW
});
$timeout.flush();
expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('29');
sliderCtrl._onPan( simulateEventAt( 30 ));
$timeout.flush();
expect(slider[0].querySelector('.md-thumb-text').textContent).toBe('30');
}));
it('should update the thumb text with the model value when using ng-change', inject(function($compile, $rootScope, $timeout) {
$scope = $rootScope.$new();
$scope.stayAt50 = function () {
$scope.value = 50;
};
var slider = $compile('<md-slider ng-model="value" min="0" max="100" ng-change="stayAt50()">')($scope);
var sliderCtrl = slider.controller('mdSlider');
spyOn(slider[0].querySelector('.md-track-container'), 'getBoundingClientRect').andReturn({
width: 100,
left: 0,
right: 0
});
sliderCtrl._onInput( simulateEventAt( 30, Hammer.INPUT_START ));
$timeout.flush();
expect($scope.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) {
spyOn($log, "warn");
var element = $compile(
'<md-slider min="100" max="104" step="2" ng-model="model"></md-slider>'
)($rootScope);
expect($log.warn).toHaveBeenCalled();
}));
it('should not call $log.warn if aria-label is provided', inject(function($compile, $rootScope, $timeout, $log) {
spyOn($log, "warn");
var element = $compile(
'<md-slider aria-label="banana" min="100" max="104" step="2" ng-model="model"></md-slider>'
)($rootScope);
expect($log.warn).not.toHaveBeenCalled();
}));
it('should add aria attributes', inject(function($compile, $rootScope, $timeout, $mdConstant){
var slider = $compile(
'<md-slider min="100" max="104" step="2" ng-model="model">'
)($rootScope);
$rootScope.$apply('model = 102');
expect(slider.attr('role')).toEqual('slider');
expect(slider.attr('aria-valuemin')).toEqual('100');
expect(slider.attr('aria-valuemax')).toEqual('104');
expect(slider.attr('aria-valuenow')).toEqual('102');
slider.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.LEFT_ARROW
});
$timeout.flush();
expect(slider.attr('aria-valuenow')).toEqual('100');
}));
});