Skip to content

Commit 50e7344

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 01734b3 commit 50e7344

File tree

2 files changed

+15
-3
lines changed

2 files changed

+15
-3
lines changed

src/material/input/input.spec.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,6 @@ describe('MatInput without forms', () => {
920920
expect(container.classList).toContain('mat-form-field-hide-placeholder');
921921
expect(container.classList).not.toContain('mat-form-field-should-float');
922922
expect(label.textContent.trim()).toBe('Label');
923-
expect(input.hasAttribute('placeholder')).toBe(false);
924923

925924
input.value = 'Value';
926925
fixture.detectChanges();
@@ -967,6 +966,16 @@ describe('MatInput without forms', () => {
967966
expect(container.classList).not.toContain('mat-form-field-should-float');
968967
});
969968

969+
it('should preserve the native placeholder on a non-legacy appearance', fakeAsync(() => {
970+
const fixture = createComponent(MatInputWithLabelAndPlaceholder);
971+
fixture.componentInstance.floatLabel = 'auto';
972+
fixture.componentInstance.appearance = 'standard';
973+
fixture.detectChanges();
974+
975+
expect(fixture.nativeElement.querySelector('input').hasAttribute('placeholder')).toBe(true);
976+
}));
977+
978+
970979
it('should not add the native select class if the control is not a native select', () => {
971980
const fixture = createComponent(MatInputWithId);
972981
fixture.detectChanges();
@@ -2111,14 +2120,15 @@ class MatInputWithLabel {}
21112120

21122121
@Component({
21132122
template: `
2114-
<mat-form-field [floatLabel]="floatLabel">
2123+
<mat-form-field [floatLabel]="floatLabel" [appearance]="appearance">
21152124
<mat-label>Label</mat-label>
21162125
<input matInput placeholder="Placeholder">
21172126
</mat-form-field>
21182127
`
21192128
})
21202129
class MatInputWithLabelAndPlaceholder {
21212130
floatLabel: FloatLabelType;
2131+
appearance: MatFormFieldAppearance = 'legacy';
21222132
}
21232133

21242134
@Component({

src/material/input/input.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,9 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
378378
// screen readers will read it out twice: once from the label and once from the attribute.
379379
// TODO: can be removed once we get rid of the `legacy` style for the form field, because it's
380380
// the only one that supports promoting the placeholder to a label.
381-
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
381+
const formField = this._formField;
382+
const placeholder = formField && formField.appearance === 'legacy' && !formField._hasLabel() ?
383+
null : this.placeholder;
382384
if (placeholder !== this._previousPlaceholder) {
383385
const element = this._elementRef.nativeElement;
384386
this._previousPlaceholder = placeholder;

0 commit comments

Comments
 (0)