diff --git a/build/tasks/e2e.js b/build/tasks/e2e.js index 8888188e..c2828e83 100644 --- a/build/tasks/e2e.js +++ b/build/tasks/e2e.js @@ -12,7 +12,7 @@ gulp.task('test-server', function (done) { baseDir: './tests/e2e', routes: { '/dist': './dist', - '/swagger.json': './demo/swagger.json' + '/swagger.yml': './demo/swagger.yml' }, } }, done); diff --git a/demo/swagger.yml b/demo/swagger.yml index 0a41c2ee..7af5f3d8 100644 --- a/demo/swagger.yml +++ b/demo/swagger.yml @@ -169,6 +169,21 @@ 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()); + } security: - petstore_auth: diff --git a/docs/redoc-vendor-extensions.md b/docs/redoc-vendor-extensions.md index e6dcac25..5a6bee90 100644 --- a/docs/redoc-vendor-extensions.md +++ b/docs/redoc-vendor-extensions.md @@ -1,27 +1,29 @@ # ReDoc vendor extensions ReDoc makes use of the following [vendor extensions](http://swagger.io/specification/#vendorExtensions) -### [Info Object](http://swagger.io/specification/#infoObject) vendor extensions +### [Info Object](http://swagger.io/specification/#infoObject) vendor extensions #### x-logo -| Field Name | Type | Description -| :------------- | :------: | -| x-logo | [Logo Object](#logoObject) | The information about API logo -##### Usage in Redoc +| Field Name | Type | Description | +| :------------- | :-----------: | :---------- | +| x-logo | [Logo Object](#logoObject) | The information about API logo | + +###### Usage in Redoc `x-logo` is used to specify API logo. The corresponding image are displayed just above side-menu. -#### Logo Object +#### Logo Object The information about API logo -##### Fixed fields -| Field Name | Type | Description -| :---------- | :------: | -| url | string | The URL pointing to the spec logo. MUST be in the format of a URL -| backgroundColor | string | background color to be used. MUST be in [CSS color syntax](https://developer.mozilla.org/en/docs/Web/CSS/color) +###### Fixed fields +| Field Name | Type | Description | +| :-------------- | :------: | :---------- | +| url | string | The URL pointing to the spec logo. MUST be in the format of a URL +| backgroundColor | string | background color to be used. MUST be in [CSS color syntax](https://developer.mozilla.org/en/docs/Web/CSS/color) | -##### x-logo example -```yaml +###### x-logo example +json +```json { "info": { "version": "1.0.0", @@ -33,15 +35,14 @@ The information about API logo } } ``` +yaml ```yaml -{ - info: - version: "1.0.0" - title: "Swagger Petstore" - x-logo: - url: "https://rebilly.github.io/ReDoc/petstore-logo.png" - backgroundColor: "white" -} +info: + version: "1.0.0" + title: "Swagger Petstore" + x-logo: + url: "https://rebilly.github.io/ReDoc/petstore-logo.png" + backgroundColor: "white" ``` @@ -49,15 +50,16 @@ The information about API logo ### [Tag object](http://swagger.io/specification/#tagObject) vendor extensions #### x-traitTag -| Field Name | Type | Description -| :------------- | :------: | -| x-traitTag | boolean | In Swagger two operations can have multiply tags. This property distinguish between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) +| Field Name | Type | Description | +| :------------- | :------: | :---------- | +| x-traitTag | boolean | In Swagger two operations can have multiply tags. This property distinguish between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) | -##### Usage in Redoc +###### Usage in Redoc Tags that have `x-traitTag` set to `true` are listed in side-menu but don't have any subitems (operations). Tag `description` is rendered as well. This is useful for handling out common things like Pagination, Rate-Limits, etc. -##### x-traitTag example +###### x-traitTag example +json ```json { "name": "Pagination", @@ -65,6 +67,7 @@ This is useful for handling out common things like Pagination, Rate-Limits, etc. "x-traitTag": true } ``` +yaml ```yaml name: Pagination description: Pagination description (can use markdown syntax) @@ -74,32 +77,32 @@ x-traitTag: true ### [Operation Object](http://swagger.io/specification/#operationObject) vendor extensions #### x-code-samples -| Field Name | Type | Description -| :------------- | :------: | -| x-code-samples | [[Code Sample Object](#codeSampleObject)] | A list of code samples associated with operation +| Field Name | Type | Description | +| :------------- | :------: | :---------- | +| x-code-samples | [ [Code Sample Object](#codeSampleObject) ] | A list of code samples associated with operation | -##### Usage in ReDoc -x-code-samples are rendered on the right panel of ReDoc +###### Usage in ReDoc +`x-code-samples` are rendered on the right panel of ReDoc -#### Code Sample Object +#### Code Sample Object Operation code sample -##### Fixed fields -| Field Name | Type | Description -| :---------- | :------: | :----------- -| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) -| source | string | Code sample source code +###### Fixed fields +| Field Name | Type | Description | +| :---------- | :------: | :----------- | +| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) | +| source | string | Code sample source code | -##### Code Sample Object example -```yaml +###### Code Sample Object example +json +```json { "lang": "JavaScript", "source": "console.log('Hello World');" } ``` +yaml ```yaml -{ - lang: JavaScript - source: console.log('Hello World'); -} +lang: JavaScript +source: console.log('Hello World'); ``` 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 }; diff --git a/package.json b/package.json index 458fb67b..0b95f705 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redoc", "description": "Swagger-generated API Reference Documentation", - "version": "0.5.0", + "version": "0.5.1", "repository": { "type": "git", "url": "git://github.com/Rebilly/ReDoc" @@ -10,7 +10,7 @@ "scripts": { "test": "gulp lint && ./build/run_tests.sh", "prepublish": "gulp build", - "postinstall": "jspm install", + "postinstall": "npm install jspm && jspm install", "start": "gulp serve", "build-dist": "gulp build", "branch-release": "git reset --hard && branch-release", diff --git a/tests/e2e/index.html b/tests/e2e/index.html index 30d11c24..599429c5 100644 --- a/tests/e2e/index.html +++ b/tests/e2e/index.html @@ -14,7 +14,7 @@