Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 3d5ff5d

Browse files
committed
fix(calendar, datepicker): in year view, md-date-filter only evaluates first day of month
- add `md-month-filter` API - no longer apply `md-date-filter` to year view - fix JSDoc typos - add JSDoc, tests, and demos Fixes #11703
1 parent e625a9c commit 3d5ff5d

File tree

6 files changed

+64
-23
lines changed

6 files changed

+64
-23
lines changed

src/components/datepicker/demoValidations/index.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,15 @@ <h4>Inside a md-input-container</h4>
3939
</div>
4040
<div layout-gt-xs="row">
4141
<div flex-gt-xs>
42-
<h4>Date-picker that only allows for certain months to be selected</h4>
42+
<h4>Date-picker that only allows for even months</h4>
4343
<md-datepicker ng-model="ctrl.myDate" md-placeholder="Enter date" md-mode="month"
44-
md-date-filter="ctrl.filter"></md-datepicker>
44+
md-month-filter="ctrl.evenMonthsPredicate"></md-datepicker>
45+
</div>
46+
<div flex-gt-xs>
47+
<h4>Only allow even months and weekends</h4>
48+
<md-datepicker ng-model="ctrl.myDate" md-placeholder="Enter date"
49+
md-month-filter="ctrl.evenMonthsPredicate" md-current-view="year"
50+
md-date-filter="ctrl.onlyWeekendsPredicate"></md-datepicker>
4551
</div>
4652
</div>
4753
</md-content>

src/components/datepicker/demoValidations/script.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ angular.module('datepickerValidations', ['ngMaterial', 'ngMessages'])
2727
* @param {Date} date
2828
* @returns {boolean} return false to disable all odd numbered months, true for even months
2929
*/
30-
this.filter = function(date) {
31-
return date.getMonth() % 2;
30+
this.evenMonthsPredicate = function(date) {
31+
return date.getMonth() % 2 !== 0;
3232
};
3333
});

src/components/datepicker/js/calendar.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
* @param {Date=} md-min-date Expression representing the minimum date.
1515
* @param {Date=} md-max-date Expression representing the maximum date.
1616
* @param {(function(Date): boolean)=} md-date-filter Function expecting a date and returning a
17-
* boolean whether it can be selected or not.
17+
* boolean whether it can be selected in "day" mode or not.
18+
* @param {(function(Date): boolean)=} md-month-filter Function expecting a date and returning a
19+
* boolean whether it can be selected in "month" mode or not.
1820
* @param {String=} md-current-view Current view of the calendar. Can be either "month" or "year".
1921
* @param {String=} md-mode Restricts the user to only selecting a value from a particular view.
2022
* This option can be used if the user is only supposed to choose from a certain date type
@@ -60,6 +62,7 @@
6062
minDate: '=mdMinDate',
6163
maxDate: '=mdMaxDate',
6264
dateFilter: '=mdDateFilter',
65+
monthFilter: '=mdMonthFilter',
6366

6467
// These need to be prefixed, because Angular resets
6568
// any changes to the value due to bindToController.

src/components/datepicker/js/calendarYearBody.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@
113113

114114
if (this.dateUtil.isMonthWithinRange(
115115
firstOfMonth, calendarCtrl.minDate, calendarCtrl.maxDate) &&
116-
(!angular.isFunction(this.calendarCtrl.dateFilter) ||
117-
this.calendarCtrl.dateFilter(firstOfMonth))) {
116+
(!angular.isFunction(calendarCtrl.monthFilter) ||
117+
calendarCtrl.monthFilter(firstOfMonth))) {
118118
var selectionIndicator = document.createElement('span');
119119
selectionIndicator.classList.add('md-calendar-date-selection-indicator');
120120
selectionIndicator.textContent = cellText;

src/components/datepicker/js/datepickerDirective.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
* @param {Date=} md-min-date Expression representing a min date (inclusive).
3030
* @param {Date=} md-max-date Expression representing a max date (inclusive).
3131
* @param {(function(Date): boolean)=} md-date-filter Function expecting a date and returning a
32-
* boolean whether it can be selected or not.
32+
* boolean whether it can be selected in "day" mode or not. Returning false will also trigger a
33+
* `filtered` model validation error.
34+
* @param {(function(Date): boolean)=} md-month-filter Function expecting a date and returning a
35+
* boolean whether it can be selected in "month" mode or not. Returning false will also trigger a
36+
* `filtered` model validation error.
3337
* @param {String=} md-placeholder The date input placeholder value.
3438
* @param {String=} md-open-on-focus When present, the calendar will be opened when the input
3539
* is focused.
@@ -49,7 +53,7 @@
4953
* * `"calendar"` - Only hides the calendar icon.
5054
* * `"triangle"` - Only hides the triangle icon.
5155
* @param {Object=} md-date-locale Allows for the values from the `$mdDateLocaleProvider` to be
52-
* ovewritten on a per-element basis (e.g. `msgOpenCalendar` can be overwritten with
56+
* overwritten on a per-element basis (e.g. `msgOpenCalendar` can be overwritten with
5357
* `md-date-locale="{ msgOpenCalendar: 'Open a special calendar' }"`).
5458
*
5559
* @description
@@ -127,6 +131,7 @@
127131
'md-min-date="ctrl.minDate" ' +
128132
'md-max-date="ctrl.maxDate" ' +
129133
'md-date-filter="ctrl.dateFilter" ' +
134+
'md-month-filter="ctrl.monthFilter" ' +
130135
'ng-model="ctrl.date" ng-if="ctrl.isCalendarOpen">' +
131136
'</md-calendar>' +
132137
'</div>' +
@@ -140,6 +145,7 @@
140145
currentView: '@mdCurrentView',
141146
mode: '@mdMode',
142147
dateFilter: '=mdDateFilter',
148+
monthFilter: '=mdMonthFilter',
143149
isOpen: '=?mdIsOpen',
144150
debounceInterval: '=mdDebounceInterval',
145151
dateLocale: '=mdDateLocale'
@@ -641,6 +647,10 @@
641647
if (angular.isFunction(this.dateFilter)) {
642648
this.ngModelCtrl.$setValidity('filtered', this.dateFilter(date));
643649
}
650+
651+
if (angular.isFunction(this.monthFilter)) {
652+
this.ngModelCtrl.$setValidity('filtered', this.monthFilter(date));
653+
}
644654
} else {
645655
// The date is seen as "not a valid date" if there is *something* set
646656
// (i.e.., not null or undefined), but that something isn't a valid date.
@@ -714,7 +724,8 @@
714724
*/
715725
DatePickerCtrl.prototype.isDateEnabled = function(opt_date) {
716726
return this.dateUtil.isDateWithinRange(opt_date, this.minDate, this.maxDate) &&
717-
(!angular.isFunction(this.dateFilter) || this.dateFilter(opt_date));
727+
(!angular.isFunction(this.dateFilter) || this.dateFilter(opt_date)) &&
728+
(!angular.isFunction(this.monthFilter) || this.monthFilter(opt_date));
718729
};
719730

720731
/** Position and attach the floating calendar to the document. */
@@ -743,11 +754,11 @@
743754
// then it's possible that the already-scrolled body has a negative top/left. In this case,
744755
// we want to treat the "real" top as (0 - bodyRect.top). In a normal scrolling situation,
745756
// though, the top of the viewport should just be the body's scroll position.
746-
var viewportTop = (bodyRect.top < 0 && document.body.scrollTop == 0) ?
757+
var viewportTop = (bodyRect.top < 0 && document.body.scrollTop === 0) ?
747758
-bodyRect.top :
748759
document.body.scrollTop;
749760

750-
var viewportLeft = (bodyRect.left < 0 && document.body.scrollLeft == 0) ?
761+
var viewportLeft = (bodyRect.left < 0 && document.body.scrollLeft === 0) ?
751762
-bodyRect.left :
752763
document.body.scrollLeft;
753764

src/components/datepicker/js/datepickerDirective.spec.js

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@ describe('md-datepicker', function() {
1212

1313
var DATEPICKER_TEMPLATE =
1414
'<md-datepicker name="birthday" ' +
15-
'md-max-date="maxDate" ' +
16-
'md-min-date="minDate" ' +
17-
'md-date-filter="dateFilter"' +
18-
'ng-model="myDate" ' +
19-
'ng-change="dateChangedHandler()" ' +
20-
'ng-focus="focusHandler()" ' +
21-
'ng-blur="blurHandler()" ' +
22-
'ng-required="isRequired" ' +
23-
'ng-disabled="isDisabled">' +
15+
'md-max-date="maxDate" ' +
16+
'md-min-date="minDate" ' +
17+
'md-date-filter="dateFilter"' +
18+
'md-month-filter="monthFilter"' +
19+
'ng-model="myDate" ' +
20+
'ng-change="dateChangedHandler()" ' +
21+
'ng-focus="focusHandler()" ' +
22+
'ng-blur="blurHandler()" ' +
23+
'ng-required="isRequired" ' +
24+
'ng-disabled="isDisabled">' +
2425
'</md-datepicker>';
2526

2627
beforeEach(module('material.components.datepicker', 'material.components.input', 'ngAnimateMock'));
@@ -275,7 +276,7 @@ describe('md-datepicker', function() {
275276
expect(formCtrl.$error['maxdate']).toBeTruthy();
276277
});
277278

278-
it('should set `filtered` $error flag on the form', function() {
279+
it('should set `filtered` $error flag on the form when date doesn\'t pass filter', function() {
279280
pageScope.dateFilter = function(date) {
280281
return date.getDay() === 1;
281282
};
@@ -285,6 +286,16 @@ describe('md-datepicker', function() {
285286
expect(formCtrl.$error['filtered']).toBeTruthy();
286287
});
287288

289+
it('should set `filtered` $error flag on the form when month doesn\'t pass filter', function() {
290+
pageScope.monthFilter = function(date) {
291+
return date.getMonth() === 10;
292+
};
293+
populateInputElement('2016-01-03');
294+
controller.ngModelCtrl.$render();
295+
296+
expect(formCtrl.$error['filtered']).toBeTruthy();
297+
});
298+
288299
it('should add the invalid class when the form is submitted', function() {
289300
// This needs to be recompiled, in order to reproduce conditions where a form is
290301
// submitted, without the datepicker having being touched (usually it has it's value
@@ -383,7 +394,7 @@ describe('md-datepicker', function() {
383394
expect(controller.inputContainer).not.toHaveClass('md-datepicker-invalid');
384395
});
385396

386-
it('should not update the model when value is not enabled', function() {
397+
it('should not update the model when value is not enabled due to date filter', function() {
387398
pageScope.dateFilter = function(date) {
388399
return date.getDay() === 1;
389400
};
@@ -393,6 +404,16 @@ describe('md-datepicker', function() {
393404
expect(controller.ngModelCtrl.$modelValue).toEqual(initialDate);
394405
});
395406

407+
it('should not update the model when value is not enabled due to month filter', function() {
408+
pageScope.monthFilter = function(date) {
409+
return date.getMonth() === 10;
410+
};
411+
pageScope.$apply();
412+
413+
populateInputElement('5/30/2014');
414+
expect(controller.ngModelCtrl.$modelValue).toEqual(initialDate);
415+
});
416+
396417
it('should become touched from blurring closing the pane', function() {
397418
populateInputElement('17/1/2015');
398419

0 commit comments

Comments
 (0)