Skip to content

Commit

Permalink
feat: Agnostic a11y-tabs component
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeyber committed Apr 27, 2021
1 parent a48ec0f commit 5b1a887
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1 @@
<ng-container *ngIf="components$ | async as components">
<ng-container *ngFor="let component of components; let i = index">
<ng-container *ngIf="component">
<button [class.active]="i === activeTabNum" (click)="select(i, $event)">
{{
component.title | cxTranslate: { param: tabTitleParams[i] | async }
}}
</button>
</ng-container>
</ng-container>

<ng-container *ngFor="let component of components; let i = index">
<ng-container *ngIf="component">
<div [class.active]="i === activeTabNum">
<ng-template [cxOutlet]="component.flexType" [cxOutletContext]="{}">
<ng-container [cxComponentWrapper]="component"></ng-container>
</ng-template>
</div>
</ng-container>
</ng-container>
</ng-container>
<cx-tab [tabs$]="tabs$"></cx-tab>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ComponentWrapperDirective } from '../../../cms-structure/page/component
import { CmsComponentData } from '../../../cms-structure/page/model/index';
import { BreakpointService } from '../../../layout/breakpoint/breakpoint.service';
import { BREAKPOINT } from '../../../layout/config/layout-config';
import { Tab } from '../tab/Tab';

@Component({
selector: 'cx-tab-paragraph-container',
Expand Down Expand Up @@ -87,6 +88,15 @@ export class TabParagraphContainerComponent
)
);

tabs$: Observable<Tab[]> = this.components$.pipe(
map((components) =>
components.map((component) => ({
title: component.title,
cxComponent: component,
}))
)
);

select(tabNum: number, event?: MouseEvent): void {
this.breakpointService
?.isDown(BREAKPOINT.sm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import { NgModule } from '@angular/core';
import { CmsConfig, I18nModule, provideDefaultConfig } from '@spartacus/core';
import { OutletModule } from '../../../cms-structure/outlet/outlet.module';
import { PageComponentModule } from '../../../cms-structure/page/component/page-component.module';
import { TabModule } from '../tab/tab.module';
import { TabParagraphContainerComponent } from './tab-paragraph-container.component';

@NgModule({
imports: [CommonModule, PageComponentModule, OutletModule, I18nModule],
imports: [
CommonModule,
PageComponentModule,
OutletModule,
I18nModule,
TabModule,
],
providers: [
provideDefaultConfig(<CmsConfig>{
cmsComponents: {
Expand Down
7 changes: 7 additions & 0 deletions projects/storefrontlib/src/cms-components/content/tab/Tab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TemplateRef } from '@angular/core';

export interface Tab {
title: string;
template?: TemplateRef<any>;
cxComponent?: any;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<ng-container *ngIf="tabs$ | async as tabs">
<div role="tablist" aria-label="Product Information" style="display: block">
<button
*ngFor="let tab of tabs; let i = index"
[class.active]="isOpen(i)"
(click)="select(i, $event)"
(keydown.ArrowLeft)="select(i - 1, $event)"
(keydown.ArrowUp)="select(i - 1, $event)"
(keydown.ArrowRight)="select(i + 1, $event)"
(keydown.ArrowDown)="select(i + 1, $event)"
[attr.aria-selected]="isOpen(i)"
[attr.aria-expanded]="isOpen(i)"
[attr.tabindex]="isOpen(i) ? 0 : -1"
aria-controls="nils-tab"
id="nils"
#tabButton
>
{{ tab.title | cxTranslate }}
</button>
</div>

<div
*ngFor="let tab of tabs; let i = index"
[class.active]="isOpen(i)"
role="tabpanel"
aria-labelledby="nils"
id="nils-tab"
[attr.tabindex]="isOpen(i) ? 0 : -1"
[style.display]="isOpen(i) ? 'block' : 'none'"
>
<ng-container
*ngIf="tab.template"
[ngTemplateOutlet]="tab.template"
></ng-container>

<ng-template
*ngIf="tab.cxComponent as component"
[cxOutlet]="component.flexType"
[cxOutletContext]="{}"
#componentTemplate
>
<ng-container [cxComponentWrapper]="component"></ng-container>
</ng-template>
</div>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
ChangeDetectionStrategy,
Component,
Input,
QueryList,
ViewChildren,
} from '@angular/core';
import { Observable } from 'rxjs';
import { filter, take } from 'rxjs/operators';
import { Tab } from './Tab';

@Component({
selector: 'cx-tab',
templateUrl: './tab.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabComponent {
@Input() tabs$: Observable<Tab[]>;
@Input() openTabs: number[] = [0];

@ViewChildren('tabButton')
tabButtons: QueryList<any>;

constructor() {}

select(tabNum: number, event?: MouseEvent | KeyboardEvent): void {
event?.preventDefault();

tabNum = this.keepTabNumInBounds(tabNum);
this.openTabs = [tabNum];
this.tabButtons.toArray()[tabNum].nativeElement.focus();
}

isOpen(tabNum: number): boolean {
return this.openTabs.find((open: number) => open === tabNum) !== undefined;
}

private keepTabNumInBounds(tabNum: number) {
this.tabs$
.pipe(
filter((tabs) => !!tabs),
take(1)
)
.subscribe((tabs) => {
tabNum =
tabNum < 0
? tabs.length - 1
: tabNum > tabs.length - 1
? (tabNum = 0)
: tabNum;
});
return tabNum;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { I18nModule } from '@spartacus/core';
import { OutletModule } from '../../../cms-structure/outlet/outlet.module';
import { PageComponentModule } from '../../../cms-structure/page/component/page-component.module';
import { TabComponent } from './tab.component';

@NgModule({
imports: [CommonModule, PageComponentModule, OutletModule, I18nModule],
declarations: [TabComponent],
entryComponents: [TabComponent],
exports: [TabComponent],
})
export class TabModule {}

0 comments on commit 5b1a887

Please sign in to comment.