From d4cfa78fdd52158bb8a5a37366b0779f28c6821e Mon Sep 17 00:00:00 2001 From: Steve Cavanagh Date: Tue, 28 Oct 2014 23:15:11 -0500 Subject: [PATCH 1/4] fix(datepicker): Add minDate and maxDate validation for popup. --- src/datepicker/datepicker.js | 64 +++++++++++++++++++++++- src/datepicker/test/datepicker.spec.js | 67 +++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 4 deletions(-) diff --git a/src/datepicker/datepicker.js b/src/datepicker/datepicker.js index 05784947d0..776b0237fa 100644 --- a/src/datepicker/datepicker.js +++ b/src/datepicker/datepicker.js @@ -440,7 +440,10 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi currentText: '@', clearText: '@', closeText: '@', - dateDisabled: '&' + dateDisabled: '&', + minDate: '@', + maxDate: '@' + }, link: function(scope, element, attrs, ngModel) { var dateFormat, @@ -500,6 +503,59 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi if (attrs.dateDisabled) { datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })'); } + function compareDates(date1, date2) { + if (isNaN(date1) || isNaN(date2)) { + return undefined; + } + else { + return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()) ); + } + } + + function getDateLimitToCheck(limitName) { + var watchDate = scope.watchData[limitName]; + if (!watchDate) { + return null; + } else { + return new Date(watchDate); + } + } + + function isDateLimitMet(limitName, dateToCheck, viewValue) { + var dateCompare = compareDates(new Date(viewValue), dateToCheck); + if (limitName == 'minDate') { + return !dateCompare || dateCompare > 0; + } else if (limitName == 'maxDate') { + return !dateCompare || dateCompare < 0; + } + } + + function dateLimitParseFormat(limitName, viewValue, isFormatOnly) { + var dateLimit = getDateLimitToCheck(limitName); + if (dateLimit) { + var isMet = isDateLimitMet(limitName, dateLimit, viewValue); + ngModel.$setValidity(limitName, isMet); + return (isFormatOnly || isMet) ? viewValue : undefined; + } else { + return viewValue; + } + } + + function minLimitParse(viewValue) { + return dateLimitParseFormat('minDate', viewValue, false); + } + + function minLimitFormat(viewValue) { + return dateLimitParseFormat('minDate', viewValue, true); + } + + function maxLimitParse(viewValue) { + return dateLimitParseFormat('maxDate', viewValue, false); + } + + function maxLimitFormat(viewValue) { + return dateLimitParseFormat('maxDate', viewValue, true); + } function parseDate(viewValue) { if (!viewValue) { @@ -523,6 +579,10 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi } } ngModel.$parsers.unshift(parseDate); + ngModel.$parsers.unshift(minLimitParse); + ngModel.$formatters.unshift(minLimitFormat); + ngModel.$parsers.unshift(maxLimitParse); + ngModel.$formatters.unshift(maxLimitFormat); // Inner change scope.dateSelection = function(dt) { @@ -544,7 +604,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi }); }); - // Outter change + // Outer change ngModel.$render = function() { var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : ''; element.val(date); diff --git a/src/datepicker/test/datepicker.spec.js b/src/datepicker/test/datepicker.spec.js index b673d162da..075952d0ac 100644 --- a/src/datepicker/test/datepicker.spec.js +++ b/src/datepicker/test/datepicker.spec.js @@ -1123,11 +1123,19 @@ describe('datepicker directive', function () { $sniffer = _$sniffer_; $rootScope.isopen = true; $rootScope.date = new Date('September 30, 2010 15:30:00'); - var wrapElement = $compile('
')($rootScope); + $rootScope.minimumDate = new Date('January 1, 1970'); + $rootScope.maximumDate = new Date('January 1, 2020'); + $rootScope.dateDisabledHandler = jasmine.createSpy('dateDisabledHandler'); + var wrapElement = $compile('
')($rootScope); $rootScope.$digest(); assignElements(wrapElement); + })); + it('executes the dateDisabled expression for each visible day plus one for validation', function() { + expect($rootScope.dateDisabledHandler.calls.length).toEqual(42+1); + }); + it('datepicker is displayed', function() { expect(dropdownEl).not.toBeHidden(); }); @@ -1161,6 +1169,41 @@ describe('datepicker directive', function () { $rootScope.date = new Date('January 10, 1983 10:00:00'); $rootScope.$digest(); expect(inputEl.val()).toBe('1983-01-10'); + expect(inputEl).not.toHaveClass('ng-invalid'); + expect(inputEl).not.toHaveClass('ng-invalid-min-date'); + expect(inputEl).not.toHaveClass('ng-invalid-max-date'); + }); + + it('updates the input correctly when model changes to a date prior to minimum date', function() { + $rootScope.date = new Date('January 10, 1963 10:00:00'); + $rootScope.$digest(); + expect(inputEl.val()).toBe('1963-01-10'); + expect(inputEl).toHaveClass('ng-invalid'); + expect(inputEl).toHaveClass('ng-invalid-min-date'); + expect(inputEl).not.toHaveClass('ng-invalid-max-date'); + }); + + it('input is not marked with invalid minimum date when minimum is not a valid date', function() { + expect(inputEl).not.toHaveClass('ng-invalid-min-date'); + $rootScope.minimumDate = 'pizza'; + $rootScope.$digest(); + expect(inputEl).not.toHaveClass('ng-invalid-min-date'); + }); + + it('updates the input correctly when model changes to a date past the maximum date', function() { + $rootScope.date = new Date('January 10, 2023 10:00:00'); + $rootScope.$digest(); + expect(inputEl.val()).toBe('2023-01-10'); + expect(inputEl).toHaveClass('ng-invalid'); + expect(inputEl).toHaveClass('ng-invalid-max-date'); + expect(inputEl).not.toHaveClass('ng-invalid-min-date'); + }); + + it('input is not marked with invalid maximum date when maximum is not a valid date', function() { + expect(inputEl).not.toHaveClass('ng-invalid-max-date'); + $rootScope.maximumDate = new Date('pizza'); + $rootScope.$digest(); + expect(inputEl).not.toHaveClass('ng-invalid-max-date'); }); it('closes the dropdown when a day is clicked', function() { @@ -1214,7 +1257,27 @@ describe('datepicker directive', function () { expect(inputEl).not.toHaveClass('ng-invalid-date'); }); - describe('focus', function () { + it('sets `ng-invalid` for date prior to minimum date', function() { + changeInputValueTo(inputEl, '1960-12-01'); + + expect(inputEl).toHaveClass('ng-invalid'); + expect(inputEl).toHaveClass('ng-invalid-min-date'); + expect(inputEl).not.toHaveClass('ng-invalid-max-date'); + expect($rootScope.date).toBeNull(); + expect(inputEl.val()).toBe('1960-12-01'); + }); + + it('sets `ng-invalid` for date past the maximum date', function() { + changeInputValueTo(inputEl, '2050-12-01'); + + expect(inputEl).toHaveClass('ng-invalid'); + expect(inputEl).not.toHaveClass('ng-invalid-min-date'); + expect(inputEl).toHaveClass('ng-invalid-max-date'); + expect($rootScope.date).toBeNull(); + expect(inputEl.val()).toBe('2050-12-01'); + }); + + describe('focus', function () { beforeEach(function() { var body = $document.find('body'); body.append(inputEl); From 80fcd4733e2aee65d32a3dea32890b1cb2284fca Mon Sep 17 00:00:00 2001 From: Steve Cavanagh Date: Sun, 8 Feb 2015 20:40:35 -0600 Subject: [PATCH 2/4] fix(datepicker): Add min&max formatted date validation for popup. --- src/datepicker/datepicker.js | 2 +- src/datepicker/test/datepicker.spec.js | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/datepicker/datepicker.js b/src/datepicker/datepicker.js index 776b0237fa..2fc9b8ac7d 100644 --- a/src/datepicker/datepicker.js +++ b/src/datepicker/datepicker.js @@ -522,7 +522,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi } function isDateLimitMet(limitName, dateToCheck, viewValue) { - var dateCompare = compareDates(new Date(viewValue), dateToCheck); + var dateCompare = compareDates(parseDate(viewValue, dateFormat) || new Date(viewValue), dateToCheck); if (limitName == 'minDate') { return !dateCompare || dateCompare > 0; } else if (limitName == 'maxDate') { diff --git a/src/datepicker/test/datepicker.spec.js b/src/datepicker/test/datepicker.spec.js index 075952d0ac..1d8de17938 100644 --- a/src/datepicker/test/datepicker.spec.js +++ b/src/datepicker/test/datepicker.spec.js @@ -1122,11 +1122,12 @@ describe('datepicker directive', function () { $document = _$document_; $sniffer = _$sniffer_; $rootScope.isopen = true; + $rootScope.format='yyyy-MM-dd'; $rootScope.date = new Date('September 30, 2010 15:30:00'); $rootScope.minimumDate = new Date('January 1, 1970'); $rootScope.maximumDate = new Date('January 1, 2020'); $rootScope.dateDisabledHandler = jasmine.createSpy('dateDisabledHandler'); - var wrapElement = $compile('
')($rootScope); + var wrapElement = $compile('
')($rootScope); $rootScope.$digest(); assignElements(wrapElement); @@ -1263,16 +1264,31 @@ describe('datepicker directive', function () { expect(inputEl).toHaveClass('ng-invalid'); expect(inputEl).toHaveClass('ng-invalid-min-date'); expect(inputEl).not.toHaveClass('ng-invalid-max-date'); + expect(inputEl).not.toHaveClass('ng-invalid-date'); expect($rootScope.date).toBeNull(); expect(inputEl.val()).toBe('1960-12-01'); }); + it('sets `ng-invalid` for date (european format) prior to minimum date', function() { + $rootScope.format='dd.MM.yyyy'; + $rootScope.$digest(); + changeInputValueTo(inputEl, '20.12.1960'); + + expect(inputEl).toHaveClass('ng-invalid'); + expect(inputEl).toHaveClass('ng-invalid-min-date'); + expect(inputEl).not.toHaveClass('ng-invalid-max-date'); + expect(inputEl).not.toHaveClass('ng-invalid-date'); + expect($rootScope.date).toBeNull(); + expect(inputEl.val()).toBe('20.12.1960'); + }); + it('sets `ng-invalid` for date past the maximum date', function() { changeInputValueTo(inputEl, '2050-12-01'); expect(inputEl).toHaveClass('ng-invalid'); expect(inputEl).not.toHaveClass('ng-invalid-min-date'); expect(inputEl).toHaveClass('ng-invalid-max-date'); + expect(inputEl).not.toHaveClass('ng-invalid-date'); expect($rootScope.date).toBeNull(); expect(inputEl.val()).toBe('2050-12-01'); }); From b7cb0cbddc96f9e0ef5d1ab31f28e0c192b5d575 Mon Sep 17 00:00:00 2001 From: Steve Cavanagh Date: Sat, 21 Feb 2015 20:44:30 -0600 Subject: [PATCH 3/4] fix(datepicker): Use dateParser for comparing limits, not parseDate. --- src/datepicker/datepicker.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datepicker/datepicker.js b/src/datepicker/datepicker.js index 2fc9b8ac7d..899276671b 100644 --- a/src/datepicker/datepicker.js +++ b/src/datepicker/datepicker.js @@ -522,7 +522,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi } function isDateLimitMet(limitName, dateToCheck, viewValue) { - var dateCompare = compareDates(parseDate(viewValue, dateFormat) || new Date(viewValue), dateToCheck); + var parsedDate = dateParser.parse(viewValue, dateFormat); + var dateCompare = compareDates(parsedDate, dateToCheck); if (limitName == 'minDate') { return !dateCompare || dateCompare > 0; } else if (limitName == 'maxDate') { From 30c10972b2a8354b4f46738e2818fd0afe19c2c2 Mon Sep 17 00:00:00 2001 From: Steve Cavanagh Date: Sat, 21 Feb 2015 21:25:06 -0600 Subject: [PATCH 4/4] fix(datePicker): Expect invalid dates set to undefined, not null. --- src/datepicker/test/datepicker.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/datepicker/test/datepicker.spec.js b/src/datepicker/test/datepicker.spec.js index 1d8de17938..1da684c431 100644 --- a/src/datepicker/test/datepicker.spec.js +++ b/src/datepicker/test/datepicker.spec.js @@ -1265,7 +1265,7 @@ describe('datepicker directive', function () { expect(inputEl).toHaveClass('ng-invalid-min-date'); expect(inputEl).not.toHaveClass('ng-invalid-max-date'); expect(inputEl).not.toHaveClass('ng-invalid-date'); - expect($rootScope.date).toBeNull(); + expect($rootScope.date).toBeUndefined(); expect(inputEl.val()).toBe('1960-12-01'); }); @@ -1278,7 +1278,7 @@ describe('datepicker directive', function () { expect(inputEl).toHaveClass('ng-invalid-min-date'); expect(inputEl).not.toHaveClass('ng-invalid-max-date'); expect(inputEl).not.toHaveClass('ng-invalid-date'); - expect($rootScope.date).toBeNull(); + expect($rootScope.date).toBeUndefined(); expect(inputEl.val()).toBe('20.12.1960'); }); @@ -1289,7 +1289,7 @@ describe('datepicker directive', function () { expect(inputEl).not.toHaveClass('ng-invalid-min-date'); expect(inputEl).toHaveClass('ng-invalid-max-date'); expect(inputEl).not.toHaveClass('ng-invalid-date'); - expect($rootScope.date).toBeNull(); + expect($rootScope.date).toBeUndefined(); expect(inputEl.val()).toBe('2050-12-01'); });