diff --git a/src/dev-app/chips/chips-demo.html b/src/dev-app/chips/chips-demo.html
index b5ebf78d29b4..178ed81cc080 100644
--- a/src/dev-app/chips/chips-demo.html
+++ b/src/dev-app/chips/chips-demo.html
@@ -124,6 +124,8 @@
Form Field
[matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)" />
+ <mat-hint>
is supported too!
+ <mat-error>
is supported too!
diff --git a/src/material-experimental/mdc-chips/chip-listbox.spec.ts b/src/material-experimental/mdc-chips/chip-listbox.spec.ts
index 2161a0d43cb3..e8a9bbfd1f59 100644
--- a/src/material-experimental/mdc-chips/chip-listbox.spec.ts
+++ b/src/material-experimental/mdc-chips/chip-listbox.spec.ts
@@ -638,6 +638,12 @@ describe('MDC-based MatChipListbox', () => {
expect(chipArray[4].focus).not.toHaveBeenCalled();
});
+
+ it('should support user binding to `aria-describedby`', fakeAsync(() => {
+ chipListboxInstance.userAriaDescribedBy = 'test';
+ fixture.detectChanges();
+ expect(chipListboxNativeElement.getAttribute('aria-describedby')).toBe('test');
+ }));
});
describe('multiple selection', () => {
diff --git a/src/material-experimental/mdc-chips/chip-listbox.ts b/src/material-experimental/mdc-chips/chip-listbox.ts
index 2f2effcee1f4..779dc8ef8260 100644
--- a/src/material-experimental/mdc-chips/chip-listbox.ts
+++ b/src/material-experimental/mdc-chips/chip-listbox.ts
@@ -66,8 +66,6 @@ export const MAT_CHIP_LISTBOX_CONTROL_VALUE_ACCESSOR: any = {
'class': 'mdc-evolution-chip-set mat-mdc-chip-listbox',
'[attr.role]': 'role',
'[tabIndex]': 'empty ? -1 : tabIndex',
- // TODO: replace this binding with use of AriaDescriber
- '[attr.aria-describedby]': '_ariaDescribedby || null',
'[attr.aria-required]': 'role ? required : null',
'[attr.aria-disabled]': 'disabled.toString()',
'[attr.aria-multiselectable]': 'multiple',
diff --git a/src/material-experimental/mdc-chips/chip-set.spec.ts b/src/material-experimental/mdc-chips/chip-set.spec.ts
index 358a59a60f55..6fb657457911 100644
--- a/src/material-experimental/mdc-chips/chip-set.spec.ts
+++ b/src/material-experimental/mdc-chips/chip-set.spec.ts
@@ -75,6 +75,12 @@ describe('MDC-based MatChipSet', () => {
fixture.detectChanges();
expect(chipSetNativeElement.getAttribute('role')).toBe('list');
});
+
+ it('should support user binding to `aria-describedby`', fakeAsync(() => {
+ chipSetInstance.userAriaDescribedBy = 'test';
+ fixture.detectChanges();
+ expect(chipSetNativeElement.getAttribute('aria-describedby')).toBe('test');
+ }));
});
});
diff --git a/src/material-experimental/mdc-chips/chip-set.ts b/src/material-experimental/mdc-chips/chip-set.ts
index ede85c8e3ed2..0202cd7b6d32 100644
--- a/src/material-experimental/mdc-chips/chip-set.ts
+++ b/src/material-experimental/mdc-chips/chip-set.ts
@@ -55,6 +55,7 @@ const _MatChipSetMixinBase = mixinTabIndex(MatChipSetBase);
'class': 'mat-mdc-chip-set mdc-evolution-chip-set',
'(keydown)': '_handleKeydown($event)',
'[attr.role]': 'role',
+ '[attr.aria-describedby]': 'userAriaDescribedBy || null',
},
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -85,6 +86,17 @@ export class MatChipSet
return this._getChipStream(chip => chip.destroyed);
}
+ @Input('aria-describedby')
+ get userAriaDescribedBy(): string {
+ return this._userAriaDescribedBy;
+ }
+
+ set userAriaDescribedBy(userAriaDescribedBy: string) {
+ this._userAriaDescribedBy = userAriaDescribedBy;
+ }
+
+ private _userAriaDescribedBy: string;
+
/** Whether the chip set is disabled. */
@Input()
get disabled(): boolean {
diff --git a/src/material/chips/chip-list.spec.ts b/src/material/chips/chip-list.spec.ts
index 528657485d66..f14cea8285b8 100644
--- a/src/material/chips/chip-list.spec.ts
+++ b/src/material/chips/chip-list.spec.ts
@@ -749,6 +749,8 @@ describe('MatChipList', () => {
let nativeChips: HTMLElement[];
describe('single selection', () => {
+ let chipListEl: HTMLElement;
+
beforeEach(() => {
fixture = createComponent(BasicChipList);
fixture.detectChanges();
@@ -757,6 +759,7 @@ describe('MatChipList', () => {
.queryAll(By.css('mat-chip'))
.map(chip => chip.nativeElement);
chips = fixture.componentInstance.chips;
+ chipListEl = fixture.debugElement.query(By.css('mat-chip-list'))!.nativeElement;
});
it('should take an initial view value with reactive forms', () => {
@@ -949,6 +952,22 @@ describe('MatChipList', () => {
expect(formField.classList).not.toContain('mat-focused');
}));
+
+ it('should set `aria-describedby` to the id of the mat-hint', fakeAsync(() => {
+ expect(chipListEl.getAttribute('aria-describedby')).toBeNull();
+
+ fixture.componentInstance.hint = 'test';
+ fixture.detectChanges();
+ const hint = fixture.debugElement.query(By.css('.mat-hint')).nativeElement;
+ expect(chipListEl.getAttribute('aria-describedby')).toBe(hint.getAttribute('id'));
+ expect(chipListEl.getAttribute('aria-describedby')).toMatch(/^mat-hint-\d+$/);
+ }));
+
+ it('should support user binding to `aria-describedby`', fakeAsync(() => {
+ fixture.componentInstance.ariaDescribedBy = 'test';
+ fixture.detectChanges();
+ expect(chipListEl.getAttribute('aria-describedby')).toBe('test');
+ }));
});
describe('multiple selection', () => {
@@ -1586,11 +1605,13 @@ class FormFieldChipList {
template: `
{{ food.viewValue }}
+ {{ hint }}
`,
})
@@ -1605,7 +1626,9 @@ class BasicChipList {
{value: 'pasta-6', viewValue: 'Pasta'},
{value: 'sushi-7', viewValue: 'Sushi'},
];
+ ariaDescribedBy: string = '';
control = new FormControl(null);
+ hint: string;
tabIndexOverride: number;
selectable: boolean;
diff --git a/src/material/chips/chip-list.ts b/src/material/chips/chip-list.ts
index 9fbeab038666..92519de160ab 100644
--- a/src/material/chips/chip-list.ts
+++ b/src/material/chips/chip-list.ts
@@ -197,7 +197,16 @@ export class MatChipList
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
- @Input('aria-describedby') userAriaDescribedBy: string;
+ @Input('aria-describedby')
+ get userAriaDescribedBy(): string {
+ return this._userAriaDescribedBy;
+ }
+ set userAriaDescribedBy(userAriaDescribedBy: string) {
+ this._userAriaDescribedBy = userAriaDescribedBy;
+ this.stateChanges.next();
+ }
+
+ private _userAriaDescribedBy: string;
/** An object used to control when error messages are shown. */
@Input() override errorStateMatcher: ErrorStateMatcher;
diff --git a/tools/public_api_guard/material/chips.md b/tools/public_api_guard/material/chips.md
index 29bb7709427f..30f872f53f22 100644
--- a/tools/public_api_guard/material/chips.md
+++ b/tools/public_api_guard/material/chips.md
@@ -256,7 +256,8 @@ export class MatChipList extends _MatChipListBase implements MatFormFieldControl
_uid: string;
protected _updateFocusForDestroyedChips(): void;
protected _updateTabIndex(): void;
- userAriaDescribedBy: string;
+ get userAriaDescribedBy(): string;
+ set userAriaDescribedBy(userAriaDescribedBy: string);
_userTabIndex: number | null;
get value(): any;
set value(value: any);