Skip to content

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

Closed
zarend wants to merge 1 commit into
angular:mainfrom
zarend:date-range-input-group
Closed

zarend wants to merge 1 commit into
angular:mainfrom
zarend:date-range-input-group

Conversation

@zarend

@zarend zarend commented May 21, 2022 •

Copy link
Copy Markdown
Contributor

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
@zarend
zarend requested review from crisbeto and mmalerba as code owners May 25, 2022 22:56
@github-actions

github-actions Bot commented May 25, 2022 •

Copy link
Copy Markdown

constructor(
private _elementRef: ElementRef<HTMLElement>,
private _ngZone: NgZone,
private _intl: MatDatepickerIntl,

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

}

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

@zarend zarend Jun 14, 2022 •

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
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
@zarend
zarend force-pushed the date-range-input-group branch from 134a4d8 to 13434c5 Compare June 13, 2022 22:58
@zarend
zarend requested a review from crisbeto June 13, 2022 22:58
@crisbeto

Copy link
Copy Markdown
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
Copy Markdown

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