Skip to content

Commit 474af8c

Browse files
committed
fix(datepicker): don't set aria-labelledby if form field does not have a label
Currently we always set the `aria-labelledby` on the range inputs which will be invalid if the form field doesn't have a label. These changes add an extra check around it.
1 parent a3dabc9 commit 474af8c

File tree

5 files changed

+43
-13
lines changed

5 files changed

+43
-13
lines changed

src/dev-app/datepicker/datepicker-demo.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ <h2>Datepicker with custom header extending the default header</h2>
173173
<h2>Range picker</h2>
174174

175175
<div class="demo-range-group">
176-
<mat-form-field appearance="legacy">
176+
<mat-form-field>
177+
<mat-label>Enter a date range</mat-label>
177178
<mat-date-range-input
178179
[formGroup]="range1"
179180
[rangePicker]="range1Picker"
@@ -183,8 +184,8 @@ <h2>Range picker</h2>
183184
[comparisonStart]="comparisonStart"
184185
[comparisonEnd]="comparisonEnd"
185186
[dateFilter]="filterOdd ? dateFilter : undefined">
186-
<input matStartDate formControlName="start"/>
187-
<input matEndDate formControlName="end"/>
187+
<input matStartDate formControlName="start" placeholder="Start date"/>
188+
<input matEndDate formControlName="end" placeholder="End date"/>
188189
</mat-date-range-input>
189190
<mat-datepicker-toggle [for]="range1Picker" matSuffix></mat-datepicker-toggle>
190191
<mat-date-range-picker

src/material/datepicker/date-range-input-parts.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ export interface MatDateRangeInputParent<D> {
5656
_endInput: MatDateRangeInputPartBase<D>;
5757
_groupDisabled: boolean;
5858
_ariaDescribedBy: string | null;
59-
_ariaLabelledBy: string | null;
60-
_handleChildValueChange: () => void;
61-
_openDatepicker: () => void;
59+
_getAriaLabelledby(): string | null;
60+
_handleChildValueChange(): void;
61+
_openDatepicker(): void;
6262
}
6363

6464
/**
@@ -196,7 +196,7 @@ const _MatDateRangeInputBase:
196196
'(change)': '_onChange()',
197197
'(keydown)': '_onKeydown($event)',
198198
'[attr.id]': '_rangeInput.id',
199-
'[attr.aria-labelledby]': '_rangeInput._ariaLabelledBy',
199+
'[attr.aria-labelledby]': '_rangeInput._getAriaLabelledby()',
200200
'[attr.aria-describedby]': '_rangeInput._ariaDescribedBy',
201201
'[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null',
202202
'[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null',
@@ -277,7 +277,7 @@ export class MatStartDate<D> extends _MatDateRangeInputBase<D> implements CanUpd
277277
'(input)': '_onInput($event.target.value)',
278278
'(change)': '_onChange()',
279279
'(keydown)': '_onKeydown($event)',
280-
'[attr.aria-labelledby]': '_rangeInput._ariaLabelledBy',
280+
'[attr.aria-labelledby]': '_rangeInput._getAriaLabelledby()',
281281
'[attr.aria-describedby]': '_rangeInput._ariaDescribedBy',
282282
'[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null',
283283
'[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null',

src/material/datepicker/date-range-input.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ describe('MatDateRangeInput', () => {
173173
expect(end.nativeElement.getAttribute('aria-describedby')).toBe(labelId);
174174
});
175175

176+
it('should not set aria-labelledby if the form field does not have a label', () => {
177+
const fixture = createComponent(RangePickerNoLabel);
178+
fixture.detectChanges();
179+
const {start, end} = fixture.componentInstance;
180+
181+
expect(start.nativeElement.getAttribute('aria-labelledby')).toBeFalsy();
182+
expect(end.nativeElement.getAttribute('aria-labelledby')).toBeFalsy();
183+
});
184+
176185
it('should float the form field label when either input is focused', () => {
177186
const fixture = createComponent(StandardRangePicker);
178187
fixture.detectChanges();
@@ -568,6 +577,7 @@ class StandardRangePicker {
568577
})
569578
class RangePickerNoStart {}
570579

580+
571581
@Component({
572582
template: `
573583
<mat-form-field>
@@ -599,3 +609,20 @@ class RangePickerNgModel {
599609
end: Date | null = null;
600610
}
601611

612+
613+
@Component({
614+
template: `
615+
<mat-form-field>
616+
<mat-date-range-input [rangePicker]="rangePicker">
617+
<input #start matStartDate/>
618+
<input #end matEndDate/>
619+
</mat-date-range-input>
620+
621+
<mat-date-range-picker #rangePicker></mat-date-range-picker>
622+
</mat-form-field>
623+
`
624+
})
625+
class RangePickerNoLabel {
626+
@ViewChild('start') start: ElementRef<HTMLInputElement>;
627+
@ViewChild('end') end: ElementRef<HTMLInputElement>;
628+
}

src/material/datepicker/date-range-input.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,6 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
173173
/** Value for the `aria-describedby` attribute of the inputs. */
174174
_ariaDescribedBy: string | null = null;
175175

176-
/** Value for the `aria-labelledby` attribute of the inputs. */
177-
_ariaLabelledBy: string | null = null;
178-
179176
/** Date selection model currently registered with the input. */
180177
private _model: MatDateSelectionModel<DateRange<D>> | undefined;
181178

@@ -214,7 +211,6 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
214211

215212
// TODO(crisbeto): remove `as any` after #18206 lands.
216213
this.ngControl = control as any;
217-
this._ariaLabelledBy = _formField ? _formField._labelId : null;
218214
}
219215

220216
/**
@@ -306,6 +302,12 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
306302
return (!this._formField || this._formField._hideControlPlaceholder()) && this.empty;
307303
}
308304

305+
/** Gets the value for the `aria-labelledby` attribute of the inputs. */
306+
_getAriaLabelledby() {
307+
const formField = this._formField;
308+
return formField && formField._hasFloatingLabel() ? formField._labelId : null;
309+
}
310+
309311
/**
310312
* @param obj The object to check.
311313
* @returns The given object if it is both a date instance and valid, otherwise null.

tools/public_api_guard/material/datepicker.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,6 @@ export declare class MatDatepickerToggleIcon {
287287

288288
export declare class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>, MatDatepickerControl<D>, MatDateRangeInputParent<D>, AfterContentInit, OnDestroy {
289289
_ariaDescribedBy: string | null;
290-
_ariaLabelledBy: string | null;
291290
_disabledChange: Subject<boolean>;
292291
_endInput: MatEndDate<D>;
293292
_groupDisabled: boolean;
@@ -318,6 +317,7 @@ export declare class MatDateRangeInput<D> implements MatFormFieldControl<DateRan
318317
stateChanges: Subject<void>;
319318
get value(): DateRange<D> | null;
320319
constructor(_changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef<HTMLElement>, control: ControlContainer, _dateAdapter: DateAdapter<D>, _formField?: MatFormField | undefined);
320+
_getAriaLabelledby(): string | null;
321321
_getInputMirrorValue(): string;
322322
_handleChildValueChange(): void;
323323
_openDatepicker(): void;

0 commit comments

Comments
 (0)