From 620d206ac8a13413167a43dbd8209864d66747cb Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Wed, 3 Feb 2016 16:47:20 +0200 Subject: [PATCH] Synchronize code samples tabs #20 --- demo/swagger.yml | 35 +++++++ lib/common/components/Tabs/tabs.js | 28 +++++- lib/common/components/Tabs/tabs.spec.js | 94 +++++++++++++++++-- .../RequestSamples/request-samples.html | 2 +- .../RequestSamples/request-samples.js | 27 +++++- lib/events.js | 4 +- 6 files changed, 174 insertions(+), 16 deletions(-) diff --git a/demo/swagger.yml b/demo/swagger.yml index 0a41c2ee..d7ab6e7b 100644 --- a/demo/swagger.yml +++ b/demo/swagger.yml @@ -169,6 +169,41 @@ description: "Pet not found" 405: description: "Validation exception" + x-code-samples: + - + lang: PHP + source: |- + $form = new \PetStore\Entities\Pet(); + $form->setPetId(1); + $form->setPetType("Dog"); + $form->setName("Rex"); + // set other fields + + try { + $pet = $client->pets()->update($form); + } catch (UnprocessableEntityException $e) { + var_dump($e->getErrors()); + } + - + lang: C# + source: |- + PetStore.v1.Pet pet = new PetStore.v1.Pet(); + pet.setApiKey("your api key"); + pet.petType = PetStore.v1.Pet.TYPE_DOG; + pet.id = 1; + pet.name = "Rex"; + // set other fields + + PetStoreResponse response = pet.update(); + if (response.statusCode == HttpStatusCode.OK) + { + // Successfully updated + } + else + { + // Something wrong -- check response for errors + Console.WriteLine(response.getRawResponse()); + } security: - petstore_auth: diff --git a/lib/common/components/Tabs/tabs.js b/lib/common/components/Tabs/tabs.js index f091afa9..5345f282 100644 --- a/lib/common/components/Tabs/tabs.js +++ b/lib/common/components/Tabs/tabs.js @@ -1,10 +1,11 @@ 'use strict'; -import {Component, View} from 'angular2/core'; +import {Component, View, EventEmitter} from 'angular2/core'; import {CORE_DIRECTIVES} from 'angular2/common'; @Component({ - selector: 'tabs' + selector: 'tabs', + events: ['change'] }) @View({ template: ` @@ -20,13 +21,34 @@ import {CORE_DIRECTIVES} from 'angular2/common'; export class Tabs { constructor() { this.tabs = []; + this.change = new EventEmitter(); } - selectTab(tab) { + selectTab(tab, notify = true) { + if (tab.active) return; this.tabs.forEach((tab) => { tab.active = false; }); tab.active = true; + notify && this.change.next(tab.tabTitle); + } + + selectyByTitle(tabTitle, notify = false) { + let prevActive; + let newActive; + this.tabs.forEach((tab) => { + if (tab.active) prevActive = tab; + tab.active = false; + if (tab.tabTitle === tabTitle) { + newActive = tab; + } + }); + if (newActive) { + newActive.active = true; + } else { + prevActive.active = true; + } + notify && this.change.next(tabTitle); } addTab(tab) { diff --git a/lib/common/components/Tabs/tabs.spec.js b/lib/common/components/Tabs/tabs.spec.js index 9e7145a7..02e5ea70 100644 --- a/lib/common/components/Tabs/tabs.spec.js +++ b/lib/common/components/Tabs/tabs.spec.js @@ -1,6 +1,6 @@ 'use strict'; -import { getChildDebugElement, getChildDebugElementAll, mouseclick } from 'tests/helpers'; +import { getChildDebugElement, getChildDebugElementAll } from 'tests/helpers'; import {Component, View} from 'angular2/core'; import { @@ -16,9 +16,10 @@ describe('Common components', () => { describe('Tabs Component', () => { let builder; let component; - let nativeElement; let childDebugEls; + let debugEl; let fixture; + let hostComponent; beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; @@ -26,10 +27,10 @@ describe('Common components', () => { beforeEach((done) => { builder.createAsync(TestApp).then(_fixture => { fixture = _fixture; - let debugEl = getChildDebugElement(fixture.debugElement, 'tabs'); + hostComponent = fixture.debugElement.componentInstance; + debugEl = getChildDebugElement(fixture.debugElement, 'tabs'); childDebugEls = getChildDebugElementAll(debugEl, 'tab'); component = debugEl.componentInstance; - nativeElement = debugEl.nativeElement; done(); }, err => done.fail(err)); }); @@ -54,13 +55,82 @@ describe('Common components', () => { it('should change active tab on click', () => { fixture.detectChanges(); - let headerEls = nativeElement.querySelectorAll('li'); + //let headerEls = nativeElement.querySelectorAll('li'); let tabs = childDebugEls.map(debugEl => debugEl.componentInstance); let [tab1, tab2] = tabs; - mouseclick(headerEls[0]); - tab1.active.should.be.false; - tab2.active.should.be.true; + let tabsInst = debugEl.componentInstance; + tabsInst.selectTab(tab2); + tab1.active.should.be.false(); + tab2.active.should.be.true(); + }); + + it('should change tab by title and not emit Event', (done) => { + fixture.detectChanges(); + let tabs = childDebugEls.map(debugEl => debugEl.componentInstance); + let [tab1, tab2] = tabs; + let tab2Title = 'Tab2'; + + let tabsInst = debugEl.componentInstance; + tabsInst.selectyByTitle(tab2Title); + tab1.active.should.be.false(); + tab2.active.should.be.true(); + + setTimeout(() => { + hostComponent.eventLog.should.be.deepEqual([]); + done(); + }); + }); + + + it('should emit event on tab Change', (done) => { + fixture.detectChanges(); + let tabs = childDebugEls.map(debugEl => debugEl.componentInstance); + let tab2 = tabs[1]; + let tabsInst = debugEl.componentInstance; + tabsInst.selectTab(tab2, true); + + setTimeout(() => { + hostComponent.eventLog.should.be.deepEqual(['Tab2']); + done(); + }); + }); + + it('should emit event on tab change by Title with notify true', (done) => { + fixture.detectChanges(); + let tab2Title = 'Tab2'; + + let tabsInst = debugEl.componentInstance; + tabsInst.selectyByTitle(tab2Title, true); + + setTimeout(() => { + hostComponent.eventLog.should.be.deepEqual(['Tab2']); + done(); + }); + }); + + it('should not emit event on tab change with notify false', (done) => { + fixture.detectChanges(); + let tabs = childDebugEls.map(debugEl => debugEl.componentInstance); + let tab2 = tabs[1]; + let tabsInst = debugEl.componentInstance; + tabsInst.selectTab(tab2, false); + + setTimeout(() => { + hostComponent.eventLog.should.be.deepEqual([]); + done(); + }); + }); + + it('should leave current tab active if selectyByTitle non existing title', () => { + fixture.detectChanges(); + let tabs = childDebugEls.map(debugEl => debugEl.componentInstance); + let [tab1, tab2] = tabs; + + let tabsInst = debugEl.componentInstance; + tabsInst.selectyByTitle('badTitle'); + tab1.active.should.be.true(); + tab2.active.should.be.false(); }); }); }); @@ -71,10 +141,16 @@ describe('Common components', () => { @View({ directives: [Tabs, Tab], template: - ` + ` Test Test ` }) class TestApp { + constructor() { + this.eventLog = []; + } + onEvent(event) { + this.eventLog.push(event); + } } diff --git a/lib/components/RequestSamples/request-samples.html b/lib/components/RequestSamples/request-samples.html index 2f71bdd2..60ede141 100644 --- a/lib/components/RequestSamples/request-samples.html +++ b/lib/components/RequestSamples/request-samples.html @@ -1,6 +1,6 @@
Request samples
- + diff --git a/lib/components/RequestSamples/request-samples.js b/lib/components/RequestSamples/request-samples.js index 6bebaa32..f193c033 100644 --- a/lib/components/RequestSamples/request-samples.js +++ b/lib/components/RequestSamples/request-samples.js @@ -6,17 +6,38 @@ import {Tabs, Tab} from '../../common/components/Tabs/tabs'; import SchemaSample from '../SchemaSample/schema-sample'; import {PrismPipe} from '../../utils/pipes'; +import {ViewChildren, QueryList, ChangeDetectorRef, ChangeDetectionStrategy} from 'angular2/core'; +import {redocEvents} from '../../events'; + @RedocComponent({ selector: 'request-samples', templateUrl: './lib/components/RequestSamples/request-samples.html', styleUrls: ['./lib/components/RequestSamples/request-samples.css'], directives: [SchemaSample, Tabs, Tab], inputs: ['bodySchemaPtr'], - pipes: [PrismPipe] + pipes: [PrismPipe], + changeDetection: ChangeDetectionStrategy.OnPush }) export default class RequestSamples extends BaseComponent { - constructor(schemaMgr) { + constructor(schemaMgr, tabs, changeDetector) { super(schemaMgr); + tabs.changes.subscribe(_ => { + this.tabs = tabs.first; + this.subscribeForEvents(_); + }); + this.changeDetector = changeDetector; + } + + changeLangNotify(lang) { + redocEvents.samplesLanguageChanged.next(lang); + } + + subscribeForEvents() { + if (!this.tabs) return; + redocEvents.samplesLanguageChanged.subscribe((sampleLang) => { + this.tabs.selectyByTitle(sampleLang); + this.changeDetector.markForCheck(); + }); } prepareModel() { @@ -25,3 +46,5 @@ export default class RequestSamples extends BaseComponent { this.data.samples = this.componentSchema['x-code-samples'] || []; } } + +RequestSamples.parameters = RequestSamples.parameters.concat([ [new ViewChildren(Tabs), QueryList], [ChangeDetectorRef] ]); diff --git a/lib/events.js b/lib/events.js index 62fb85dc..77b6a0de 100644 --- a/lib/events.js +++ b/lib/events.js @@ -3,6 +3,8 @@ import {EventEmitter} from 'angular2/core'; var bootsrEmmiter = new EventEmitter(); +var langChanged = new EventEmitter(); export var redocEvents = { - bootstrapped: bootsrEmmiter + bootstrapped: bootsrEmmiter, + samplesLanguageChanged: langChanged };