From e15780be6c6745f890c191a921751fbc00025262 Mon Sep 17 00:00:00 2001 From: Michael Knoll Date: Sun, 26 Jul 2015 20:47:24 -0400 Subject: [PATCH 1/5] feat(timepicker): Added min/max attributes for timepicker. --- src/timepicker/test/timepicker.spec.js | 515 +++++++++++++++++++++++++ src/timepicker/timepicker.js | 95 ++++- template/timepicker/timepicker.html | 10 +- 3 files changed, 604 insertions(+), 16 deletions(-) diff --git a/src/timepicker/test/timepicker.spec.js b/src/timepicker/test/timepicker.spec.js index b266b4102c..93201bbdb9 100644 --- a/src/timepicker/test/timepicker.spec.js +++ b/src/timepicker/test/timepicker.spec.js @@ -1005,5 +1005,520 @@ describe('timepicker directive', function () { }); }); + describe('when used with min', function() { + var changeInputValueTo; + beforeEach(inject(function($sniffer) { + element = $compile('')($rootScope); + $rootScope.$digest(); + changeInputValueTo = function (inputEl, value) { + inputEl.val(value); + inputEl.trigger($sniffer.hasEvent('input') ? 'input' : 'change'); + $rootScope.$digest(); + }; + })); + + it('should not decrease hours when it would result in a time earlier than min', function() { + var down = getHoursButton(false); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.min = newTime(13, 41); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(true); + + doClick(down); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + hoursEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + hoursEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + }); + + it('should decrease hours when it would not result in a time earlier than min', function() { + var down = getHoursButton(false); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.min = newTime(0, 0); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(false); + + doClick(down); + expect(getTimeState()).toEqual(['01', '40', 'PM']); + expect(getModelState()).toEqual([13, 40]); + + hoursEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '40', 'PM']); + expect(getModelState()).toEqual([12, 40]); + + hoursEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '40', 'AM']); + expect(getModelState()).toEqual([11, 40]); + }); + + it('should not decrease minutes when it would result in a time ealier than min', function() { + var down = getMinutesButton(false); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.min = newTime(14, 40); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(true); + + doClick(down); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + minutessEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + minutessEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + }); + + it('should decrease minutes when it would not result in a time ealier than min', function() { + var down = getMinutesButton(false); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.min = newTime(0, 0); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(false); + + doClick(down); + expect(getTimeState()).toEqual(['02', '39', 'PM']); + expect(getModelState()).toEqual([14, 39]); + + minutessEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '38', 'PM']); + expect(getModelState()).toEqual([14, 38]); + + minutessEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '37', 'PM']); + expect(getModelState()).toEqual([14, 37]); + }); + + it('should not increase hours when time would rollover', function() { + var up = getHoursButton(true); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.time = newTime(23, 0); + $rootScope.min = newTime(13, 40); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(true); + + doClick(up); + expect(getTimeState()).toEqual(['11', '00', 'PM']); + expect(getModelState()).toEqual([23, 0]); + + hoursEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '00', 'PM']); + expect(getModelState()).toEqual([23, 0]); + + hoursEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '00', 'PM']); + expect(getModelState()).toEqual([23, 0]); + }); + + it('should not increase minutes when time would rollover', function() { + var up = getMinutesButton(true); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.time = newTime(23, 59); + $rootScope.min = newTime(13, 40); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(true); + + doClick(up); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + + minutessEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + + minutessEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + }); + + it('should not change meridian when it would result a in time earlier than min', function() { + var button = getMeridianButton(); + + $rootScope.min = newTime(2, 41); + $rootScope.$digest(); + + expect(button.hasClass('disabled')).toBe(true); + + doClick(button); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + }); + + it('should change meridian when it would not result in a time earlier than min', function() { + var button = getMeridianButton(); + + $rootScope.min = newTime(2, 39); + $rootScope.$digest(); + + expect(button.hasClass('disabled')).toBe(false); + + doClick(button); + expect(getTimeState()).toEqual(['02', '40', 'AM']); + expect(getModelState()).toEqual([2, 40]); + }); + + it('should return invalid when the hours are changes such that the time is earlier than min', function() { + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + + $rootScope.min = newTime(14, 0); + $rootScope.$digest(); + + changeInputValueTo(hoursEl, 1); + expect($rootScope.time).toBe(null); + expect(hoursEl.parent().hasClass('has-error')).toBe(true); + expect(element.hasClass('ng-invalid-time')).toBe(true); + }); + + it('should return valid when the hours are changes such that the time is not earlier than min', function() { + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + + $rootScope.min = newTime(14, 41); + $rootScope.$digest(); + + changeInputValueTo(hoursEl, 3); + expect(getTimeState()).toEqual(['3', '40', 'PM']); + expect(getModelState()).toEqual([15, 40]); + expect(hoursEl.parent().hasClass('has-error')).toBe(false); + expect(element.hasClass('ng-invalid-time')).toBe(false); + }); + + it('should return invalid when the minutes are changes such that the time is earlier than min', function() { + var inputs = element.find('input'); + var minutesEl = inputs.eq(1); + + $rootScope.min = newTime(14, 30); + $rootScope.$digest(); + + changeInputValueTo(minutesEl, 1); + expect($rootScope.time).toBe(null); + expect(minutesEl.parent().hasClass('has-error')).toBe(true); + expect(element.hasClass('ng-invalid-time')).toBe(true); + }); + + it('should return valid when the minutes are changes such that the time is not earlier than min', function() { + var inputs = element.find('input'); + var minutesEl = inputs.eq(1); + + $rootScope.min = newTime(14, 41); + $rootScope.$digest(); + + changeInputValueTo(minutesEl, 42); + expect(getTimeState()).toEqual(['02', '42', 'PM']); + expect(getModelState()).toEqual([14, 42]); + expect(minutesEl.parent().hasClass('has-error')).toBe(false); + expect(element.hasClass('ng-invalid-time')).toBe(false); + }); + }); + + describe('when used with max', function() { + var changeInputValueTo; + beforeEach(inject(function($sniffer) { + element = $compile('')($rootScope); + $rootScope.$digest(); + changeInputValueTo = function (inputEl, value) { + inputEl.val(value); + inputEl.trigger($sniffer.hasEvent('input') ? 'input' : 'change'); + $rootScope.$digest(); + }; + })); + + it('should not increase hours when it would result in a time later than max', function() { + var up = getHoursButton(true); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.max = newTime(15, 39); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(true); + + doClick(up); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + hoursEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + hoursEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + }); + + it('should increase hours when it would not result in a time later than max', function() { + var up = getHoursButton(true); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.max = newTime(23, 59); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(false); + + doClick(up); + expect(getTimeState()).toEqual(['03', '40', 'PM']); + expect(getModelState()).toEqual([15, 40]); + + hoursEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '40', 'PM']); + expect(getModelState()).toEqual([16, 40]); + + hoursEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['05', '40', 'PM']); + expect(getModelState()).toEqual([17, 40]); + }); + + it('should not increase minutes when it would result in a time later than max', function() { + var up = getMinutesButton(true); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.max = newTime(14, 40); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(true); + + doClick(up); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + minutessEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + + minutessEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + }); + + it('should increase minutes when it would not result in a time later than max', function() { + var up = getMinutesButton(true); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.max = newTime(23, 59); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(false); + + doClick(up); + expect(getTimeState()).toEqual(['02', '41', 'PM']); + expect(getModelState()).toEqual([14, 41]); + + minutessEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '42', 'PM']); + expect(getModelState()).toEqual([14, 42]); + + minutessEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '43', 'PM']); + expect(getModelState()).toEqual([14, 43]); + }); + + it('should not decrease hours when time would rollover', function() { + var down = getHoursButton(false); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.time = newTime(0, 59); + $rootScope.max = newTime(13, 40); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(true); + + doClick(down); + expect(getTimeState()).toEqual(['12', '59', 'AM']); + expect(getModelState()).toEqual([0, 59]); + + hoursEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '59', 'AM']); + expect(getModelState()).toEqual([0, 59]); + + hoursEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '59', 'AM']); + expect(getModelState()).toEqual([0, 59]); + }); + + it('should not decrease minutes when time would rollover', function() { + var down = getMinutesButton(false); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.time = newTime(0, 0); + $rootScope.max = newTime(13, 40); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(true); + + doClick(down); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); + + minutessEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); + + minutessEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); + }); + + it('should not change meridian when it would result a in time later than max', function() { + var button = getMeridianButton(); + + $rootScope.time = newTime(2, 40); + $rootScope.max = newTime(14, 39); + $rootScope.$digest(); + + expect(button.hasClass('disabled')).toBe(true); + + doClick(button); + expect(getTimeState()).toEqual(['02', '40', 'AM']); + expect(getModelState()).toEqual([2, 40]); + }); + + it('should change meridian when it would not result in a time later than max', function() { + var button = getMeridianButton(); + + $rootScope.time = newTime(2, 40); + $rootScope.max = newTime(14, 41); + $rootScope.$digest(); + + expect(button.hasClass('disabled')).toBe(false); + + doClick(button); + expect(getTimeState()).toEqual(['02', '40', 'PM']); + expect(getModelState()).toEqual([14, 40]); + }); + + it('should return invalid when the hours are changes such that the time is later than max', function() { + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + + $rootScope.max = newTime(14, 0); + $rootScope.$digest(); + + changeInputValueTo(hoursEl, 3); + expect($rootScope.time).toBe(null); + expect(hoursEl.parent().hasClass('has-error')).toBe(true); + expect(element.hasClass('ng-invalid-time')).toBe(true); + }); + + it('should return valid when the hours are changes such that the time is not later than max', function() { + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + + $rootScope.max = newTime(15, 41); + $rootScope.$digest(); + + changeInputValueTo(hoursEl, 3); + expect(getTimeState()).toEqual(['3', '40', 'PM']); + expect(getModelState()).toEqual([15, 40]); + expect(hoursEl.parent().hasClass('has-error')).toBe(false); + expect(element.hasClass('ng-invalid-time')).toBe(false); + }); + + it('should return invalid when the minutes are changes such that the time is later than max', function() { + var inputs = element.find('input'); + var minutesEl = inputs.eq(1); + + $rootScope.max = newTime(14, 50); + $rootScope.$digest(); + + changeInputValueTo(minutesEl, 51); + expect($rootScope.time).toBe(null); + expect(minutesEl.parent().hasClass('has-error')).toBe(true); + expect(element.hasClass('ng-invalid-time')).toBe(true); + }); + + it('should return valid when the minutes are changes such that the time is not later than max', function() { + var inputs = element.find('input'); + var minutesEl = inputs.eq(1); + + $rootScope.max = newTime(14, 42); + $rootScope.$digest(); + + changeInputValueTo(minutesEl, 41); + expect(getTimeState()).toEqual(['02', '41', 'PM']); + expect(getModelState()).toEqual([14, 41]); + expect(minutesEl.parent().hasClass('has-error')).toBe(false); + expect(element.hasClass('ng-invalid-time')).toBe(false); + }); + }); }); diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js index fed6d39b8e..fa8233ab5b 100644 --- a/src/timepicker/timepicker.js +++ b/src/timepicker/timepicker.js @@ -55,6 +55,50 @@ angular.module('ui.bootstrap.timepicker', []) }); } + var min; + if ($attrs.min) { + $scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + }); + } + + var max; + if ($attrs.max) { + $scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + }); + } + + $scope.noIncrementHours = function() { + var incrementedSelected = addMinutes( selected, hourStep * 60 ); + return incrementedSelected > max || (min && incrementedSelected < selected); + }; + + $scope.noDecrementHours = function() { + var decrementedSelected = addMinutes( selected, - hourStep * 60 ); + return decrementedSelected < min || (max && decrementedSelected > selected); + }; + + $scope.noIncrementMinutes = function() { + var incrementedSelected = addMinutes( selected, minuteStep ); + return incrementedSelected > max || (min && incrementedSelected < selected); + }; + + $scope.noDecrementMinutes = function() { + var decrementedSelected = addMinutes( selected, - minuteStep ); + return decrementedSelected < min || (max && decrementedSelected > selected); + }; + + $scope.noToggleMeridian = function() { + if(selected.getHours() < 13) { + return addMinutes( selected, 12 * 60 ) > max; + } else { + return addMinutes( selected, - 12 * 60 ) < min; + } + }; + // 12H / 24H mode $scope.showMeridian = timepickerConfig.showMeridian; if ($attrs.showMeridian) { @@ -177,7 +221,11 @@ angular.module('ui.bootstrap.timepicker', []) if ( angular.isDefined(hours) ) { selected.setHours( hours ); - refresh( 'h' ); + if( selected < min || selected > max) { + invalidate(true); + } else { + refresh( 'h' ); + } } else { invalidate(true); } @@ -196,7 +244,11 @@ angular.module('ui.bootstrap.timepicker', []) if ( angular.isDefined(minutes) ) { selected.setMinutes( minutes ); - refresh( 'm' ); + if( selected < min || selected > max) { + invalidate(undefined, true); + } else { + refresh( 'm' ); + } } else { invalidate(undefined, true); } @@ -222,7 +274,13 @@ angular.module('ui.bootstrap.timepicker', []) if ( date ) { selected = date; } - makeValid(); + if( selected < min || selected > max ) { + ngModelCtrl.$setValidity('time', false); + $scope.invalidHours = true; + $scope.invalidMinutes = true; + } else { + makeValid(); + } updateTemplate(); } }; @@ -254,9 +312,14 @@ angular.module('ui.bootstrap.timepicker', []) $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; } - function addMinutes( minutes ) { - var dt = new Date( selected.getTime() + minutes * 60000 ); - selected.setHours( dt.getHours(), dt.getMinutes() ); + function addMinutes( date, minutes ) { + var dt = new Date( date.getTime() + minutes * 60000 ); + var newDate = new Date( date.getTime() ).setHours( dt.getHours(), dt.getMinutes() ); + return new Date( newDate ); + } + + function addMinutesToSelected( minutes ) { + selected = addMinutes( selected, minutes ); refresh(); } @@ -264,19 +327,29 @@ angular.module('ui.bootstrap.timepicker', []) $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; $scope.incrementHours = function() { - addMinutes( hourStep * 60 ); + if(!$scope.noIncrementHours()) { + addMinutesToSelected( hourStep * 60 ); + } }; $scope.decrementHours = function() { - addMinutes( - hourStep * 60 ); + if(!$scope.noDecrementHours()) { + addMinutesToSelected( - hourStep * 60 ); + } }; $scope.incrementMinutes = function() { - addMinutes( minuteStep ); + if(!$scope.noIncrementMinutes()) { + addMinutesToSelected( minuteStep ); + } }; $scope.decrementMinutes = function() { - addMinutes( - minuteStep ); + if(!$scope.noDecrementMinutes()) { + addMinutesToSelected( - minuteStep ); + } }; $scope.toggleMeridian = function() { - addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) ); + if(!$scope.noToggleMeridian()) { + addMinutesToSelected( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) ); + } }; }]) diff --git a/template/timepicker/timepicker.html b/template/timepicker/timepicker.html index 4a3bd50f49..8dc8648dbd 100644 --- a/template/timepicker/timepicker.html +++ b/template/timepicker/timepicker.html @@ -1,9 +1,9 @@ - + - + @@ -14,12 +14,12 @@ - + - + - + From d67763fde1a320bd0850ee4cb82c62ed4c4268f0 Mon Sep 17 00:00:00 2001 From: Michael Knoll Date: Fri, 31 Jul 2015 20:28:56 -0400 Subject: [PATCH 2/5] fix(timepicker): Removing unnecessary conditional --- src/timepicker/timepicker.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js index fa8233ab5b..53e9bf4176 100644 --- a/src/timepicker/timepicker.js +++ b/src/timepicker/timepicker.js @@ -56,20 +56,16 @@ angular.module('ui.bootstrap.timepicker', []) } var min; - if ($attrs.min) { - $scope.$parent.$watch($parse($attrs.min), function(value) { - var dt = new Date(value); - min = isNaN(dt) ? undefined : dt; - }); - } + $scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + }); var max; - if ($attrs.max) { - $scope.$parent.$watch($parse($attrs.max), function(value) { - var dt = new Date(value); - max = isNaN(dt) ? undefined : dt; - }); - } + $scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + }); $scope.noIncrementHours = function() { var incrementedSelected = addMinutes( selected, hourStep * 60 ); From b86b66849f5a333882bea9e4d614c4d83518cc4a Mon Sep 17 00:00:00 2001 From: Michael Knoll Date: Sat, 1 Aug 2015 11:30:03 -0400 Subject: [PATCH 3/5] fix(timepicker): Removing unnecessary Date instance in addMinutes(). --- src/timepicker/timepicker.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js index 53e9bf4176..94660e88f7 100644 --- a/src/timepicker/timepicker.js +++ b/src/timepicker/timepicker.js @@ -310,8 +310,9 @@ angular.module('ui.bootstrap.timepicker', []) function addMinutes( date, minutes ) { var dt = new Date( date.getTime() + minutes * 60000 ); - var newDate = new Date( date.getTime() ).setHours( dt.getHours(), dt.getMinutes() ); - return new Date( newDate ); + var newDate = new Date( date ); + newDate.setHours( dt.getHours(), dt.getMinutes() ); + return newDate; } function addMinutesToSelected( minutes ) { From 21e5a75691a47d5dd599e24fe0f36bfe76b04474 Mon Sep 17 00:00:00 2001 From: Michael Knoll Date: Wed, 5 Aug 2015 20:39:13 -0400 Subject: [PATCH 4/5] style(timepicker): updating if block styling --- src/timepicker/timepicker.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js index 94660e88f7..8604b9e62d 100644 --- a/src/timepicker/timepicker.js +++ b/src/timepicker/timepicker.js @@ -88,7 +88,7 @@ angular.module('ui.bootstrap.timepicker', []) }; $scope.noToggleMeridian = function() { - if(selected.getHours() < 13) { + if (selected.getHours() < 13) { return addMinutes( selected, 12 * 60 ) > max; } else { return addMinutes( selected, - 12 * 60 ) < min; @@ -217,7 +217,7 @@ angular.module('ui.bootstrap.timepicker', []) if ( angular.isDefined(hours) ) { selected.setHours( hours ); - if( selected < min || selected > max) { + if ( selected < min || selected > max) { invalidate(true); } else { refresh( 'h' ); @@ -240,7 +240,7 @@ angular.module('ui.bootstrap.timepicker', []) if ( angular.isDefined(minutes) ) { selected.setMinutes( minutes ); - if( selected < min || selected > max) { + if (selected < min || selected > max) { invalidate(undefined, true); } else { refresh( 'm' ); @@ -270,7 +270,7 @@ angular.module('ui.bootstrap.timepicker', []) if ( date ) { selected = date; } - if( selected < min || selected > max ) { + if (selected < min || selected > max) { ngModelCtrl.$setValidity('time', false); $scope.invalidHours = true; $scope.invalidMinutes = true; @@ -324,27 +324,27 @@ angular.module('ui.bootstrap.timepicker', []) $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; $scope.incrementHours = function() { - if(!$scope.noIncrementHours()) { + if (!$scope.noIncrementHours()) { addMinutesToSelected( hourStep * 60 ); } }; $scope.decrementHours = function() { - if(!$scope.noDecrementHours()) { + if (!$scope.noDecrementHours()) { addMinutesToSelected( - hourStep * 60 ); } }; $scope.incrementMinutes = function() { - if(!$scope.noIncrementMinutes()) { + if (!$scope.noIncrementMinutes()) { addMinutesToSelected( minuteStep ); } }; $scope.decrementMinutes = function() { - if(!$scope.noDecrementMinutes()) { + if (!$scope.noDecrementMinutes()) { addMinutesToSelected( - minuteStep ); } }; $scope.toggleMeridian = function() { - if(!$scope.noToggleMeridian()) { + if (!$scope.noToggleMeridian()) { addMinutesToSelected( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) ); } }; From 1c0108d0572f2c328d44ad21e3b161b52a9cadfd Mon Sep 17 00:00:00 2001 From: Michael Knoll Date: Thu, 6 Aug 2015 22:01:32 -0400 Subject: [PATCH 5/5] fix(timepicker): Allow time to rollover if within min or max --- src/timepicker/test/timepicker.spec.js | 180 ++++++++++++++++++++++--- src/timepicker/timepicker.js | 8 +- 2 files changed, 164 insertions(+), 24 deletions(-) diff --git a/src/timepicker/test/timepicker.spec.js b/src/timepicker/test/timepicker.spec.js index 93201bbdb9..ceb83117db 100644 --- a/src/timepicker/test/timepicker.spec.js +++ b/src/timepicker/test/timepicker.spec.js @@ -14,8 +14,7 @@ describe('timepicker directive', function () { function newTime(hours, minutes) { var time = new Date(); - time.setHours(hours); - time.setMinutes(minutes); + time.setHours(hours, minutes, 0, 0); return time; } @@ -1125,35 +1124,71 @@ describe('timepicker directive', function () { expect(getModelState()).toEqual([14, 37]); }); - it('should not increase hours when time would rollover', function() { + it('should not increase hours when time would rollover to a time earlier than min', function() { var up = getHoursButton(true); var inputs = element.find('input'); var hoursEl = inputs.eq(0); var upMouseWheelEvent = wheelThatMouse(1); var upKeydownEvent = keydown('up'); - $rootScope.time = newTime(23, 0); + $rootScope.time = newTime(23, 59); $rootScope.min = newTime(13, 40); $rootScope.$digest(); expect(up.hasClass('disabled')).toBe(true); doClick(up); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); hoursEl.trigger( upKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + }); + + it('should increase hours when time would rollover to a time not earlier than min', function() { + var up = getHoursButton(true); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.min = newTime(0, 0); + + $rootScope.time = newTime(23, 59); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(false); + + doClick(up); + expect(getTimeState()).toEqual(['12', '59', 'AM']); + expect(getModelState()).toEqual([0, 59]); + + $rootScope.time = newTime(23, 59); + $rootScope.$digest(); + + hoursEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '59', 'AM']); + expect(getModelState()).toEqual([0, 59]); + + $rootScope.time = newTime(23, 59); + $rootScope.$digest(); + + hoursEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '59', 'AM']); + expect(getModelState()).toEqual([0, 59]); }); - it('should not increase minutes when time would rollover', function() { + + it('should not increase minutes when time would rollover to a time earlier than min', function() { var up = getMinutesButton(true); var inputs = element.find('input'); var minutessEl = inputs.eq(1); @@ -1181,6 +1216,41 @@ describe('timepicker directive', function () { expect(getModelState()).toEqual([23, 59]); }); + it('should increase minutes when time would rollover to a time not earlier than min', function() { + var up = getMinutesButton(true); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var upMouseWheelEvent = wheelThatMouse(1); + var upKeydownEvent = keydown('up'); + + $rootScope.min = newTime(0, 0); + + $rootScope.time = newTime(23, 59); + $rootScope.$digest(); + + expect(up.hasClass('disabled')).toBe(false); + + doClick(up); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); + + $rootScope.time = newTime(23, 59); + $rootScope.$digest(); + + minutessEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); + + $rootScope.time = newTime(23, 59); + $rootScope.$digest(); + + minutessEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); + }); + it('should not change meridian when it would result a in time earlier than min', function() { var button = getMeridianButton(); @@ -1382,35 +1452,70 @@ describe('timepicker directive', function () { expect(getModelState()).toEqual([14, 43]); }); - it('should not decrease hours when time would rollover', function() { + it('should not decrease hours when time would rollover to a time later than max', function() { var down = getHoursButton(false); var inputs = element.find('input'); var hoursEl = inputs.eq(0); var downMouseWheelEvent = wheelThatMouse(-1); var downKeydownEvent = keydown('down'); - $rootScope.time = newTime(0, 59); + $rootScope.time = newTime(0, 0); $rootScope.max = newTime(13, 40); $rootScope.$digest(); expect(down.hasClass('disabled')).toBe(true); doClick(down); - expect(getTimeState()).toEqual(['12', '59', 'AM']); - expect(getModelState()).toEqual([0, 59]); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '59', 'AM']); - expect(getModelState()).toEqual([0, 59]); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); hoursEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '59', 'AM']); - expect(getModelState()).toEqual([0, 59]); + expect(getTimeState()).toEqual(['12', '00', 'AM']); + expect(getModelState()).toEqual([0, 0]); }); - it('should not decrease minutes when time would rollover', function() { + it('should decrease hours when time would rollover to a time not later than max', function() { + var down = getHoursButton(false); + var inputs = element.find('input'); + var hoursEl = inputs.eq(0); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.max = newTime(23, 59); + + $rootScope.time = newTime(0, 0); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(false); + + doClick(down); + expect(getTimeState()).toEqual(['11', '00', 'PM']); + expect(getModelState()).toEqual([23, 0]); + + $rootScope.time = newTime(0, 0); + $rootScope.$digest(); + + hoursEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '00', 'PM']); + expect(getModelState()).toEqual([23, 0]); + + $rootScope.time = newTime(0, 0); + $rootScope.$digest(); + + hoursEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '00', 'PM']); + expect(getModelState()).toEqual([23, 0]); + }); + + it('should not decrease minutes when time would rollover to a time later than max', function() { var down = getMinutesButton(false); var inputs = element.find('input'); var minutessEl = inputs.eq(1); @@ -1438,6 +1543,41 @@ describe('timepicker directive', function () { expect(getModelState()).toEqual([0, 0]); }); + it('should decrease minutes when time would rollover to a time not later than max', function() { + var down = getMinutesButton(false); + var inputs = element.find('input'); + var minutessEl = inputs.eq(1); + var downMouseWheelEvent = wheelThatMouse(-1); + var downKeydownEvent = keydown('down'); + + $rootScope.max = newTime(23, 59); + + $rootScope.time = newTime(0, 0); + $rootScope.$digest(); + + expect(down.hasClass('disabled')).toBe(false); + + doClick(down); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + + $rootScope.time = newTime(0, 0); + $rootScope.$digest(); + + minutessEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + + $rootScope.time = newTime(0, 0); + $rootScope.$digest(); + + minutessEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['11', '59', 'PM']); + expect(getModelState()).toEqual([23, 59]); + }); + it('should not change meridian when it would result a in time later than max', function() { var button = getMeridianButton(); diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js index 8604b9e62d..e3be8d72b5 100644 --- a/src/timepicker/timepicker.js +++ b/src/timepicker/timepicker.js @@ -69,22 +69,22 @@ angular.module('ui.bootstrap.timepicker', []) $scope.noIncrementHours = function() { var incrementedSelected = addMinutes( selected, hourStep * 60 ); - return incrementedSelected > max || (min && incrementedSelected < selected); + return incrementedSelected > max || (incrementedSelected < selected && incrementedSelected < min); }; $scope.noDecrementHours = function() { var decrementedSelected = addMinutes( selected, - hourStep * 60 ); - return decrementedSelected < min || (max && decrementedSelected > selected); + return decrementedSelected < min || (decrementedSelected > selected && decrementedSelected > max); }; $scope.noIncrementMinutes = function() { var incrementedSelected = addMinutes( selected, minuteStep ); - return incrementedSelected > max || (min && incrementedSelected < selected); + return incrementedSelected > max || (incrementedSelected < selected && incrementedSelected < min); }; $scope.noDecrementMinutes = function() { var decrementedSelected = addMinutes( selected, - minuteStep ); - return decrementedSelected < min || (max && decrementedSelected > selected); + return decrementedSelected < min || (decrementedSelected > selected && decrementedSelected > max); }; $scope.noToggleMeridian = function() {