Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(platform): list accessibility issues #11018

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions libs/core/src/lib/list/list-item/list-item.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/member-ordering */
import {
AfterContentInit,
Attribute,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Expand Down Expand Up @@ -132,6 +133,10 @@ export class ListItemComponent<T = any>
@HostBinding('class.fd-list__item--link')
link = false;

/** Aria-role attribute. */
@Input()
ariaRole: Nullable<string>;

/** @hidden */
@ContentChild(FD_RADIO_BUTTON_COMPONENT)
set radio(value: RadioButtonComponent) {
Expand Down Expand Up @@ -187,6 +192,11 @@ export class ListItemComponent<T = any>

/** @hidden */
@HostBinding('attr.role')
private get roleAttr(): string {
return this.ariaRole || this._role;
}

/** @hidden */
private _role = 'listitem'; // default for li elements

/** @hidden */
Expand All @@ -201,6 +211,7 @@ export class ListItemComponent<T = any>
constructor(
public readonly elementRef: ElementRef,
private readonly _changeDetectorRef: ChangeDetectorRef,
@Attribute('role') private readonly _defaultRole: string | null,
@Optional() @Inject(FD_LIST_UNREAD_INDICATOR) private readonly _unreadIndicator?: ListUnreadIndicator
) {
super(elementRef);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h5>Cozy</h5>
<fdp-list>
<fdp-list-group-header groupHeaderTitle="Group header 1"></fdp-list-group-header>
<fdp-list ariaLabel="Grouped list">
<fdp-list-group-header groupHeaderTitle="Group header 1" ariaLabel="Group header 1"></fdp-list-group-header>
<fdp-standard-list-item title="Title1"></fdp-standard-list-item>
<fdp-standard-list-item title="Title2"></fdp-standard-list-item>
<fdp-standard-list-item title="Title3"></fdp-standard-list-item>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { BehaviorSubject, Observable, of } from 'rxjs';

import { ListDataSource, DataProvider } from '@fundamental-ngx/platform/shared';
import { DataProvider, ListDataSource } from '@fundamental-ngx/platform/shared';

export interface User {
firstName: string;
Expand Down Expand Up @@ -114,9 +114,13 @@ const list_elements: User[] = [
];

export class ListDataProvider extends DataProvider<User> {
private readonly _totalItems = new BehaviorSubject(0);
constructor() {
super();
}
getTotalItems(): Observable<number> {
return this._totalItems.asObservable();
}
fetch(params: Map<string, string>): Observable<User[]> {
let data = list_elements;
const name = params.get('name');
Expand All @@ -125,6 +129,8 @@ export class ListDataProvider extends DataProvider<User> {
data = data.filter((user) => user.firstName.toLowerCase().indexOf(keyword) > -1);
}

this._totalItems.next(data.length);

return of(data);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { BehaviorSubject, Observable, of } from 'rxjs';

import { ListDataSource, DataProvider } from '@fundamental-ngx/platform/shared';
import { DataProvider, ListDataSource } from '@fundamental-ngx/platform/shared';

export interface User {
firstName: string;
Expand Down Expand Up @@ -114,9 +114,13 @@ const list_elements: User[] = [
];

export class ListDataProvider extends DataProvider<User> {
private readonly _totalItems = new BehaviorSubject(0);
constructor() {
super();
}
getTotalItems(): Observable<number> {
return this._totalItems.asObservable();
}
fetch(params: Map<string, string>): Observable<User[]> {
let data = list_elements;
const name = params.get('name');
Expand All @@ -125,6 +129,9 @@ export class ListDataProvider extends DataProvider<User> {
data = data.filter((user) => user.firstName.toLowerCase().indexOf(keyword) > -1);
}

// Update total items count when new request completed.
this._totalItems.next(data.length);

return of(data);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
(selectedItemChange)="_showItemInfo($event)"
[selection]="true"
[ariaMultiselectable]="true"
role="listbox"
>
<fdp-standard-list-item *fdpItemDef="let address" [title]="address.name"></fdp-standard-list-item>
</fdp-list>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
(selectedItemChange)="_showItemInfo($event)"
[selection]="true"
[ariaMultiselectable]="false"
role="listbox"
>
<fdp-standard-list-item
*fdpItemDef="let address"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
(keyup)="_onKeyUp($event)"
[attr.aria-label]="ariaLabel"
[attr.aria-level]="ariaLevel"
[attr.aria-posinet]="ariaPosinet"
[attr.role]="role"
[attr.aria-posinset]="ariaPosinset"
[attr.aria-setsize]="ariaSetSize | async"
[ariaRole]="_listItemRole"
[attr.aria-selected]="_selectedAttr"
>
<button #action class="fd-list__title" [attr.aria-label]="title" [attr.title]="title" (click)="_onActionClick()">
{{ title }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Component, ElementRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
Expand All @@ -6,6 +7,9 @@ import { RouterTestingModule } from '@angular/router/testing';
import { ActionListItemComponent } from './action-list-item.component';

import { PlatformListModule } from '../list.module';
import { SelectionType } from '../models/list';
import { BaseListItem } from '../base-list-item';
import { ListComponent } from '../list.component';

export interface Action {
title: string;
Expand All @@ -14,7 +18,7 @@ export interface Action {
@Component({
selector: 'fdp-test-fdp-action-list-item',
template: `
<fdp-list>
<fdp-list [selectionMode]="selectionMode">
<fdp-action-list-item title="Action 1"> </fdp-action-list-item>
<fdp-action-list-item title="Action 2"> </fdp-action-list-item>
<fdp-action-list-item title="Action 3"> </fdp-action-list-item>
Expand All @@ -26,9 +30,14 @@ class ActionListItemComponentTestComponent {
@ViewChild(ActionListItemComponent, { read: ElementRef, static: true })
actionListElement: ElementRef;

@ViewChild(ListComponent)
list: ListComponent<any>;

itemclick: string;
enterPress: string;

selectionMode: SelectionType = 'none';

onItemClick(): void {
this.itemclick = 'mouse is clicked';
}
Expand Down Expand Up @@ -84,10 +93,35 @@ describe('ActionListItemComponent', () => {
expect(actionItems[0].nativeElement.classList).toContain('fd-list__item--action');
});

it('Should display action item with role as option', () => {
it('Should display list item with role as listitem', () => {
const listContainer = fixture.debugElement.query(By.css('fdp-action-list-item .fd-list__item--action'));
fixture.detectChanges();
expect(listContainer.nativeElement.getAttribute('role')).toEqual('listitem');
});

it('Should display action item with role as option', async () => {
component.selectionMode = 'multi';
fixture.detectChanges();
const listItem = fixture.debugElement.query(By.css('fdp-action-list-item')).componentInstance as BaseListItem;
listItem.ngAfterViewInit();
fixture.detectChanges();
await fixture.whenStable();
let listContainer = fixture.debugElement.query(By.css('fdp-action-list-item .fd-list__item--action'));
expect(listContainer.nativeElement.getAttribute('role')).toEqual('option');
component.selectionMode = 'single';
fixture.detectChanges();
listItem.ngAfterViewInit();
fixture.detectChanges();
await fixture.whenStable();
listContainer = fixture.debugElement.query(By.css('fdp-action-list-item .fd-list__item--action'));
expect(listContainer.nativeElement.getAttribute('role')).toEqual('option');
component.selectionMode = 'none';
fixture.detectChanges();
listItem.ngAfterViewInit();
fixture.detectChanges();
await fixture.whenStable();
listContainer = fixture.debugElement.query(By.css('fdp-action-list-item .fd-list__item--action'));
expect(listContainer.nativeElement.getAttribute('role')).toEqual('listitem');
});

it('Should Action 1 Action 2 Action 3 and as Action 4 as list item', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ENTER, SPACE } from '@angular/cdk/keycodes';
import {
ChangeDetectionStrategy,
Component,
Expand All @@ -7,7 +8,6 @@ import {
Output,
ViewChild
} from '@angular/core';
import { ENTER, SPACE } from '@angular/cdk/keycodes';

import { KeyUtil } from '@fundamental-ngx/cdk/utils';

Expand All @@ -22,7 +22,10 @@ export class ActionChangeEvent {
selector: 'fdp-action-list-item',
templateUrl: './action-list-item.component.html',
providers: [{ provide: BaseListItem, useExisting: forwardRef(() => ActionListItemComponent) }],
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
role: 'none'
}
})
export class ActionListItemComponent extends BaseListItem {
/** Access button element*/
Expand Down
Loading
Loading