Skip to content

Commit 6ed537d

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 f7d7502 commit 6ed537d

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
@@ -958,7 +958,6 @@ describe('MatInput without forms', () => {
958958
expect(container.classList).toContain('mat-form-field-hide-placeholder');
959959
expect(container.classList).not.toContain('mat-form-field-should-float');
960960
expect(label.textContent.trim()).toBe('Label');
961-
expect(input.hasAttribute('placeholder')).toBe(false);
962961

963962
input.value = 'Value';
964963
fixture.detectChanges();
@@ -1005,6 +1004,16 @@ describe('MatInput without forms', () => {
10051004
expect(container.classList).not.toContain('mat-form-field-should-float');
10061005
});
10071006

1007+
it('should preserve the native placeholder on a non-legacy appearance', fakeAsync(() => {
1008+
const fixture = createComponent(MatInputWithLabelAndPlaceholder);
1009+
fixture.componentInstance.floatLabel = 'auto';
1010+
fixture.componentInstance.appearance = 'standard';
1011+
fixture.detectChanges();
1012+
1013+
expect(fixture.nativeElement.querySelector('input').hasAttribute('placeholder')).toBe(true);
1014+
}));
1015+
1016+
10081017
it('should not add the native select class if the control is not a native select', () => {
10091018
const fixture = createComponent(MatInputWithId);
10101019
fixture.detectChanges();
@@ -2109,14 +2118,15 @@ class MatInputWithLabel {}
21092118

21102119
@Component({
21112120
template: `
2112-
<mat-form-field [floatLabel]="floatLabel">
2121+
<mat-form-field [floatLabel]="floatLabel" [appearance]="appearance">
21132122
<mat-label>Label</mat-label>
21142123
<input matInput placeholder="Placeholder">
21152124
</mat-form-field>
21162125
`
21172126
})
21182127
class MatInputWithLabelAndPlaceholder {
21192128
floatLabel: FloatLabelType;
2129+
appearance: MatFormFieldAppearance = 'legacy';
21202130
}
21212131

21222132
@Component({

src/material/input/input.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,9 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
369369
// screen readers will read it out twice: once from the label and once from the attribute.
370370
// TODO: can be removed once we get rid of the `legacy` style for the form field, because it's
371371
// the only one that supports promoting the placeholder to a label.
372-
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
372+
const formField = this._formField;
373+
const placeholder = formField && formField.appearance === 'legacy' && !formField._hasLabel() ?
374+
null : this.placeholder;
373375
if (placeholder !== this._previousPlaceholder) {
374376
const element = this._elementRef.nativeElement;
375377
this._previousPlaceholder = placeholder;

0 commit comments

Comments
 (0)