Skip to content

Commit

Permalink
fix(material/datepicker): add aria labels to <input/>s for Start/En…
Browse files Browse the repository at this point in the history
…d Date

Give `matDateStart` aria label of "Start Date" and give `matDateEnd`
a label of "End Date" by wrapping in a `<label>` element. Apply the aria
label of the form field to the `<mat-date-range-input>` component, which
has role of group. Previously the placeholder was used to communicate
which of the inputs was the start date and which was the end date.

Only affects the DOM structure and a11y tree. Does not change the visual appearance.

Consider the [Basic date range picker
example](https://material.angular.io/components/datepicker/overview#date-range-picker-overview):
```
<mat-form-field appearance="fill">
  <mat-label>Enter a date range</mat-label>
  <mat-date-range-input [rangePicker]="picker">
    <input matStartDate placeholder="Start date">
    <input matEndDate placeholder="End date">
  </mat-date-range-input>
  ...
</mat-form-field>

```

Previously, it would produce an accessibility tree that looks something
like this.
```
group "Enter a date range"
  LabelText
    StaticText "Enter a date range"
    textbox "Enter a date range"
  Textbox "End date"
```

Problems with this approach.
1. Screen reader does not announce "Start Date" right away or not at
2. "Start date"/"End date" come from the placeholder put a label would
   be more appropriate.

With this commit applied, accessibility is consistent between both
inputs, and it is easier to tell which of the two is the start and which
is the end.
```
group "Enter a date range"
  LabelText
    textbox "Start Date"
  LabelText
    textbox "End Date"
```

Fixes: angular#23445
  • Loading branch information
zarend committed Jun 8, 2022
1 parent 176213d commit 134a4d8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 7 deletions.
10 changes: 6 additions & 4 deletions src/material/datepicker/date-range-input.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
class="mat-date-range-input-container"
cdkMonitorSubtreeFocus
(cdkFocusChange)="_updateFocus($event)">
<div class="mat-date-range-input-start-wrapper">
<label class="mat-date-range-input-start-wrapper">
<ng-content select="input[matStartDate]"></ng-content>
<span
class="mat-date-range-input-mirror"
aria-hidden="true">{{_getInputMirrorValue()}}</span>
</div>
<span class="cdk-visually-hidden">{{_getStartDateLabel()}}</span>
</label>

<span
class="mat-date-range-input-separator"
[class.mat-date-range-input-separator-hidden]="_shouldHideSeparator()">{{separator}}</span>

<div class="mat-date-range-input-end-wrapper">
<label class="mat-date-range-input-end-wrapper">
<ng-content select="input[matEndDate]"></ng-content>
</div>
<span class="cdk-visually-hidden">{{_getEndDateLabel()}}</span>
</label>
</div>

4 changes: 2 additions & 2 deletions src/material/datepicker/date-range-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ describe('MatDateRangeInput', () => {
it('should point the label aria-owns to the id of the start input', () => {
const fixture = createComponent(StandardRangePicker);
fixture.detectChanges();
const label = fixture.nativeElement.querySelector('label');
const label = fixture.nativeElement.querySelector('label.mat-form-field-label');
const start = fixture.componentInstance.start.nativeElement;

expect(start.id).toBeTruthy();
Expand All @@ -170,7 +170,7 @@ describe('MatDateRangeInput', () => {
it('should point the range input aria-labelledby to the form field label', () => {
const fixture = createComponent(StandardRangePicker);
fixture.detectChanges();
const labelId = fixture.nativeElement.querySelector('label').id;
const labelId = fixture.nativeElement.querySelector('label.mat-form-field-label').id;
const rangeInput = fixture.nativeElement.querySelector('.mat-date-range-input');

expect(labelId).toBeTruthy();
Expand Down
17 changes: 16 additions & 1 deletion src/material/datepicker/date-range-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
Inject,
OnChanges,
SimpleChanges,
inject,
InjectFlags,
} from '@angular/core';
import {MatFormFieldControl, MatFormField, MAT_FORM_FIELD} from '@angular/material/form-field';
import {ThemePalette, DateAdapter} from '@angular/material/core';
Expand All @@ -39,6 +41,7 @@ import {createMissingDateImplError} from './datepicker-errors';
import {DateFilterFn, dateInputsHaveChanged} from './datepicker-input-base';
import {MatDateRangePickerInput} from './date-range-picker';
import {DateRange, MatDateSelectionModel} from './date-selection-model';
import {MatDatepickerIntl} from './datepicker-intl';

let nextUniqueId = 0;

Expand Down Expand Up @@ -137,6 +140,8 @@ export class MatDateRangeInput<D>
}
private _required: boolean;

private readonly _intl = inject(MatDatepickerIntl, InjectFlags.Optional);

/** Function that can be used to filter out dates within the date range picker. */
@Input()
get dateFilter() {
Expand Down Expand Up @@ -380,7 +385,7 @@ export class MatDateRangeInput<D>
);
}

/** Gets the value for the `aria-labelledby` attribute of the inputs. */
/** Gets the value for the `aria-labelledby` attribute of the group. */
_getAriaLabelledby() {
const formField = this._formField;
return formField && formField._hasFloatingLabel() ? formField._labelId : null;
Expand All @@ -392,6 +397,16 @@ export class MatDateRangeInput<D>
this.stateChanges.next();
}

/** Gets the value for the aria label for the start date input. */
_getStartDateLabel(): string | null {
return this._intl ? this._intl.startDateLabel : null;
}

/** Gets the value for the aria label for the end date input. */
_getEndDateLabel(): string | null {
return this._intl ? this._intl.endDateLabel : null;
}

/** Re-runs the validators on the start/end inputs. */
private _revalidate() {
if (this._startInput) {
Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/material/datepicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,8 +621,10 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
focused: boolean;
_getAriaLabelledby(): string | null;
getConnectedOverlayOrigin(): ElementRef;
_getEndDateLabel(): string | null;
_getInputMirrorValue(): string;
getOverlayLabelId(): string | null;
_getStartDateLabel(): string | null;
getStartValue(): D | null;
getThemePalette(): ThemePalette;
// (undocumented)
Expand Down

0 comments on commit 134a4d8

Please sign in to comment.