839 lines
30 KiB
TypeScript
839 lines
30 KiB
TypeScript
import {
|
|
dispatchFakeEvent,
|
|
dispatchMouseEvent,
|
|
dispatchTouchEvent,
|
|
} from '@angular/cdk/testing/private';
|
|
import {Component} from '@angular/core';
|
|
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
|
|
import {By} from '@angular/platform-browser';
|
|
import {MatCalendarBody, MatCalendarCell, MatCalendarUserEvent} from './calendar-body';
|
|
|
|
describe('MatCalendarBody', () => {
|
|
beforeEach(waitForAsync(() => {
|
|
TestBed.configureTestingModule({
|
|
imports: [MatCalendarBody, StandardCalendarBody, RangeCalendarBody],
|
|
});
|
|
}));
|
|
|
|
describe('standard calendar body', () => {
|
|
let fixture: ComponentFixture<StandardCalendarBody>;
|
|
let testComponent: StandardCalendarBody;
|
|
let calendarBodyNativeElement: Element;
|
|
let rowEls: Element[];
|
|
let labelEls: Element[];
|
|
let cellEls: Element[];
|
|
|
|
function refreshElementLists() {
|
|
rowEls = Array.from(calendarBodyNativeElement.querySelectorAll('tr'));
|
|
labelEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-label'));
|
|
cellEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell'));
|
|
}
|
|
|
|
beforeEach(() => {
|
|
fixture = TestBed.createComponent(StandardCalendarBody);
|
|
fixture.detectChanges();
|
|
|
|
const calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody))!;
|
|
calendarBodyNativeElement = calendarBodyDebugElement.nativeElement;
|
|
testComponent = fixture.componentInstance;
|
|
|
|
refreshElementLists();
|
|
});
|
|
|
|
it('creates body', () => {
|
|
expect(rowEls.length).toBe(3);
|
|
expect(labelEls.length).toBe(1);
|
|
expect(cellEls.length).toBe(14);
|
|
});
|
|
|
|
it('highlights today', () => {
|
|
const todayCells = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-today')!;
|
|
expect(todayCells.length).toBe(1);
|
|
|
|
const todayCell = todayCells[0];
|
|
|
|
expect(todayCell).not.toBeNull();
|
|
expect(todayCell.textContent!.trim()).toBe('3');
|
|
});
|
|
|
|
it('sets aria-current="date" on today', () => {
|
|
const todayCells = calendarBodyNativeElement.querySelectorAll(
|
|
'[aria-current="date"] .mat-calendar-body-today',
|
|
)!;
|
|
expect(todayCells.length).toBe(1);
|
|
|
|
const todayCell = todayCells[0];
|
|
|
|
expect(todayCell).not.toBeNull();
|
|
expect(todayCell.textContent!.trim()).toBe('3');
|
|
});
|
|
|
|
it('does not highlight today if today is not within the scope', () => {
|
|
testComponent.todayValue = 100000;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
const todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!;
|
|
expect(todayCell).toBeNull();
|
|
});
|
|
|
|
it('does not set aria-current="date" on any cell if today is not ' + 'the scope', () => {
|
|
testComponent.todayValue = 100000;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
const todayCell = calendarBodyNativeElement.querySelector(
|
|
'[aria-current="date"] .mat-calendar-body-today',
|
|
)!;
|
|
expect(todayCell).toBeNull();
|
|
});
|
|
|
|
it('highlights selected', () => {
|
|
const selectedCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-selected')!;
|
|
expect(selectedCell).not.toBeNull();
|
|
expect(selectedCell.innerHTML.trim()).toBe('4');
|
|
});
|
|
|
|
it('should set aria-pressed correctly', () => {
|
|
const pressedCells = cellEls.filter(c => c.getAttribute('aria-pressed') === 'true');
|
|
const depressedCells = cellEls.filter(c => c.getAttribute('aria-pressed') === 'false');
|
|
|
|
expect(pressedCells.length).withContext('Expected one cell to be marked as pressed.').toBe(1);
|
|
expect(depressedCells.length)
|
|
.withContext('Expected remaining cells to be marked as not pressed.')
|
|
.toBe(cellEls.length - 1);
|
|
});
|
|
|
|
it('places label in first row if space is available', () => {
|
|
testComponent.rows[0] = testComponent.rows[0].slice(3);
|
|
testComponent.rows = testComponent.rows.slice();
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
refreshElementLists();
|
|
|
|
expect(rowEls.length).toBe(2);
|
|
expect(labelEls.length).toBe(1);
|
|
expect(cellEls.length).toBe(11);
|
|
expect(rowEls[0].firstElementChild!.classList)
|
|
.withContext('first cell should be the label')
|
|
.toContain('mat-calendar-body-label');
|
|
expect(labelEls[0].getAttribute('colspan')).toBe('3');
|
|
});
|
|
|
|
it('cell should be selected on click', () => {
|
|
const todayElement = calendarBodyNativeElement.querySelector(
|
|
'.mat-calendar-body-today',
|
|
) as HTMLElement;
|
|
todayElement.click();
|
|
fixture.detectChanges();
|
|
|
|
expect(todayElement.classList)
|
|
.withContext('today should be selected')
|
|
.toContain('mat-calendar-body-selected');
|
|
});
|
|
|
|
it('should mark active date', () => {
|
|
expect((cellEls[10] as HTMLElement).innerText.trim()).toBe('11');
|
|
expect(cellEls[10].classList).toContain('mat-calendar-body-active');
|
|
});
|
|
|
|
it('should set a class on even dates', () => {
|
|
expect((cellEls[0] as HTMLElement).innerText.trim()).toBe('1');
|
|
expect((cellEls[1] as HTMLElement).innerText.trim()).toBe('2');
|
|
expect(cellEls[0].classList).not.toContain('even');
|
|
expect(cellEls[1].classList).toContain('even');
|
|
});
|
|
|
|
it('should have a focus indicator', () => {
|
|
expect(cellEls.every(element => !!element.querySelector('.mat-focus-indicator'))).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('range calendar body', () => {
|
|
const startClass = 'mat-calendar-body-range-start';
|
|
const inRangeClass = 'mat-calendar-body-in-range';
|
|
const endClass = 'mat-calendar-body-range-end';
|
|
const comparisonStartClass = 'mat-calendar-body-comparison-start';
|
|
const inComparisonClass = 'mat-calendar-body-in-comparison-range';
|
|
const comparisonEndClass = 'mat-calendar-body-comparison-end';
|
|
const bridgeStart = 'mat-calendar-body-comparison-bridge-start';
|
|
const bridgeEnd = 'mat-calendar-body-comparison-bridge-end';
|
|
const previewStartClass = 'mat-calendar-body-preview-start';
|
|
const inPreviewClass = 'mat-calendar-body-in-preview';
|
|
const previewEndClass = 'mat-calendar-body-preview-end';
|
|
let fixture: ComponentFixture<RangeCalendarBody>;
|
|
let testComponent: RangeCalendarBody;
|
|
let cells: HTMLElement[];
|
|
|
|
beforeEach(() => {
|
|
fixture = TestBed.createComponent(RangeCalendarBody);
|
|
fixture.detectChanges();
|
|
testComponent = fixture.componentInstance;
|
|
cells = Array.from(fixture.nativeElement.querySelectorAll('.mat-calendar-body-cell'));
|
|
});
|
|
|
|
it('should render a range', () => {
|
|
testComponent.startValue = 1;
|
|
testComponent.endValue = 5;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).toContain(startClass);
|
|
expect(cells[1].classList).toContain(inRangeClass);
|
|
expect(cells[2].classList).toContain(inRangeClass);
|
|
expect(cells[3].classList).toContain(inRangeClass);
|
|
expect(cells[4].classList).toContain(endClass);
|
|
});
|
|
|
|
it('should render a comparison range', () => {
|
|
testComponent.comparisonStart = 1;
|
|
testComponent.comparisonEnd = 5;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).toContain(comparisonStartClass);
|
|
expect(cells[1].classList).toContain(inComparisonClass);
|
|
expect(cells[2].classList).toContain(inComparisonClass);
|
|
expect(cells[3].classList).toContain(inComparisonClass);
|
|
expect(cells[4].classList).toContain(comparisonEndClass);
|
|
});
|
|
|
|
it('should be able to render two completely overlapping ranges', () => {
|
|
testComponent.startValue = testComponent.comparisonStart = 1;
|
|
testComponent.endValue = testComponent.comparisonEnd = 5;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).toContain(startClass);
|
|
expect(cells[0].classList).toContain(comparisonStartClass);
|
|
|
|
expect(cells[1].classList).toContain(inRangeClass);
|
|
expect(cells[1].classList).toContain(inComparisonClass);
|
|
|
|
expect(cells[2].classList).toContain(inRangeClass);
|
|
expect(cells[2].classList).toContain(inComparisonClass);
|
|
|
|
expect(cells[3].classList).toContain(inRangeClass);
|
|
expect(cells[3].classList).toContain(inComparisonClass);
|
|
|
|
expect(cells[4].classList).toContain(endClass);
|
|
expect(cells[4].classList).toContain(comparisonEndClass);
|
|
});
|
|
|
|
it(
|
|
'should mark a cell as a start bridge if it is the end of the main range ' +
|
|
'and the start of the comparison',
|
|
() => {
|
|
testComponent.startValue = 1;
|
|
testComponent.endValue = 5;
|
|
testComponent.comparisonStart = 5;
|
|
testComponent.comparisonEnd = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[4].classList).toContain(bridgeStart);
|
|
},
|
|
);
|
|
|
|
it('should not mark a cell as a start bridge if there is no end range value', () => {
|
|
testComponent.startValue = 1;
|
|
testComponent.endValue = null;
|
|
testComponent.comparisonStart = 5;
|
|
testComponent.comparisonEnd = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(bridgeStart))).toBe(false);
|
|
});
|
|
|
|
it(
|
|
'should mark a cell as an end bridge if it is the start of the main range ' +
|
|
'and the end of the comparison',
|
|
() => {
|
|
testComponent.comparisonStart = 1;
|
|
testComponent.comparisonEnd = 5;
|
|
testComponent.startValue = 5;
|
|
testComponent.endValue = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[4].classList).toContain(bridgeEnd);
|
|
},
|
|
);
|
|
|
|
it('should not mark a cell as an end bridge if there is no end range value', () => {
|
|
testComponent.comparisonStart = 1;
|
|
testComponent.comparisonEnd = 5;
|
|
testComponent.startValue = 5;
|
|
testComponent.endValue = null;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(bridgeEnd))).toBe(false);
|
|
});
|
|
|
|
it('should be able to show a main range inside a comparison range', () => {
|
|
testComponent.comparisonStart = 1;
|
|
testComponent.comparisonEnd = 5;
|
|
testComponent.startValue = 2;
|
|
testComponent.endValue = 4;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).toContain(comparisonStartClass);
|
|
|
|
expect(cells[1].classList).toContain(inComparisonClass);
|
|
expect(cells[1].classList).toContain(startClass);
|
|
|
|
expect(cells[2].classList).toContain(inComparisonClass);
|
|
expect(cells[2].classList).toContain(inRangeClass);
|
|
|
|
expect(cells[3].classList).toContain(inComparisonClass);
|
|
expect(cells[3].classList).toContain(endClass);
|
|
|
|
expect(cells[4].classList).toContain(comparisonEndClass);
|
|
});
|
|
|
|
it('should be able to show a comparison range inside a main range', () => {
|
|
testComponent.startValue = 1;
|
|
testComponent.endValue = 5;
|
|
testComponent.comparisonStart = 2;
|
|
testComponent.comparisonEnd = 4;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).toContain(startClass);
|
|
|
|
expect(cells[1].classList).toContain(inRangeClass);
|
|
expect(cells[1].classList).toContain(comparisonStartClass);
|
|
|
|
expect(cells[2].classList).toContain(inRangeClass);
|
|
expect(cells[2].classList).toContain(inComparisonClass);
|
|
|
|
expect(cells[3].classList).toContain(inRangeClass);
|
|
expect(cells[3].classList).toContain(comparisonEndClass);
|
|
|
|
expect(cells[4].classList).toContain(endClass);
|
|
});
|
|
|
|
it('should be able to show a range that is larger than the calendar', () => {
|
|
testComponent.startValue = -10;
|
|
testComponent.endValue = 100;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.every(cell => cell.classList.contains(inRangeClass))).toBe(true);
|
|
expect(cells.some(cell => cell.classList.contains(startClass))).toBe(false);
|
|
expect(cells.some(cell => cell.classList.contains(endClass))).toBe(false);
|
|
});
|
|
|
|
it('should be able to show a comparison range that is larger than the calendar', () => {
|
|
testComponent.comparisonStart = -10;
|
|
testComponent.comparisonEnd = 100;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.every(cell => cell.classList.contains(inComparisonClass))).toBe(true);
|
|
expect(cells.some(cell => cell.classList.contains(comparisonStartClass))).toBe(false);
|
|
expect(cells.some(cell => cell.classList.contains(comparisonEndClass))).toBe(false);
|
|
});
|
|
|
|
it('should be able to show a range that starts before the beginning of the calendar', () => {
|
|
testComponent.startValue = -10;
|
|
testComponent.endValue = 2;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(startClass))).toBe(false);
|
|
expect(cells[0].classList).toContain(inRangeClass);
|
|
expect(cells[1].classList).toContain(endClass);
|
|
});
|
|
|
|
it('should be able to show a comparison range that starts before the beginning of the calendar', () => {
|
|
testComponent.comparisonStart = -10;
|
|
testComponent.comparisonEnd = 2;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(comparisonStartClass))).toBe(false);
|
|
expect(cells[0].classList).toContain(inComparisonClass);
|
|
expect(cells[1].classList).toContain(comparisonEndClass);
|
|
});
|
|
|
|
it('should be able to show a range that ends after the end of the calendar', () => {
|
|
testComponent.startValue = 27;
|
|
testComponent.endValue = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(endClass))).toBe(false);
|
|
expect(cells[26].classList).toContain(startClass);
|
|
expect(cells[27].classList).toContain(inRangeClass);
|
|
});
|
|
|
|
it('should be able to show a comparison range that ends after the end of the calendar', () => {
|
|
testComponent.comparisonStart = 27;
|
|
testComponent.comparisonEnd = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(comparisonEndClass))).toBe(false);
|
|
expect(cells[26].classList).toContain(comparisonStartClass);
|
|
expect(cells[27].classList).toContain(inComparisonClass);
|
|
});
|
|
|
|
it('should be able to show a range that ends after the end of the calendar', () => {
|
|
testComponent.startValue = 27;
|
|
testComponent.endValue = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(endClass))).toBe(false);
|
|
expect(cells[26].classList).toContain(startClass);
|
|
expect(cells[27].classList).toContain(inRangeClass);
|
|
});
|
|
|
|
it('should not to mark a date as both the start and end', () => {
|
|
testComponent.startValue = 1;
|
|
testComponent.endValue = 1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).not.toContain(startClass);
|
|
expect(cells[0].classList).not.toContain(inRangeClass);
|
|
expect(cells[0].classList).not.toContain(endClass);
|
|
});
|
|
|
|
it('should not mark a date as both the comparison start and end', () => {
|
|
testComponent.comparisonStart = 1;
|
|
testComponent.comparisonEnd = 1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).not.toContain(comparisonStartClass);
|
|
expect(cells[0].classList).not.toContain(inComparisonClass);
|
|
expect(cells[0].classList).not.toContain(comparisonEndClass);
|
|
});
|
|
|
|
it('should not mark a date as the range end if it comes before the start', () => {
|
|
testComponent.startValue = 2;
|
|
testComponent.endValue = 1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).not.toContain(endClass);
|
|
expect(cells[0].classList).not.toContain(inRangeClass);
|
|
expect(cells[1].classList).not.toContain(startClass);
|
|
});
|
|
|
|
it('should not mark a date as the comparison range end if it comes before the start', () => {
|
|
testComponent.comparisonStart = 2;
|
|
testComponent.comparisonEnd = 1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[0].classList).not.toContain(comparisonEndClass);
|
|
expect(cells[0].classList).not.toContain(inComparisonClass);
|
|
expect(cells[1].classList).not.toContain(comparisonStartClass);
|
|
});
|
|
|
|
it('should not show a range if there is no start', () => {
|
|
testComponent.startValue = null;
|
|
testComponent.endValue = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(inRangeClass))).toBe(false);
|
|
expect(cells.some(cell => cell.classList.contains(endClass))).toBe(false);
|
|
});
|
|
|
|
it('should not show a comparison range if there is no start', () => {
|
|
testComponent.comparisonStart = null;
|
|
testComponent.comparisonEnd = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(inComparisonClass))).toBe(false);
|
|
expect(cells.some(cell => cell.classList.contains(comparisonEndClass))).toBe(false);
|
|
});
|
|
|
|
it('should not show a comparison range if there is no end', () => {
|
|
testComponent.comparisonStart = 10;
|
|
testComponent.comparisonEnd = null;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(inComparisonClass))).toBe(false);
|
|
expect(cells.some(cell => cell.classList.contains(comparisonEndClass))).toBe(false);
|
|
});
|
|
|
|
it('should preview the selected range after the user clicks on a start and hovers away', () => {
|
|
cells[2].click();
|
|
fixture.detectChanges();
|
|
|
|
dispatchMouseEvent(cells[5], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(inPreviewClass);
|
|
expect(cells[5].classList).toContain(previewEndClass);
|
|
|
|
// Go a few cells ahead.
|
|
dispatchMouseEvent(cells[7], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[5].classList).not.toContain(previewEndClass);
|
|
expect(cells[5].classList).toContain(inPreviewClass);
|
|
expect(cells[6].classList).toContain(inPreviewClass);
|
|
expect(cells[7].classList).toContain(previewEndClass);
|
|
|
|
// Go back a few cells.
|
|
dispatchMouseEvent(cells[4], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[5].classList).not.toContain(inPreviewClass);
|
|
expect(cells[6].classList).not.toContain(inPreviewClass);
|
|
expect(cells[7].classList).not.toContain(previewEndClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(previewEndClass);
|
|
});
|
|
|
|
it('should preview the selected range after the user selects a start and moves focus away', () => {
|
|
cells[2].click();
|
|
fixture.detectChanges();
|
|
|
|
dispatchFakeEvent(cells[5], 'focus');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(inPreviewClass);
|
|
expect(cells[5].classList).toContain(previewEndClass);
|
|
|
|
// Go a few cells ahead.
|
|
dispatchFakeEvent(cells[7], 'focus');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[5].classList).not.toContain(previewEndClass);
|
|
expect(cells[5].classList).toContain(inPreviewClass);
|
|
expect(cells[6].classList).toContain(inPreviewClass);
|
|
expect(cells[7].classList).toContain(previewEndClass);
|
|
|
|
// Go back a few cells.
|
|
dispatchFakeEvent(cells[4], 'focus');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[5].classList).not.toContain(inPreviewClass);
|
|
expect(cells[6].classList).not.toContain(inPreviewClass);
|
|
expect(cells[7].classList).not.toContain(previewEndClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(previewEndClass);
|
|
});
|
|
|
|
it('should not be able to extend the range before the start', () => {
|
|
cells[5].click();
|
|
fixture.detectChanges();
|
|
|
|
dispatchMouseEvent(cells[2], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[5].classList).not.toContain(startClass);
|
|
expect(cells[5].classList).not.toContain(previewStartClass);
|
|
expect(cells.some(cell => cell.classList.contains(inPreviewClass))).toBe(false);
|
|
});
|
|
|
|
it(
|
|
'should be able to show a range, starting before the beginning of the calendar, ' +
|
|
'while hovering',
|
|
() => {
|
|
fixture.componentInstance.startValue = -1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
dispatchMouseEvent(cells[2], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(previewStartClass))).toBe(false);
|
|
expect(cells[0].classList).toContain(inPreviewClass);
|
|
expect(cells[1].classList).toContain(inPreviewClass);
|
|
expect(cells[2].classList).toContain(previewEndClass);
|
|
},
|
|
);
|
|
|
|
it(
|
|
'should be able to show a range, starting before the beginning of the calendar, ' +
|
|
'while moving focus',
|
|
() => {
|
|
fixture.componentInstance.startValue = -1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
dispatchMouseEvent(cells[2], 'focus');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells.some(cell => cell.classList.contains(previewStartClass))).toBe(false);
|
|
expect(cells[0].classList).toContain(inPreviewClass);
|
|
expect(cells[1].classList).toContain(inPreviewClass);
|
|
expect(cells[2].classList).toContain(previewEndClass);
|
|
},
|
|
);
|
|
|
|
it('should remove the preview if the user moves their pointer away', () => {
|
|
cells[2].click();
|
|
fixture.detectChanges();
|
|
|
|
dispatchMouseEvent(cells[4], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(previewEndClass);
|
|
|
|
// Move the pointer away.
|
|
dispatchMouseEvent(cells[4], 'mouseleave');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).not.toContain(previewStartClass);
|
|
expect(cells[3].classList).not.toContain(inPreviewClass);
|
|
expect(cells[4].classList).not.toContain(previewEndClass);
|
|
|
|
// Move the pointer back in to a different cell.
|
|
dispatchMouseEvent(cells[5], 'mouseenter');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(inPreviewClass);
|
|
expect(cells[5].classList).toContain(previewEndClass);
|
|
});
|
|
|
|
it('should remove the preview if the user moves their focus away', () => {
|
|
cells[2].click();
|
|
fixture.detectChanges();
|
|
|
|
dispatchFakeEvent(cells[4], 'focus');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(previewEndClass);
|
|
|
|
// Move the pointer away.
|
|
dispatchFakeEvent(cells[4], 'blur');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).not.toContain(previewStartClass);
|
|
expect(cells[3].classList).not.toContain(inPreviewClass);
|
|
expect(cells[4].classList).not.toContain(previewEndClass);
|
|
|
|
// Move the pointer back in to a different cell.
|
|
dispatchFakeEvent(cells[5], 'focus');
|
|
fixture.detectChanges();
|
|
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(inPreviewClass);
|
|
expect(cells[5].classList).toContain(previewEndClass);
|
|
});
|
|
|
|
it('should mark a cell as being identical to the comparison range', () => {
|
|
testComponent.comparisonStart = testComponent.comparisonEnd = 3;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
const comparisonIdenticalCells: NodeListOf<HTMLElement> =
|
|
fixture.nativeElement.querySelectorAll('.mat-calendar-body-comparison-identical');
|
|
|
|
expect(comparisonIdenticalCells.length).toBe(1);
|
|
expect(cells[2].contains(comparisonIdenticalCells[0])).toBe(true);
|
|
expect(
|
|
cells.some(cell => {
|
|
const classList = cell.classList;
|
|
return (
|
|
classList.contains(startClass) ||
|
|
classList.contains(inRangeClass) ||
|
|
classList.contains(endClass) ||
|
|
classList.contains(comparisonStartClass) ||
|
|
classList.contains(inComparisonClass) ||
|
|
classList.contains(comparisonEndClass)
|
|
);
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
describe('drag and drop ranges', () => {
|
|
beforeEach(() => {
|
|
// Pre-select a range to drag.
|
|
fixture.componentInstance.startValue = 4;
|
|
fixture.componentInstance.endValue = 6;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
});
|
|
|
|
it('triggers and previews a drag (mouse)', () => {
|
|
dispatchMouseEvent(cells[3], 'mousedown');
|
|
fixture.detectChanges();
|
|
expect(fixture.componentInstance.drag).not.toBe(null);
|
|
|
|
// Expand to earlier.
|
|
dispatchMouseEvent(cells[2], 'mouseenter');
|
|
fixture.detectChanges();
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(inPreviewClass);
|
|
expect(cells[5].classList).toContain(previewEndClass);
|
|
|
|
// End drag.
|
|
dispatchMouseEvent(cells[2], 'mouseup');
|
|
expect(fixture.componentInstance.drag).toBe(null);
|
|
});
|
|
|
|
it('triggers and previews a drag (touch)', () => {
|
|
dispatchTouchEvent(cells[3], 'touchstart');
|
|
fixture.detectChanges();
|
|
expect(fixture.componentInstance.drag).not.toBe(null);
|
|
|
|
// Expand to earlier.
|
|
const rect = cells[2].getBoundingClientRect();
|
|
|
|
dispatchTouchEvent(cells[2], 'touchmove', rect.left, rect.top, rect.left, rect.top);
|
|
fixture.detectChanges();
|
|
expect(cells[2].classList).toContain(previewStartClass);
|
|
expect(cells[3].classList).toContain(inPreviewClass);
|
|
expect(cells[4].classList).toContain(inPreviewClass);
|
|
expect(cells[5].classList).toContain(previewEndClass);
|
|
|
|
// End drag.
|
|
dispatchTouchEvent(cells[2], 'touchend', rect.left, rect.top, rect.left, rect.top);
|
|
expect(fixture.componentInstance.drag).toBe(null);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
@Component({
|
|
template: `
|
|
<table mat-calendar-body
|
|
[label]="label"
|
|
[rows]="rows"
|
|
[todayValue]="todayValue"
|
|
[startValue]="selectedValue"
|
|
[endValue]="selectedValue"
|
|
[labelMinRequiredCells]="labelMinRequiredCells"
|
|
[numCols]="numCols"
|
|
[activeCell]="10"
|
|
(selectedValueChange)="onSelect($event)">
|
|
</table>`,
|
|
standalone: true,
|
|
imports: [MatCalendarBody],
|
|
})
|
|
class StandardCalendarBody {
|
|
label = 'Jan 2017';
|
|
rows = createCalendarCells(2);
|
|
todayValue = 3;
|
|
selectedValue = 4;
|
|
labelMinRequiredCells = 3;
|
|
numCols = 7;
|
|
|
|
onSelect(event: MatCalendarUserEvent<number>) {
|
|
this.selectedValue = event.value;
|
|
}
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<table mat-calendar-body
|
|
[isRange]="true"
|
|
[rows]="rows"
|
|
[startValue]="startValue"
|
|
[endValue]="endValue"
|
|
[comparisonStart]="comparisonStart"
|
|
[comparisonEnd]="comparisonEnd"
|
|
[previewStart]="previewStart"
|
|
[previewEnd]="previewEnd"
|
|
(selectedValueChange)="onSelect($event)"
|
|
(previewChange)="previewChanged($event)"
|
|
(dragStarted)="dragStarted($event)"
|
|
(dragEnded)="dragEnded($event)"
|
|
>
|
|
</table>`,
|
|
standalone: true,
|
|
imports: [MatCalendarBody],
|
|
})
|
|
class RangeCalendarBody {
|
|
rows = createCalendarCells(4);
|
|
startValue: number | null;
|
|
endValue: number | null;
|
|
comparisonStart: number | null;
|
|
comparisonEnd: number | null;
|
|
previewStart: number | null;
|
|
previewEnd: number | null;
|
|
drag: MatCalendarUserEvent<unknown> | null = null;
|
|
|
|
onSelect(event: MatCalendarUserEvent<number>) {
|
|
const value = event.value;
|
|
if (!this.startValue) {
|
|
this.startValue = value;
|
|
} else if (!this.endValue) {
|
|
this.endValue = value;
|
|
} else {
|
|
this.startValue = value;
|
|
this.endValue = null;
|
|
}
|
|
}
|
|
|
|
previewChanged(event: MatCalendarUserEvent<MatCalendarCell<Date> | null>) {
|
|
this.previewStart = this.startValue;
|
|
this.previewEnd = event.value?.compareValue || null;
|
|
|
|
if (this.drag) {
|
|
// For sake of testing, hardcode a preview for drags.
|
|
this.previewStart = this.startValue! - 1;
|
|
this.previewEnd = this.endValue;
|
|
}
|
|
}
|
|
|
|
dragStarted(event: MatCalendarUserEvent<unknown>) {
|
|
this.drag = event;
|
|
}
|
|
|
|
dragEnded(event: MatCalendarUserEvent<unknown>) {
|
|
this.drag = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a 2d array of days, split into weeks.
|
|
* @param weeks Number of weeks that should be generated.
|
|
*/
|
|
function createCalendarCells(weeks: number): MatCalendarCell[][] {
|
|
const rows: number[][] = [];
|
|
let dayCounter = 1;
|
|
|
|
for (let i = 0; i < weeks; i++) {
|
|
const row = [];
|
|
|
|
while (row.length < 7) {
|
|
row.push(dayCounter++);
|
|
}
|
|
|
|
rows.push(row);
|
|
}
|
|
|
|
return rows.map(row =>
|
|
row.map(cell => {
|
|
return new MatCalendarCell(
|
|
cell,
|
|
`${cell}`,
|
|
`${cell}-label`,
|
|
true,
|
|
cell % 2 === 0 ? 'even' : undefined,
|
|
cell,
|
|
cell,
|
|
);
|
|
}),
|
|
);
|
|
}
|