Skip to content

Commit

Permalink
Merge pull request #1646 from UUDigitalHumanitieslab/bugfix/tabs
Browse files Browse the repository at this point in the history
fix tabs IDs en ARIA roles
  • Loading branch information
lukavdplas authored Aug 15, 2024
2 parents 9ba2ca1 + a4786b9 commit 8a733c8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('DocumentViewComponent', () => {
});

it('should create tabs', () => {
const debug = fixture.debugElement.queryAll(By.css('a[role=tab]'));
const debug = fixture.debugElement.queryAll(By.css('[role=tab]'));
expect(debug.length).toBe(2);
expect(debug[0].attributes['id']).toBe('tab-speech');
expect(debug[1].attributes['id']).toBe('tab-scan');
Expand Down
25 changes: 16 additions & 9 deletions frontend/src/app/shared/tabs/tabs.component.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
<div class="tabs is-boxed">
<ul role="tablist" aria-label="select content to display">
<li *ngFor="let tab of tabs" [class.is-active]="activeTab === tab.id">
<a #tabLink id="{{tabLinkId(tab.id)}}" role="tab"
[attr.tabindex]="activeTab === tab.id ? 0 : -1"
[attr.aria-selected]="activeTab === tab.id"
[attr.aria-controls]="tab.id"
(click)="selectTab(tab)"
(keydown.ArrowLeft)="cycleTab($event)" (keydown.ArrowRight)="cycleTab($event)">
<span class="icon" *ngIf="tab.icon"><fa-icon [icon]="tab.icon" aria-hidden="true"></fa-icon></span>
<li *ngFor="let tab of tabs"
role="tab"
#tabLink [id]="tab.elementId"
[attr.tabindex]="activeTab === tab.id ? 0 : -1"
[class.is-active]="activeTab === tab.id"
[attr.aria-selected]="activeTab === tab.id"
[attr.aria-controls]="tab.id | slugify"
(click)="selectTab(tab)"
(keydown.ArrowLeft)="cycleTab($event)"
(keydown.ArrowRight)="cycleTab($event)">
<a role="none">
<span class="icon" *ngIf="tab.icon">
<fa-icon [icon]="tab.icon" aria-hidden="true" />
</span>
<span>{{tab.label}}</span>
</a>
</li>
</ul>
</div>

<div *ngFor="let panel of tabPanels" [attr.id]="panel.id" role="tabpanel" tabindex="-1" [hidden]="panel.id !== activeTab">
<div *ngFor="let panel of tabPanels" [attr.id]="panel.id | slugify"
role="tabpanel" tabindex="-1" [hidden]="panel.id !== activeTab">
<ng-container *ngIf="panel.id === activeTab" [ngTemplateOutlet]="panel.templateRef">
</ng-container>
</div>
2 changes: 2 additions & 0 deletions frontend/src/app/shared/tabs/tabs.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ describe('TabsComponent', () => {
component = fixture.componentInstance;
component.tabs = [{
label: 'First tab',
elementId: 'tab-1',
id: 1
}, {
label: 'Second tab',
elementId: 'tab-2',
id: 2
}];
fixture.detectChanges();
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/app/shared/tabs/tabs.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import * as _ from 'lodash';
import { TabPanelDirective } from './tab-panel.directive';
import { IconDefinition } from '@fortawesome/free-solid-svg-icons';
import { modulo } from '@utils/utils';
import { SlugifyPipe } from '../pipes/slugify.pipe';
import { SlugifyPipe } from '@shared/pipes/slugify.pipe';

interface Tab {
label: string; // display name
id: string | number;
elementId: string;
icon?: IconDefinition;
};

Expand All @@ -34,6 +35,7 @@ export class TabsComponent implements AfterContentInit {
ngAfterContentInit(): void {
this.tabs = this.tabPanels.map(tabPanel => ({
id: tabPanel.id,
elementId: this.tabLinkId(tabPanel.id),
label: tabPanel.title,
icon: tabPanel.icon,
}));
Expand All @@ -48,8 +50,7 @@ export class TabsComponent implements AfterContentInit {

cycleTab(event: KeyboardEvent) {
const target = event.target as Element;
const id = target.id;
const tabIndex = this.tabs.findIndex(tab => this.tabLinkId(tab.id) === id);
const tabIndex = this.tabs.findIndex(tab => tab.elementId === target.id);

const keyBindings = {
ArrowLeft: -1,
Expand All @@ -59,14 +60,14 @@ export class TabsComponent implements AfterContentInit {
const shift = keyBindings[event.key];
const newIndex = modulo(tabIndex + shift, this.tabs.length);
const newTab = this.tabs[newIndex];
this.setTabLinkFocus(newTab.id);
this.setTabLinkFocus(newTab.elementId);
this.selectTab(newTab);
}

setTabLinkFocus(id: string | number) {
setTabLinkFocus(elementId: string) {
this.tabLinks.forEach(tabLink => {
const element = tabLink.nativeElement;
const focus = element.id === this.tabLinkId(id);
const focus = element.id === elementId;
element.tabIndex = focus ? 0 : -1;
if (focus) {
element.focus();
Expand Down

0 comments on commit 8a733c8

Please sign in to comment.