Skip to content

Commit 64a4c86

Browse files
fix(components/lists): use 'grid' role for selectable repeaters (#751)
1 parent 1343d3d commit 64a4c86

File tree

4 files changed

+21
-14
lines changed

4 files changed

+21
-14
lines changed

libs/components/lists/src/lib/modules/repeater/repeater-item.component.html

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
class="sky-repeater-item sky-padding-even-default"
33
[attr.aria-label]="itemName"
44
[attr.aria-selected]="selectable ? !!isSelected : undefined"
5-
[attr.role]="showInlineForm ? 'form' : (itemRole$ | async)?.item"
5+
[attr.role]="(itemRole$ | async)?.item"
6+
[attr.tabIndex]="tabindex"
67
[ngClass]="{
78
'sky-repeater-item-active': isActive,
89
'sky-repeater-item-collapsible': isCollapsible,
@@ -34,7 +35,10 @@
3435
</div>
3536

3637
<ng-template #skyRepeaterItemLeft>
37-
<div class="sky-repeater-item-left">
38+
<div
39+
[attr.role]="!showInlineForm ? (itemRole$ | async)?.content : undefined"
40+
class="sky-repeater-item-left"
41+
>
3842
<ng-container *ngIf="reorderable">
3943
<span
4044
aria-live="assertive"
@@ -90,14 +94,11 @@
9094
<div class="sky-repeater-item-right" #itemHeaderRef>
9195
<div
9296
class="sky-repeater-item-header"
97+
[attr.role]="(itemRole$ | async)?.title"
9398
[hidden]="titleRef.children.length === 0"
9499
(click)="headerClick()"
95100
>
96-
<div
97-
class="sky-repeater-item-title sky-emphasized"
98-
#titleRef
99-
[attr.role]="(itemRole$ | async)?.title"
100-
>
101+
<div class="sky-repeater-item-title sky-emphasized" #titleRef>
101102
<ng-content select="sky-repeater-item-title"></ng-content>
102103
</div>
103104
<button

libs/components/lists/src/lib/modules/repeater/repeater-item.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export class SkyRepeaterItemComponent
5454
* - Disabled items should not be focusable per [W3C](https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_disabled_controls).
5555
* - One item per list/grid/listbox should be tab focusable per [W3C](https://www.w3.org/TR/wai-aria-practices-1.1/#grid).
5656
*/
57-
@HostBinding()
5857
public get tabindex(): 0 | -1 {
5958
return this.#repeaterService.items.filter((item) => !item.disabled)[0] ===
6059
this
@@ -420,7 +419,7 @@ export class SkyRepeaterItemComponent
420419
':focus-within'
421420
)
422421
) {
423-
activateItem.#elementRef.nativeElement.focus();
422+
activateItem.itemRef?.nativeElement.focus();
424423
}
425424
}
426425
}

libs/components/lists/src/lib/modules/repeater/repeater.component.spec.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,15 @@ describe('Repeater item component', () => {
820820
fixture.detectChanges();
821821
await fixture.whenStable();
822822
await expectAsync(fixture.nativeElement).toBeAccessible();
823+
824+
// Confirm elements are using appropriate roles.
825+
const repeaterEl = fixture.nativeElement.querySelector('.sky-repeater');
826+
expect(repeaterEl.getAttribute('role')).toEqual('grid');
827+
expect(
828+
repeaterEl
829+
.querySelector('sky-repeater-item:first-child > .sky-repeater-item')
830+
.getAttribute('role')
831+
).toEqual('row');
823832
});
824833

825834
it('should update the isSelected property when the user clicks the checkbox', fakeAsync(() => {
@@ -1639,7 +1648,7 @@ describe('Repeater item component', () => {
16391648
cmp.showItemWithNoContent = true;
16401649
detectChangesAndTick(fixture);
16411650
const items: Element[] = Array.from(
1642-
el.querySelectorAll('sky-repeater-item')
1651+
el.querySelectorAll('.sky-repeater-item')
16431652
);
16441653
expect(items.length).toEqual(4);
16451654
const sequence = [
@@ -1733,7 +1742,7 @@ describe('Repeater item component', () => {
17331742
el.querySelector('.sky-repeater-item').getAttribute('role')
17341743
).toEqual('row');
17351744
expect(
1736-
el.querySelector('.sky-repeater-item-title').getAttribute('role')
1745+
el.querySelector('.sky-repeater-item-header').getAttribute('role')
17371746
).toEqual('rowheader');
17381747
expect(
17391748
el.querySelector('.sky-repeater-item-content').getAttribute('role')

libs/components/lists/src/lib/modules/repeater/repeater.component.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,16 +401,14 @@ export class SkyRepeaterComponent
401401
const hasInteraction =
402402
this.reorderable ||
403403
this.items?.some((item) => item.isCollapsible) ||
404+
this.items?.some((item) => !!item.selectable) ||
404405
!!(this.#elementRef.nativeElement as HTMLElement).querySelector(
405406
interactionSelector
406407
);
407408

408409
if (hasInteraction) {
409410
// If the repeater matches interaction selector https://www.w3.org/TR/wai-aria-practices-1.1/#grid
410411
autoRole = 'grid';
411-
} else if (this.items?.some((item) => !!item.selectable)) {
412-
// If the only interaction is select https://www.w3.org/TR/wai-aria-practices-1.1/#Listbox
413-
autoRole = 'listbox';
414412
}
415413

416414
if (this.role !== autoRole) {

0 commit comments

Comments
 (0)