From af742ee9a0d7c81d852869f188323a2fd20d302d Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 14 Jun 2024 09:53:59 -0700 Subject: [PATCH] test: Convert some material tests to zoneless (#29253) (cherry picked from commit ba5a4f9bc56e2f2db09cd51f5bd93626d7afadc0) --- src/material/core/ripple/ripple.spec.ts | 20 +++-- .../core/testing/option-harness.spec.ts | 13 +-- src/material/datepicker/calendar-body.spec.ts | 46 ++++++++--- src/material/datepicker/calendar.spec.ts | 23 ++++-- .../datepicker/date-range-input-parts.ts | 43 +++++----- .../datepicker/date-range-input.spec.ts | 72 ++++++++++------- src/material/datepicker/date-range-input.ts | 50 +++++++----- .../datepicker/datepicker-actions.spec.ts | 20 ++--- src/material/datepicker/datepicker-base.ts | 6 ++ src/material/datepicker/datepicker-input.ts | 31 +++++-- src/material/datepicker/datepicker.spec.ts | 80 ++++++++++++++----- src/material/datepicker/month-view.spec.ts | 39 ++++++--- .../datepicker/multi-year-view.spec.ts | 20 +++-- .../testing/calendar-harness.spec.ts | 21 +++-- .../testing/date-range-input-harness.spec.ts | 23 +++--- .../testing/datepicker-input-harness.spec.ts | 23 +++--- .../testing/datepicker-toggle-harness.spec.ts | 14 ++-- src/material/datepicker/year-view.spec.ts | 24 ++++-- tools/public_api_guard/material/datepicker.md | 4 + 19 files changed, 368 insertions(+), 204 deletions(-) diff --git a/src/material/core/ripple/ripple.spec.ts b/src/material/core/ripple/ripple.spec.ts index d29899bff0f3..2f910349e81a 100644 --- a/src/material/core/ripple/ripple.spec.ts +++ b/src/material/core/ripple/ripple.spec.ts @@ -7,8 +7,8 @@ import { dispatchMouseEvent, dispatchTouchEvent, } from '@angular/cdk/testing/private'; -import {Component, ViewChild, ViewEncapsulation, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, inject, TestBed} from '@angular/core/testing'; +import {Component, ViewChild, ViewEncapsulation} from '@angular/core'; +import {ComponentFixture, TestBed, inject} from '@angular/core/testing'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import { MAT_RIPPLE_GLOBAL_OPTIONS, @@ -35,12 +35,6 @@ describe('MatRipple', () => { dispatchFakeEvent(rippleTarget.querySelector('.mat-ripple-element')!, 'transitionend'); } - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - beforeEach(() => { TestBed.configureTestingModule({ imports: [ @@ -260,6 +254,7 @@ describe('MatRipple', () => { let radius = Math.sqrt(TARGET_HEIGHT * TARGET_HEIGHT + TARGET_WIDTH * TARGET_WIDTH) / 2; rippleDirective.centered = true; + fixture.changeDetectorRef.markForCheck(); rippleDirective.launch(0, 0); let rippleElement = rippleTarget.querySelector('.mat-ripple-element') as HTMLElement; @@ -282,6 +277,7 @@ describe('MatRipple', () => { rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple'); fixture.componentInstance.isDestroyed = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(rippleTarget, 'mousedown'); @@ -648,6 +644,7 @@ describe('MatRipple', () => { const backgroundColor = 'rgba(12, 34, 56, 0.8)'; controller.color = backgroundColor; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(rippleTarget, 'mousedown'); @@ -659,6 +656,7 @@ describe('MatRipple', () => { it('does not respond to events when disabled input is set', () => { controller.disabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(rippleTarget, 'mousedown'); @@ -667,6 +665,7 @@ describe('MatRipple', () => { expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); controller.disabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(rippleTarget, 'mousedown'); @@ -684,6 +683,7 @@ describe('MatRipple', () => { spyOn(controller.ripple, 'fadeOutAllNonPersistent').and.callThrough(); controller.disabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(controller.ripple.fadeOutAllNonPersistent).toHaveBeenCalled(); @@ -704,6 +704,7 @@ describe('MatRipple', () => { // Set the trigger element, and now events should create ripples. controller.trigger = alternateTrigger; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(alternateTrigger, 'mousedown'); @@ -714,6 +715,7 @@ describe('MatRipple', () => { it('expands ripple from center if centered input is set', () => { controller.centered = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let elementRect = rippleTarget.getBoundingClientRect(); @@ -741,6 +743,7 @@ describe('MatRipple', () => { let customRadius = 42; controller.radius = customRadius; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let elementRect = rippleTarget.getBoundingClientRect(); @@ -762,6 +765,7 @@ describe('MatRipple', () => { it('should be able to specify animation config through binding', () => { controller.animationConfig = {enterDuration: 120, exitDuration: 150}; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(rippleTarget, 'mousedown'); diff --git a/src/material/core/testing/option-harness.spec.ts b/src/material/core/testing/option-harness.spec.ts index b15e319efef4..8e8bbd5db1c2 100644 --- a/src/material/core/testing/option-harness.spec.ts +++ b/src/material/core/testing/option-harness.spec.ts @@ -1,17 +1,11 @@ -import { - Component, - ViewChildren, - QueryList, - signal, - provideZoneChangeDetection, -} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader, parallel} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {Component, QueryList, ViewChildren, signal} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import { + MAT_OPTION_PARENT_COMPONENT, MatOption, MatOptionModule, - MAT_OPTION_PARENT_COMPONENT, MatOptionParentComponent, } from '@angular/material/core'; import {MatOptionHarness} from './option-harness'; @@ -23,7 +17,6 @@ describe('MatOptionHarness', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [MatOptionModule, OptionHarnessTest], - providers: [provideZoneChangeDetection()], }).compileComponents(); fixture = TestBed.createComponent(OptionHarnessTest); diff --git a/src/material/datepicker/calendar-body.spec.ts b/src/material/datepicker/calendar-body.spec.ts index 660bd0b66a1b..b9a5e7d5d0ef 100644 --- a/src/material/datepicker/calendar-body.spec.ts +++ b/src/material/datepicker/calendar-body.spec.ts @@ -1,19 +1,14 @@ -import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {MatCalendarBody, MatCalendarCell, MatCalendarUserEvent} from './calendar-body'; -import {By} from '@angular/platform-browser'; import { - dispatchMouseEvent, 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(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [MatCalendarBody, StandardCalendarBody, RangeCalendarBody], @@ -77,6 +72,7 @@ describe('MatCalendarBody', () => { 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')!; @@ -85,6 +81,7 @@ describe('MatCalendarBody', () => { 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( @@ -112,6 +109,7 @@ describe('MatCalendarBody', () => { 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(); @@ -179,6 +177,7 @@ describe('MatCalendarBody', () => { it('should render a range', () => { testComponent.startValue = 1; testComponent.endValue = 5; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells[0].classList).toContain(startClass); @@ -191,6 +190,7 @@ describe('MatCalendarBody', () => { it('should render a comparison range', () => { testComponent.comparisonStart = 1; testComponent.comparisonEnd = 5; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells[0].classList).toContain(comparisonStartClass); @@ -203,6 +203,7 @@ describe('MatCalendarBody', () => { 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); @@ -229,6 +230,7 @@ describe('MatCalendarBody', () => { testComponent.endValue = 5; testComponent.comparisonStart = 5; testComponent.comparisonEnd = 10; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells[4].classList).toContain(bridgeStart); @@ -240,6 +242,7 @@ describe('MatCalendarBody', () => { testComponent.endValue = null; testComponent.comparisonStart = 5; testComponent.comparisonEnd = 10; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells.some(cell => cell.classList.contains(bridgeStart))).toBe(false); @@ -253,6 +256,7 @@ describe('MatCalendarBody', () => { testComponent.comparisonEnd = 5; testComponent.startValue = 5; testComponent.endValue = 10; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells[4].classList).toContain(bridgeEnd); @@ -264,6 +268,7 @@ describe('MatCalendarBody', () => { testComponent.comparisonEnd = 5; testComponent.startValue = 5; testComponent.endValue = null; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells.some(cell => cell.classList.contains(bridgeEnd))).toBe(false); @@ -274,6 +279,7 @@ describe('MatCalendarBody', () => { testComponent.comparisonEnd = 5; testComponent.startValue = 2; testComponent.endValue = 4; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells[0].classList).toContain(comparisonStartClass); @@ -295,6 +301,7 @@ describe('MatCalendarBody', () => { testComponent.endValue = 5; testComponent.comparisonStart = 2; testComponent.comparisonEnd = 4; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(cells[0].classList).toContain(startClass); @@ -314,6 +321,7 @@ describe('MatCalendarBody', () => { 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); @@ -324,6 +332,7 @@ describe('MatCalendarBody', () => { 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); @@ -334,6 +343,8 @@ describe('MatCalendarBody', () => { 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); @@ -344,6 +355,7 @@ describe('MatCalendarBody', () => { 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); @@ -354,6 +366,7 @@ describe('MatCalendarBody', () => { 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); @@ -364,6 +377,7 @@ describe('MatCalendarBody', () => { 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); @@ -374,6 +388,7 @@ describe('MatCalendarBody', () => { 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); @@ -384,6 +399,7 @@ describe('MatCalendarBody', () => { 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); @@ -394,6 +410,7 @@ describe('MatCalendarBody', () => { 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); @@ -404,6 +421,7 @@ describe('MatCalendarBody', () => { 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); @@ -414,6 +432,7 @@ describe('MatCalendarBody', () => { 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); @@ -424,6 +443,7 @@ describe('MatCalendarBody', () => { 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); @@ -433,6 +453,7 @@ describe('MatCalendarBody', () => { 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); @@ -442,6 +463,7 @@ describe('MatCalendarBody', () => { 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); @@ -529,6 +551,7 @@ describe('MatCalendarBody', () => { 'while hovering', () => { fixture.componentInstance.startValue = -1; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(cells[2], 'mouseenter'); @@ -546,6 +569,7 @@ describe('MatCalendarBody', () => { 'while moving focus', () => { fixture.componentInstance.startValue = -1; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchMouseEvent(cells[2], 'focus'); @@ -618,6 +642,7 @@ describe('MatCalendarBody', () => { 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 = @@ -645,6 +670,7 @@ describe('MatCalendarBody', () => { // Pre-select a range to drag. fixture.componentInstance.startValue = 4; fixture.componentInstance.endValue = 6; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); }); diff --git a/src/material/datepicker/calendar.spec.ts b/src/material/datepicker/calendar.spec.ts index a428af6e83c2..af306229a7bf 100644 --- a/src/material/datepicker/calendar.spec.ts +++ b/src/material/datepicker/calendar.spec.ts @@ -5,21 +5,16 @@ import { dispatchKeyboardEvent, dispatchMouseEvent, } from '@angular/cdk/testing/private'; -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {waitForAsync, ComponentFixture, inject, TestBed} from '@angular/core/testing'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed, inject, waitForAsync} from '@angular/core/testing'; import {DateAdapter, MatNativeDateModule} from '@angular/material/core'; -import {DEC, FEB, JAN, JUL, NOV} from '../testing'; import {By} from '@angular/platform-browser'; +import {DEC, FEB, JAN, JUL, NOV} from '../testing'; import {MatCalendar} from './calendar'; import {MatDatepickerIntl} from './datepicker-intl'; import {MatDatepickerModule} from './datepicker-module'; describe('MatCalendar', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [MatNativeDateModule, MatDatepickerModule], @@ -353,6 +348,7 @@ describe('MatCalendar', () => { it('should clamp startAt value below min date', () => { testComponent.startAt = new Date(2000, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.activeDate).toEqual(new Date(2016, JAN, 1)); @@ -360,6 +356,7 @@ describe('MatCalendar', () => { it('should clamp startAt value above max date', () => { testComponent.startAt = new Date(2020, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.activeDate).toEqual(new Date(2018, JAN, 1)); @@ -367,6 +364,7 @@ describe('MatCalendar', () => { it('should not go back past min date', () => { testComponent.startAt = new Date(2016, FEB, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let prevButton = calendarElement.querySelector( @@ -390,6 +388,7 @@ describe('MatCalendar', () => { it('should not go forward past max date', () => { testComponent.startAt = new Date(2017, DEC, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let nextButton = calendarElement.querySelector( @@ -416,6 +415,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.monthView, '_init').and.callThrough(); testComponent.minDate = new Date(2017, NOV, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.monthView._init).toHaveBeenCalled(); @@ -426,6 +426,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.monthView, '_init').and.callThrough(); testComponent.minDate = new Date(2016, JAN, 1, 0, 0, 0, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.monthView._init).not.toHaveBeenCalled(); @@ -436,6 +437,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.monthView, '_init').and.callThrough(); testComponent.maxDate = new Date(2017, DEC, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.monthView._init).toHaveBeenCalled(); @@ -455,6 +457,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.yearView, '_init').and.callThrough(); testComponent.minDate = new Date(2017, NOV, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.yearView._init).toHaveBeenCalled(); @@ -474,6 +477,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.yearView, '_init').and.callThrough(); testComponent.maxDate = new Date(2017, DEC, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.yearView._init).toHaveBeenCalled(); @@ -493,6 +497,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.yearView, '_init').and.callThrough(); testComponent.maxDate = new Date(2018, JAN, 1, 0, 0, 1, 0); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.yearView._init).not.toHaveBeenCalled(); @@ -509,6 +514,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.multiYearView, '_init').and.callThrough(); testComponent.minDate = new Date(2017, NOV, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.multiYearView._init).toHaveBeenCalled(); @@ -525,6 +531,7 @@ describe('MatCalendar', () => { spyOn(calendarInstance.multiYearView, '_init').and.callThrough(); testComponent.maxDate = new Date(2017, DEC, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.multiYearView._init).toHaveBeenCalled(); diff --git a/src/material/datepicker/date-range-input-parts.ts b/src/material/datepicker/date-range-input-parts.ts index fbb278676783..89aea5cb2a00 100644 --- a/src/material/datepicker/date-range-input-parts.ts +++ b/src/material/datepicker/date-range-input-parts.ts @@ -6,41 +6,42 @@ * found in the LICENSE file at https://angular.io/license */ +import {Directionality} from '@angular/cdk/bidi'; +import {BACKSPACE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; import { Directive, + DoCheck, ElementRef, - Optional, - inject, - InjectionToken, Inject, - OnInit, + InjectionToken, Injector, - DoCheck, Input, + OnInit, + Optional, + Signal, + inject, } from '@angular/core'; import { - NG_VALUE_ACCESSOR, - NG_VALIDATORS, - NgForm, + AbstractControl, FormGroupDirective, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, NgControl, + NgForm, + ValidationErrors, ValidatorFn, Validators, - AbstractControl, - ValidationErrors, } from '@angular/forms'; import { - MAT_DATE_FORMATS, DateAdapter, - MatDateFormats, ErrorStateMatcher, + MAT_DATE_FORMATS, + MatDateFormats, _ErrorStateTracker, } from '@angular/material/core'; -import {Directionality} from '@angular/cdk/bidi'; -import {BACKSPACE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; -import {MatDatepickerInputBase, DateFilterFn} from './datepicker-input-base'; -import {DateRange, DateSelectionModelChange} from './date-selection-model'; import {_computeAriaAccessibleName} from './aria-accessible-name'; +import {DateRange, DateSelectionModelChange} from './date-selection-model'; +import {DateFilterFn, MatDatepickerInputBase} from './datepicker-input-base'; /** Parent component that should be wrapped around `MatStartDate` and `MatEndDate`. */ export interface MatDateRangeInputParent { @@ -52,6 +53,8 @@ export interface MatDateRangeInputParent { opened: boolean; id: string; }; + // @breaking-change 20.0.0 property to become required. + _ariaOwns?: Signal; _startInput: MatDateRangeInputPartBase; _endInput: MatDateRangeInputPartBase; _groupDisabled: boolean; @@ -235,7 +238,9 @@ abstract class MatDateRangeInputPartBase '(change)': '_onChange()', '(keydown)': '_onKeydown($event)', '[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null', - '[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null', + '[attr.aria-owns]': `_rangeInput._ariaOwns + ? _rangeInput._ariaOwns() + : (_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null`, '[attr.min]': '_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()) : null', '[attr.max]': '_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()) : null', '(blur)': '_onBlur()', @@ -348,7 +353,9 @@ export class MatStartDate extends MatDateRangeInputPartBase { '(change)': '_onChange()', '(keydown)': '_onKeydown($event)', '[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null', - '[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null', + '[attr.aria-owns]': `_rangeInput._ariaOwns + ? _rangeInput._ariaOwns() + : (_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null`, '[attr.min]': '_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()) : null', '[attr.max]': '_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()) : null', '(blur)': '_onBlur()', diff --git a/src/material/datepicker/date-range-input.spec.ts b/src/material/datepicker/date-range-input.spec.ts index b83f217c95d9..b56002e368cf 100644 --- a/src/material/datepicker/date-range-input.spec.ts +++ b/src/material/datepicker/date-range-input.spec.ts @@ -1,45 +1,31 @@ +import {FocusMonitor} from '@angular/cdk/a11y'; +import {Directionality} from '@angular/cdk/bidi'; +import {BACKSPACE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; +import {OverlayContainer} from '@angular/cdk/overlay'; +import {dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing/private'; +import {Component, Directive, ElementRef, Provider, Type, ViewChild} from '@angular/core'; +import {ComponentFixture, TestBed, fakeAsync, flush, inject, tick} from '@angular/core/testing'; import { - Type, - Component, - ViewChild, - ElementRef, - Directive, - Provider, - provideZoneChangeDetection, -} from '@angular/core'; -import {ComponentFixture, TestBed, inject, fakeAsync, tick, flush} from '@angular/core/testing'; -import { - FormsModule, - ReactiveFormsModule, - FormGroup, FormControl, + FormGroup, + FormsModule, NG_VALIDATORS, - Validator, NgModel, + ReactiveFormsModule, + Validator, Validators, } from '@angular/forms'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {Directionality} from '@angular/cdk/bidi'; -import {OverlayContainer} from '@angular/cdk/overlay'; import {ErrorStateMatcher, MatNativeDateModule} from '@angular/material/core'; -import {MatDatepickerModule} from './datepicker-module'; import {MatFormField, MatFormFieldModule, MatLabel} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; -import {dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing/private'; -import {FocusMonitor} from '@angular/cdk/a11y'; -import {BACKSPACE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {Subscription} from 'rxjs'; import {MatDateRangeInput} from './date-range-input'; +import {MatEndDate, MatStartDate} from './date-range-input-parts'; import {MatDateRangePicker} from './date-range-picker'; -import {MatStartDate, MatEndDate} from './date-range-input-parts'; -import {Subscription} from 'rxjs'; +import {MatDatepickerModule} from './datepicker-module'; describe('MatDateRangeInput', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - function createComponent(component: Type, providers: Provider[] = []): ComponentFixture { TestBed.configureTestingModule({ imports: [ @@ -99,6 +85,7 @@ describe('MatDateRangeInput', () => { expect(separator.textContent).toBe('–'); fixture.componentInstance.separator = '/'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(separator.textContent).toBe('/'); @@ -150,6 +137,7 @@ describe('MatDateRangeInput', () => { expect(end.nativeElement.disabled).toBe(false); fixture.componentInstance.rangeDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(start.nativeElement.disabled).toBe(true); expect(end.nativeElement.disabled).toBe(true); @@ -295,6 +283,7 @@ describe('MatDateRangeInput', () => { it('should pass the minimum date from the range input to the inner inputs', () => { const fixture = createComponent(StandardRangePicker); fixture.componentInstance.minDate = new Date(2020, 3, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const {start, end} = fixture.componentInstance.range.controls; @@ -313,6 +302,7 @@ describe('MatDateRangeInput', () => { it('should pass the maximum date from the range input to the inner inputs', () => { const fixture = createComponent(StandardRangePicker); fixture.componentInstance.maxDate = new Date(2020, 1, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const {start, end} = fixture.componentInstance.range.controls; @@ -331,6 +321,7 @@ describe('MatDateRangeInput', () => { it('should pass the date filter function from the range input to the inner inputs', () => { const fixture = createComponent(StandardRangePicker); fixture.componentInstance.dateFilter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const {start, end} = fixture.componentInstance.range.controls; @@ -361,10 +352,12 @@ describe('MatDateRangeInput', () => { subscription.add(end.valueChanges.subscribe(spy)); fixture.componentInstance.dateFilter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(2); fixture.componentInstance.dateFilter = () => true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(4); @@ -389,10 +382,12 @@ describe('MatDateRangeInput', () => { subscription.add(end.valueChanges.subscribe(spy)); fixture.componentInstance.dateFilter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(2); fixture.componentInstance.dateFilter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(2); @@ -450,6 +445,7 @@ describe('MatDateRangeInput', () => { it('should revalidate if a validation field changes', () => { const fixture = createComponent(StandardRangePicker); fixture.componentInstance.minDate = new Date(2020, 3, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const {start, end} = fixture.componentInstance.range.controls; @@ -462,6 +458,7 @@ describe('MatDateRangeInput', () => { expect(end.errors?.['matDatepickerMin']).toBeTruthy(); fixture.componentInstance.minDate = new Date(2019, 3, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(start.errors?.['matDatepickerMin']).toBeFalsy(); @@ -471,6 +468,7 @@ describe('MatDateRangeInput', () => { it('should set the formatted date value as the input value', () => { const fixture = createComponent(StandardRangePicker); fixture.componentInstance.minDate = new Date(2020, 3, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const date = new Date(2020, 1, 2); const {start, end, range} = fixture.componentInstance; @@ -504,6 +502,7 @@ describe('MatDateRangeInput', () => { const fixture = createComponent(StandardRangePicker); fixture.componentInstance.minDate = new Date(2020, 1, 2); fixture.componentInstance.maxDate = new Date(2020, 1, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const {start, end} = fixture.componentInstance; @@ -615,6 +614,7 @@ describe('MatDateRangeInput', () => { 2, ); fixture.componentInstance.comparisonEnd = new Date(2020, 1, 5); + fixture.changeDetectorRef.markForCheck(); inject([OverlayContainer], (overlayContainer: OverlayContainer) => { overlayContainerElement = overlayContainer.getContainerElement(); })(); @@ -643,6 +643,7 @@ describe('MatDateRangeInput', () => { const fixture = createComponent(RangePickerNgModel); fixture.componentInstance.start = start; fixture.componentInstance.end = end; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -655,6 +656,7 @@ describe('MatDateRangeInput', () => { const assignAndAssert = (start: Date, end: Date) => { fixture.componentInstance.start = start; fixture.componentInstance.end = end; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -685,6 +687,7 @@ describe('MatDateRangeInput', () => { const fixture = createComponent(RangePickerNgModel); fixture.componentInstance.start = new Date(2020, 1, 2); fixture.componentInstance.end = new Date(2020, 2, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); const {startModel, endModel} = fixture.componentInstance; @@ -699,6 +702,7 @@ describe('MatDateRangeInput', () => { const fixture = createComponent(RangePickerNgModel); fixture.componentInstance.start = new Date(2020, 1, 2); fixture.componentInstance.end = new Date(2020, 2, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); const {startModel, endModel, startInput, endInput} = fixture.componentInstance; @@ -970,10 +974,12 @@ describe('MatDateRangeInput', () => { validator.validate.calls.reset(); fixture.componentInstance.min = minDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); fixture.componentInstance.min = new Date(minDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); @@ -987,10 +993,12 @@ describe('MatDateRangeInput', () => { validator.validate.calls.reset(); fixture.componentInstance.max = maxDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); fixture.componentInstance.max = new Date(maxDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); @@ -1005,10 +1013,12 @@ describe('MatDateRangeInput', () => { const subscription = fixture.componentInstance.rangeInput.stateChanges.subscribe(spy); fixture.componentInstance.minDate = minDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); fixture.componentInstance.minDate = new Date(minDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); @@ -1024,10 +1034,12 @@ describe('MatDateRangeInput', () => { const subscription = fixture.componentInstance.rangeInput.stateChanges.subscribe(spy); fixture.componentInstance.maxDate = maxDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); fixture.componentInstance.maxDate = new Date(maxDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); @@ -1118,6 +1130,7 @@ describe('MatDateRangeInput', () => { }, Validators.required, ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.rangeInput.required).toBe(true); @@ -1129,6 +1142,7 @@ describe('MatDateRangeInput', () => { start: new FormControl(null, Validators.required), end: new FormControl(null), }); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.rangeInput.required).toBe(true); diff --git a/src/material/datepicker/date-range-input.ts b/src/material/datepicker/date-range-input.ts index e5094061a4de..a48fdc877739 100644 --- a/src/material/datepicker/date-range-input.ts +++ b/src/material/datepicker/date-range-input.ts @@ -6,39 +6,40 @@ * found in the LICENSE file at https://angular.io/license */ +import {CdkMonitorFocus, FocusOrigin} from '@angular/cdk/a11y'; import { - Component, - ChangeDetectionStrategy, - ViewEncapsulation, - Input, - Optional, - OnDestroy, - ContentChild, AfterContentInit, + ChangeDetectionStrategy, ChangeDetectorRef, - Self, + Component, + ContentChild, ElementRef, Inject, + Input, OnChanges, + OnDestroy, + Optional, + Self, SimpleChanges, + ViewEncapsulation, booleanAttribute, + signal, } from '@angular/core'; -import {MatFormFieldControl, MAT_FORM_FIELD} from '@angular/material/form-field'; -import {ThemePalette, DateAdapter} from '@angular/material/core'; -import {NgControl, ControlContainer, Validators} from '@angular/forms'; -import {Subject, merge, Subscription} from 'rxjs'; -import {FocusOrigin, CdkMonitorFocus} from '@angular/cdk/a11y'; +import {ControlContainer, NgControl, Validators} from '@angular/forms'; +import {DateAdapter, ThemePalette} from '@angular/material/core'; +import {MAT_FORM_FIELD, MatFormFieldControl} from '@angular/material/form-field'; +import {Subject, Subscription, merge} from 'rxjs'; import { - MatStartDate, - MatEndDate, - MatDateRangeInputParent, MAT_DATE_RANGE_INPUT_PARENT, + MatDateRangeInputParent, + MatEndDate, + MatStartDate, } from './date-range-input-parts'; -import {MatDatepickerControl, MatDatepickerPanel} from './datepicker-base'; -import {createMissingDateImplError} from './datepicker-errors'; -import {DateFilterFn, dateInputsHaveChanged, _MatFormFieldPartial} from './datepicker-input-base'; import {MatDateRangePickerInput} from './date-range-picker'; import {DateRange, MatDateSelectionModel} from './date-selection-model'; +import {MatDatepickerControl, MatDatepickerPanel} from './datepicker-base'; +import {createMissingDateImplError} from './datepicker-errors'; +import {DateFilterFn, _MatFormFieldPartial, dateInputsHaveChanged} from './datepicker-input-base'; let nextUniqueId = 0; @@ -79,6 +80,7 @@ export class MatDateRangeInput OnDestroy { private _closedSubscription = Subscription.EMPTY; + private _openedSubscription = Subscription.EMPTY; /** Current value of the range input. */ get value() { @@ -120,15 +122,24 @@ export class MatDateRangeInput this._model = rangePicker.registerInput(this); this._rangePicker = rangePicker; this._closedSubscription.unsubscribe(); + this._openedSubscription.unsubscribe(); + this._ariaOwns.set(this.rangePicker.opened ? rangePicker.id : null); this._closedSubscription = rangePicker.closedStream.subscribe(() => { this._startInput?._onTouched(); this._endInput?._onTouched(); + this._ariaOwns.set(null); + }); + this._openedSubscription = rangePicker.openedStream.subscribe(() => { + this._ariaOwns.set(rangePicker.id); }); this._registerModel(this._model!); } } private _rangePicker: MatDatepickerPanel, DateRange, D>; + /** The id of the panel owned by this input. */ + _ariaOwns = signal(null); + /** Whether the input is required. */ @Input({transform: booleanAttribute}) get required(): boolean { @@ -339,6 +350,7 @@ export class MatDateRangeInput ngOnDestroy() { this._closedSubscription.unsubscribe(); + this._openedSubscription.unsubscribe(); this.stateChanges.complete(); } diff --git a/src/material/datepicker/datepicker-actions.spec.ts b/src/material/datepicker/datepicker-actions.spec.ts index 24ec0346f982..4485b9641efa 100644 --- a/src/material/datepicker/datepicker-actions.spec.ts +++ b/src/material/datepicker/datepicker-actions.spec.ts @@ -1,20 +1,15 @@ -import {Component, ElementRef, Type, ViewChild, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed, flush, fakeAsync, tick} from '@angular/core/testing'; +import {CommonModule} from '@angular/common'; +import {Component, ElementRef, Type, ViewChild} from '@angular/core'; +import {ComponentFixture, TestBed, fakeAsync, flush, tick} from '@angular/core/testing'; import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MatNativeDateModule} from '@angular/material/core'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; -import {CommonModule} from '@angular/common'; -import {MatDatepickerModule} from './datepicker-module'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MatDatepicker} from './datepicker'; +import {MatDatepickerModule} from './datepicker-module'; describe('MatDatepickerActions', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); function createComponent(component: Type): ComponentFixture { TestBed.configureTestingModule({ declarations: [component], @@ -50,6 +45,7 @@ describe('MatDatepickerActions', () => { it('should render the actions inside calendar panel in touch UI mode', fakeAsync(() => { const fixture = createComponent(DatepickerWithActions); fixture.componentInstance.touchUi = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.datepicker.open(); fixture.detectChanges(); @@ -241,6 +237,7 @@ describe('MatDatepickerActions', () => { expect(onDateChange).not.toHaveBeenCalled(); fixture.componentInstance.renderActions = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); datepicker.open(); fixture.detectChanges(); @@ -266,6 +263,7 @@ describe('MatDatepickerActions', () => { it('should be able to toggle the actions while the datepicker is open', fakeAsync(() => { const fixture = createComponent(DatepickerWithActions); fixture.componentInstance.renderActions = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.datepicker.open(); @@ -277,10 +275,12 @@ describe('MatDatepickerActions', () => { expect(content.querySelector('.mat-datepicker-actions')).toBeFalsy(); fixture.componentInstance.renderActions = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(content.querySelector('.mat-datepicker-actions')).toBeTruthy(); fixture.componentInstance.renderActions = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(content.querySelector('.mat-datepicker-actions')).toBeFalsy(); })); diff --git a/src/material/datepicker/datepicker-base.ts b/src/material/datepicker/datepicker-base.ts index 65d488d5ffa8..b22a52399580 100644 --- a/src/material/datepicker/datepicker-base.ts +++ b/src/material/datepicker/datepicker-base.ts @@ -524,6 +524,8 @@ export abstract class MatDatepickerBase< private _injector = inject(Injector); + private readonly _changeDetectorRef = inject(ChangeDetectorRef); + constructor( private _overlay: Overlay, /** @@ -542,6 +544,10 @@ export abstract class MatDatepickerBase< } this._scrollStrategy = scrollStrategy; + + this._model.selectionChanged.subscribe(() => { + this._changeDetectorRef.markForCheck(); + }); } ngOnChanges(changes: SimpleChanges) { diff --git a/src/material/datepicker/datepicker-input.ts b/src/material/datepicker/datepicker-input.ts index f84990e45d85..849af176df18 100644 --- a/src/material/datepicker/datepicker-input.ts +++ b/src/material/datepicker/datepicker-input.ts @@ -6,15 +6,24 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, forwardRef, Inject, Input, OnDestroy, Optional} from '@angular/core'; +import { + Directive, + ElementRef, + forwardRef, + Inject, + Input, + OnDestroy, + Optional, + signal, +} from '@angular/core'; import {NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidatorFn, Validators} from '@angular/forms'; import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats, ThemePalette} from '@angular/material/core'; import {MAT_FORM_FIELD} from '@angular/material/form-field'; import {MAT_INPUT_VALUE_ACCESSOR} from '@angular/material/input'; import {Subscription} from 'rxjs'; -import {MatDatepickerInputBase, DateFilterFn, _MatFormFieldPartial} from './datepicker-input-base'; -import {MatDatepickerControl, MatDatepickerPanel} from './datepicker-base'; import {DateSelectionModelChange} from './date-selection-model'; +import {MatDatepickerControl, MatDatepickerPanel} from './datepicker-base'; +import {_MatFormFieldPartial, DateFilterFn, MatDatepickerInputBase} from './datepicker-input-base'; /** @docs-private */ export const MAT_DATEPICKER_VALUE_ACCESSOR: any = { @@ -41,7 +50,7 @@ export const MAT_DATEPICKER_VALIDATORS: any = { host: { 'class': 'mat-datepicker-input', '[attr.aria-haspopup]': '_datepicker ? "dialog" : null', - '[attr.aria-owns]': '(_datepicker?.opened && _datepicker.id) || null', + '[attr.aria-owns]': '_ariaOwns()', '[attr.min]': 'min ? _dateAdapter.toIso8601(min) : null', '[attr.max]': 'max ? _dateAdapter.toIso8601(max) : null', // Used by the test harness to tie this input to its calendar. We can't depend on @@ -61,18 +70,29 @@ export class MatDatepickerInput implements MatDatepickerControl, OnDestroy { private _closedSubscription = Subscription.EMPTY; + private _openedSubscription = Subscription.EMPTY; /** The datepicker that this input is associated with. */ @Input() set matDatepicker(datepicker: MatDatepickerPanel, D | null, D>) { if (datepicker) { this._datepicker = datepicker; - this._closedSubscription = datepicker.closedStream.subscribe(() => this._onTouched()); + this._ariaOwns.set(datepicker.opened ? datepicker.id : null); + this._closedSubscription = datepicker.closedStream.subscribe(() => { + this._onTouched(); + this._ariaOwns.set(null); + }); + this._openedSubscription = datepicker.openedStream.subscribe(() => { + this._ariaOwns.set(datepicker.id); + }); this._registerModel(datepicker.registerInput(this)); } } _datepicker: MatDatepickerPanel, D | null, D>; + /** The id of the panel owned by this input. */ + protected _ariaOwns = signal(null); + /** The minimum valid date. */ @Input() get min(): D | null { @@ -161,6 +181,7 @@ export class MatDatepickerInput override ngOnDestroy() { super.ngOnDestroy(); this._closedSubscription.unsubscribe(); + this._openedSubscription.unsubscribe(); } /** Opens the associated datepicker. */ diff --git a/src/material/datepicker/datepicker.spec.ts b/src/material/datepicker/datepicker.spec.ts index 3de72d5c0640..b63f518b46c3 100644 --- a/src/material/datepicker/datepicker.spec.ts +++ b/src/material/datepicker/datepicker.spec.ts @@ -20,15 +20,7 @@ import { dispatchMouseEvent, typeInElement, } from '@angular/cdk/testing/private'; -import { - Component, - Directive, - Provider, - Type, - ViewChild, - ViewEncapsulation, - provideZoneChangeDetection, -} from '@angular/core'; +import {Component, Directive, Provider, Type, ViewChild, ViewEncapsulation} from '@angular/core'; import {ComponentFixture, TestBed, fakeAsync, flush, inject, tick} from '@angular/core/testing'; import { FormControl, @@ -59,12 +51,6 @@ import { describe('MatDatepicker', () => { const SUPPORTS_INTL = typeof Intl != 'undefined'; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - // Creates a test component fixture. function createComponent( component: Type, @@ -130,6 +116,7 @@ describe('MatDatepicker', () => { it('touch should open dialog', fakeAsync(() => { testComponent.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(document.querySelector('.mat-datepicker-dialog')).toBeNull(); @@ -144,6 +131,7 @@ describe('MatDatepicker', () => { it('should not be able to open more than one dialog', fakeAsync(() => { testComponent.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(document.querySelectorAll('.mat-datepicker-dialog').length).toBe(0); @@ -167,6 +155,7 @@ describe('MatDatepicker', () => { it('should open datepicker if opened input is set to true', fakeAsync(() => { testComponent.opened = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); flush(); @@ -174,6 +163,7 @@ describe('MatDatepicker', () => { expect(document.querySelector('.mat-datepicker-content')).not.toBeNull(); testComponent.opened = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); @@ -182,6 +172,7 @@ describe('MatDatepicker', () => { it('open in disabled mode should not open the calendar', fakeAsync(() => { testComponent.disabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(document.querySelector('.cdk-overlay-pane')).toBeNull(); @@ -199,6 +190,7 @@ describe('MatDatepicker', () => { it('disabled datepicker input should open the calendar if datepicker is enabled', fakeAsync(() => { testComponent.datepicker.disabled = false; testComponent.datepickerInput.disabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(document.querySelector('.cdk-overlay-pane')).toBeNull(); @@ -304,6 +296,7 @@ describe('MatDatepicker', () => { it('close should close dialog', fakeAsync(() => { testComponent.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.datepicker.open(); @@ -321,6 +314,7 @@ describe('MatDatepicker', () => { it('setting selected via click should update input and close calendar', fakeAsync(() => { testComponent.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.datepicker.open(); @@ -342,6 +336,7 @@ describe('MatDatepicker', () => { it('setting selected via enter press should update input and close calendar', fakeAsync(() => { testComponent.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.datepicker.open(); @@ -456,6 +451,7 @@ describe('MatDatepicker', () => { it('input should aria-owns calendar after opened in touch mode', fakeAsync(() => { testComponent.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let inputEl = fixture.debugElement.query(By.css('input'))!.nativeElement; @@ -476,6 +472,7 @@ describe('MatDatepicker', () => { it('should not throw when given wrong data type', () => { testComponent.date = '1/1/2017' as any; + fixture.changeDetectorRef.markForCheck(); expect(() => fixture.detectChanges()).not.toThrow(); }); @@ -511,9 +508,6 @@ describe('MatDatepicker', () => { it('should reset the datepicker when it is closed externally', fakeAsync(() => { TestBed.resetTestingModule(); - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); const scrolledSubject = new Subject(); @@ -639,6 +633,7 @@ describe('MatDatepicker', () => { it('should show the invisible close button on focus', fakeAsync(() => { testComponent.opened = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); flush(); @@ -657,6 +652,7 @@ describe('MatDatepicker', () => { it('should close the overlay when clicking on the invisible close button', fakeAsync(() => { testComponent.opened = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); flush(); @@ -724,6 +720,7 @@ describe('MatDatepicker', () => { expect(model.selection).toBeNull(); testComponent.assignedDatepicker = testComponent.datepicker; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.assignedDatepicker.select(toSelect); @@ -918,6 +915,7 @@ describe('MatDatepicker', () => { let selected = new Date(2017, JAN, 1); testComponent.selected = selected; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -983,6 +981,7 @@ describe('MatDatepicker', () => { expect(inputEl.classList).toContain('ng-pristine'); testComponent.selected = new Date(2017, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -1154,6 +1153,7 @@ describe('MatDatepicker', () => { }; fixture.componentInstance.formControl = formControl; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); @@ -1222,6 +1222,7 @@ describe('MatDatepicker', () => { it('should be able to change the button `aria-label`', () => { fixture.componentInstance.ariaLabel = 'Toggle the datepicker'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const button = fixture.debugElement.query(By.css('button'))!; @@ -1241,6 +1242,7 @@ describe('MatDatepicker', () => { it('should not open calendar when toggle clicked if datepicker is disabled', () => { testComponent.datepicker.disabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const toggle = fixture.debugElement.query(By.css('button'))!.nativeElement; @@ -1257,6 +1259,7 @@ describe('MatDatepicker', () => { expect(testComponent.datepicker.disabled).toBe(false); testComponent.input.disabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const toggle = fixture.debugElement.query(By.css('button'))!.nativeElement; @@ -1283,6 +1286,7 @@ describe('MatDatepicker', () => { let toggle = fixture.debugElement.query(By.css('button'))!.nativeElement; fixture.componentInstance.touchUI = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); toggle.focus(); @@ -1315,15 +1319,13 @@ describe('MatDatepicker', () => { fixture.destroy(); TestBed.resetTestingModule(); - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); fixture = createComponent(DatepickerWithToggleInShadowDom, [MatNativeDateModule]); fixture.detectChanges(); testComponent = fixture.componentInstance; const toggle = fixture.debugElement.query(By.css('button'))!.nativeElement; fixture.componentInstance.touchUI = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); toggle.focus(); @@ -1345,6 +1347,7 @@ describe('MatDatepicker', () => { fixture.componentInstance.touchUI = false; fixture.componentInstance.restoreFocus = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); toggle.focus(); @@ -1380,6 +1383,7 @@ describe('MatDatepicker', () => { // Important: we're testing the touchUI behavior on particular. fixture.componentInstance.touchUI = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); // Focus the input before opening so that the datepicker restores focus to it on close. @@ -1499,6 +1503,7 @@ describe('MatDatepicker', () => { it('should float the placeholder when an invalid value is entered', () => { testComponent.datepickerInput.value = 'totally-not-a-date' as any; + fixture.changeDetectorRef.markForCheck(); fixture.debugElement.nativeElement.querySelector('input').value = 'totally-not-a-date'; fixture.detectChanges(); @@ -1509,6 +1514,7 @@ describe('MatDatepicker', () => { it('should pass the form field theme color to the overlay', fakeAsync(() => { testComponent.formField.color = 'primary'; + fixture.changeDetectorRef.markForCheck(); testComponent.datepicker.open(); fixture.detectChanges(); tick(); @@ -1523,6 +1529,7 @@ describe('MatDatepicker', () => { flush(); testComponent.formField.color = 'warn'; + fixture.changeDetectorRef.markForCheck(); testComponent.datepicker.open(); contentEl = document.querySelector('.mat-datepicker-content')!; @@ -1537,6 +1544,7 @@ describe('MatDatepicker', () => { it('should prefer the datepicker color over the form field one', fakeAsync(() => { testComponent.datepicker.color = 'accent'; testComponent.formField.color = 'warn'; + fixture.changeDetectorRef.markForCheck(); testComponent.datepicker.open(); fixture.detectChanges(); tick(); @@ -1597,6 +1605,7 @@ describe('MatDatepicker', () => { it('should mark invalid when value is before min', fakeAsync(() => { testComponent.date = new Date(2009, DEC, 31); + fixture.changeDetectorRef.markForCheck(); revalidate(); expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList).toContain( @@ -1606,6 +1615,7 @@ describe('MatDatepicker', () => { it('should mark invalid when value is after max', fakeAsync(() => { testComponent.date = new Date(2020, JAN, 2); + fixture.changeDetectorRef.markForCheck(); revalidate(); expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList).toContain( @@ -1615,6 +1625,7 @@ describe('MatDatepicker', () => { it('should not mark invalid when value equals min', fakeAsync(() => { testComponent.date = testComponent.datepicker._getMinDate(); + fixture.changeDetectorRef.markForCheck(); revalidate(); expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList).not.toContain( @@ -1624,6 +1635,7 @@ describe('MatDatepicker', () => { it('should not mark invalid when value equals max', fakeAsync(() => { testComponent.date = testComponent.datepicker._getMaxDate(); + fixture.changeDetectorRef.markForCheck(); revalidate(); expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList).not.toContain( @@ -1633,6 +1645,7 @@ describe('MatDatepicker', () => { it('should not mark invalid when value is between min and max', fakeAsync(() => { testComponent.date = new Date(2010, JAN, 2); + fixture.changeDetectorRef.markForCheck(); revalidate(); expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList).not.toContain( @@ -1694,10 +1707,12 @@ describe('MatDatepicker', () => { }; testComponent.date = new Date(2020, JAN, 5); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.minDate = new Date(2020, JAN, 3); testComponent.maxDate = new Date(2020, JAN, 7); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.datepicker.open(); @@ -1709,12 +1724,14 @@ describe('MatDatepicker', () => { expect(disabledCellCount).not.toBe(0); testComponent.minDate = new Date(2020, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(getDisabledCells()).not.toBe(disabledCellCount); disabledCellCount = getDisabledCells(); testComponent.maxDate = new Date(2020, JAN, 10); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(getDisabledCells()).not.toBe(disabledCellCount); @@ -1740,6 +1757,7 @@ describe('MatDatepicker', () => { it('should mark input invalid', fakeAsync(() => { testComponent.date = new Date(2017, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -1749,6 +1767,7 @@ describe('MatDatepicker', () => { ); testComponent.date = new Date(2017, JAN, 2); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -1777,6 +1796,7 @@ describe('MatDatepicker', () => { const classList = fixture.debugElement.query(By.css('input'))!.nativeElement.classList; testComponent.date = new Date(2017, JAN, 1); testComponent.filter = () => true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -1784,6 +1804,7 @@ describe('MatDatepicker', () => { expect(classList).not.toContain('ng-invalid'); testComponent.filter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -1795,6 +1816,7 @@ describe('MatDatepicker', () => { const spy = jasmine.createSpy('change spy'); const subscription = fixture.componentInstance.model.valueChanges?.subscribe(spy); testComponent.filter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -1802,6 +1824,7 @@ describe('MatDatepicker', () => { expect(spy).toHaveBeenCalledTimes(1); testComponent.filter = () => false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -2117,6 +2140,7 @@ describe('MatDatepicker', () => { ); fixture.componentInstance.touch = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.datepicker.open(); fixture.detectChanges(); @@ -2253,6 +2277,7 @@ describe('MatDatepicker', () => { it('should be able to customize the calendar position along the X axis', fakeAsync(() => { input.style.top = input.style.left = '200px'; testComponent.xPosition = 'end'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.datepicker.open(); @@ -2270,6 +2295,7 @@ describe('MatDatepicker', () => { it('should be able to customize the calendar position along the Y axis', fakeAsync(() => { input.style.bottom = input.style.left = '100px'; testComponent.yPosition = 'above'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.datepicker.open(); @@ -2310,6 +2336,7 @@ describe('MatDatepicker', () => { const selected = new Date(2017, SEP, 1); testComponent.date = selected; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); fixture.detectChanges(); @@ -2374,10 +2401,12 @@ describe('MatDatepicker', () => { validator.validate.calls.reset(); fixture.componentInstance.min = minDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); fixture.componentInstance.min = new Date(minDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); @@ -2396,10 +2425,12 @@ describe('MatDatepicker', () => { validator.validate.calls.reset(); fixture.componentInstance.max = maxDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); fixture.componentInstance.max = new Date(maxDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(validator.validate).toHaveBeenCalledTimes(1); @@ -2414,10 +2445,12 @@ describe('MatDatepicker', () => { const subscription = fixture.componentInstance.datepickerInput.stateChanges.subscribe(spy); fixture.componentInstance.min = minDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); fixture.componentInstance.min = new Date(minDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); @@ -2433,10 +2466,12 @@ describe('MatDatepicker', () => { const subscription = fixture.componentInstance.datepickerInput.stateChanges.subscribe(spy); fixture.componentInstance.max = maxDate; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); fixture.componentInstance.max = new Date(maxDate); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).toHaveBeenCalledTimes(1); @@ -2462,18 +2497,21 @@ describe('MatDatepicker', () => { it('should accept a single class', () => { testComponent.panelClass = 'foobar'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(testComponent.datepicker.panelClass).toEqual(['foobar']); }); it('should accept multiple classes', () => { testComponent.panelClass = 'foo bar'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(testComponent.datepicker.panelClass).toEqual(['foo', 'bar']); }); it('should work with ngClass', fakeAsync(() => { testComponent.panelClass = ['foo', 'bar']; + fixture.changeDetectorRef.markForCheck(); testComponent.datepicker.open(); fixture.detectChanges(); tick(); diff --git a/src/material/datepicker/month-view.spec.ts b/src/material/datepicker/month-view.spec.ts index 9f97c1193070..12e1d9e38c67 100644 --- a/src/material/datepicker/month-view.spec.ts +++ b/src/material/datepicker/month-view.spec.ts @@ -3,6 +3,7 @@ import { DOWN_ARROW, END, ENTER, + ESCAPE, HOME, LEFT_ARROW, PAGE_DOWN, @@ -10,27 +11,26 @@ import { RIGHT_ARROW, SPACE, UP_ARROW, - ESCAPE, } from '@angular/cdk/keycodes'; import { + createKeyboardEvent, + dispatchEvent, dispatchFakeEvent, dispatchKeyboardEvent, dispatchMouseEvent, - createKeyboardEvent, - dispatchEvent, } from '@angular/cdk/testing/private'; -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {MAT_DATE_FORMATS, MatNativeDateModule} from '@angular/material/core'; -import {DEC, FEB, JAN, MAR, NOV} from '../testing'; import {By} from '@angular/platform-browser'; -import {MatCalendarUserEvent, MatCalendarBody} from './calendar-body'; -import {MatMonthView} from './month-view'; -import {DateRange} from './date-selection-model'; +import {DEC, FEB, JAN, MAR, NOV} from '../testing'; +import {MatCalendarBody, MatCalendarUserEvent} from './calendar-body'; import { - MAT_DATE_RANGE_SELECTION_STRATEGY, 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', () => { @@ -48,7 +48,6 @@ describe('MatMonthView', () => { MonthViewWithDateClass, ], providers: [ - provideZoneChangeDetection(), {provide: Directionality, useFactory: () => (dir = {value: 'ltr'})}, {provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: DefaultMatCalendarRangeStrategy}, ], @@ -88,6 +87,7 @@ describe('MatMonthView', () => { 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'); @@ -114,6 +114,7 @@ describe('MatMonthView', () => { beforeEach(() => { testComponent.selected = initialRange; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); }); @@ -267,6 +268,7 @@ describe('MatMonthView', () => { expect(calendarBodyEl).not.toBeNull(); dir.value = 'ltr'; fixture.componentInstance.date = new Date(2017, JAN, 5); + fixture.changeDetectorRef.markForCheck(); dispatchFakeEvent(calendarBodyEl, 'focus'); fixture.detectChanges(); }); @@ -277,6 +279,7 @@ describe('MatMonthView', () => { 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); @@ -320,6 +323,7 @@ describe('MatMonthView', () => { 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); @@ -335,6 +339,7 @@ describe('MatMonthView', () => { 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); @@ -369,6 +374,7 @@ describe('MatMonthView', () => { 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(); @@ -436,6 +442,7 @@ describe('MatMonthView', () => { 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(); @@ -478,6 +485,7 @@ describe('MatMonthView', () => { () => { 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(); @@ -523,6 +531,7 @@ describe('MatMonthView', () => { 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'); @@ -570,6 +579,7 @@ describe('MatMonthView', () => { 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(); @@ -614,6 +624,7 @@ describe('MatMonthView', () => { new Date(2017, JAN, 10), new Date(2017, JAN, 15), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(getRangeElements().length) @@ -633,6 +644,7 @@ describe('MatMonthView', () => { 'date while selecting a single date', () => { testComponent.selected = new Date(2017, JAN, 10); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.selectedChangeSpy).not.toHaveBeenCalled(); @@ -653,6 +665,7 @@ describe('MatMonthView', () => { () => { const selectedDate = new Date(2017, JAN, 10); testComponent.selected = new DateRange(selectedDate, null); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.selectedChangeSpy).not.toHaveBeenCalled(); @@ -675,6 +688,7 @@ describe('MatMonthView', () => { () => { const date = new Date(2017, JAN, 10); testComponent.selected = date; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.userSelectionSpy).not.toHaveBeenCalled(); @@ -737,6 +751,7 @@ describe('MatMonthView', () => { activeDate.getMonth(), activeDate.getDate(), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); @@ -751,6 +766,7 @@ describe('MatMonthView', () => { activeDate.getMonth(), activeDate.getDate(), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); @@ -799,7 +815,6 @@ describe('MatMonthView', () => { MonthViewWithDateClass, ], providers: [ - provideZoneChangeDetection(), {provide: Directionality, useFactory: () => ({value: 'ltr'})}, {provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: DefaultMatCalendarRangeStrategy}, { diff --git a/src/material/datepicker/multi-year-view.spec.ts b/src/material/datepicker/multi-year-view.spec.ts index 77b5f67ee65f..cafcb2835bca 100644 --- a/src/material/datepicker/multi-year-view.spec.ts +++ b/src/material/datepicker/multi-year-view.spec.ts @@ -10,11 +10,11 @@ import { UP_ARROW, } from '@angular/cdk/keycodes'; import {dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing/private'; -import {Component, ViewChild, provideZoneChangeDetection} from '@angular/core'; -import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Component, ViewChild} from '@angular/core'; +import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {MatNativeDateModule} from '@angular/material/core'; -import {JAN, MAR} from '../testing'; import {By} from '@angular/platform-browser'; +import {JAN, MAR} from '../testing'; import {MatCalendarBody} from './calendar-body'; import {MatMultiYearView, yearsPerPage, yearsPerRow} from './multi-year-view'; @@ -33,10 +33,7 @@ describe('MatMultiYearView', () => { MultiYearViewWithMinMaxDate, MultiYearViewWithDateClass, ], - providers: [ - provideZoneChangeDetection(), - {provide: Directionality, useFactory: () => (dir = {value: 'ltr'})}, - ], + providers: [{provide: Directionality, useFactory: () => (dir = {value: 'ltr'})}], }); TestBed.compileComponents(); @@ -68,6 +65,7 @@ describe('MatMultiYearView', () => { it('does not show selected year if in different range', () => { testComponent.selected = new Date(2040, JAN, 10); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let selectedEl = multiYearViewNativeElement.querySelector('.mat-calendar-body-selected'); @@ -117,6 +115,7 @@ describe('MatMultiYearView', () => { expect(calendarBodyEl).not.toBeNull(); dir.value = 'ltr'; fixture.componentInstance.date = new Date(2017, JAN, 3); + fixture.changeDetectorRef.markForCheck(); dispatchFakeEvent(calendarBodyEl, 'focus'); fixture.detectChanges(); }); @@ -219,6 +218,7 @@ describe('MatMultiYearView', () => { it('should go to the year that is focused', () => { fixture.componentInstance.date = new Date(2017, MAR, 5); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(calendarInstance.date).toEqual(new Date(2017, MAR, 5)); @@ -267,6 +267,7 @@ describe('MatMultiYearView', () => { activeDate.getMonth(), activeDate.getDate(), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); @@ -281,6 +282,7 @@ describe('MatMultiYearView', () => { activeDate.getMonth(), activeDate.getDate(), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); @@ -303,6 +305,7 @@ describe('MatMultiYearView', () => { it('should begin first page with minDate', () => { testComponent.minDate = new Date(2014, JAN, 1); testComponent.maxDate = null; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell'); @@ -326,6 +329,7 @@ describe('MatMultiYearView', () => { it('should end last page with maxDate', () => { testComponent.minDate = null; testComponent.maxDate = new Date(2020, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell'); @@ -349,6 +353,7 @@ describe('MatMultiYearView', () => { it('should end last page with maxDate', () => { testComponent.minDate = new Date(2006, JAN, 1); testComponent.maxDate = new Date(2020, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell'); @@ -358,6 +363,7 @@ describe('MatMultiYearView', () => { it('should disable dates before minDate', () => { testComponent.minDate = new Date(2006, JAN, 1); testComponent.maxDate = new Date(2020, JAN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell'); diff --git a/src/material/datepicker/testing/calendar-harness.spec.ts b/src/material/datepicker/testing/calendar-harness.spec.ts index 72255678c1a5..f6b52bea1669 100644 --- a/src/material/datepicker/testing/calendar-harness.spec.ts +++ b/src/material/datepicker/testing/calendar-harness.spec.ts @@ -1,15 +1,15 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader, parallel} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {MatNativeDateModule} from '@angular/material/core'; import { - MatDatepickerModule, DateRange, - MAT_DATE_RANGE_SELECTION_STRATEGY, DefaultMatCalendarRangeStrategy, + MAT_DATE_RANGE_SELECTION_STRATEGY, + MatDatepickerModule, } from '@angular/material/datepicker'; -import {MatNativeDateModule} from '@angular/material/core'; -import {MatCalendarHarness, CalendarView} from './calendar-harness'; +import {CalendarView, MatCalendarHarness} from './calendar-harness'; /** Date at which the calendars are set. */ const calendarDate = new Date(2020, 7, 1); @@ -17,11 +17,6 @@ const calendarDate = new Date(2020, 7, 1); describe('MatCalendarHarness', () => { let fixture: ComponentFixture; let loader: HarnessLoader; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -116,6 +111,7 @@ describe('MatCalendarHarness', () => { calendarDate.getMonth(), 20, ); + fixture.changeDetectorRef.markForCheck(); const calendar = await loader.getHarness(MatCalendarHarness.with({selector: '#single'})); const cells = await calendar.getCells(); @@ -198,6 +194,7 @@ describe('MatCalendarHarness', () => { calendarDate.getMonth(), 8, ); + fixture.changeDetectorRef.markForCheck(); expect(await allCells[4].isComparisonRangeStart()).toBe(true); expect(await allCells[4].isInComparisonRange()).toBe(true); @@ -277,6 +274,7 @@ describe('MatCalendarHarness', () => { calendarDate.getMonth(), 3, ); + fixture.changeDetectorRef.markForCheck(); const calendar = await loader.getHarness(MatCalendarHarness.with({selector: '#single'})); const cells = await calendar.getCells({disabled: true}); @@ -296,6 +294,7 @@ describe('MatCalendarHarness', () => { calendarDate.getMonth(), 8, ); + fixture.changeDetectorRef.markForCheck(); const cells = await calendar.getCells({inComparisonRange: true}); expect(await parallel(() => cells.map(cell => cell.getText()))).toEqual(['5', '6', '7', '8']); diff --git a/src/material/datepicker/testing/date-range-input-harness.spec.ts b/src/material/datepicker/testing/date-range-input-harness.spec.ts index 8998f1c61f5d..a4997805da1b 100644 --- a/src/material/datepicker/testing/date-range-input-harness.spec.ts +++ b/src/material/datepicker/testing/date-range-input-harness.spec.ts @@ -1,9 +1,9 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader, parallel} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {MatNativeDateModule} from '@angular/material/core'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {FormsModule} from '@angular/forms'; +import {MatNativeDateModule} from '@angular/material/core'; import { MatDateRangeInput, MatDateRangePicker, @@ -15,20 +15,14 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MatCalendarHarness} from './calendar-harness'; import { MatDateRangeInputHarness, - MatStartDateHarness, MatEndDateHarness, + MatStartDateHarness, } from './date-range-input-harness'; describe('matDateRangeInputHarness', () => { let fixture: ComponentFixture; let loader: HarnessLoader; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ @@ -55,6 +49,7 @@ describe('matDateRangeInputHarness', () => { expect(await input.isDisabled()).toBe(false); fixture.componentInstance.disabled = true; + fixture.changeDetectorRef.markForCheck(); expect(await input.isDisabled()).toBe(true); }); @@ -63,6 +58,7 @@ describe('matDateRangeInputHarness', () => { expect(await input.isRequired()).toBe(false); fixture.componentInstance.required = true; + fixture.changeDetectorRef.markForCheck(); expect(await input.isRequired()).toBe(true); }); @@ -76,6 +72,7 @@ describe('matDateRangeInputHarness', () => { fixture.componentInstance.startDate = new Date(2020, 0, 1, 12, 0, 0); fixture.componentInstance.endDate = new Date(2020, 1, 2, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect(await input.getValue()).toBe('1/1/2020 – 2/2/2020'); }); @@ -100,6 +97,7 @@ describe('matDateRangeInputHarness', () => { it('should be able to open and close a calendar in touch mode', async () => { fixture.componentInstance.touchUi = true; + fixture.changeDetectorRef.markForCheck(); const input = await loader.getHarness(MatDateRangeInputHarness.with({selector: '[basic]'})); expect(await input.isCalendarOpen()).toBe(false); @@ -123,6 +121,7 @@ describe('matDateRangeInputHarness', () => { expect(await parallel(() => [start.isDisabled(), end.isDisabled()])).toEqual([false, false]); fixture.componentInstance.subInputsDisabled = true; + fixture.changeDetectorRef.markForCheck(); expect(await parallel(() => [start.isDisabled(), end.isDisabled()])).toEqual([true, true]); }); @@ -133,6 +132,7 @@ describe('matDateRangeInputHarness', () => { expect(await parallel(() => [start.isRequired(), end.isRequired()])).toEqual([false, false]); fixture.componentInstance.subInputsRequired = true; + fixture.changeDetectorRef.markForCheck(); expect(await parallel(() => [start.isRequired(), end.isRequired()])).toEqual([true, true]); }); @@ -142,6 +142,7 @@ describe('matDateRangeInputHarness', () => { fixture.componentInstance.startDate = new Date(2020, 0, 1, 12, 0, 0); fixture.componentInstance.endDate = new Date(2020, 1, 2, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect( await parallel(() => { @@ -199,6 +200,7 @@ describe('matDateRangeInputHarness', () => { expect(await parallel(() => [start.getMin(), end.getMin()])).toEqual([null, null]); fixture.componentInstance.minDate = new Date(2020, 0, 1, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect( await parallel(() => { return [start.getMin(), end.getMin()]; @@ -213,6 +215,7 @@ describe('matDateRangeInputHarness', () => { expect(await parallel(() => [start.getMax(), end.getMax()])).toEqual([null, null]); fixture.componentInstance.maxDate = new Date(2020, 0, 1, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect( await parallel(() => { diff --git a/src/material/datepicker/testing/datepicker-input-harness.spec.ts b/src/material/datepicker/testing/datepicker-input-harness.spec.ts index b597bb0adfeb..f9a34c1d40a5 100644 --- a/src/material/datepicker/testing/datepicker-input-harness.spec.ts +++ b/src/material/datepicker/testing/datepicker-input-harness.spec.ts @@ -1,24 +1,18 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader, parallel} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {DateAdapter, MatNativeDateModule} from '@angular/material/core'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {FormsModule} from '@angular/forms'; +import {DateAdapter, MatNativeDateModule} from '@angular/material/core'; import {MatDatepickerModule} from '@angular/material/datepicker'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {MatDatepickerInputHarness} from './datepicker-input-harness'; import {MatCalendarHarness} from './calendar-harness'; +import {MatDatepickerInputHarness} from './datepicker-input-harness'; describe('MatDatepickerInputHarness', () => { let fixture: ComponentFixture; let loader: HarnessLoader; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ @@ -42,6 +36,7 @@ describe('MatDatepickerInputHarness', () => { it('should filter inputs based on their value', async () => { fixture.componentInstance.date = new Date(2020, 0, 1, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); const inputs = await loader.getAllHarnesses(MatDatepickerInputHarness.with({value: /2020/})); expect(inputs.length).toBe(1); }); @@ -66,6 +61,7 @@ describe('MatDatepickerInputHarness', () => { expect(await input.isDisabled()).toBe(false); fixture.componentInstance.disabled = true; + fixture.changeDetectorRef.markForCheck(); expect(await input.isDisabled()).toBe(true); }); @@ -74,12 +70,14 @@ describe('MatDatepickerInputHarness', () => { expect(await input.isRequired()).toBe(false); fixture.componentInstance.required = true; + fixture.changeDetectorRef.markForCheck(); expect(await input.isRequired()).toBe(true); }); it('should get the input value', async () => { const input = await loader.getHarness(MatDatepickerInputHarness.with({selector: '#basic'})); fixture.componentInstance.date = new Date(2020, 0, 1, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect(await input.getValue()).toBe('1/1/2020'); }); @@ -108,11 +106,13 @@ describe('MatDatepickerInputHarness', () => { for (let value of validValues) { fixture.componentInstance.date = value; + fixture.changeDetectorRef.markForCheck(); expect(await input.getValue()).toBe('FORMATTED_VALUE'); } for (let value of invalidValues) { fixture.componentInstance.date = value; + fixture.changeDetectorRef.markForCheck(); expect(await input.getValue()).toBe(''); } }); @@ -142,12 +142,14 @@ describe('MatDatepickerInputHarness', () => { it('should get the minimum date of the input', async () => { const inputs = await loader.getAllHarnesses(MatDatepickerInputHarness); fixture.componentInstance.minDate = new Date(2020, 0, 1, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect(await parallel(() => inputs.map(input => input.getMin()))).toEqual(['2020-01-01', null]); }); it('should get the maximum date of the input', async () => { const inputs = await loader.getAllHarnesses(MatDatepickerInputHarness); fixture.componentInstance.maxDate = new Date(2020, 0, 1, 12, 0, 0); + fixture.changeDetectorRef.markForCheck(); expect(await parallel(() => inputs.map(input => input.getMax()))).toEqual(['2020-01-01', null]); }); @@ -164,6 +166,7 @@ describe('MatDatepickerInputHarness', () => { it('should be able to open and close a calendar in touch mode', async () => { fixture.componentInstance.touchUi = true; + fixture.changeDetectorRef.markForCheck(); const input = await loader.getHarness(MatDatepickerInputHarness.with({selector: '#basic'})); expect(await input.isCalendarOpen()).toBe(false); diff --git a/src/material/datepicker/testing/datepicker-toggle-harness.spec.ts b/src/material/datepicker/testing/datepicker-toggle-harness.spec.ts index 80cdded59f4c..ee68c85b9da7 100644 --- a/src/material/datepicker/testing/datepicker-toggle-harness.spec.ts +++ b/src/material/datepicker/testing/datepicker-toggle-harness.spec.ts @@ -1,23 +1,17 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader, parallel} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatNativeDateModule} from '@angular/material/core'; import {MatDatepickerModule} from '@angular/material/datepicker'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {MatDatepickerToggleHarness} from './datepicker-toggle-harness'; import {MatCalendarHarness} from './calendar-harness'; +import {MatDatepickerToggleHarness} from './datepicker-toggle-harness'; describe('MatDatepickerToggleHarness', () => { let fixture: ComponentFixture; let loader: HarnessLoader; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ @@ -43,6 +37,7 @@ describe('MatDatepickerToggleHarness', () => { expect(await toggle.isDisabled()).toBe(false); fixture.componentInstance.disabled = true; + fixture.changeDetectorRef.markForCheck(); expect(await toggle.isDisabled()).toBe(true); }); @@ -68,6 +63,7 @@ describe('MatDatepickerToggleHarness', () => { it('should be able to open and close a calendar in touch mode', async () => { fixture.componentInstance.touchUi = true; + fixture.changeDetectorRef.markForCheck(); const toggle = await loader.getHarness(MatDatepickerToggleHarness.with({selector: '#basic'})); expect(await toggle.isCalendarOpen()).toBe(false); diff --git a/src/material/datepicker/year-view.spec.ts b/src/material/datepicker/year-view.spec.ts index f70af7a728e5..e6f8c0699e3d 100644 --- a/src/material/datepicker/year-view.spec.ts +++ b/src/material/datepicker/year-view.spec.ts @@ -10,11 +10,11 @@ import { UP_ARROW, } from '@angular/cdk/keycodes'; import {dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing/private'; -import {Component, ViewChild, provideZoneChangeDetection} from '@angular/core'; -import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Component, ViewChild} from '@angular/core'; +import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {MatNativeDateModule} from '@angular/material/core'; -import {AUG, DEC, FEB, JAN, JUL, JUN, MAR, MAY, NOV, OCT, SEP} from '../testing'; import {By} from '@angular/platform-browser'; +import {AUG, DEC, FEB, JAN, JUL, JUN, MAR, MAY, NOV, OCT, SEP} from '../testing'; import {MatCalendarBody} from './calendar-body'; import {MatYearView} from './year-view'; @@ -32,10 +32,7 @@ describe('MatYearView', () => { YearViewWithDateFilter, YearViewWithDateClass, ], - providers: [ - provideZoneChangeDetection(), - {provide: Directionality, useFactory: () => (dir = {value: 'ltr'})}, - ], + providers: [{provide: Directionality, useFactory: () => (dir = {value: 'ltr'})}], }); TestBed.compileComponents(); @@ -72,6 +69,7 @@ describe('MatYearView', () => { it('does not show selected month if in different year', () => { testComponent.selected = new Date(2016, MAR, 10); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let selectedEl = yearViewNativeElement.querySelector('.mat-calendar-body-selected'); @@ -105,6 +103,7 @@ describe('MatYearView', () => { it('should allow selection of month with less days than current active date', () => { testComponent.date = new Date(2017, JUL, 31); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.yearView._monthSelected({value: JUN, event: null!}); @@ -131,6 +130,7 @@ describe('MatYearView', () => { expect(calendarBodyEl).not.toBeNull(); dir.value = 'ltr'; fixture.componentInstance.date = new Date(2017, JAN, 5); + fixture.changeDetectorRef.markForCheck(); dispatchFakeEvent(calendarBodyEl, 'focus'); fixture.detectChanges(); }); @@ -194,6 +194,7 @@ describe('MatYearView', () => { expect(calendarInstance.date).toEqual(new Date(2016, SEP, 5)); calendarInstance.date = new Date(2017, JUL, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', UP_ARROW); @@ -202,6 +203,7 @@ describe('MatYearView', () => { expect(calendarInstance.date).toEqual(new Date(2017, MAR, 1)); calendarInstance.date = new Date(2017, DEC, 10); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', UP_ARROW); @@ -217,6 +219,7 @@ describe('MatYearView', () => { expect(calendarInstance.date).toEqual(new Date(2017, MAY, 5)); calendarInstance.date = new Date(2017, JUN, 1); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', DOWN_ARROW); @@ -225,6 +228,7 @@ describe('MatYearView', () => { expect(calendarInstance.date).toEqual(new Date(2017, OCT, 1)); calendarInstance.date = new Date(2017, SEP, 30); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', DOWN_ARROW); @@ -235,6 +239,7 @@ describe('MatYearView', () => { it('should go to first month of the year on home press', () => { calendarInstance.date = new Date(2017, SEP, 30); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', HOME); @@ -250,6 +255,7 @@ describe('MatYearView', () => { it('should go to last month of the year on end press', () => { calendarInstance.date = new Date(2017, OCT, 31); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', END); @@ -265,6 +271,7 @@ describe('MatYearView', () => { it('should go back one year on page up press', () => { calendarInstance.date = new Date(2016, FEB, 29); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', PAGE_UP); @@ -280,6 +287,7 @@ describe('MatYearView', () => { it('should go forward one year on page down press', () => { calendarInstance.date = new Date(2016, FEB, 29); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchKeyboardEvent(calendarBodyEl, 'keydown', PAGE_DOWN); @@ -339,6 +347,7 @@ describe('MatYearView', () => { activeDate.getMonth(), activeDate.getDate(), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); @@ -353,6 +362,7 @@ describe('MatYearView', () => { activeDate.getMonth(), activeDate.getDate(), ); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(spy).not.toHaveBeenCalled(); diff --git a/tools/public_api_guard/material/datepicker.md b/tools/public_api_guard/material/datepicker.md index 08b0d2f9ab0b..8b1ed1385618 100644 --- a/tools/public_api_guard/material/datepicker.md +++ b/tools/public_api_guard/material/datepicker.md @@ -46,6 +46,7 @@ import { OnInit } from '@angular/core'; import { Overlay } from '@angular/cdk/overlay'; import { Portal } from '@angular/cdk/portal'; import { ScrollStrategy } from '@angular/cdk/overlay'; +import { Signal } from '@angular/core'; import { SimpleChanges } from '@angular/core'; import { Subject } from 'rxjs'; import { TemplatePortal } from '@angular/cdk/portal'; @@ -55,6 +56,7 @@ import { ValidationErrors } from '@angular/forms'; import { Validator } from '@angular/forms'; import { ValidatorFn } from '@angular/forms'; import { ViewContainerRef } from '@angular/core'; +import { WritableSignal } from '@angular/core'; // @public export type DateFilterFn = (date: D | null) => boolean; @@ -438,6 +440,7 @@ export interface MatDatepickerControl { // @public export class MatDatepickerInput extends MatDatepickerInputBase implements MatDatepickerControl, OnDestroy { constructor(elementRef: ElementRef, dateAdapter: DateAdapter, dateFormats: MatDateFormats, _formField?: _MatFormFieldPartial | undefined); + protected _ariaOwns: WritableSignal; // (undocumented) protected _assignValueToModel(value: D | null): void; get dateFilter(): DateFilterFn; @@ -571,6 +574,7 @@ export class MatDatepickerToggleIcon { export class MatDateRangeInput implements MatFormFieldControl>, MatDatepickerControl, MatDateRangeInputParent, MatDateRangePickerInput, AfterContentInit, OnChanges, OnDestroy { constructor(_changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef, control: ControlContainer, _dateAdapter: DateAdapter, _formField?: _MatFormFieldPartial | undefined); _ariaDescribedBy: string | null; + _ariaOwns: WritableSignal; comparisonEnd: D | null; comparisonStart: D | null; controlType: string;