Skip to content

Commit

Permalink
fix(datepicker): don't call date filter function if date is out of bo…
Browse files Browse the repository at this point in the history
…unds

Doesn't call the `dateFilter` function for date is before the minimum or after the maximum in the month view. We were already doing this for the rest of the views, but I added some tests so we have coverage for it.

Fixes angular#18411.
  • Loading branch information
crisbeto committed Feb 6, 2020
1 parent 68a2f89 commit 68ea6c8
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 36 deletions.
44 changes: 33 additions & 11 deletions src/material/datepicker/month-view.spec.ts
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
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
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
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

0 comments on commit 68ea6c8

Please sign in to comment.