Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit b08e993

Browse files
bekospkozlowski-opensource
authored andcommitted
feat(timepicker): plug into ngModel controller
Closes #773 Closes #785
1 parent 5de7121 commit b08e993

File tree

6 files changed

+235
-90
lines changed

6 files changed

+235
-90
lines changed

misc/demo/assets/demo.css

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ body {
99
opacity: 0;
1010
}
1111

12+
.ng-invalid {
13+
border: 1px solid red !important;
14+
}
15+
1216
section {
1317
padding-top: 30px;
1418
}

src/timepicker/docs/demo.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<div ng-controller="TimepickerDemoCtrl">
2-
<timepicker ng-model="mytime" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></timepicker>
2+
<div ng-model="mytime" ng-change="changed()" class="well well-small" style="display:inline-block;">
3+
<timepicker hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></timepicker>
4+
</div>
35

46
<pre>Time is: {{mytime | date:'shortTime' }}</pre>
57

src/timepicker/docs/demo.js

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ var TimepickerDemoCtrl = function ($scope) {
2121
$scope.mytime = d;
2222
};
2323

24+
$scope.changed = function () {
25+
console.log('Time changed to: ' + $scope.mytime);
26+
};
27+
2428
$scope.clear = function() {
2529
$scope.mytime = null;
2630
};

src/timepicker/test/timepicker.spec.js

+131-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('timepicker directive', function () {
88
$rootScope = _$rootScope_;
99
$rootScope.time = newTime(14, 40);
1010

11-
element = $compile('<timepicker ng-model="time"></timepicker>')($rootScope);
11+
element = $compile('<timepicker ng-model="$parent.time"></timepicker>')($rootScope);
1212
$rootScope.$digest();
1313
}));
1414

@@ -82,6 +82,15 @@ describe('timepicker directive', function () {
8282
expect(getModelState()).toEqual([14, 40]);
8383
});
8484

85+
it('has `selected` current time when model is initially cleared', function() {
86+
$rootScope.time = null;
87+
element = $compile('<timepicker ng-model="$parent.time"></timepicker>')($rootScope);
88+
$rootScope.$digest();
89+
90+
expect($rootScope.time).toBe(null);
91+
expect(getTimeState()).not.toEqual(['', '', '']);
92+
});
93+
8594
it('changes inputs when model changes value', function() {
8695
$rootScope.time = newTime(11, 50);
8796
$rootScope.$digest();
@@ -235,7 +244,7 @@ describe('timepicker directive', function () {
235244
});
236245

237246
it('changes only the time part when minutes change', function() {
238-
element = $compile('<timepicker ng-model="time" minute-step="15"></timepicker>')($rootScope);
247+
element = $compile('<timepicker ng-model="$parent.time" minute-step="15"></timepicker>')($rootScope);
239248
$rootScope.time = newTime(0, 0);
240249
$rootScope.$digest();
241250

@@ -367,7 +376,7 @@ describe('timepicker directive', function () {
367376
$rootScope.hstep = 2;
368377
$rootScope.mstep = 30;
369378
$rootScope.time = newTime(14, 0);
370-
element = $compile('<timepicker ng-model="time" hour-step="hstep" minute-step="mstep"></timepicker>')($rootScope);
379+
element = $compile('<timepicker ng-model="$parent.time" hour-step="hstep" minute-step="mstep"></timepicker>')($rootScope);
371380
$rootScope.$digest();
372381
});
373382

@@ -530,7 +539,7 @@ describe('timepicker directive', function () {
530539
beforeEach(function() {
531540
$rootScope.meridian = false;
532541
$rootScope.time = newTime(14, 10);
533-
element = $compile('<timepicker ng-model="time" show-meridian="meridian"></timepicker>')($rootScope);
542+
element = $compile('<timepicker ng-model="$parent.time" show-meridian="meridian"></timepicker>')($rootScope);
534543
$rootScope.$digest();
535544
});
536545

@@ -559,6 +568,14 @@ describe('timepicker directive', function () {
559568
expect(getModelState()).toEqual([14, 10]);
560569
expect(getMeridianTd().css('display')).toBe('none');
561570
});
571+
572+
it('handles correctly initially empty model on parent element', function() {
573+
$rootScope.time = null;
574+
element = $compile('<span ng-model="time"><timepicker show-meridian="meridian"></timepicker></span>')($rootScope);
575+
$rootScope.$digest();
576+
577+
expect($rootScope.time).toBe(null);
578+
});
562579
});
563580

564581
describe('setting timepickerConfig steps', function() {
@@ -568,7 +585,7 @@ describe('timepicker directive', function () {
568585
timepickerConfig.hourStep = 2;
569586
timepickerConfig.minuteStep = 10;
570587
timepickerConfig.showMeridian = false;
571-
element = $compile('<timepicker ng-model="time"></timepicker>')($rootScope);
588+
element = $compile('<timepicker ng-model="$parent.time"></timepicker>')($rootScope);
572589
$rootScope.$digest();
573590
}));
574591
afterEach(inject(function(timepickerConfig) {
@@ -614,7 +631,7 @@ describe('timepicker directive', function () {
614631
angular.extend(originalConfig, timepickerConfig);
615632
timepickerConfig.meridians = ['π.μ.', 'μ.μ.'];
616633
timepickerConfig.showMeridian = true;
617-
element = $compile('<timepicker ng-model="time"></timepicker>')($rootScope);
634+
element = $compile('<timepicker ng-model="$parent.time"></timepicker>')($rootScope);
618635
$rootScope.$digest();
619636
}));
620637
afterEach(inject(function(timepickerConfig) {
@@ -637,10 +654,9 @@ describe('timepicker directive', function () {
637654
});
638655

639656
describe('user input validation', function () {
640-
641657
var changeInputValueTo;
642658

643-
beforeEach(inject(function(_$compile_, _$rootScope_, $sniffer) {
659+
beforeEach(inject(function($sniffer) {
644660
changeInputValueTo = function (inputEl, value) {
645661
inputEl.val(value);
646662
inputEl.trigger($sniffer.hasEvent('input') ? 'input' : 'change');
@@ -661,7 +677,7 @@ describe('timepicker directive', function () {
661677
expect(getModelState()).toEqual([14, 40]);
662678
});
663679

664-
it('updates hours & pads on input blur', function() {
680+
it('updates hours & pads on input change & pads on blur', function() {
665681
var el = getHoursInputEl();
666682

667683
changeInputValueTo(el, 5);
@@ -673,7 +689,7 @@ describe('timepicker directive', function () {
673689
expect(getModelState()).toEqual([17, 40]);
674690
});
675691

676-
it('updates minutes & pads on input blur', function() {
692+
it('updates minutes & pads on input change & pads on blur', function() {
677693
var el = getMinutesInputEl();
678694

679695
changeInputValueTo(el, 9);
@@ -691,13 +707,15 @@ describe('timepicker directive', function () {
691707
changeInputValueTo(el, 'pizza');
692708
expect($rootScope.time).toBe(null);
693709
expect(el.parent().hasClass('error')).toBe(true);
710+
expect(element.hasClass('ng-invalid-time')).toBe(true);
694711

695712
changeInputValueTo(el, 8);
696713
el.blur();
697714
$rootScope.$digest();
698715
expect(getTimeState()).toEqual(['08', '40', 'PM']);
699716
expect(getModelState()).toEqual([20, 40]);
700717
expect(el.parent().hasClass('error')).toBe(false);
718+
expect(element.hasClass('ng-invalid-time')).toBe(false);
701719
});
702720

703721
it('clears model when input minutes is invalid & alerts the UI', function() {
@@ -706,28 +724,130 @@ describe('timepicker directive', function () {
706724
changeInputValueTo(el, 'pizza');
707725
expect($rootScope.time).toBe(null);
708726
expect(el.parent().hasClass('error')).toBe(true);
727+
expect(element.hasClass('ng-invalid-time')).toBe(true);
709728

710729
changeInputValueTo(el, 22);
711730
expect(getTimeState()).toEqual(['02', '22', 'PM']);
712731
expect(getModelState()).toEqual([14, 22]);
713732
expect(el.parent().hasClass('error')).toBe(false);
733+
expect(element.hasClass('ng-invalid-time')).toBe(false);
714734
});
715735

716736
it('handles 12/24H mode change', function() {
717737
$rootScope.meridian = true;
718-
element = $compile('<timepicker ng-model="time" show-meridian="meridian"></timepicker>')($rootScope);
738+
element = $compile('<timepicker ng-model="$parent.time" show-meridian="meridian"></timepicker>')($rootScope);
719739
$rootScope.$digest();
720740

721741
var el = getHoursInputEl();
722742

723743
changeInputValueTo(el, '16');
724744
expect($rootScope.time).toBe(null);
725745
expect(el.parent().hasClass('error')).toBe(true);
746+
expect(element.hasClass('ng-invalid-time')).toBe(true);
726747

727748
$rootScope.meridian = false;
728749
$rootScope.$digest();
729750
expect(getTimeState(true)).toEqual(['16', '40']);
730751
expect(getModelState()).toEqual([16, 40]);
752+
expect(element.hasClass('ng-invalid-time')).toBe(false);
753+
});
754+
});
755+
756+
describe('when model is not a Date', function() {
757+
beforeEach(inject(function() {
758+
eelement = $compile('<timepicker ng-model="$parent.time"></timepicker>')($rootScope);
759+
}));
760+
761+
it('should not be invalid when the model is null', function() {
762+
$rootScope.time = null;
763+
$rootScope.$digest();
764+
expect(element.hasClass('ng-invalid-time')).toBe(false);
765+
});
766+
767+
it('should not be invalid when the model is undefined', function() {
768+
$rootScope.time = undefined;
769+
$rootScope.$digest();
770+
expect(element.hasClass('ng-invalid-time')).toBe(false);
771+
});
772+
773+
it('should not be invalid when the model is a valid string date representation', function() {
774+
$rootScope.time = 'September 30, 2010 15:30:00';
775+
$rootScope.$digest();
776+
expect(element.hasClass('ng-invalid-time')).toBe(false);
777+
expect(getTimeState()).toEqual(['03', '30', 'PM']);
778+
});
779+
780+
it('should be invalid when the model is not a valid string date representation', function() {
781+
$rootScope.time = 'pizza';
782+
$rootScope.$digest();
783+
expect(element.hasClass('ng-invalid-time')).toBe(true);
784+
});
785+
786+
it('should return valid when the model becomes valid', function() {
787+
$rootScope.time = 'pizza';
788+
$rootScope.$digest();
789+
expect(element.hasClass('ng-invalid-time')).toBe(true);
790+
791+
$rootScope.time = new Date();
792+
$rootScope.$digest();
793+
expect(element.hasClass('ng-invalid-time')).toBe(false);
794+
});
795+
796+
it('should return valid when the model is cleared', function() {
797+
$rootScope.time = 'pizza';
798+
$rootScope.$digest();
799+
expect(element.hasClass('ng-invalid-time')).toBe(true);
800+
801+
$rootScope.time = null;
802+
$rootScope.$digest();
803+
expect(element.hasClass('ng-invalid-time')).toBe(false);
804+
});
805+
});
806+
807+
describe('use with `ng-required` directive', function() {
808+
beforeEach(inject(function() {
809+
$rootScope.time = null;
810+
element = $compile('<timepicker ng-model="$parent.time" ng-required="true"></timepicker>')($rootScope);
811+
$rootScope.$digest();
812+
}));
813+
814+
it('should be invalid initially', function() {
815+
expect(element.hasClass('ng-invalid')).toBe(true);
816+
});
817+
818+
it('should be valid if model has been specified', function() {
819+
$rootScope.time = new Date();
820+
$rootScope.$digest();
821+
expect(element.hasClass('ng-invalid')).toBe(false);
822+
});
823+
});
824+
825+
describe('use with `ng-change` directive', function() {
826+
beforeEach(inject(function() {
827+
$rootScope.changeHandler = jasmine.createSpy('changeHandler');
828+
$rootScope.time = new Date();
829+
element = $compile('<timepicker ng-model="$parent.time" ng-change="$parent.changeHandler()"></timepicker>')($rootScope);
830+
$rootScope.$digest();
831+
}));
832+
833+
it('should not be called initially', function() {
834+
expect($rootScope.changeHandler).not.toHaveBeenCalled();
835+
});
836+
837+
it('should be called when hours / minutes buttons clicked', function() {
838+
var btn1 = getHoursButton(true);
839+
var btn2 = getMinutesButton(false);
840+
841+
doClick(btn1, 2);
842+
doClick(btn2, 3);
843+
$rootScope.$digest();
844+
expect($rootScope.changeHandler.callCount).toBe(5);
845+
});
846+
847+
it('should not be called when model changes programatically', function() {
848+
$rootScope.time = new Date();
849+
$rootScope.$digest();
850+
expect($rootScope.changeHandler).not.toHaveBeenCalled();
731851
});
732852
});
733853

0 commit comments

Comments
 (0)