From 3cf848de743690ea9be1c5ac8ad890a1bda62a27 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Wed, 23 Dec 2015 08:09:54 -0800 Subject: [PATCH] feat(tabs): basic behavioral tests for tab selection binding and input --- ng2-material/components/tabs/tabs.ts | 60 +++++++------ test/components/tabs/tabs_spec.ts | 125 +++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 27 deletions(-) create mode 100644 test/components/tabs/tabs_spec.ts diff --git a/ng2-material/components/tabs/tabs.ts b/ng2-material/components/tabs/tabs.ts index 4341874c..b9f8de63 100644 --- a/ng2-material/components/tabs/tabs.ts +++ b/ng2-material/components/tabs/tabs.ts @@ -1,11 +1,14 @@ import { - Component, Directive, Input, QueryList, Attribute, AfterContentInit, - ViewContainerRef, TemplateRef, ContentChildren + Component, Directive, Input, QueryList, Attribute, AfterViewInit, + ViewContainerRef, TemplateRef, ContentChildren, forwardRef } from 'angular2/core'; import {isPresent} from "angular2/src/facade/lang"; import {Ink} from "../../core/util/ink"; import {ViewEncapsulation} from "angular2/core"; import {NgFor} from "angular2/common"; +import {Host} from "angular2/core"; +import {SkipSelf} from "angular2/core"; +import {Query} from "angular2/core"; // TODO: behaviors to test @@ -34,7 +37,9 @@ export class MdTab { } @Input() set active(active: boolean) { - if (active == this._active) return; + if (active == this._active) { + return; + } this._active = active; if (active) { this.viewContainer.createEmbeddedView(this.templateRef); @@ -58,7 +63,7 @@ export class MdTab { `, directives: [NgFor], + properties: ['selected'], encapsulation: ViewEncapsulation.None }) -export class MdTabs implements AfterContentInit { - @ContentChildren(MdTab) panes: QueryList; +export class MdTabs { @Input() mdNoScroll: boolean = false; - constructor(@Attribute('mdNoScroll') noScroll: string) { + constructor(@Query(MdTab) public panes: QueryList, + @Attribute('mdNoScroll') noScroll: string) { this.mdNoScroll = isPresent(noScroll); + this.panes.changes.subscribe((_) => { + this.panes.toArray().forEach((p: MdTab, index: number) => { + p.active = index === this._selected; + }); + }); } - private _selectedIndex: number = -1; - get selectedIndex(): number { - return this._selectedIndex; + private _selected: number = 0; + get selected(): number { + return this._selected; } - set selectedIndex(value: number) { + @Input() + set selected(index: number) { let panes = this.panes.toArray(); - if (value > 0 && value < panes.length) { - this.select(panes[value]); - this._selectedIndex = value; + let pane = null; + if (index >= 0 && index < panes.length) { + pane = panes[index]; } + this.selectedTab = pane; + this._selected = index; } - get selected(): MdTab { + get selectedTab(): MdTab { let result = null; this.panes.toArray().forEach((p: MdTab) => { if (p.active) { @@ -110,11 +124,11 @@ export class MdTabs implements AfterContentInit { return result; } - select(pane: MdTab) { + set selectedTab(value: MdTab) { this.panes.toArray().forEach((p: MdTab, index: number) => { - p.active = p == pane; + p.active = p == value; if (p.active) { - this._selectedIndex = index; + this._selected = index; } }); } @@ -123,14 +137,6 @@ export class MdTabs implements AfterContentInit { if (event && Ink.canApply(event.target)) { Ink.rippleEvent(event.target, event); } - this.select(pane); - } - - ngAfterContentInit(): any { - setTimeout(()=> { - if (this._selectedIndex === -1) { - this.select(this.panes.toArray()[0]); - } - }, 0); + this.selectedTab = pane; } } diff --git a/test/components/tabs/tabs_spec.ts b/test/components/tabs/tabs_spec.ts new file mode 100644 index 00000000..7016ba9e --- /dev/null +++ b/test/components/tabs/tabs_spec.ts @@ -0,0 +1,125 @@ +import { + AsyncTestCompleter, + TestComponentBuilder, + beforeEach, + beforeEachProviders, + describe, + expect, + inject, + it, +} from 'angular2/testing_internal'; +import {DebugElement} from 'angular2/src/core/debug/debug_element'; +import {Component, View, provide} from 'angular2/core'; +import {UrlResolver} from 'angular2/compiler'; +import {TestUrlResolver} from '../../test_url_resolver'; +import {MdTab, MdTabs} from '../../../ng2-material/components/tabs/tabs'; +import {MATERIAL_PROVIDERS} from '../../../ng2-material/all'; +import {ComponentFixture} from "angular2/testing"; +import {CORE_DIRECTIVES} from "angular2/common"; +import {findChildrenByAttribute,findChildrenByTag,findChildByTag} from "../../util"; +import {TimerWrapper} from "angular2/src/facade/async"; + + +export function main() { + + interface ITabsFixture { + fixture:ComponentFixture; + tabs:MdTabs; + tabButtons:HTMLElement[]; + } + @Component({selector: 'test-app'}) + @View({ + directives: [CORE_DIRECTIVES, MdTabs, MdTab], + template: ` + + + + + ` + }) + class TestComponent { + selectedIndex: number = 2; + } + + describe('Tabs', () => { + let builder: TestComponentBuilder; + + function setup(template: string = null): Promise { + let prep = template === null ? + builder.createAsync(TestComponent) : + builder.overrideTemplate(TestComponent, template).createAsync(TestComponent); + return prep.then((fixture: ComponentFixture) => { + fixture.detectChanges(); + let tabs = findChildByTag(fixture.debugElement, 'md-tabs').componentInstance; + let tabButtons = findChildrenByTag(fixture.debugElement, 'md-tab-item').map(b => b.nativeElement); + return { + fixture: fixture, + tabs: tabs, + tabButtons: tabButtons + }; + }).catch(console.error.bind(console)); + } + + beforeEachProviders(() => [ + MATERIAL_PROVIDERS, + provide(UrlResolver, {useValue: new TestUrlResolver()}), + ]); + beforeEach(inject([TestComponentBuilder], (tcb) => { + builder = tcb; + })); + + describe('md-tabs', () => { + it('should initialize with first tab selected', inject([AsyncTestCompleter], (async) => { + setup().then((api: ITabsFixture) => { + expect(api.tabs.selected).toBe(0); + expect(api.tabs.selectedTab).toBe(null); + async.done(); + }); + })); + it('should update selected and selectedTab when changed by clicking on tab buttons', inject([AsyncTestCompleter], (async) => { + setup().then((api: ITabsFixture) => { + api.tabButtons[1].click(); + expect(api.tabs.selected).toBe(1); + expect(api.tabs.selectedTab).not.toBeNull(); + + api.tabButtons[0].click(); + expect(api.tabs.selected).toBe(0); + expect(api.tabs.selectedTab).not.toBeNull(); + async.done(); + }); + })); + it('should update selectedTab when selected is set', inject([AsyncTestCompleter], (async) => { + setup().then((api: ITabsFixture) => { + expect(api.tabs.selectedTab).toBeNull(); + api.tabs.selected = 1; + expect(api.tabs.selectedTab).not.toBeNull(); + + api.tabs.selected = 0; + expect(api.tabs.selectedTab).not.toBeNull(); + + api.tabs.selected = -1; + expect(api.tabs.selectedTab).toBeNull(); + + async.done(); + }); + })); + + it('should bind [selected] to an index', inject([AsyncTestCompleter], (async) => { + let template = ` + + + + + `; + + setup(template).then((api: ITabsFixture) => { + expect(api.tabs.selected).toBe(2); + async.done(); + }); + })); + }); + }); + + +} +