Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(material/datepicker): add aria labels to <input/>s for Start/En… #24949

Closed
wants to merge 1 commit into from

Conversation

zarend
Copy link
Contributor

@zarend zarend commented May 21, 2022

Description

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
:

<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: #23445

Note to reviewers

The first commit can be ignored as it is from another PR

@zarend zarend added Accessibility This issue is related to accessibility (a11y) target: patch This PR is targeted for the next patch release area: material/datepicker labels May 21, 2022
@zarend zarend force-pushed the date-range-input-group branch 2 times, most recently from f417783 to ba2f2c8 Compare May 25, 2022 22:38
@zarend zarend added dev-app preview When applied, previews of the dev-app are deployed to Firebase target: minor This PR is targeted for the next minor release and removed target: patch This PR is targeted for the next patch release labels May 25, 2022
@zarend zarend marked this pull request as ready for review May 25, 2022 22:56
@github-actions
Copy link

github-actions bot commented May 25, 2022

src/material/datepicker/calendar-body.spec.ts Outdated Show resolved Hide resolved
constructor(
private _elementRef: ElementRef<HTMLElement>,
private _ngZone: NgZone,
private _intl: MatDatepickerIntl,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the calendar body is exposed as a public API, this will have to be marked as optional initially. Here's an example of how it should be done: https://github.com/angular/components/blob/main/src/cdk/table/table.ts#L518

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just use the new inject() style? I think that's probably what we want to start doing. As a reference, see #24941

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I made it optional. (This commit is on another PR, #24950).

Would switching all of Datepicker to the new inject() style be considered a breaking change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily, but the shape of the constructor would have to stay the same. You can try changing it to something like:

constructor(...args: any[]) {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I used inject() to add the MatDatepickerIntl dependency in a non-breaking way. No changes to the constructor are required.

src/material/datepicker/date-range-input.html Show resolved Hide resolved
src/material/datepicker/date-range-input.ts Outdated Show resolved Hide resolved
@@ -392,6 +394,16 @@ export class MatDateRangeInput<D>
this.stateChanges.next();
}

/** Gets the value for the aria label for the start date input. */
_getStartDateLabel() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it wouldn't make more sense for the start/end date labels to be configurable either on mat-date-range-input or on matStartDate/matEndDate, instead of through MatDatepickerIntl. The problem with having it be in the provider is that it affects all range inputs in the app, but they may represent different things. E.g. in a travel site the flights search might say "Departure" and "Return" while the hotel search would say "Check in" and "Check out".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good point, didn't think of that. MatDatepickerIntl is injectable, so if there were a Flights pages and a Hotel pages in the same app. The Flight page could inject "Departure"/"Arrival" and the hotel page could inject "Check in"/"Check out". It seems like we have this case covered, but I should definitely add a unit test for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are other cases where this wouldn't work though. Like if you had two different range inputs on the same page.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't get it; I think I might not be understanding how DI works. If there were two different range inputs rendered by two different components, couldn't the two inject their own values for "Start Date"/"End Date"?

Something like injecting either HotelMatDatepickerIntl or AirlineMatDatepickerIntl depending on the need.

Copy link
Contributor Author

@zarend zarend Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it would be necessary to make the labels configurable on mat-date-range-input or on matStartDate/matEndDate, instead of through MatDatepickerIntl.

On the one hand, it makes sense for developers to be able to able to set the descriptions for each individual inputs. On the other hand, we would also need a way to specify an aria-description on the calendar pop-up for date ranges of length 1, where the start date and end date are on the same day (e.g. "Start and end date"). In general, is there a requirement for how customizable the matStartDate/matEndDate are?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with doing it through DI is if you have more than one date range input in the same component. E.g.

@Component({
  providers: [{provide: MatDatepickerIntl, useClass: CustomDatepickerIntl}],
  template: `
    <mat-date-range-input></mat-date-range-input>
    <mat-date-range-input></mat-date-range-input>
  `
})
class Comp {}

In this case both range inputs will end up with the same descriptions, even though they might not represent the same values.

@zarend zarend force-pushed the date-range-input-group branch 2 times, most recently from 31909cc to 134a4d8 Compare June 8, 2022 18:03
…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
@crisbeto
Copy link
Member

The discussion on date-range-input.ts wasn't resolved so that's why I haven't been re-reviewing.

@zarend zarend closed this Aug 2, 2022
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Accessibility This issue is related to accessibility (a11y) area: material/datepicker dev-app preview When applied, previews of the dev-app are deployed to Firebase target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug(datepicker): inconsistent announcement of start date and end
3 participants