Skip to content

Commit

Permalink
fix(material/input): preserve native placeholder on non-legacy appear…
Browse files Browse the repository at this point in the history
…ances

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.
  • Loading branch information
crisbeto committed Sep 7, 2021
1 parent 01734b3 commit 50e7344
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 3 deletions.
14 changes: 12 additions & 2 deletions src/material/input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,6 @@ describe('MatInput without forms', () => {
expect(container.classList).toContain('mat-form-field-hide-placeholder');
expect(container.classList).not.toContain('mat-form-field-should-float');
expect(label.textContent.trim()).toBe('Label');
expect(input.hasAttribute('placeholder')).toBe(false);

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

it('should preserve the native placeholder on a non-legacy appearance', fakeAsync(() => {
const fixture = createComponent(MatInputWithLabelAndPlaceholder);
fixture.componentInstance.floatLabel = 'auto';
fixture.componentInstance.appearance = 'standard';
fixture.detectChanges();

expect(fixture.nativeElement.querySelector('input').hasAttribute('placeholder')).toBe(true);
}));


it('should not add the native select class if the control is not a native select', () => {
const fixture = createComponent(MatInputWithId);
fixture.detectChanges();
Expand Down Expand Up @@ -2111,14 +2120,15 @@ class MatInputWithLabel {}

@Component({
template: `
<mat-form-field [floatLabel]="floatLabel">
<mat-form-field [floatLabel]="floatLabel" [appearance]="appearance">
<mat-label>Label</mat-label>
<input matInput placeholder="Placeholder">
</mat-form-field>
`
})
class MatInputWithLabelAndPlaceholder {
floatLabel: FloatLabelType;
appearance: MatFormFieldAppearance = 'legacy';
}

@Component({
Expand Down
4 changes: 3 additions & 1 deletion src/material/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,9 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
// screen readers will read it out twice: once from the label and once from the attribute.
// TODO: can be removed once we get rid of the `legacy` style for the form field, because it's
// the only one that supports promoting the placeholder to a label.
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
const formField = this._formField;
const placeholder = formField && formField.appearance === 'legacy' && !formField._hasLabel() ?
null : this.placeholder;
if (placeholder !== this._previousPlaceholder) {
const element = this._elementRef.nativeElement;
this._previousPlaceholder = placeholder;
Expand Down

0 comments on commit 50e7344

Please sign in to comment.