Skip to content

Commit f05e65a

Browse files
authored
fix(material/chips): allow for role to be overwritten on chip list and chip (#15794)
Allows for the ARIA `role` of the `mat-chip-list` and `mat-chip` to be overwritten. Fixes #15787.
1 parent 175937e commit f05e65a

File tree

12 files changed

+102
-23
lines changed

12 files changed

+102
-23
lines changed

src/material-experimental/mdc-chips/chip-grid.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ describe('MDC-based MatChipGrid', () => {
103103

104104
expect(chipGridNativeElement.hasAttribute('role')).toBe(false);
105105
});
106+
107+
it('should be able to set a custom role', () => {
108+
testComponent.role = 'listbox';
109+
fixture.detectChanges();
110+
111+
expect(chipGridNativeElement.getAttribute('role')).toBe('listbox');
112+
});
106113
});
107114

108115
describe('focus behaviors', () => {
@@ -1028,7 +1035,7 @@ describe('MDC-based MatChipGrid', () => {
10281035

10291036
@Component({
10301037
template: `
1031-
<mat-chip-grid [tabIndex]="tabIndex" #chipGrid>
1038+
<mat-chip-grid [tabIndex]="tabIndex" [role]="role" #chipGrid>
10321039
<mat-chip-row *ngFor="let i of chips"
10331040
[editable]="editable">
10341041
{{name}} {{i + 1}}
@@ -1041,6 +1048,7 @@ class StandardChipGrid {
10411048
tabIndex: number = 0;
10421049
chips = [0, 1, 2, 3, 4];
10431050
editable = false;
1051+
role: string | null = null;
10441052
}
10451053

10461054
@Component({

src/material-experimental/mdc-chips/chip-grid.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export class MatChipGrid
143143
/** The chip input to add more chips */
144144
protected _chipInput: MatChipTextControl;
145145

146+
protected override _defaultRole = 'grid';
147+
146148
/**
147149
* Function when touched. Set as part of ControlValueAccessor implementation.
148150
* @docs-private
@@ -186,11 +188,6 @@ export class MatChipGrid
186188
);
187189
}
188190

189-
/** The ARIA role applied to the chip grid. */
190-
override get role(): string | null {
191-
return this.empty ? null : 'grid';
192-
}
193-
194191
/**
195192
* Implemented as part of MatFormFieldControl.
196193
* @docs-private

src/material-experimental/mdc-chips/chip-listbox.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ describe('MDC-based MatChipListbox', () => {
8989
expect(chipListboxNativeElement.hasAttribute('role')).toBe(false);
9090
});
9191

92+
it('should be able to set a custom role', () => {
93+
testComponent.role = 'grid';
94+
fixture.detectChanges();
95+
96+
expect(chipListboxNativeElement.getAttribute('role')).toBe('grid');
97+
});
98+
9299
it('should not set aria-required when it does not have a role', () => {
93100
testComponent.chips = [];
94101
fixture.detectChanges();
@@ -745,7 +752,7 @@ describe('MDC-based MatChipListbox', () => {
745752

746753
@Component({
747754
template: `
748-
<mat-chip-listbox [tabIndex]="tabIndex" [selectable]="selectable">
755+
<mat-chip-listbox [tabIndex]="tabIndex" [selectable]="selectable" [role]="role">
749756
<mat-chip-option *ngFor="let i of chips" (select)="chipSelect(i)"
750757
(deselect)="chipDeselect(i)">
751758
{{name}} {{i + 1}}
@@ -759,6 +766,7 @@ class StandardChipListbox {
759766
chipDeselect: (index?: number) => void = () => {};
760767
tabIndex: number = 0;
761768
chips = [0, 1, 2, 3, 4];
769+
role: string | null = null;
762770
}
763771

764772
@Component({

src/material-experimental/mdc-chips/chip-listbox.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,8 @@ export class MatChipListbox
9999
*/
100100
_onChange: (value: any) => void = () => {};
101101

102-
/** The ARIA role applied to the chip listbox. */
103102
// TODO: MDC uses `grid` here
104-
override get role(): string | null {
105-
return this.empty ? null : 'listbox';
106-
}
103+
protected override _defaultRole = 'listbox';
107104

108105
/** Whether the user should be allowed to select multiple chips. */
109106
@Input()

src/material-experimental/mdc-chips/chip-option.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,17 @@ describe('MDC-based Option Chips', () => {
185185
.withContext('Expected chip ripples to be disabled.')
186186
.toBe(true);
187187
});
188+
189+
it('should have the correct role', () => {
190+
expect(chipNativeElement.getAttribute('role')).toBe('presentation');
191+
});
192+
193+
it('should be able to set a custom role', () => {
194+
chipInstance.role = 'button';
195+
fixture.detectChanges();
196+
197+
expect(chipNativeElement.getAttribute('role')).toBe('button');
198+
});
188199
});
189200

190201
describe('keyboard behavior', () => {

src/material-experimental/mdc-chips/chip-row.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ describe('MDC-based Row Chips', () => {
104104

105105
expect(event.defaultPrevented).toBe(true);
106106
});
107+
108+
it('should have the correct role', () => {
109+
expect(chipNativeElement.getAttribute('role')).toBe('row');
110+
});
111+
112+
it('should be able to set a custom role', () => {
113+
chipInstance.role = 'button';
114+
fixture.detectChanges();
115+
116+
expect(chipNativeElement.getAttribute('role')).toBe('button');
117+
});
107118
});
108119

109120
describe('keyboard behavior', () => {

src/material-experimental/mdc-chips/chip-set.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ export class MatChipSet
8888
/** Subject that emits when the component has been destroyed. */
8989
protected _destroyed = new Subject<void>();
9090

91+
/** Role to use if it hasn't been overwritten by the user. */
92+
protected _defaultRole = 'presentation';
93+
9194
/** Combined stream of all of the child chips' remove events. */
9295
get chipDestroyedChanges(): Observable<MatChipEvent> {
9396
return this._getChipStream(chip => chip.destroyed);
@@ -163,17 +166,17 @@ export class MatChipSet
163166
/** The ARIA role applied to the chip set. */
164167
@Input()
165168
get role(): string | null {
166-
if (this._role) {
167-
return this._role;
168-
} else {
169-
return this.empty ? null : 'presentation';
169+
if (this._explicitRole) {
170+
return this._explicitRole;
170171
}
172+
173+
return this.empty ? null : this._defaultRole;
171174
}
172175

173176
set role(value: string | null) {
174-
this._role = value;
177+
this._explicitRole = value;
175178
}
176-
private _role: string | null = null;
179+
private _explicitRole: string | null = null;
177180

178181
/** Whether any of the chips inside of this chip-set has focus. */
179182
get focused(): boolean {

src/material/chips/chip-list.spec.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ describe('MatChipList', () => {
191191
expect(chipListNativeElement.hasAttribute('role')).toBe(false);
192192
expect(chipListNativeElement.hasAttribute('aria-required')).toBe(false);
193193
});
194+
195+
it('should be able to set a custom role', () => {
196+
fixture.componentInstance.chipList.role = 'grid';
197+
fixture.detectChanges();
198+
199+
expect(chipListNativeElement.getAttribute('role')).toBe('grid');
200+
});
194201
});
195202

196203
describe('focus behaviors', () => {
@@ -1725,9 +1732,9 @@ class FalsyValueChipList {
17251732
@Component({
17261733
template: `
17271734
<mat-chip-list>
1728-
<mat-chip *ngFor="let food of foods" [value]="food.value" [selected]="food.selected">
1729-
{{ food.viewValue }}
1730-
</mat-chip>
1735+
<mat-chip *ngFor="let food of foods" [value]="food.value" [selected]="food.selected">
1736+
{{ food.viewValue }}
1737+
</mat-chip>
17311738
</mat-chip-list>
17321739
`,
17331740
})
@@ -1738,6 +1745,7 @@ class SelectedChipList {
17381745
{value: 2, viewValue: 'Pasta', selected: true},
17391746
];
17401747
@ViewChildren(MatChip) chips: QueryList<MatChip>;
1748+
@ViewChild(MatChipList, {static: false}) chipList: MatChipList;
17411749
}
17421750

17431751
@Component({

src/material/chips/chip-list.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,18 @@ export class MatChipList
180180
}
181181

182182
/** The ARIA role applied to the chip list. */
183+
@Input()
183184
get role(): string | null {
185+
if (this._explicitRole) {
186+
return this._explicitRole;
187+
}
188+
184189
return this.empty ? null : 'listbox';
185190
}
191+
set role(role: string | null) {
192+
this._explicitRole = role;
193+
}
194+
private _explicitRole?: string | null;
186195

187196
/**
188197
* Implemented as part of MatFormFieldControl.

src/material/chips/chip.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,28 @@ describe('MatChip', () => {
7373

7474
expect(chip.getAttribute('tabindex')).toBe('15');
7575
});
76+
77+
it('should have the correct role', () => {
78+
fixture = TestBed.createComponent(BasicChip);
79+
fixture.detectChanges();
80+
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
81+
chipNativeElement = chipDebugElement.nativeElement;
82+
83+
expect(chipNativeElement.getAttribute('role')).toBe('option');
84+
});
85+
86+
it('should be able to set a custom role', () => {
87+
fixture = TestBed.createComponent(BasicChip);
88+
fixture.detectChanges();
89+
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
90+
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
91+
chipNativeElement = chipDebugElement.nativeElement;
92+
93+
chipInstance.role = 'gridcell';
94+
fixture.detectChanges();
95+
96+
expect(chipNativeElement.getAttribute('role')).toBe('gridcell');
97+
});
7698
});
7799

78100
describe('MatChip', () => {

0 commit comments

Comments
 (0)