Skip to content

Commit be89dcd

Browse files
committed
fix(material/input): preserve native placeholder on non-legacy appearances
The `legacy` form field appearance has a feature where it promotes the input placeholder to the form field label which introduces a problem where screen readers will read out the placeholder twice. Some time ago we added logic to clear the placeholder, but it seems to be a bit too aggressive since it also clears the placeholder for other appearances. These changes scope the workaround only to the case when a placeholder would be promoted to a label. Fixes #20903.
1 parent 29ac6cf commit be89dcd

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed

src/material-experimental/mdc-input/input.spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,17 @@ describe('MatMdcInput without forms', () => {
882882
expect(formField.classList).not.toContain('mat-mdc-form-field-type-mat-native-select');
883883
});
884884

885+
it('should preserve the native placeholder on a non-legacy appearance', fakeAsync(() => {
886+
const fixture = createComponent(MatInputWithLabelAndPlaceholder);
887+
fixture.componentInstance.floatLabel = 'auto';
888+
fixture.componentInstance.appearance = 'outline';
889+
fixture.detectChanges();
890+
891+
expect(fixture.nativeElement.querySelector('input').getAttribute('placeholder')).toBe(
892+
'Placeholder',
893+
);
894+
}));
895+
885896
it(
886897
'should use the native input value when determining whether ' +
887898
'the element is empty with a custom accessor',
@@ -1861,14 +1872,15 @@ class MatInputWithLabel {}
18611872

18621873
@Component({
18631874
template: `
1864-
<mat-form-field [floatLabel]="floatLabel">
1875+
<mat-form-field [floatLabel]="floatLabel" [appearance]="appearance">
18651876
<mat-label>Label</mat-label>
18661877
<input matInput placeholder="Placeholder">
18671878
</mat-form-field>
18681879
`,
18691880
})
18701881
class MatInputWithLabelAndPlaceholder {
18711882
floatLabel: FloatLabelType;
1883+
appearance: MatFormFieldAppearance;
18721884
}
18731885

18741886
@Component({

src/material/input/input.spec.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,6 @@ describe('MatInput without forms', () => {
973973
expect(container.classList).toContain('mat-form-field-hide-placeholder');
974974
expect(container.classList).not.toContain('mat-form-field-should-float');
975975
expect(label.textContent.trim()).toBe('Label');
976-
expect(input.hasAttribute('placeholder')).toBe(false);
977976

978977
input.value = 'Value';
979978
fixture.detectChanges();
@@ -1020,6 +1019,17 @@ describe('MatInput without forms', () => {
10201019
expect(container.classList).not.toContain('mat-form-field-should-float');
10211020
});
10221021

1022+
it('should preserve the native placeholder on a non-legacy appearance', fakeAsync(() => {
1023+
const fixture = createComponent(MatInputWithLabelAndPlaceholder);
1024+
fixture.componentInstance.floatLabel = 'auto';
1025+
fixture.componentInstance.appearance = 'standard';
1026+
fixture.detectChanges();
1027+
1028+
expect(fixture.nativeElement.querySelector('input').getAttribute('placeholder')).toBe(
1029+
'Placeholder',
1030+
);
1031+
}));
1032+
10231033
it('should not add the native select class if the control is not a native select', () => {
10241034
const fixture = createComponent(MatInputWithId);
10251035
fixture.detectChanges();
@@ -2232,14 +2242,15 @@ class MatInputWithLabel {}
22322242

22332243
@Component({
22342244
template: `
2235-
<mat-form-field [floatLabel]="floatLabel">
2245+
<mat-form-field [floatLabel]="floatLabel" [appearance]="appearance">
22362246
<mat-label>Label</mat-label>
22372247
<input matInput placeholder="Placeholder">
22382248
</mat-form-field>
22392249
`,
22402250
})
22412251
class MatInputWithLabelAndPlaceholder {
22422252
floatLabel: FloatLabelType;
2253+
appearance: MatFormFieldAppearance = 'legacy';
22432254
}
22442255

22452256
@Component({

src/material/input/input.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,11 @@ export class MatInput
409409
// screen readers will read it out twice: once from the label and once from the attribute.
410410
// TODO: can be removed once we get rid of the `legacy` style for the form field, because it's
411411
// the only one that supports promoting the placeholder to a label.
412-
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
412+
const formField = this._formField;
413+
const placeholder =
414+
formField && formField.appearance === 'legacy' && !formField._hasLabel?.()
415+
? null
416+
: this.placeholder;
413417
if (placeholder !== this._previousPlaceholder) {
414418
const element = this._elementRef.nativeElement;
415419
this._previousPlaceholder = placeholder;

0 commit comments

Comments
 (0)