Skip to content

Commit 19ca1b2

Browse files
crisbetoandrewseguin
authored andcommitted
fix(material/autocomplete): avoid assigning invalid aria-labelledby values (#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)
1 parent 032c4e4 commit 19ca1b2

File tree

5 files changed

+53
-6
lines changed

5 files changed

+53
-6
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,18 @@ describe('MDC-based MatAutocomplete', () => {
15131513
expect(panel.hasAttribute('aria-label')).toBe(false);
15141514
});
15151515

1516+
it('should trim aria-labelledby if the input does not have a label', () => {
1517+
fixture.componentInstance.hasLabel = false;
1518+
fixture.detectChanges();
1519+
fixture.componentInstance.ariaLabelledby = 'myLabelId';
1520+
fixture.componentInstance.trigger.openPanel();
1521+
fixture.detectChanges();
1522+
1523+
const panel =
1524+
fixture.debugElement.query(By.css('.mat-mdc-autocomplete-panel'))!.nativeElement;
1525+
expect(panel.getAttribute('aria-labelledby')).toBe(`myLabelId`);
1526+
});
1527+
15161528
it('should clear aria-labelledby from the panel if an aria-label is set', () => {
15171529
fixture.componentInstance.ariaLabel = 'My label';
15181530
fixture.componentInstance.trigger.openPanel();
@@ -1524,6 +1536,17 @@ describe('MDC-based MatAutocomplete', () => {
15241536
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
15251537
});
15261538

1539+
it('should clear aria-labelledby if the form field does not have a label', () => {
1540+
fixture.componentInstance.hasLabel = false;
1541+
fixture.detectChanges();
1542+
fixture.componentInstance.trigger.openPanel();
1543+
fixture.detectChanges();
1544+
1545+
const panel =
1546+
fixture.debugElement.query(By.css('.mat-mdc-autocomplete-panel'))!.nativeElement;
1547+
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
1548+
});
1549+
15271550
it('should support setting a custom aria-label', () => {
15281551
fixture.componentInstance.ariaLabel = 'Custom Label';
15291552
fixture.componentInstance.trigger.openPanel();
@@ -2762,7 +2785,7 @@ describe('MDC-based MatAutocomplete', () => {
27622785

27632786
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
27642787
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
2765-
<mat-label>State</mat-label>
2788+
<mat-label *ngIf="hasLabel">State</mat-label>
27662789
<input
27672790
matInput
27682791
placeholder="State"
@@ -2795,6 +2818,7 @@ class SimpleAutocomplete implements OnDestroy {
27952818
width: number;
27962819
disableRipple = false;
27972820
autocompleteDisabled = false;
2821+
hasLabel = true;
27982822
ariaLabel: string;
27992823
ariaLabelledby: string;
28002824
panelClass = 'class-one class-two';

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ export abstract class _MatAutocompleteTriggerBase implements ControlValueAccesso
594594
if (!overlayRef) {
595595
this._portal = new TemplatePortal(this.autocomplete.template,
596596
this._viewContainerRef,
597-
{id: this._formField?._labelId});
597+
{id: this._formField?.getLabelId()});
598598
overlayRef = this._overlay.create(this._getOverlayConfig());
599599
this._overlayRef = overlayRef;
600600

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,17 @@ describe('MatAutocomplete', () => {
15001500
expect(panel.hasAttribute('aria-label')).toBe(false);
15011501
});
15021502

1503+
it('should trim aria-labelledby if the input does not have a label', () => {
1504+
fixture.componentInstance.hasLabel = false;
1505+
fixture.detectChanges();
1506+
fixture.componentInstance.ariaLabelledby = 'myLabelId';
1507+
fixture.componentInstance.trigger.openPanel();
1508+
fixture.detectChanges();
1509+
1510+
const panel = fixture.debugElement.query(By.css('.mat-autocomplete-panel'))!.nativeElement;
1511+
expect(panel.getAttribute('aria-labelledby')).toBe(`myLabelId`);
1512+
});
1513+
15031514
it('should clear aria-labelledby from the panel if an aria-label is set', () => {
15041515
fixture.componentInstance.ariaLabel = 'My label';
15051516
fixture.componentInstance.trigger.openPanel();
@@ -1510,6 +1521,16 @@ describe('MatAutocomplete', () => {
15101521
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
15111522
});
15121523

1524+
it('should clear aria-labelledby if the form field does not have a label', () => {
1525+
fixture.componentInstance.hasLabel = false;
1526+
fixture.detectChanges();
1527+
fixture.componentInstance.trigger.openPanel();
1528+
fixture.detectChanges();
1529+
1530+
const panel = fixture.debugElement.query(By.css('.mat-autocomplete-panel'))!.nativeElement;
1531+
expect(panel.hasAttribute('aria-labelledby')).toBe(false);
1532+
});
1533+
15131534
it('should support setting a custom aria-label', () => {
15141535
fixture.componentInstance.ariaLabel = 'Custom Label';
15151536
fixture.componentInstance.trigger.openPanel();
@@ -2760,9 +2781,9 @@ describe('MatAutocomplete', () => {
27602781

27612782
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
27622783
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
2784+
<mat-label *ngIf="hasLabel">State</mat-label>
27632785
<input
27642786
matInput
2765-
placeholder="State"
27662787
[matAutocomplete]="auto"
27672788
[matAutocompletePosition]="position"
27682789
[matAutocompleteDisabled]="autocompleteDisabled"
@@ -2792,6 +2813,7 @@ class SimpleAutocomplete implements OnDestroy {
27922813
width: number;
27932814
disableRipple = false;
27942815
autocompleteDisabled = false;
2816+
hasLabel = true;
27952817
ariaLabel: string;
27962818
ariaLabelledby: string;
27972819
panelClass = 'class-one class-two';

src/material/autocomplete/autocomplete.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,13 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp
258258
}
259259

260260
/** Gets the aria-labelledby for the autocomplete panel. */
261-
_getPanelAriaLabelledby(labelId: string): string | null {
261+
_getPanelAriaLabelledby(labelId: string | null): string | null {
262262
if (this.ariaLabel) {
263263
return null;
264264
}
265265

266-
return this.ariaLabelledby ? labelId + ' ' + this.ariaLabelledby : labelId;
266+
const labelExpression = labelId ? labelId + ' ' : '';
267+
return this.ariaLabelledby ? labelExpression + this.ariaLabelledby : labelId;
267268
}
268269

269270

tools/public_api_guard/material/autocomplete.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export declare abstract class _MatAutocompleteBase extends _MatAutocompleteMixin
2727
template: TemplateRef<any>;
2828
constructor(_changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef<HTMLElement>, defaults: MatAutocompleteDefaultOptions, platform?: Platform);
2929
_emitSelectEvent(option: _MatOptionBase): void;
30-
_getPanelAriaLabelledby(labelId: string): string | null;
30+
_getPanelAriaLabelledby(labelId: string | null): string | null;
3131
_getScrollTop(): number;
3232
_setScrollTop(scrollTop: number): void;
3333
_setVisibility(): void;

0 commit comments

Comments
 (0)