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

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

Merged
merged 4 commits into from
Jul 10, 2018
Merged
Show file tree
Hide file tree
Changes from 3 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
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