Skip to content

fix(datepicker): don't call date filter function if date is out of bounds #18419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions src/material/datepicker/month-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,22 +292,37 @@ describe('MatMonthView', () => {
});

describe('month view with date filter', () => {
let fixture: ComponentFixture<MonthViewWithDateFilter>;
let monthViewNativeElement: Element;
it('should disable filtered dates', () => {
const fixture = TestBed.createComponent(MonthViewWithDateFilter);
fixture.detectChanges();

beforeEach(() => {
fixture = TestBed.createComponent(MonthViewWithDateFilter);
let cells = fixture.nativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).toContain('mat-calendar-body-disabled');
expect(cells[1].classList).not.toContain('mat-calendar-body-disabled');
});

it('should not call the date filter function if the date is before the min date', () => {
const fixture = TestBed.createComponent(MonthViewWithDateFilter);
const activeDate = fixture.componentInstance.activeDate;
const spy = spyOn(fixture.componentInstance, 'dateFilter').and.callThrough();
fixture.componentInstance.minDate =
new Date(activeDate.getFullYear() + 1, activeDate.getMonth(), activeDate.getDate());
fixture.detectChanges();

let monthViewDebugElement = fixture.debugElement.query(By.directive(MatMonthView))!;
monthViewNativeElement = monthViewDebugElement.nativeElement;
expect(spy).not.toHaveBeenCalled();
});

it('should disable filtered dates', () => {
let cells = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).toContain('mat-calendar-body-disabled');
expect(cells[1].classList).not.toContain('mat-calendar-body-disabled');
it('should not call the date filter function if the date is after the max date', () => {
const fixture = TestBed.createComponent(MonthViewWithDateFilter);
const activeDate = fixture.componentInstance.activeDate;
const spy = spyOn(fixture.componentInstance, 'dateFilter').and.callThrough();
fixture.componentInstance.maxDate =
new Date(activeDate.getFullYear() - 1, activeDate.getMonth(), activeDate.getDate());
fixture.detectChanges();

expect(spy).not.toHaveBeenCalled();
});

});

describe('month view with custom date classes', () => {
Expand Down Expand Up @@ -342,10 +357,17 @@ class StandardMonthView {


@Component({
template: `<mat-month-view [activeDate]="activeDate" [dateFilter]="dateFilter"></mat-month-view>`
template: `
<mat-month-view
[activeDate]="activeDate"
[dateFilter]="dateFilter"
[minDate]="minDate"
[maxDate]="maxDate"></mat-month-view>`
})
class MonthViewWithDateFilter {
activeDate = new Date(2017, JAN, 1);
minDate: Date | null = null;
maxDate: Date | null = null;
dateFilter(date: Date) {
return date.getDate() % 2 == 0;
}
Expand Down
4 changes: 2 additions & 2 deletions src/material/datepicker/month-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,9 @@ export class MatMonthView<D> implements AfterContentInit, OnDestroy {
/** Date filter for the month */
private _shouldEnableDate(date: D): boolean {
return !!date &&
(!this.dateFilter || this.dateFilter(date)) &&
(!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) &&
(!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0);
(!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0) &&
(!this.dateFilter || this.dateFilter(date));
}

/**
Expand Down
44 changes: 32 additions & 12 deletions src/material/datepicker/multi-year-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,22 +231,37 @@ describe('MatMultiYearView', () => {
});

describe('multi year view with date filter', () => {
let fixture: ComponentFixture<MultiYearViewWithDateFilter>;
let multiYearViewNativeElement: Element;
it('should disable years with no enabled days', () => {
const fixture = TestBed.createComponent(MultiYearViewWithDateFilter);
fixture.detectChanges();

beforeEach(() => {
fixture = TestBed.createComponent(MultiYearViewWithDateFilter);
const cells = fixture.nativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).not.toContain('mat-calendar-body-disabled');
expect(cells[1].classList).toContain('mat-calendar-body-disabled');
});

it('should not call the date filter function if the date is before the min date', () => {
const fixture = TestBed.createComponent(MultiYearViewWithDateFilter);
const activeDate = fixture.componentInstance.activeDate;
const spy = spyOn(fixture.componentInstance, 'dateFilter').and.callThrough();
fixture.componentInstance.minDate =
new Date(activeDate.getFullYear() + 1, activeDate.getMonth(), activeDate.getDate());
fixture.detectChanges();

const multiYearViewDebugElement = fixture.debugElement.query(By.directive(MatMultiYearView))!;
multiYearViewNativeElement = multiYearViewDebugElement.nativeElement;
expect(spy).not.toHaveBeenCalled();
});

it('should disablex years with no enabled days', () => {
const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).not.toContain('mat-calendar-body-disabled');
expect(cells[1].classList).toContain('mat-calendar-body-disabled');
it('should not call the date filter function if the date is after the max date', () => {
const fixture = TestBed.createComponent(MultiYearViewWithDateFilter);
const activeDate = fixture.componentInstance.activeDate;
const spy = spyOn(fixture.componentInstance, 'dateFilter').and.callThrough();
fixture.componentInstance.maxDate =
new Date(activeDate.getFullYear() - 1, activeDate.getMonth(), activeDate.getDate());
fixture.detectChanges();

expect(spy).not.toHaveBeenCalled();
});

});

describe('multi year view with minDate only', () => {
Expand Down Expand Up @@ -345,12 +360,17 @@ class StandardMultiYearView {

@Component({
template: `
<mat-multi-year-view [(activeDate)]="activeDate" [dateFilter]="dateFilter">
</mat-multi-year-view>
<mat-multi-year-view
[(activeDate)]="activeDate"
[dateFilter]="dateFilter"
[minDate]="minDate"
[maxDate]="maxDate"></mat-multi-year-view>
`
})
class MultiYearViewWithDateFilter {
activeDate = new Date(2017, JAN, 1);
minDate: Date | null = null;
maxDate: Date | null = null;
dateFilter(date: Date) {
return date.getFullYear() !== 2017;
}
Expand Down
44 changes: 33 additions & 11 deletions src/material/datepicker/year-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,22 +297,37 @@ describe('MatYearView', () => {
});

describe('year view with date filter', () => {
let fixture: ComponentFixture<YearViewWithDateFilter>;
let yearViewNativeElement: Element;
it('should disable months with no enabled days', () => {
const fixture = TestBed.createComponent(YearViewWithDateFilter);
fixture.detectChanges();

beforeEach(() => {
fixture = TestBed.createComponent(YearViewWithDateFilter);
const cells = fixture.nativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).not.toContain('mat-calendar-body-disabled');
expect(cells[1].classList).toContain('mat-calendar-body-disabled');
});

it('should not call the date filter function if the date is before the min date', () => {
const fixture = TestBed.createComponent(YearViewWithDateFilter);
const activeDate = fixture.componentInstance.activeDate;
const spy = spyOn(fixture.componentInstance, 'dateFilter').and.callThrough();
fixture.componentInstance.minDate =
new Date(activeDate.getFullYear() + 1, activeDate.getMonth(), activeDate.getDate());
fixture.detectChanges();

const yearViewDebugElement = fixture.debugElement.query(By.directive(MatYearView))!;
yearViewNativeElement = yearViewDebugElement.nativeElement;
expect(spy).not.toHaveBeenCalled();
});

it('should disable months with no enabled days', () => {
const cells = yearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).not.toContain('mat-calendar-body-disabled');
expect(cells[1].classList).toContain('mat-calendar-body-disabled');
it('should not call the date filter function if the date is after the max date', () => {
const fixture = TestBed.createComponent(YearViewWithDateFilter);
const activeDate = fixture.componentInstance.activeDate;
const spy = spyOn(fixture.componentInstance, 'dateFilter').and.callThrough();
fixture.componentInstance.maxDate =
new Date(activeDate.getFullYear() - 1, activeDate.getMonth(), activeDate.getDate());
fixture.detectChanges();

expect(spy).not.toHaveBeenCalled();
});

});
});

Expand All @@ -332,10 +347,17 @@ class StandardYearView {


@Component({
template: `<mat-year-view [activeDate]="activeDate" [dateFilter]="dateFilter"></mat-year-view>`
template: `
<mat-year-view
[activeDate]="activeDate"
[dateFilter]="dateFilter"
[minDate]="minDate"
[maxDate]="maxDate"></mat-year-view>`
})
class YearViewWithDateFilter {
activeDate = new Date(2017, JAN, 1);
minDate: Date | null = null;
maxDate: Date | null = null;
dateFilter(date: Date) {
if (date.getMonth() == JAN) {
return date.getDate() == 10;
Expand Down