Skip to content

Commit 29a88c7

Browse files
committed
fix(material/datepicker): add aria description to calendar body cells tocommunicate start or end of date range
Add 'Start of date range' aria description to first cell in selected date range, and add 'End of date range' aria description to last cell in selected date range. Fix accessibility issue where screen reader does not communicate if the selected cell is the start date or the end date. Fixes #23442
1 parent 64b6506 commit 29a88c7

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

src/material/datepicker/calendar-body.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
[class.mat-calendar-body-preview-end]="_isPreviewEnd(item.compareValue)"
6161
[class.mat-calendar-body-in-preview]="_isInPreview(item.compareValue)"
6262
[attr.aria-label]="item.ariaLabel"
63+
[attr.aria-description]="_getAriaDescription(item.compareValue)"
6364
[attr.aria-disabled]="!item.enabled || null"
6465
[attr.aria-pressed]="_isSelected(item.compareValue)"
6566
[attr.aria-current]="todayValue === item.compareValue ? 'date' : null"

src/material/datepicker/calendar-body.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
AfterViewChecked,
2222
} from '@angular/core';
2323
import {take} from 'rxjs/operators';
24+
import {MatDatepickerIntl} from './datepicker-intl';
2425

2526
/** Extra CSS classes that can be associated with a calendar cell. */
2627
export type MatCalendarCellCssClasses = string | string[] | Set<string> | {[key: string]: any};
@@ -151,7 +152,11 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked {
151152
/** Width of an individual cell. */
152153
_cellWidth: string;
153154

154-
constructor(private _elementRef: ElementRef<HTMLElement>, private _ngZone: NgZone) {
155+
constructor(
156+
private _elementRef: ElementRef<HTMLElement>,
157+
private _ngZone: NgZone,
158+
private _intl: MatDatepickerIntl,
159+
) {
155160
_ngZone.runOutsideAngular(() => {
156161
const element = _elementRef.nativeElement;
157162
element.addEventListener('mouseenter', this._enterHandler, true);
@@ -174,6 +179,27 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked {
174179
}
175180
}
176181

182+
/**
183+
* Provides an aria-description for the cell to communicate if it is the
184+
* start or end of the selected date range
185+
*
186+
* TODO: add unit test
187+
*/
188+
_getAriaDescription(value: number): string | null {
189+
if (!this.isRange) {
190+
return null;
191+
}
192+
193+
if (this.startValue === value && this.endValue === value) {
194+
return this._intl.startAndEndDateLabel;
195+
} else if (this.startValue === value) {
196+
return this._intl.startDateLabel;
197+
} else if (this.endValue === value) {
198+
return this._intl.endDateLabel;
199+
}
200+
return null;
201+
}
202+
177203
/** Returns whether a cell should be marked as selected. */
178204
_isSelected(value: number) {
179205
return this.startValue === value || this.endValue === value;

src/material/datepicker/datepicker-intl.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ export class MatDatepickerIntl {
5151
/** A label for the 'switch to year view' button (used by screen readers). */
5252
switchToMultiYearViewLabel: string = 'Choose month and year';
5353

54+
/** A label for the first date of a range of dates. */
55+
startDateLabel: string = 'Start date';
56+
57+
/** A label for the last date of a range of dates. */
58+
endDateLabel: string = 'End date';
59+
60+
/** A label for a date range that starts and ends on the same day. */
61+
startAndEndDateLabel: string = 'Start and end date';
62+
5463
/** Formats a range of years. */
5564
formatYearRange(start: string, end: string): string {
5665
return `${start} \u2013 ${end}`;

0 commit comments

Comments
 (0)