sass-references/angular-material/material/datepicker/month-view.spec.ts

916 lines
34 KiB
TypeScript
Raw Permalink Normal View History

2024-12-06 10:42:08 +08:00
import {Direction, Directionality} from '@angular/cdk/bidi';
import {
DOWN_ARROW,
END,
ENTER,
ESCAPE,
HOME,
LEFT_ARROW,
PAGE_DOWN,
PAGE_UP,
RIGHT_ARROW,
SPACE,
UP_ARROW,
} from '@angular/cdk/keycodes';
import {
createKeyboardEvent,
dispatchEvent,
dispatchFakeEvent,
dispatchKeyboardEvent,
dispatchMouseEvent,
} from '@angular/cdk/testing/private';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {MAT_DATE_FORMATS, MatNativeDateModule} from '@angular/material/core';
import {By} from '@angular/platform-browser';
import {DEC, FEB, JAN, MAR, NOV} from '../testing';
import {MatCalendarBody, MatCalendarUserEvent} from './calendar-body';
import {
DefaultMatCalendarRangeStrategy,
MAT_DATE_RANGE_SELECTION_STRATEGY,
} from './date-range-selection-strategy';
import {DateRange} from './date-selection-model';
import {MatMonthView} from './month-view';
describe('MatMonthView', () => {
describe('standard providers', () => {
let dir: {value: Direction};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
MatNativeDateModule,
MatCalendarBody,
MatMonthView,
// Test components.
StandardMonthView,
MonthViewWithDateFilter,
MonthViewWithDateClass,
],
providers: [
{provide: Directionality, useFactory: () => (dir = {value: 'ltr'})},
{provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: DefaultMatCalendarRangeStrategy},
],
});
}));
describe('standard month view', () => {
let fixture: ComponentFixture<StandardMonthView>;
let testComponent: StandardMonthView;
let monthViewNativeElement: Element;
beforeEach(() => {
fixture = TestBed.createComponent(StandardMonthView);
fixture.detectChanges();
let monthViewDebugElement = fixture.debugElement.query(By.directive(MatMonthView))!;
monthViewNativeElement = monthViewDebugElement.nativeElement;
testComponent = fixture.componentInstance;
});
it('has correct month label', () => {
let labelEl = monthViewNativeElement.querySelector('.mat-calendar-body-label')!;
expect(labelEl.innerHTML.trim()).toBe('JAN');
});
it('has 31 days', () => {
let cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell')!;
expect(cellEls.length).toBe(31);
});
it('shows selected date if in same month', () => {
let selectedEl = monthViewNativeElement.querySelector('.mat-calendar-body-selected')!;
expect(selectedEl.innerHTML.trim()).toBe('10');
});
it('does not show selected date if in different month', () => {
testComponent.selected = new Date(2017, MAR, 10);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
let selectedEl = monthViewNativeElement.querySelector('.mat-calendar-body-selected');
expect(selectedEl).toBeNull();
});
it('fires selected change event on cell clicked', () => {
let cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
(cellEls[cellEls.length - 1] as HTMLElement).click();
fixture.detectChanges();
let selectedEl = monthViewNativeElement.querySelector('.mat-calendar-body-selected')!;
expect(selectedEl.innerHTML.trim()).toBe('31');
});
it('should mark active date', () => {
let cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
expect((cellEls[4] as HTMLElement).innerText.trim()).toBe('5');
expect(cellEls[4].classList).toContain('mat-calendar-body-active');
});
describe('drag and drop with default range strategy', () => {
const initialRange = new DateRange(new Date(2017, JAN, 10), new Date(2017, JAN, 13));
beforeEach(() => {
testComponent.selected = initialRange;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
});
function getDaysMatching(selector: string) {
return Array.from(monthViewNativeElement.querySelectorAll(selector)).map(elem =>
Number(elem.textContent!.trim()),
);
}
it('drags the range start', () => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
dispatchMouseEvent(cellEls[9], 'mousedown');
fixture.detectChanges();
// Grow range.
dispatchMouseEvent(cellEls[8], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([9, 10, 11, 12, 13]);
// Shrink range.
dispatchMouseEvent(cellEls[10], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([11, 12, 13]);
// Move range past end.
dispatchMouseEvent(cellEls[13], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([14, 15, 16, 17]);
// End drag.
dispatchMouseEvent(cellEls[13], 'mouseup');
fixture.detectChanges();
expect(testComponent.selected).toEqual(
new DateRange(new Date(2017, JAN, 14), new Date(2017, JAN, 17)),
);
});
it('drags the range end', () => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
dispatchMouseEvent(cellEls[12], 'mousedown');
fixture.detectChanges();
// Grow range.
dispatchMouseEvent(cellEls[13], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([10, 11, 12, 13, 14]);
// Shrink range.
dispatchMouseEvent(cellEls[11], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([10, 11, 12]);
// Move range before start.
dispatchMouseEvent(cellEls[8], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([6, 7, 8, 9]);
// End drag.
dispatchMouseEvent(cellEls[8], 'mouseup');
fixture.detectChanges();
expect(testComponent.selected).toEqual(
new DateRange(new Date(2017, JAN, 6), new Date(2017, JAN, 9)),
);
});
it('drags the range middle', () => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
dispatchMouseEvent(cellEls[11], 'mousedown');
fixture.detectChanges();
// Move range down.
dispatchMouseEvent(cellEls[10], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([9, 10, 11, 12]);
// Move range up.
dispatchMouseEvent(cellEls[12], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([11, 12, 13, 14]);
// End drag.
dispatchMouseEvent(cellEls[12], 'mouseup');
fixture.detectChanges();
expect(testComponent.selected).toEqual(
new DateRange(new Date(2017, JAN, 11), new Date(2017, JAN, 14)),
);
});
it('does nothing when dragging outside range', () => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
dispatchMouseEvent(cellEls[8], 'mousedown');
fixture.detectChanges();
dispatchMouseEvent(cellEls[7], 'mouseenter');
fixture.detectChanges();
expect(getDaysMatching('.mat-calendar-body-in-preview')).toEqual([]);
dispatchMouseEvent(cellEls[7], 'mouseup');
fixture.detectChanges();
expect(testComponent.selected).toEqual(initialRange);
});
});
describe('a11y', () => {
it('should set the correct role on the internal table node', () => {
const table = monthViewNativeElement.querySelector('table')!;
expect(table.getAttribute('role')).toBe('grid');
});
it('should set the correct scope on the table headers', () => {
const nonDividerHeaders = monthViewNativeElement.querySelectorAll(
'.mat-calendar-table-header th:not(.mat-calendar-table-header-divider)',
);
const dividerHeader = monthViewNativeElement.querySelector(
'.mat-calendar-table-header-divider',
)!;
expect(
Array.from(nonDividerHeaders).every(header => {
return header.getAttribute('scope') === 'col';
}),
).toBe(true);
expect(dividerHeader.hasAttribute('scope')).toBe(false);
});
describe('calendar body', () => {
let calendarBodyEl: HTMLElement;
let calendarInstance: StandardMonthView;
beforeEach(() => {
calendarInstance = fixture.componentInstance;
calendarBodyEl = fixture.debugElement.nativeElement.querySelector(
'.mat-calendar-body',
) as HTMLElement;
expect(calendarBodyEl).not.toBeNull();
dir.value = 'ltr';
fixture.componentInstance.date = new Date(2017, JAN, 5);
fixture.changeDetectorRef.markForCheck();
dispatchFakeEvent(calendarBodyEl, 'focus');
fixture.detectChanges();
});
it('should decrement date on left arrow press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 4));
calendarInstance.date = new Date(2017, JAN, 1);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2016, DEC, 31));
});
it('should increment date on left arrow press in rtl', () => {
dir.value = 'rtl';
dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 6));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 7));
});
it('should increment date on right arrow press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 6));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 7));
});
it('should decrement date on right arrow press in rtl', () => {
dir.value = 'rtl';
dispatchKeyboardEvent(calendarBodyEl, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 4));
calendarInstance.date = new Date(2017, JAN, 1);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchKeyboardEvent(calendarBodyEl, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2016, DEC, 31));
});
it('should go up a row on up arrow press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', UP_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2016, DEC, 29));
calendarInstance.date = new Date(2017, JAN, 7);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchKeyboardEvent(calendarBodyEl, 'keydown', UP_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2016, DEC, 31));
});
it('should go down a row on down arrow press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', DOWN_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 12));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', DOWN_ARROW);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 19));
});
it('should go to beginning of the month on home press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', HOME);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 1));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', HOME);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 1));
});
it('should go to end of the month on end press', () => {
calendarInstance.date = new Date(2017, JAN, 10);
fixture.changeDetectorRef.markForCheck();
dispatchKeyboardEvent(calendarBodyEl, 'keydown', END);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 31));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', END);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 31));
});
it('should go back one month on page up press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', PAGE_UP);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2016, DEC, 5));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', PAGE_UP);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2016, NOV, 5));
});
it('should go forward one month on page down press', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', PAGE_DOWN);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, FEB, 5));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', PAGE_DOWN);
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, MAR, 5));
});
it('should select active date on enter', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(testComponent.selected).toEqual(new Date(2017, JAN, 10));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', ENTER);
fixture.detectChanges();
dispatchKeyboardEvent(calendarBodyEl, 'keyup', ENTER);
fixture.detectChanges();
expect(testComponent.selected).toEqual(new Date(2017, JAN, 4));
});
it('should select active date on space', () => {
dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(testComponent.selected).toEqual(new Date(2017, JAN, 10));
dispatchKeyboardEvent(calendarBodyEl, 'keydown', SPACE);
fixture.detectChanges();
dispatchKeyboardEvent(calendarBodyEl, 'keyup', SPACE);
fixture.detectChanges();
expect(testComponent.selected).toEqual(new Date(2017, JAN, 4));
});
it('should cancel the current range selection when pressing escape', () => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
testComponent.selected = new DateRange(new Date(2017, JAN, 10), null);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchMouseEvent(cellEls[15], 'mouseenter');
fixture.detectChanges();
// Note that here we only care that _some_ kind of range is rendered. There are
// plenty of tests in the calendar body which assert that everything is correct.
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-start').length,
).toBeGreaterThan(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-in-preview').length,
).toBeGreaterThan(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-end').length,
).toBeGreaterThan(0);
const event = createKeyboardEvent('keydown', ESCAPE, 'Escape');
spyOn(event, 'stopPropagation');
dispatchEvent(calendarBodyEl, event);
fixture.detectChanges();
// Expect the range range to have been cleared.
expect(
monthViewNativeElement.querySelectorAll(
[
'.mat-calendar-body-preview-start',
'.mat-calendar-body-in-preview',
'.mat-calendar-body-preview-end',
].join(','),
).length,
).toBe(0);
expect(event.stopPropagation).toHaveBeenCalled();
expect(event.defaultPrevented).toBe(true);
expect(testComponent.selected).toBeFalsy();
});
it(
'should not cancel the current range selection when pressing escape with a ' +
'modifier key',
() => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
testComponent.selected = new DateRange(new Date(2017, JAN, 10), null);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchMouseEvent(cellEls[15], 'mouseenter');
fixture.detectChanges();
const rangeStarts = monthViewNativeElement.querySelectorAll(
'.mat-calendar-body-preview-start',
).length;
const rangeMids = monthViewNativeElement.querySelectorAll(
'.mat-calendar-body-in-preview',
).length;
const rangeEnds = monthViewNativeElement.querySelectorAll(
'.mat-calendar-body-preview-end',
).length;
// Note that here we only care that _some_ kind of range is rendered. There are
// plenty of tests in the calendar body which assert that everything is correct.
expect(rangeStarts).toBeGreaterThan(0);
expect(rangeMids).toBeGreaterThan(0);
expect(rangeEnds).toBeGreaterThan(0);
const event = createKeyboardEvent('keydown', ESCAPE, 'Escape', {alt: true});
spyOn(event, 'stopPropagation');
dispatchEvent(calendarBodyEl, event);
fixture.detectChanges();
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-start').length,
).toBe(rangeStarts);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-in-preview').length,
).toBe(rangeMids);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-end').length,
).toBe(rangeEnds);
expect(event.stopPropagation).not.toHaveBeenCalled();
expect(event.defaultPrevented).toBe(false);
expect(testComponent.selected).toBeTruthy();
},
);
it('cancels the active drag but not the selection on escape during an active drag', () => {
const cellEls = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
const selectedRange = new DateRange(new Date(2017, JAN, 10), new Date(2017, JAN, 17));
testComponent.selected = selectedRange;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchMouseEvent(cellEls[11], 'mousedown');
fixture.detectChanges();
dispatchMouseEvent(cellEls[4], 'mouseenter');
fixture.detectChanges();
const rangeStarts = monthViewNativeElement.querySelectorAll(
'.mat-calendar-body-preview-start',
).length;
const rangeMids = monthViewNativeElement.querySelectorAll(
'.mat-calendar-body-in-preview',
).length;
const rangeEnds = monthViewNativeElement.querySelectorAll(
'.mat-calendar-body-preview-end',
).length;
// Note that here we only care that _some_ kind of range is rendered. There are
// plenty of tests in the calendar body which assert that everything is correct.
expect(rangeStarts).toBeGreaterThan(0);
expect(rangeMids).toBeGreaterThan(0);
expect(rangeEnds).toBeGreaterThan(0);
const event = createKeyboardEvent('keydown', ESCAPE, 'Escape');
spyOn(event, 'stopPropagation');
dispatchEvent(calendarBodyEl, event);
fixture.detectChanges();
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-start').length,
).toBe(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-in-preview').length,
).toBe(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-end').length,
).toBe(0);
expect(event.stopPropagation).toHaveBeenCalled();
expect(event.defaultPrevented).toBe(true);
expect(testComponent.selected).toEqual(selectedRange);
});
it('should clear the preview range when the user is done selecting', () => {
const cellEls =
monthViewNativeElement.querySelectorAll<HTMLElement>('.mat-calendar-body-cell');
testComponent.selected = new DateRange(new Date(2017, JAN, 10), null);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
dispatchMouseEvent(cellEls[15], 'mouseenter');
fixture.detectChanges();
// Note that here we only care that _some_ kind of range is rendered. There are
// plenty of tests in the calendar body which assert that everything is correct.
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-start').length,
).toBeGreaterThan(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-in-preview').length,
).toBeGreaterThan(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-end').length,
).toBeGreaterThan(0);
cellEls[15].click();
fixture.detectChanges();
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-start').length,
).toBe(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-in-preview').length,
).toBe(0);
expect(
monthViewNativeElement.querySelectorAll('.mat-calendar-body-preview-end').length,
).toBe(0);
});
it('should not clear the range when pressing escape while there is no preview', () => {
const getRangeElements = () =>
monthViewNativeElement.querySelectorAll(
[
'.mat-calendar-body-range-start',
'.mat-calendar-body-in-range',
'.mat-calendar-body-range-end',
].join(','),
);
testComponent.selected = new DateRange(
new Date(2017, JAN, 10),
new Date(2017, JAN, 15),
);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
expect(getRangeElements().length)
.withContext('Expected range to be present on init.')
.toBeGreaterThan(0);
dispatchKeyboardEvent(calendarBodyEl, 'keydown', ESCAPE);
fixture.detectChanges();
expect(getRangeElements().length)
.withContext('Expected range to be present after pressing the escape key.')
.toBeGreaterThan(0);
});
it(
'should not fire the selected change event when clicking on an already-selected ' +
'date while selecting a single date',
() => {
testComponent.selected = new Date(2017, JAN, 10);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
expect(fixture.componentInstance.selectedChangeSpy).not.toHaveBeenCalled();
const selectedCell = monthViewNativeElement.querySelector(
'.mat-calendar-body-selected',
) as HTMLElement;
selectedCell.click();
fixture.detectChanges();
expect(fixture.componentInstance.selectedChangeSpy).not.toHaveBeenCalled();
},
);
it(
'should fire the selected change event when clicking on an already-selected ' +
'date while selecting a range',
() => {
const selectedDate = new Date(2017, JAN, 10);
testComponent.selected = new DateRange(selectedDate, null);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
expect(fixture.componentInstance.selectedChangeSpy).not.toHaveBeenCalled();
const selectedCell = monthViewNativeElement.querySelector(
'.mat-calendar-body-selected',
) as HTMLElement;
selectedCell.click();
fixture.detectChanges();
expect(fixture.componentInstance.selectedChangeSpy).toHaveBeenCalledWith(
selectedDate,
);
},
);
it(
'should fire the _userSelection event with the correct value when clicking ' +
'on a selected date',
() => {
const date = new Date(2017, JAN, 10);
testComponent.selected = date;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
expect(fixture.componentInstance.userSelectionSpy).not.toHaveBeenCalled();
const selectedCell = monthViewNativeElement.querySelector(
'.mat-calendar-body-selected',
) as HTMLElement;
selectedCell.click();
fixture.detectChanges();
expect(fixture.componentInstance.userSelectionSpy).toHaveBeenCalledWith(
jasmine.objectContaining({value: date}),
);
},
);
it('should go to month that is focused', () => {
const jan11Cell = fixture.debugElement.nativeElement.querySelector(
'[data-mat-row="1"][data-mat-col="3"] button',
) as HTMLElement;
dispatchFakeEvent(jan11Cell, 'focus');
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 11));
});
it('should not call `.focus()` when the active date is focused', () => {
const jan5Cell = fixture.debugElement.nativeElement.querySelector(
'[data-mat-row="0"][data-mat-col="4"] button',
) as HTMLElement;
const focusSpy = (jan5Cell.focus = jasmine.createSpy('cellFocused'));
dispatchFakeEvent(jan5Cell, 'focus');
fixture.detectChanges();
expect(calendarInstance.date).toEqual(new Date(2017, JAN, 5));
expect(focusSpy).not.toHaveBeenCalled();
});
});
});
});
describe('month view with date filter', () => {
it('should disable filtered dates', () => {
const fixture = TestBed.createComponent(MonthViewWithDateFilter);
fixture.detectChanges();
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.changeDetectorRef.markForCheck();
fixture.detectChanges();
expect(spy).not.toHaveBeenCalled();
});
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.changeDetectorRef.markForCheck();
fixture.detectChanges();
expect(spy).not.toHaveBeenCalled();
});
});
describe('month view with custom date classes', () => {
let fixture: ComponentFixture<MonthViewWithDateClass>;
let monthViewNativeElement: Element;
let dateClassSpy: jasmine.Spy;
beforeEach(() => {
fixture = TestBed.createComponent(MonthViewWithDateClass);
dateClassSpy = spyOn(fixture.componentInstance, 'dateClass').and.callThrough();
fixture.detectChanges();
let monthViewDebugElement = fixture.debugElement.query(By.directive(MatMonthView))!;
monthViewNativeElement = monthViewDebugElement.nativeElement;
});
it('should be able to add a custom class to some dates', () => {
let cells = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
expect(cells[0].classList).not.toContain('even');
expect(cells[1].classList).toContain('even');
});
it('should call dateClass with the correct view name', () => {
expect(dateClassSpy).toHaveBeenCalledWith(jasmine.any(Date), 'month');
});
});
});
describe('month view with custom date formats', () => {
let fixture: ComponentFixture<StandardMonthView>;
let monthViewNativeElement: Element;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
MatNativeDateModule,
MatCalendarBody,
MatMonthView,
// Test components.
StandardMonthView,
MonthViewWithDateFilter,
MonthViewWithDateClass,
],
providers: [
{provide: Directionality, useFactory: () => ({value: 'ltr'})},
{provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: DefaultMatCalendarRangeStrategy},
{
provide: MAT_DATE_FORMATS,
useValue: {
parse: {
dateInput: null,
},
display: {
dateInput: {year: 'numeric', month: 'numeric', day: 'numeric'},
monthLabel: {year: 'numeric', month: 'short'},
monthYearLabel: {year: 'numeric', month: 'short'},
dateA11yLabel: {year: 'numeric', month: 'long', day: 'numeric'},
monthYearA11yLabel: {year: 'numeric', month: 'long'},
},
},
},
],
});
fixture = TestBed.createComponent(StandardMonthView);
fixture.detectChanges();
let monthViewDebugElement = fixture.debugElement.query(By.directive(MatMonthView))!;
monthViewNativeElement = monthViewDebugElement.nativeElement;
}));
it('has correct month label', () => {
let labelEl = monthViewNativeElement.querySelector('.mat-calendar-body-label')!;
expect(labelEl.innerHTML.trim()).toBe('Jan 2017');
});
});
});
@Component({
template: `
<mat-month-view
[(activeDate)]="date"
[(selected)]="selected"
(selectedChange)="selectedChangeSpy($event)"
(_userSelection)="userSelectionSpy($event)"
(dragStarted)="dragStarted($event)"
(dragEnded)="dragEnded($event)"
[activeDrag]="activeDrag"></mat-month-view>
`,
standalone: true,
imports: [MatMonthView],
})
class StandardMonthView {
date = new Date(2017, JAN, 5);
selected: Date | DateRange<Date> = new Date(2017, JAN, 10);
selectedChangeSpy = jasmine.createSpy('selectedChange');
userSelectionSpy = jasmine.createSpy('userSelection');
activeDrag: MatCalendarUserEvent<Date> | null = null;
dragStarted(event: MatCalendarUserEvent<Date>) {
this.activeDrag = event;
}
dragEnded(event: MatCalendarUserEvent<DateRange<Date> | null>) {
if (!this.activeDrag) return;
if (event.value) {
this.selected = event.value;
}
this.activeDrag = null;
}
}
@Component({
template: `
<mat-month-view
[activeDate]="activeDate"
[dateFilter]="dateFilter"
[minDate]="minDate"
[maxDate]="maxDate"></mat-month-view>`,
standalone: true,
imports: [MatMonthView],
})
class MonthViewWithDateFilter {
activeDate = new Date(2017, JAN, 1);
minDate: Date | null = null;
maxDate: Date | null = null;
dateFilter(date: Date) {
return date.getDate() % 2 == 0;
}
}
@Component({
template: `<mat-month-view [activeDate]="activeDate" [dateClass]="dateClass"></mat-month-view>`,
standalone: true,
imports: [MatMonthView],
})
class MonthViewWithDateClass {
activeDate = new Date(2017, JAN, 1);
dateClass(date: Date) {
return date.getDate() % 2 == 0 ? 'even' : undefined;
}
}