-
Notifications
You must be signed in to change notification settings - Fork 6.8k
fix(datepicker): add footer with close button for screen readers #14429
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,33 @@ | ||
<mat-calendar cdkTrapFocus | ||
[id]="datepicker.id" | ||
[ngClass]="datepicker.panelClass" | ||
[startAt]="datepicker.startAt" | ||
[startView]="datepicker.startView" | ||
[minDate]="datepicker._minDate" | ||
[maxDate]="datepicker._maxDate" | ||
[dateFilter]="datepicker._dateFilter" | ||
[headerComponent]="datepicker.calendarHeaderComponent" | ||
[selected]="datepicker._selected" | ||
[dateClass]="datepicker.dateClass" | ||
[@fadeInCalendar]="'enter'" | ||
(selectedChange)="datepicker.select($event)" | ||
(yearSelected)="datepicker._selectYear($event)" | ||
(monthSelected)="datepicker._selectMonth($event)" | ||
(_userSelection)="datepicker.close()"> | ||
</mat-calendar> | ||
<div cdkTrapFocus> | ||
<mat-calendar | ||
[id]="datepicker.id" | ||
[ngClass]="datepicker.panelClass" | ||
[startAt]="datepicker.startAt" | ||
[startView]="datepicker.startView" | ||
[minDate]="datepicker._minDate" | ||
[maxDate]="datepicker._maxDate" | ||
[dateFilter]="datepicker._dateFilter" | ||
[headerComponent]="datepicker.calendarHeaderComponent" | ||
[selected]="datepicker._selected" | ||
[dateClass]="datepicker.dateClass" | ||
[@fadeInCalendar]="'enter'" | ||
(selectedChange)="datepicker.select($event)" | ||
(yearSelected)="datepicker._selectYear($event)" | ||
(monthSelected)="datepicker._selectMonth($event)" | ||
(_userSelection)="datepicker.close()"> | ||
</mat-calendar> | ||
|
||
<!-- An invisible close button so users with screen readers can easily close the datepicker --> | ||
<div cdkMonitorSubtreeFocus | ||
(cdkFocusChange)="closeButtonElementOrigin = formatOrigin($event); markForCheck()"> | ||
<button mat-button | ||
disableRipple | ||
type="button" | ||
[class.cdk-visually-hidden]="closeButtonElementOrigin === 'blurred'" | ||
class="mat-datepicker-close-button" | ||
[attr.aria-label]="closeButtonLabel" | ||
(click)="datepicker.close()"> | ||
{{closeButtonLabel}} | ||
</button> | ||
</div> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,17 @@ $mat-datepicker-touch-max-height: 788px; | |
} | ||
} | ||
|
||
.mat-datepicker-close-button { | ||
position: absolute; | ||
margin-top: 10px; | ||
&.cdk-keyboard-focused { | ||
background: #2468cf; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where are these colors coming from? Would a |
||
color: #ffffff; | ||
z-index: 999; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems unnecessarily high, will |
||
text-decoration: none; | ||
} | ||
} | ||
|
||
.mat-datepicker-content-touch { | ||
display: block; | ||
// make sure the dialog scrolls rather than being cropped on ludicrously small screens | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ import {DOCUMENT} from '@angular/common'; | |
import { | ||
AfterViewInit, | ||
ChangeDetectionStrategy, | ||
ChangeDetectorRef, | ||
Component, | ||
ComponentRef, | ||
ElementRef, | ||
|
@@ -51,6 +52,8 @@ import {matDatepickerAnimations} from './datepicker-animations'; | |
import {createMissingDateImplError} from './datepicker-errors'; | ||
import {MatDatepickerInput} from './datepicker-input'; | ||
import {MatCalendarCellCssClasses} from './calendar-body'; | ||
import {MatDatepickerIntl} from './datepicker-intl'; | ||
import {FocusOrigin} from '@angular/cdk/a11y'; | ||
|
||
/** Used to generate a unique ID for each datepicker instance. */ | ||
let datepickerUid = 0; | ||
|
@@ -117,13 +120,31 @@ export class MatDatepickerContent<D> extends _MatDatepickerContentMixinBase | |
/** Whether the datepicker is above or below the input. */ | ||
_isAbove: boolean; | ||
|
||
constructor(elementRef: ElementRef) { | ||
/** For the focus monitor */ | ||
closeButtonElementOrigin: string = this.formatOrigin(null); | ||
|
||
get closeButtonLabel(): string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Getters compile down to a lot of code (relative to just a plain method). Can you change this to a method (e.g. |
||
return this._intl.closeCalendarLabel; | ||
} | ||
|
||
constructor(private _intl: MatDatepickerIntl, | ||
private _ngZone: NgZone, | ||
private _changeDetectorRef: ChangeDetectorRef, | ||
elementRef: ElementRef) { | ||
super(elementRef); | ||
} | ||
|
||
ngAfterViewInit() { | ||
this._calendar.focusActiveCell(); | ||
} | ||
|
||
formatOrigin(origin: FocusOrigin): string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use a boolean variable (e.g. |
||
return origin ? origin + ' focused' : 'blurred'; | ||
} | ||
|
||
markForCheck() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this shouldn't be necessary if you switch to just using the |
||
this._ngZone.run(() => this._changeDetectorRef.markForCheck()); | ||
} | ||
} | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like overkill, you can just use
(focus)
and(blur)
events on the button