Skip to content

Commit

Permalink
fix(material/autocomplete): avoid assigning invalid aria-labelledby v…
Browse files Browse the repository at this point in the history
…alues (#22261)

* Fixes that we assign `aria-labelledby` even if there is no label.
* Fixes that we don't trim the value of `aria-labelledby` when there is no label, but
there is a custom `aria-labelledby`.

Fixes #22256.

(cherry picked from commit ee49922)
  • Loading branch information
crisbeto authored and andrewseguin committed Mar 19, 2021
1 parent 032c4e4 commit 19ca1b2
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 6 deletions.
26 changes: 25 additions & 1 deletion src/material-experimental/mdc-autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,18 @@ describe('MDC-based MatAutocomplete', () => {
expect(panel.hasAttribute('aria-label')).toBe(false);
});

it('should trim aria-labelledby if the input does not have a label', () => {
fixture.componentInstance.hasLabel = false;
fixture.detectChanges();
fixture.componentInstance.ariaLabelledby = 'myLabelId';
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();

const panel =
fixture.debugElement.query(By.css('.mat-mdc-autocomplete-panel'))!.nativeElement;
expect(panel.getAttribute('aria-labelledby')).toBe(`myLabelId`);
});

it('should clear aria-labelledby from the panel if an aria-label is set', () => {
fixture.componentInstance.ariaLabel = 'My label';
fixture.componentInstance.trigger.openPanel();
Expand All @@ -1524,6 +1536,17 @@ describe('MDC-based MatAutocomplete', () => {
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
});

it('should clear aria-labelledby if the form field does not have a label', () => {
fixture.componentInstance.hasLabel = false;
fixture.detectChanges();
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();

const panel =
fixture.debugElement.query(By.css('.mat-mdc-autocomplete-panel'))!.nativeElement;
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
});

it('should support setting a custom aria-label', () => {
fixture.componentInstance.ariaLabel = 'Custom Label';
fixture.componentInstance.trigger.openPanel();
Expand Down Expand Up @@ -2762,7 +2785,7 @@ describe('MDC-based MatAutocomplete', () => {

const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
<mat-label>State</mat-label>
<mat-label *ngIf="hasLabel">State</mat-label>
<input
matInput
placeholder="State"
Expand Down Expand Up @@ -2795,6 +2818,7 @@ class SimpleAutocomplete implements OnDestroy {
width: number;
disableRipple = false;
autocompleteDisabled = false;
hasLabel = true;
ariaLabel: string;
ariaLabelledby: string;
panelClass = 'class-one class-two';
Expand Down
2 changes: 1 addition & 1 deletion src/material/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ export abstract class _MatAutocompleteTriggerBase implements ControlValueAccesso
if (!overlayRef) {
this._portal = new TemplatePortal(this.autocomplete.template,
this._viewContainerRef,
{id: this._formField?._labelId});
{id: this._formField?.getLabelId()});
overlayRef = this._overlay.create(this._getOverlayConfig());
this._overlayRef = overlayRef;

Expand Down
24 changes: 23 additions & 1 deletion src/material/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,17 @@ describe('MatAutocomplete', () => {
expect(panel.hasAttribute('aria-label')).toBe(false);
});

it('should trim aria-labelledby if the input does not have a label', () => {
fixture.componentInstance.hasLabel = false;
fixture.detectChanges();
fixture.componentInstance.ariaLabelledby = 'myLabelId';
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();

const panel = fixture.debugElement.query(By.css('.mat-autocomplete-panel'))!.nativeElement;
expect(panel.getAttribute('aria-labelledby')).toBe(`myLabelId`);
});

it('should clear aria-labelledby from the panel if an aria-label is set', () => {
fixture.componentInstance.ariaLabel = 'My label';
fixture.componentInstance.trigger.openPanel();
Expand All @@ -1510,6 +1521,16 @@ describe('MatAutocomplete', () => {
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
});

it('should clear aria-labelledby if the form field does not have a label', () => {
fixture.componentInstance.hasLabel = false;
fixture.detectChanges();
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();

const panel = fixture.debugElement.query(By.css('.mat-autocomplete-panel'))!.nativeElement;
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
});

it('should support setting a custom aria-label', () => {
fixture.componentInstance.ariaLabel = 'Custom Label';
fixture.componentInstance.trigger.openPanel();
Expand Down Expand Up @@ -2760,9 +2781,9 @@ describe('MatAutocomplete', () => {

const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
<mat-label *ngIf="hasLabel">State</mat-label>
<input
matInput
placeholder="State"
[matAutocomplete]="auto"
[matAutocompletePosition]="position"
[matAutocompleteDisabled]="autocompleteDisabled"
Expand Down Expand Up @@ -2792,6 +2813,7 @@ class SimpleAutocomplete implements OnDestroy {
width: number;
disableRipple = false;
autocompleteDisabled = false;
hasLabel = true;
ariaLabel: string;
ariaLabelledby: string;
panelClass = 'class-one class-two';
Expand Down
5 changes: 3 additions & 2 deletions src/material/autocomplete/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,12 +258,13 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp
}

/** Gets the aria-labelledby for the autocomplete panel. */
_getPanelAriaLabelledby(labelId: string): string | null {
_getPanelAriaLabelledby(labelId: string | null): string | null {
if (this.ariaLabel) {
return null;
}

return this.ariaLabelledby ? labelId + ' ' + this.ariaLabelledby : labelId;
const labelExpression = labelId ? labelId + ' ' : '';
return this.ariaLabelledby ? labelExpression + this.ariaLabelledby : labelId;
}


Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/material/autocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export declare abstract class _MatAutocompleteBase extends _MatAutocompleteMixin
template: TemplateRef<any>;
constructor(_changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef<HTMLElement>, defaults: MatAutocompleteDefaultOptions, platform?: Platform);
_emitSelectEvent(option: _MatOptionBase): void;
_getPanelAriaLabelledby(labelId: string): string | null;
_getPanelAriaLabelledby(labelId: string | null): string | null;
_getScrollTop(): number;
_setScrollTop(scrollTop: number): void;
_setVisibility(): void;
Expand Down

0 comments on commit 19ca1b2

Please sign in to comment.