diff --git a/src/material/datepicker/calendar-body.ts b/src/material/datepicker/calendar-body.ts index 3749c74bb0f6..ce6f6f480772 100644 --- a/src/material/datepicker/calendar-body.ts +++ b/src/material/datepicker/calendar-body.ts @@ -20,7 +20,7 @@ import { OnDestroy, AfterViewChecked, } from '@angular/core'; -import {take} from 'rxjs/operators'; +import {delay, take} from 'rxjs/operators'; /** Extra CSS classes that can be associated with a calendar cell. */ export type MatCalendarCellCssClasses = string | string[] | Set | {[key: string]: any}; @@ -31,6 +31,8 @@ export type MatCalendarCellClassFunction = ( view: 'month' | 'year' | 'multi-year', ) => MatCalendarCellCssClasses; +export const FOCUS_ACTIVE_CELL_DELAY = 20; + /** * An internal class that represents the data corresponding to a single calendar cell. * @docs-private @@ -216,10 +218,14 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { return cellNumber == this.activeCell; } - /** Focuses the active cell after the microtask queue is empty. */ + /** + * Focuses the active cell after the microtask queue is empty. + * + * Adds a 20ms delay to fix Voiceover losing focus when pressing PageUp/PageDown (issue #24330). + */ _focusActiveCell(movePreview = true) { this._ngZone.runOutsideAngular(() => { - this._ngZone.onStable.pipe(take(1)).subscribe(() => { + this._ngZone.onStable.pipe(take(1), delay(FOCUS_ACTIVE_CELL_DELAY)).subscribe(() => { const activeCell: HTMLElement | null = this._elementRef.nativeElement.querySelector( '.mat-calendar-body-active', );