Skip to content

Commit 0d232a6

Browse files
authored
refactor(material/datepicker): Remove use of zone onStable for focus and dropdown positioning (#28658)
1 parent 172c741 commit 0d232a6

File tree

3 files changed

+32
-30
lines changed

3 files changed

+32
-30
lines changed

src/material/datepicker/calendar-body.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import {
2121
OnDestroy,
2222
AfterViewChecked,
2323
inject,
24+
afterNextRender,
25+
Injector,
2426
} from '@angular/core';
25-
import {take} from 'rxjs/operators';
2627
import {NgClass} from '@angular/common';
2728

2829
/** Extra CSS classes that can be associated with a calendar cell. */
@@ -189,6 +190,8 @@ export class MatCalendarBody<D = any> implements OnChanges, OnDestroy, AfterView
189190

190191
private _didDragSinceMouseDown = false;
191192

193+
private _injector = inject(Injector);
194+
192195
constructor(
193196
private _elementRef: ElementRef<HTMLElement>,
194197
private _ngZone: NgZone,
@@ -309,8 +312,8 @@ export class MatCalendarBody<D = any> implements OnChanges, OnDestroy, AfterView
309312
* Adding delay also complicates writing tests.
310313
*/
311314
_focusActiveCell(movePreview = true) {
312-
this._ngZone.runOutsideAngular(() => {
313-
this._ngZone.onStable.pipe(take(1)).subscribe(() => {
315+
afterNextRender(
316+
() => {
314317
setTimeout(() => {
315318
const activeCell: HTMLElement | null = this._elementRef.nativeElement.querySelector(
316319
'.mat-calendar-body-active',
@@ -324,8 +327,9 @@ export class MatCalendarBody<D = any> implements OnChanges, OnDestroy, AfterView
324327
activeCell.focus();
325328
}
326329
});
327-
});
328-
});
330+
},
331+
{injector: this._injector},
332+
);
329333
}
330334

331335
/** Focuses the active cell after change detection has run and the microtask queue is empty. */

src/material/datepicker/calendar.spec.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,9 @@ import {
44
dispatchFakeEvent,
55
dispatchKeyboardEvent,
66
dispatchMouseEvent,
7-
MockNgZone,
87
} from '@angular/cdk/testing/private';
9-
import {Component, NgZone} from '@angular/core';
10-
import {
11-
fakeAsync,
12-
waitForAsync,
13-
ComponentFixture,
14-
inject,
15-
TestBed,
16-
tick,
17-
} from '@angular/core/testing';
8+
import {Component} from '@angular/core';
9+
import {waitForAsync, ComponentFixture, inject, TestBed} from '@angular/core/testing';
1810
import {DateAdapter, MatNativeDateModule} from '@angular/material/core';
1911
import {DEC, FEB, JAN, JUL, NOV} from '../testing';
2012
import {By} from '@angular/platform-browser';
@@ -23,16 +15,10 @@ import {MatDatepickerIntl} from './datepicker-intl';
2315
import {MatDatepickerModule} from './datepicker-module';
2416

2517
describe('MatCalendar', () => {
26-
let zone: MockNgZone;
27-
2818
beforeEach(waitForAsync(() => {
2919
TestBed.configureTestingModule({
3020
imports: [MatNativeDateModule, MatDatepickerModule],
31-
providers: [
32-
MatDatepickerIntl,
33-
{provide: NgZone, useFactory: () => (zone = new MockNgZone())},
34-
{provide: Directionality, useFactory: () => ({value: 'ltr'})},
35-
],
21+
providers: [MatDatepickerIntl, {provide: Directionality, useFactory: () => ({value: 'ltr'})}],
3622
declarations: [
3723
// Test components.
3824
StandardCalendar,
@@ -183,19 +169,19 @@ describe('MatCalendar', () => {
183169
expect(calendarBodyEl.getAttribute('tabindex')).toBe('-1');
184170
});
185171

186-
it('should not move focus to the active cell on init', () => {
172+
it('should not move focus to the active cell on init', waitForAsync(async () => {
187173
const activeCell = calendarBodyEl.querySelector(
188174
'.mat-calendar-body-active',
189175
)! as HTMLElement;
190176

191177
spyOn(activeCell, 'focus').and.callThrough();
192178
fixture.detectChanges();
193-
zone.simulateZoneExit();
179+
await new Promise(resolve => setTimeout(resolve));
194180

195181
expect(activeCell.focus).not.toHaveBeenCalled();
196-
});
182+
}));
197183

198-
it('should move focus to the active cell when the view changes', fakeAsync(() => {
184+
it('should move focus to the active cell when the view changes', waitForAsync(async () => {
199185
calendarInstance.currentView = 'multi-year';
200186
fixture.detectChanges();
201187

@@ -204,8 +190,7 @@ describe('MatCalendar', () => {
204190
)! as HTMLElement;
205191
spyOn(activeCell, 'focus').and.callThrough();
206192

207-
zone.simulateZoneExit();
208-
tick();
193+
await new Promise(resolve => setTimeout(resolve));
209194

210195
expect(activeCell.focus).toHaveBeenCalled();
211196
}));

src/material/datepicker/datepicker-base.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import {
5151
OnInit,
5252
inject,
5353
booleanAttribute,
54+
afterNextRender,
55+
Injector,
5456
} from '@angular/core';
5557
import {DateAdapter, ThemePalette} from '@angular/material/core';
5658
import {AnimationEvent} from '@angular/animations';
@@ -509,9 +511,15 @@ export abstract class MatDatepickerBase<
509511
/** Emits when the datepicker's state changes. */
510512
readonly stateChanges = new Subject<void>();
511513

514+
private _injector = inject(Injector);
515+
512516
constructor(
513517
private _overlay: Overlay,
514-
private _ngZone: NgZone,
518+
/**
519+
* @deprecated parameter is unused and will be removed
520+
* @breaking-change 19.0.0
521+
*/
522+
_unusedNgZone: NgZone,
515523
private _viewContainerRef: ViewContainerRef,
516524
@Inject(MAT_DATEPICKER_SCROLL_STRATEGY) scrollStrategy: any,
517525
@Optional() private _dateAdapter: DateAdapter<D>,
@@ -749,7 +757,12 @@ export abstract class MatDatepickerBase<
749757

750758
// Update the position once the calendar has rendered. Only relevant in dropdown mode.
751759
if (!isDialog) {
752-
this._ngZone.onStable.pipe(take(1)).subscribe(() => overlayRef.updatePosition());
760+
afterNextRender(
761+
() => {
762+
overlayRef.updatePosition();
763+
},
764+
{injector: this._injector},
765+
);
753766
}
754767
}
755768

0 commit comments

Comments
 (0)