Skip to content

Commit

Permalink
feat(tabs): add tabs component
Browse files Browse the repository at this point in the history
  • Loading branch information
Amil-Gaoul authored and apust committed Aug 28, 2020
1 parent 7ff9988 commit 9208f20
Show file tree
Hide file tree
Showing 18 changed files with 400 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './src/radio/index';
export * from './src/select/index';
export * from './src/snackbar/index';
export * from './src/spinner/index';
export * from './src/tabs/index';
export * from './src/tag/index';
export * from './src/textarea/index';
export * from './src/tooltip/index';
Expand Down
42 changes: 42 additions & 0 deletions src/packages/core/src/tabs/__tests__/tab-title.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { NavigationExtras, Router, Routes } from '@angular/router';

import { ETabTitle } from '../tab-title';
import { EHome } from '../demo/home/home';

describe('ETabTitle', (): void => {
let component: ETabTitle;
let fixture: ComponentFixture<ETabTitle>;
let router: Router;
const routes: Routes = [
{
path: 'home',
component: EHome
}
];

beforeEach(async((): void => {
TestBed.configureTestingModule({
imports: [ RouterTestingModule.withRoutes(routes) ],
declarations: [ ETabTitle ]
}).compileComponents();
}));

beforeEach((): void => {
fixture = TestBed.createComponent(ETabTitle);
component = fixture.componentInstance;
router = TestBed.inject(Router);
});

it('should create', (): void => {
expect(component).toBeTruthy();
});

it('should navigate', (): void => {
const navigateSpy: jasmine.Spy<(commands: any[], extras?: NavigationExtras) => Promise<boolean>> = spyOn(router, 'navigate');

router.navigate(['home']);
expect(navigateSpy).toHaveBeenCalledWith(['home']);
});
});
25 changes: 25 additions & 0 deletions src/packages/core/src/tabs/__tests__/tab.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { ETab } from '../tab';

describe('ETab', (): void => {
let component: ETab;
let fixture: ComponentFixture<ETab>;

beforeEach(async((): void => {
TestBed.configureTestingModule({
declarations: [ ETab ]
})
.compileComponents();
}));

beforeEach((): void => {
fixture = TestBed.createComponent(ETab);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', (): void => {
expect(component).toBeTruthy();
});
});
27 changes: 27 additions & 0 deletions src/packages/core/src/tabs/__tests__/tabs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {ETabs} from '../tabs';
import {ETab} from '../tab';

describe('ETabs', (): void => {
let component: ETabs;
let fixture: ComponentFixture<ETabs>;

beforeEach(async((): void => {
TestBed.configureTestingModule({
declarations: [ ETabs, ETab ]
}).compileComponents();
}));

beforeEach((): void => {
fixture = TestBed.createComponent(ETabs);
component = fixture.componentInstance;
});

it('should create', (): void => {
expect(component).toBeTruthy();
});

it('should have as className "undefined"', (): void => {
expect(component.className).toEqual(undefined);
});
});
9 changes: 9 additions & 0 deletions src/packages/core/src/tabs/demo/about/about.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';

@Component({
selector: 'e-about',
template: `
<p>About Component</p>
`
})
export class EAbout { }
26 changes: 26 additions & 0 deletions src/packages/core/src/tabs/demo/demo-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {EHome} from './home/home';
import {EAbout} from './about/about';

const routes: Routes = [
{
path: 'home',
component: EHome
},
{
path: 'about',
component: EAbout
},
{
path: '**',
redirectTo: 'home',
pathMatch: 'full'
}
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class EDemoRoutingModule { }
20 changes: 20 additions & 0 deletions src/packages/core/src/tabs/demo/demo.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { EHome } from './home/home';
import { EAbout } from './about/about';
import { EDemo } from './demo';
import { EDemoRoutingModule } from './demo-routing.module';
import { ETabsModule } from '../tabs.module';

@NgModule({
declarations: [EHome, EAbout, EDemo],
imports: [
CommonModule,
RouterModule,
EDemoRoutingModule,
ETabsModule
],
exports: [EDemo, EHome, EAbout]
})
export class EDemoModule { }
29 changes: 29 additions & 0 deletions src/packages/core/src/tabs/demo/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {Component} from '@angular/core';

@Component({
selector: 'e-demo',
template: `
<e-tabs>
<e-tab
*ngFor="let tab of tabs"
[title]="tab.title"
[routerLink]="tab.link">
</e-tab>
</e-tabs>
<router-outlet></router-outlet>
`
})
export class EDemo {

public tabs: any[] = [
{
title: 'Home',
link: 'home'
},
{
title: 'About',
link: 'about'
}
];

}
9 changes: 9 additions & 0 deletions src/packages/core/src/tabs/demo/home/home.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';

@Component({
selector: 'e-home',
template: `
<p>Home Component</p>
`
})
export class EHome {}
4 changes: 4 additions & 0 deletions src/packages/core/src/tabs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './tabs';
export * from './tab';
export * from './tab-title';
export * from './tabs.module';
22 changes: 22 additions & 0 deletions src/packages/core/src/tabs/tab-title.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div
*ngIf="!tab.routerLink"
class="_e_tabs__item"
[ngClass]="{
'_e_tabs__item_selected': tab.selected
}">
{{ tab.titleStr }}
<ng-container *ngTemplateOutlet="tab.titleTpl"></ng-container>
</div>
<div
*ngIf="tab.routerLink"
class="_e_tabs__item"
[ngClass]="{
'_e_tabs__item_selected': tab.selected
}"
[routerLink]="tab?.routerLink"
[routerLinkActive]="'_e_tabs__item'"
[queryParams]="tab?.queryParams"
role="link">
{{ tab.titleStr }}
<ng-container *ngTemplateOutlet="tab.titleTpl"></ng-container>
</div>
13 changes: 13 additions & 0 deletions src/packages/core/src/tabs/tab-title.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { ETab } from './tab';

@Component({
selector: 'div[eTabTitle]',
templateUrl: './tab-title.html',
encapsulation: ViewEncapsulation.None
})
export class ETabTitle {

@Input() public tab: ETab;

}
6 changes: 6 additions & 0 deletions src/packages/core/src/tabs/tab.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div
[attr.tabindex]="tabIndex"
role="tabpanel"
[hidden]="!selected">
<ng-content></ng-content>
</div>
47 changes: 47 additions & 0 deletions src/packages/core/src/tabs/tab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {Component, Input, TemplateRef, ViewEncapsulation} from '@angular/core';

@Component({
selector: 'e-tab',
templateUrl: './tab.html',
encapsulation: ViewEncapsulation.None
})
export class ETab {

@Input() public tabIndex: number;
/**
* Tab selected state.
*/
@Input() public selected: boolean = false;

/**
* Router link.
*/
@Input() public routerLink: string | string[];

/**
* Router query params (model: {[k: string]: string}).
*/
@Input() public queryParams: { [k: string]: string };

/**
* Plain text title for the tab.
*/
@Input() public set title(value: string | TemplateRef<any>) {
if (value instanceof TemplateRef) {
this.titleTpl = value;
} else {
this.titleStr = value;
}
}
/**
* Text title for the tab.
* @internal
*/
public titleStr: string;
/**
* Template title for the tab.
* @internal
*/
public titleTpl: TemplateRef<any>;

}
10 changes: 10 additions & 0 deletions src/packages/core/src/tabs/tabs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<nav
[class]="className"
[ngClass]="{
'_e_tabs': true
}">
<ng-container *ngFor="let t of tabs">
<div eTabTitle [tab]="t" (click)="onSelectTab(t)"></div>
</ng-container>
</nav>
<ng-content select="e-tab"></ng-content>
16 changes: 16 additions & 0 deletions src/packages/core/src/tabs/tabs.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {ETabs} from './tabs';
import {ETab} from './tab';
import {ETabTitle} from './tab-title';

@NgModule({
declarations: [ETabs, ETab, ETabTitle],
imports: [
CommonModule,
RouterModule
],
exports: [ETabs, ETab, ETabTitle]
})
export class ETabsModule { }
58 changes: 58 additions & 0 deletions src/packages/core/src/tabs/tabs.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import { moduleMetadata } from '@storybook/angular';
import { RouterModule } from '@angular/router';
import { APP_BASE_HREF } from '@angular/common';

import { ETab, ETabs, ETabsModule } from './';
import { EBadgeModule } from '../badge'
import { EDemoModule } from './demo/demo.module';

# Tabs

<Meta
title="Core/Tabs"
decorators={[
moduleMetadata({
imports: [ ETabsModule, EBadgeModule, EDemoModule, RouterModule.forRoot([], { useHash: true }) ],
providers: [{ provide: APP_BASE_HREF, useValue : '/' }]
})
]}
/>

<Props of={ETab} />
<Props of={ETabs} />

<Preview>
<Story name="default" height="100px">
{{
template: `
<e-tabs>
<e-tab [title]="tab1">
Tab 1 content
</e-tab>
<e-tab [title]="tab2">
<ng-template #tab2>
Tab 2
<e-badge style="display: inline-flex; margin-left: 8px;">2</e-badge>
</ng-template>
Tab 2 content
</e-tab>
</e-tabs>
`,
props: {
tab1: 'Tab 1',
}
}}
</Story>
</Preview>

<Preview>
<Story name="withRouting" height="130px">
{{
template: `
<e-demo></e-demo>
`,
}}
</Story>
</Preview>

Loading

0 comments on commit 9208f20

Please sign in to comment.