Skip to content

Commit

Permalink
feat(tab-select): initial implementation for tab select *experimental* (
Browse files Browse the repository at this point in the history
#1187)

* feat(tab-select): initial implementation for tab select *experimental*

first pass on tab select component and will be treated as experimental while we use it in multiple products to ensure quality

* docs(tab-select): make backgroundColor optional in README

* chore(tab-select): add initial set of unit tests

* feat(): add disabled ripple on tabs for test-bed
  • Loading branch information
emoralesb05 authored Jul 10, 2018
1 parent 6f8a3bf commit 66503a9
Show file tree
Hide file tree
Showing 17 changed files with 634 additions and 2 deletions.
8 changes: 7 additions & 1 deletion src/platform/experimental/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@
"Steven Ov <[email protected]>",
"Jenn Medellin <[email protected]>",
"Julie Knowles <[email protected]>"
]
],
"peerDependencies": {
"@angular/common": "^0.0.0-NG",
"@angular/core": "^0.0.0-NG",
"@angular/cdk": "^0.0.0-MATERIAL",
"@angular/material": "^0.0.0-MATERIAL"
}
}
1 change: 1 addition & 0 deletions src/platform/experimental/public-api.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './template-rename-me-experiment-module/index';
export * from './tab-select/index';
90 changes: 90 additions & 0 deletions src/platform/experimental/tab-select/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# td-tab-select (experimental)

`td-tab-select` element generates a tab group component that behaves like a `mat-select`.

## API Summary

#### Inputs

+ value?: any
+ Sets the value of the component.
+ disabled?: boolean
+ Sets disabled state of the component.
+ disabledRipple?: boolean
+ Disables ripple effect on component.
+ color?: ThemePalette
+ Color of the tab group.
+ backgroundColor?: ThemePalette
+ Background color of the tab group.

#### Events

+ valueChange: function(value: any)
+ Event that emits whenever the raw value of the select changes.
+ This is here primarily to facilitate the two-way binding for the `value` input.

# td-tab-option

`td-tab-option` element generates a tab component to which a value can be binded to.

## API Summary

#### Inputs

+ value?: any
+ Bind a value to the component.
+ disabled?: boolean
+ Sets disabled state of the component.

## Setup

Import the [CovalentTabSelectModule] in your NgModule:

```typescript
import { CovalentTabSelectModule } from '@covalent/experimental/tab-select';
@NgModule({
imports: [
CovalentTabSelectModule,
...
],
...
})
export class MyModule {}
```

## Usage

Example without forms:

```html
<td-tab-select [(value)]="myValue">
<td-tab-option [value]="1">Label 1</td-tab-option>
<td-tab-option [value]="2">Label 2</td-tab-option>
<td-tab-option [value]="3">Label 3</td-tab-option>
</td-tab-select>
```

Example with forms:

```html
<td-tab-select [(ngModel)]="myValue">
<td-tab-option [value]="1">Label 1</td-tab-option>
<td-tab-option [value]="2">Label 2</td-tab-option>
<td-tab-option [value]="3">Label 3</td-tab-option>
</td-tab-select>
```

Example with all inputs/outputs:

```html
<td-tab-select [value]="myValue"
[backgroundColor]="'primary'"
[color]="'accent'"
[disabled]="false"
[disabledRipple]="false"
(valueChange)="myValue = $event">
<td-tab-option [value]="1" [disabled]="false">Label 1</td-tab-option>
<td-tab-option [value]="2">Label 2</td-tab-option>
<td-tab-option [value]="3" [disabled]="true">Label 3</td-tab-option>
</td-tab-select>
```
1 change: 1 addition & 0 deletions src/platform/experimental/tab-select/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public-api';
7 changes: 7 additions & 0 deletions src/platform/experimental/tab-select/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ngPackage": {
"lib": {
"entryFile": "index.ts"
}
}
}
3 changes: 3 additions & 0 deletions src/platform/experimental/tab-select/public-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './tab-select.module';
export * from './tab-select.component';
export * from './tab-option.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<ng-template>
<ng-content></ng-content>
</ng-template>
Empty file.
54 changes: 54 additions & 0 deletions src/platform/experimental/tab-select/tab-option.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
Component,
Input,
ChangeDetectionStrategy,
ChangeDetectorRef,
ViewChild,
TemplateRef,
OnInit,
ViewContainerRef,
} from '@angular/core';

import { TemplatePortal } from '@angular/cdk/portal';
import { mixinDisabled, ICanDisable } from '@covalent/core/common';

export class TdTabOptionBase {
constructor(public _viewContainerRef: ViewContainerRef,
public _changeDetectorRef: ChangeDetectorRef) {}
}

/* tslint:disable-next-line */
export const _TdTabOptionMixinBase = mixinDisabled(TdTabOptionBase);

@Component({
selector: 'td-tab-option',
templateUrl: './tab-option.component.html',
styleUrls: ['./tab-option.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
/* tslint:disable-next-line */
inputs: ['disabled'],
})
export class TdTabOptionComponent extends _TdTabOptionMixinBase implements ICanDisable, OnInit {

private _contentPortal: TemplatePortal<any>;
get content(): TemplatePortal<any> {
return this._contentPortal;
}

@ViewChild(TemplateRef) _content: TemplateRef<any>;

/**
* Value to which the option will be binded to.
*/
@Input('value') value: any;

constructor(_viewContainerRef: ViewContainerRef,
_changeDetectorRef: ChangeDetectorRef) {
super(_viewContainerRef, _changeDetectorRef);
}

ngOnInit(): void {
this._contentPortal = new TemplatePortal(this._content, this._viewContainerRef);
}

}
16 changes: 16 additions & 0 deletions src/platform/experimental/tab-select/tab-select.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<mat-tab-group [backgroundColor]="backgroundColor"
[color]="color"
[disableRipple]="disableRipple"
[selectedIndex]="selectedIndex"
(selectedIndexChange)="selectedIndexChange($event)">
<ng-template let-tabOption
ngFor
[ngForOf]="tabOptions">
<mat-tab [disabled]="tabOption.disabled || disabled">
<ng-template matTabLabel>
<ng-template [cdkPortalOutlet]="tabOption.content">
</ng-template>
</ng-template>
</mat-tab>
</ng-template>
</mat-tab-group>
Empty file.
185 changes: 185 additions & 0 deletions src/platform/experimental/tab-select/tab-select.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import {
TestBed,
inject,
async,
ComponentFixture,
} from '@angular/core/testing';
import {
Component,
DebugElement,
} from '@angular/core';
import {
FormsModule,
} from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { By } from '@angular/platform-browser';
import {
CovalentTabSelectModule,
} from './public-api';

describe('Component: TabSelect', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
TdTabSelectBasicTestComponent,
TdTabSelectFormsTestComponent,
TdTabSelectDynamicTestComponent,
],
imports: [
NoopAnimationsModule,
FormsModule,
CovalentTabSelectModule,
],
});
TestBed.compileComponents();
}));

it('should render tab select with all tabs disabled',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdTabSelectBasicTestComponent);
let component: TdTabSelectBasicTestComponent = fixture.debugElement.componentInstance;
component.disabled = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.value).toBe(undefined);
expect(fixture.debugElement.queryAll(By.css('.mat-tab-disabled')).length).toBe(3);
});
}),
));

it('should render tab select with all options and click on second option to activate it',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdTabSelectBasicTestComponent);
let component: TdTabSelectBasicTestComponent = fixture.debugElement.componentInstance;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(3);
fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1].nativeElement.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1].nativeElement.className).toContain('mat-tab-label-active');
});
});
}),
));

it('should render tab select with all options with the second option active',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdTabSelectBasicTestComponent);
let component: TdTabSelectBasicTestComponent = fixture.debugElement.componentInstance;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.value).toBe(undefined);
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(3);
fixture.componentInstance.value = 2;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1].nativeElement.className).toContain('mat-tab-label-active');
});
});
}),
));

it('should render tab select with first option active and then switch to 3rd option (value)',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdTabSelectBasicTestComponent);
let component: TdTabSelectBasicTestComponent = fixture.debugElement.componentInstance;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.value).toBe(undefined);
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(3);
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label'))[0].nativeElement.className).toContain('mat-tab-label-active');
fixture.componentInstance.value = 3;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label'))[2].nativeElement.className).toContain('mat-tab-label-active');
});
});
});
}),
));

it('should render tab select with first option active and then switch to 3rd option (ngModel)',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdTabSelectFormsTestComponent);
let component: TdTabSelectFormsTestComponent = fixture.debugElement.componentInstance;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.value).toBe(1);
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(3);
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label'))[0].nativeElement.className).toContain('mat-tab-label-active');
fixture.componentInstance.value = 3;
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label'))[2].nativeElement.className).toContain('mat-tab-label-active');
});
});
});
});
}),
));

it('should render dynamic tab options from an ngFor loop',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdTabSelectDynamicTestComponent);
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.value).toBe(undefined);
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(3);

let tabNo: number = 1;
fixture.debugElement.queryAll(By.css('.mat-tab-label .mat-tab-label-content')).forEach((element: DebugElement) => {
expect((<HTMLElement>element.nativeElement).innerHTML).toContain('Option ' + tabNo++);
});
});
})));
});

@Component({
selector: 'td-tab-select-basic-test',
template: `
<td-tab-select [(value)]="value" [disabled]="disabled">
<td-tab-option [value]="1">Option 1</td-tab-option>
<td-tab-option [value]="2">Option 2</td-tab-option>
<td-tab-option [value]="3">Option 3</td-tab-option>
</td-tab-select>
`,
})
class TdTabSelectBasicTestComponent {
disabled: boolean = false;
value: any;
}

@Component({
selector: 'td-tab-forms-basic-test',
template: `
<td-tab-select [(ngModel)]="value">
<td-tab-option [value]="1">Option 1</td-tab-option>
<td-tab-option [value]="2">Option 2</td-tab-option>
<td-tab-option [value]="3">Option 3</td-tab-option>
</td-tab-select>
`,
})
class TdTabSelectFormsTestComponent {
value: any;
}

@Component({
selector: 'td-tab-select-dynamic-test',
template: `
<td-tab-select [(value)]="value">
<td-tab-option *ngFor="let option of options" [value]="option.value">{{option.label}}</td-tab-option>
</td-tab-select>
`,
})
class TdTabSelectDynamicTestComponent {
options: any[] = [{label: 'Option 1', value: 1}, {label: 'Option 2', value: 2}, {label: 'Option 3', value: 3}];
value: any;
}
Loading

0 comments on commit 66503a9

Please sign in to comment.