Merge branch 'master' into releases

This commit is contained in:
Roman Hotsiy 2016-02-22 15:27:17 +02:00
commit e3025b0d06
48 changed files with 583 additions and 355 deletions

View File

@ -9,7 +9,8 @@
"globals": {
"should": true,
"expect": true,
"sinon": true
"sinon": true,
"Reflect": true
},
"rules": {
"quotes": [2, "single"],

View File

@ -29,6 +29,8 @@ cache:
before_install:
- travis_retry npm install jspm
- jspm config registries.github.auth $JSPM_GITHUB_AUTH_TOKEN
before_script:
- npm run jspm-install
before_deploy:
- npm run build-dist
deploy:
@ -38,12 +40,6 @@ deploy:
on:
branch: master
condition: $JOB != e2e
- skip_cleanup: true
provider: script
script: npm run branch-release
on:
branch: master
condition: $JOB != e2e
- provider: npm
skip_cleanup: true
email: gotsijroman@gmail.com
@ -51,3 +47,9 @@ deploy:
secure: PuhWLERrCEFmXmdFpw2OVFlqpOIVDmgwk5JUJOYaFdVCh/smp0+jZCQ4vrdFpuG96rnDVirD+A8xvW6NgsNNaRthLgOB/LRdFN69rU6Gvn3At6wlnC55t5dlhxPvCfnzJcHVBLXX4EmMkjnZqDg2uczXTzPodr3FnQJNuXmP8B33fzDVLyHccvXZ90abwXWVrgRIXPU28niqCR8DOC2OTzs7wqz+BLNkYDRRbyYXsg62HWuD33x5iof5IqBmhzBt3usCGmF3QGcgHrXHdZw3sZnit8+Bua++3KrXR0x6HGXXN1AoXVmCAkCa5OTQ5R3tCRxiJN3P2KLnvWeZR74sTFkovJB/6pGCvbJ/c7Wnuw6sD7SgOUBD359ULB6lAf5OnxBLoNebX4JxxVXF+zA4E3Bl44VxkzDpPWc15xqBPMB5vBREzMVmJ5mExn2s5cmLQjADbl9h0y6gZnhnNJ+iTmqtrVyM0ZkF2rPrzrTdGD+ULmRIlTMkdD1bh+/TJ3RdXT3P4/zNUJmiNnvgnnJVYYvsGaXWF+7uCVHT/8k2RsoSHqgkqh0gkDqGSwVix55y5mC7T2Vk9lMBhm6MvFJXaonOX0kxJS4EDQ3plPd6/ybG+TLhwggYnQ8o9msU5Nt6FpUShKiezjKurIhbQZdwlVivX3tahjW2QjNDO58xGgY=
on:
tags: true
- skip_cleanup: true
provider: script
script: npm run branch-release
on:
branch: master
condition: $JOB != e2e

View File

@ -1,5 +1,5 @@
# ReDoc
[![Build Status](https://travis-ci.org/Rebilly/ReDoc.svg?branch=master)](https://travis-ci.org/Rebilly/ReDoc) [![Coverage Status](https://coveralls.io/repos/Rebilly/ReDoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Rebilly/ReDoc?branch=master) [![Code Climate](https://codeclimate.com/github/Rebilly/ReDoc/badges/gpa.svg)](https://codeclimate.com/github/Rebilly/ReDoc) [![David](https://david-dm.org/Rebilly/ReDoc/dev-status.svg)](https://david-dm.org/Rebilly/ReDoc#info=devDependencies)
[![Build Status](https://travis-ci.org/Rebilly/ReDoc.svg?branch=master)](https://travis-ci.org/Rebilly/ReDoc) [![Coverage Status](https://coveralls.io/repos/Rebilly/ReDoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Rebilly/ReDoc?branch=master) [![Code Climate](https://codeclimate.com/github/Rebilly/ReDoc/badges/gpa.svg)](https://codeclimate.com/github/Rebilly/ReDoc) [![David](https://david-dm.org/Rebilly/ReDoc/dev-status.svg)](https://david-dm.org/Rebilly/ReDoc#info=devDependencies) [![Stories in Ready](https://badge.waffle.io/Rebilly/ReDoc.png?label=ready&title=Ready)](https://waffle.io/Rebilly/ReDoc)
[![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/swagger-parser) [![Bower](http://img.shields.io/bower/v/redoc.svg)](http://bower.io/) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Rebilly/ReDoc/blob/master/LICENSE)

View File

@ -42,12 +42,14 @@ gulp.task('inlineTemplates', ['sass'], function() {
});
var JS_DEV_DEPS = [
'lib/utils/browser-update.js',
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/babel-polyfill/dist/polyfill.js'
];
var JS_DEV_DEPS_MIN = [
'lib/utils/browser-update.js',
'node_modules/zone.js/dist/zone-microtask.min.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/babel-polyfill/dist/polyfill.min.js'
@ -91,7 +93,7 @@ function bundle(outputFile, minify, cb) {
builder
.buildStatic(path.join(paths.tmp, paths.sourceEntryPoint),
outputFile,
{ format:'umd', sourceMaps: true, lowResSourceMaps: true, minify: minify }
{ format:'umd', sourceMaps: true, mangle: false, lowResSourceMaps: true, minify: minify }
)
.then(function() {
cb();

View File

@ -17,7 +17,7 @@
<redoc scroll-y-offset="body > nav" spec-url='swagger.yml'></redoc>
<!-- ReDoc built file with all dependencies included -->
<script src="dist/redoc.js"> </script>
<script src="main.js"> </script>
<script src="dist/redoc.js"> </script>
</body>
</html>

View File

@ -8,4 +8,6 @@
Redoc.init(schemaUrlInput.value);
return false;
})
//window.redocDebugMode = true;
})();

View File

@ -49,18 +49,19 @@ module.exports = function (config) {
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/babel-polyfill/dist/polyfill.js',
'node_modules/reflect-metadata/Reflect.js'
'./node_modules/reflect-metadata/Reflect.js'
],
jspm: {
config: 'system.config.js',
loadFiles: ['tests/unit/*.spec.js', 'tests/helpers.js', 'lib/**/*.js'],
loadFiles: ['tests/setup.js', 'tests/helpers.js', 'tests/unit/*.spec.js', 'lib/**/*.js'],
serveFiles: ['tests/schemas/**/*.json','tests/schemas/**/*.yml', 'lib/**/*.html', '.tmp/lib/**/*.css'],
nocache: true
},
proxies: {
'/tests/': '/base/tests/',
'/lib/components/Redoc/redoc-loading-styles.css': '/base/.tmp/lib/components/Redoc/redoc-loading-styles.css',
'/lib/': '/base/lib/',
'/jspm_packages/': '/base/jspm_packages/',
'/node_modules/': '/base/node_modules/',

View File

@ -7,16 +7,17 @@ import {BrowserDomAdapter} from 'angular2/platform/browser';
selector: '[sticky-sidebar]',
inputs: ['scrollParent', 'scrollYOffset']
})
@Reflect.metadata('parameters', [[ElementRef], [BrowserDomAdapter]])
export default class StickySidebar {
constructor(elementRef, dom) {
this.element = elementRef.nativeElement;
this.$element = elementRef.nativeElement;
this.dom = dom;
// initial styling
this.dom.setStyle(this.element, 'position', 'absolute');
this.dom.setStyle(this.element, 'top', '0');
this.dom.setStyle(this.element, 'bottom', '0');
this.dom.setStyle(this.element, 'max-height', '100%');
this.dom.setStyle(this.$element, 'position', 'absolute');
this.dom.setStyle(this.$element, 'top', '0');
this.dom.setStyle(this.$element, 'bottom', '0');
this.dom.setStyle(this.$element, 'max-height', '100%');
}
bind() {
@ -29,7 +30,7 @@ export default class StickySidebar {
}
updatePosition() {
if ( this.scrollY + this.scrollYOffset() >= this.redocEl.offsetTop) {
if ( this.scrollY + this.scrollYOffset() >= this.$redocEl.offsetTop) {
this.stick();
} else {
this.unstick();
@ -37,13 +38,13 @@ export default class StickySidebar {
}
stick() {
this.dom.setStyle(this.element, 'position', 'fixed');
this.dom.setStyle(this.element, 'top', this.scrollYOffset() + 'px');
this.dom.setStyle(this.$element, 'position', 'fixed');
this.dom.setStyle(this.$element, 'top', this.scrollYOffset() + 'px');
}
unstick() {
this.dom.setStyle(this.element, 'position', 'absolute');
this.dom.setStyle(this.element, 'top', 0);
this.dom.setStyle(this.$element, 'position', 'absolute');
this.dom.setStyle(this.$element, 'top', 0);
}
get scrollY() {
@ -51,7 +52,8 @@ export default class StickySidebar {
}
ngOnInit() {
this.redocEl = this.element.offsetParent;
// FIXME use more reliable code
this.$redocEl = this.$element.offsetParent;
this.bind();
}
@ -59,5 +61,3 @@ export default class StickySidebar {
this.unbind();
}
}
StickySidebar.parameters = [ [ElementRef], [BrowserDomAdapter] ];

View File

@ -65,16 +65,25 @@ export class Tabs {
})
@View({
template: `
<div class="tab-wrap" [hidden]="!active">
<div class="tab-wrap" [ngClass]="{ 'active': active }">
<ng-content></ng-content>
</div>
`
`,
directives: [CORE_DIRECTIVES],
styles: [`
.tab-wrap {
display: none;
}
.tab-wrap.active {
display: block;
}`
]
})
@Reflect.metadata('parameters', [ [Tabs] ])
export class Tab {
constructor(tabs) {
this.active = false;
tabs.addTab(this);
}
}
Tab.parameters = [ [ Tabs ] ];

View File

@ -64,6 +64,7 @@ span.zippy-indicator {
}
.zippy-hidden {
overflow: hidden;
visibility: hidden;
height: 0;
padding: 0;

View File

@ -56,7 +56,6 @@ describe('Redoc components', () => {
@Component({selector: 'test-app'})
@View({
directives: [ApiInfo],
providers: [SchemaManager],
template:
`<api-info></api-info>`
})

View File

@ -7,7 +7,7 @@ import {RedocComponent, BaseComponent} from '../base';
styleUrls: ['./lib/components/ApiLogo/api-logo.css'],
templateUrl: './lib/components/ApiLogo/api-logo.html'
})
export default class ApiInfo extends BaseComponent {
export default class ApiLogo extends BaseComponent {
constructor(schemaMgr) {
super(schemaMgr);
}

View File

@ -30,8 +30,11 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
}
.param-name {
flex-grow: 0;
-ms-flex-grow: 0;
display: inline-block;
font-size: 14px;
padding: $cell-padding $cell-spacing $cell-padding 0;
padding: $cell-padding 0 $cell-padding 0;
font-weight: bold;
box-sizing: border-box;
line-height: $param-name-height;
@ -40,15 +43,24 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
position: relative;
}
.param-name-content {
padding-right: $cell-spacing;
display: inline-block;
}
.param-info {
width: 100%;
//width: 100%;
flex-grow: 1;
-ms-flex-grow: 1;
padding: $cell-padding 0;
box-sizing: border-box;
border-bottom: 1px solid #ccc;
display: inline-block;
}
.param {
display: flex;
display: -ms-flexbox;
}
.param-required {
@ -72,11 +84,11 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
color: #999;
}
.param-type.string {
.param-type.string, .enum-value.string {
color: rgba(0, 80, 0, 0.7);
}
.param-type.integer, .param-type.number {
.param-type.integer, .param-type.number, .enum-value.number {
color: rgba(74, 139, 179, 0.8);
}
@ -84,7 +96,7 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
color: rgba(0, 50, 159, 0.7);
}
.param-type.boolean {
.param-type.boolean, .enum-value.boolean {
color: firebrick;
}
@ -158,3 +170,25 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
.param-schema .param-wrap:first-of-type .param-name:before {
display: none !important;
}
.param-enum {
color: #666;
&:before {
content: "Values: {"
}
&:after {
content: "}"
}
> .enum-value {
&:after {
content: ", ";
}
&:last-of-type:after {
content: none;
}
}
}

View File

@ -2,27 +2,41 @@
import {Component, View, ElementRef} from 'angular2/core';
import {CORE_DIRECTIVES} from 'angular2/common';
import JsonSchema from './json-schema';
import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
import JsonSchema from './json-schema';
import OptionsManager from '../../options';
import SchemaManager from '../../utils/SchemaManager';
var cache = {};
@Component({
selector: 'json-schema-lazy',
inputs: ['pointer']
inputs: ['pointer', 'auto']
})
@View({
template: '',
directives: [CORE_DIRECTIVES]
})
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef], [DynamicComponentLoader], [OptionsManager]])
export default class JsonSchemaLazy {
constructor(elementRef, dcl) {
constructor(schemaMgr, elementRef, dcl, optionsMgr) {
this.elementRef = elementRef;
this.dcl = dcl;
this.optionsMgr = optionsMgr;
this.schemaMgr = schemaMgr;
}
normalizePointer() {
let schema = this.schemaMgr.byPointer(this.pointer);
return schema && schema.$ref || this.pointer;
}
load() {
if (OptionsManager.instance().options.disableLazySchemas) return;
if (this.optionsMgr.options.disableLazySchemas) return;
if (this.loaded) return;
if (this.pointer) {
this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
@ -31,5 +45,46 @@ export default class JsonSchemaLazy {
}
this.loaded = true;
}
// cache JsonSchema view
loadCached() {
this.pointer = this.normalizePointer(this.pointer);
if (cache[this.pointer]) {
cache[this.pointer].then((compRef) => {
setTimeout( ()=> {
let $element = compRef.location.nativeElement;
// skip caching view with tabs inside (discriminator) as it needs attached controller
if ($element.querySelector('tabs')) {
this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
compRef.instance.pointer = this.pointer;
compRef.hostView.changeDetectorRef.markForCheck();
});
return;
}
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
} );
});
} else {
cache[this.pointer] = this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
compRef.instance.pointer = this.pointer;
compRef.hostView.changeDetectorRef.markForCheck();
return compRef;
});
}
}
ngAfterViewInit() {
if (!this.auto) return;
this.loadCached();
}
ngOnDestroy() {
// clear cache
cache = {};
}
}
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
JsonSchemaLazy.parameters = [[ElementRef], [DynamicComponentLoader]];

View File

@ -3,6 +3,7 @@
import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core';
import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import {
TestComponentBuilder,
@ -14,6 +15,7 @@ import {
import JsonSchemaLazy from 'lib/components/JsonSchema/json-schema-lazy';
import SchemaManager from 'lib/utils/SchemaManager';
import OptionsManager from 'lib/options';
describe('Redoc components', () => {
describe('JsonSchemaLazy Component', () => {
@ -26,7 +28,9 @@ describe('Redoc components', () => {
instance: {}
};
beforeEachProviders(() => [
provide(SchemaManager, {useValue: schemaMgr})
provide(SchemaManager, {useValue: schemaMgr}),
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter}),
provide(OptionsManager, {useClass: OptionsManager})
]);
beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => {
builder = tcb;
@ -79,7 +83,6 @@ describe('Redoc components', () => {
@Component({selector: 'test-app'})
@View({
directives: [JsonSchemaLazy],
providers: [SchemaManager, DynamicComponentLoader],
template:
`<json-schema-lazy></json-schema-lazy>`
})

View File

@ -6,13 +6,16 @@
<div *ngFor="#prop of data.properties" class="param-wrap">
<div class="param" [ngClass]="{'discriminator': prop.isDiscriminator}">
<div class="param-name">
<span>{{prop._name}}</span>
<span class="param-name-content">{{prop._name}}</span>
</div>
<div class="param-info">
<div>
<span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint}"
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}</span>
<span *ngIf="prop.isRequired" class="param-required">Required</span>
<span *ngIf="prop.required" class="param-required">Required</span>
<div *ngIf="prop.enum" class="param-enum">
<span *ngFor="#enumItem of prop.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
</div>
</div>
<div class="param-description" innerHtml="{{prop.description | marked}}"></div>
<div class="discriminator-info" *ngIf="prop.isDiscriminator"> This field value determines the exact schema: </div>

View File

@ -1,8 +1,9 @@
'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {Tabs, Tab} from '../../common/components/Tabs/tabs';
import {ElementRef} from 'angular2/core';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import {Tabs, Tab} from '../../common/components/Tabs/tabs';
import JsonPointer from '../../utils/JsonPointer';
@RedocComponent({
@ -12,10 +13,11 @@ import JsonPointer from '../../utils/JsonPointer';
directives: [JsonSchema, Tabs, Tab],
inputs: ['isArray', 'final']
})
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef]])
export default class JsonSchema extends BaseComponent {
constructor(schemaMgr, elementRef) {
super(schemaMgr);
this.element = elementRef.nativeElement;
this.$element = elementRef.nativeElement;
this.final = false;
}
@ -71,7 +73,8 @@ export default class JsonSchema extends BaseComponent {
let discriminatorFieldIdx = -1;
let props = Object.keys(schema.properties).map((prop, idx) => {
let propData = schema.properties[prop];
propData = this.injectPropData(prop, propData, schema);
let propPointer = JsonPointer.join(this.pointer, ['properties', prop]);
propData = JsonSchema.injectPropData(propData, prop, propPointer, this.requiredMap, schema);
if (propData.isDiscriminator) discriminatorFieldIdx = idx;
return propData;
});
@ -83,32 +86,23 @@ export default class JsonSchema extends BaseComponent {
this.data.properties = props;
}
adjustNameColumnWidth() {
// TODO handle internal schemes differently
let names = [].slice.call(this.element.querySelectorAll('.param-name'));
let widths = names.map(el => el.offsetWidth);
let maxWidth = Math.max(...widths);
if (!maxWidth) return;
names.forEach(el => {
el.style.minWidth = maxWidth + 'px';
});
static injectPropData(propData, propName, propPointer, requiredMap, schema) {
let propEnum;
let discrValues = this.element.querySelector('tabs ul');
if (discrValues) discrValues.style.paddingLeft = maxWidth + 'px';
}
injectPropData(prop, propData, schema) {
propData = Object.assign({}, propData);
propData._name = prop;
propData.isRequired = this.requiredMap[prop];
propData._name = propName;
propData.required = propData.required || (requiredMap && requiredMap[propName]);
propData._displayType = propData.type;
propData.isDiscriminator = (schema.discriminator === prop);
propData.isDiscriminator = (schema && schema.discriminator === propName);
propEnum = propData.enum;
if (propData.type === 'array') {
let itemType = propData.items.type;
let itemFormat = propData.items.format;
propEnum = propData.items.enum;
if (itemType === 'object' || !itemType) {
itemType = propData.items.title || 'object';
propData._pointer = propData.items._pointer || JsonPointer.join(this.pointer, ['properties', prop, 'items']);
propData._pointer = propData.items._pointer
|| JsonPointer.join(propPointer, ['items']);
}
propData._displayType = `${itemType}`;
propData.format = itemFormat;
@ -126,12 +120,38 @@ export default class JsonSchema extends BaseComponent {
}
if (propData.format) propData._displayFormat = `<${propData.format}>`;
if (propEnum) {
propData.enum = propEnum.map((value) => {
return {val: value, type: typeof value};
});
}
return propData;
}
init() {
setTimeout(() => this.adjustNameColumnWidth());
ngAfterViewInit() {
// adjust widht only on parent level
let $el = this.$element.parentElement;
while($el && $el.tagName !== 'JSON-SCHEMA' && $el.tagName !== 'REDOC') {
$el = $el.parentElement;
}
if ($el && $el.tagName === 'REDOC' ) {
this.adjustNameColumnWidth();
}
}
adjustNameColumnWidth() {
// TODO handle internal schemes differently
let names = [].slice.call(this.$element.querySelectorAll('.param-name-content'));
let widths = [144];//names.map(el => el.offsetWidth);
let maxWidth = Math.max(...widths);
if (!maxWidth) return;
names.forEach(el => {
el.parentNode.style.minWidth = maxWidth + 'px';
});
let discrValues = this.$element.querySelector('tabs ul');
if (discrValues) discrValues.style.paddingLeft = maxWidth + 'px';
}
}
JsonSchema.parameters = JsonSchema.parameters.concat([[ElementRef]]);

View File

@ -3,6 +3,10 @@
/* styles for array-schema for array */
$array-marker-font-sz: 12px;
$array-marker-line-height: 1.5;
:host {
display: block;
}
.params-wrap.params-array:before, .params-wrap.params-array:after {
display: block;
font-weight: bold;

View File

@ -2,6 +2,7 @@
import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core';
import OptionsManager from 'lib/options';
import {
TestComponentBuilder,
@ -21,7 +22,8 @@ describe('Redoc components', () => {
let schemaMgr = new SchemaManager();
let fixture;
beforeEachProviders(() => [
provide(SchemaManager, {useValue: schemaMgr})
provide(SchemaManager, {useValue: schemaMgr}),
provide(OptionsManager, {useClass: OptionsManager})
]);
beforeEach(inject([TestComponentBuilder], (tcb) => {
builder = tcb;

View File

@ -1,7 +1,7 @@
<div class="method">
<div class="method-content">
<h2 class="method-header sharable-header">
<a class="share-link" href="#{{tag}}{{pointer}}"></a>{{data.methodInfo.summary}}
<a class="share-link" href="#{{data.methodAnchor}}"></a>{{data.methodInfo.summary}}
</h2>
<h3 class="method-endpoint">
<span class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</span>

View File

@ -2,6 +2,7 @@
import {JsonPointer} from '../../utils/JsonPointer';
import {RedocComponent, BaseComponent} from '../base';
import ParamsList from '../ParamsList/params-list';
import ResponsesList from '../ResponsesList/responses-list';
import ResponsesSamples from '../ResponsesSamples/responses-samples';
@ -28,6 +29,11 @@ export default class Method extends BaseComponent {
this.data.methodInfo = this.componentSchema;
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
this.data.bodyParam = this.findBodyParam();
if (this.componentSchema.operationId) {
this.data.methodAnchor = 'operation/' + this.componentSchema.operationId;
} else {
this.data.methodAnchor = 'tag/' + this.tag + this.pointer;
}
}
filterMainTags(tags) {

View File

@ -2,6 +2,7 @@
import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import {
TestComponentBuilder,
@ -13,13 +14,16 @@ import {
import Method from 'lib/components/Method/method';
import SchemaManager from 'lib/utils/SchemaManager';
import OptionsManager from 'lib/options';
describe('Redoc components', () => {
describe('Method Component', () => {
let builder;
let component;
beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()})
provide(SchemaManager, {useValue: new SchemaManager()}),
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter}),
provide(OptionsManager, {useClass: OptionsManager})
]);
beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
builder = tcb;

View File

@ -1,10 +1,10 @@
<div class="methods">
<div class="tag" *ngFor="#tag of data.tags">
<div class="tag-info" [attr.tag]="tag.name">
<h1 class="sharable-header"> <a class="share-link" href="#{{tag.name}}"></a>{{tag.name}} </h1>
<h1 class="sharable-header"> <a class="share-link" href="#tag/{{tag.name}}"></a>{{tag.name}} </h1>
<p *ngIf="tag.description" innerHtml="{{ tag.description | marked }}"> </p>
</div>
<method *ngFor="#method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer"
[attr.tag]="method.tag" [tag]="method.tag"></method>
[attr.tag]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId"></method>
</div>
</div>

View File

@ -2,6 +2,8 @@
import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core';
import OptionsManager from 'lib/options';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import {
TestComponentBuilder,
@ -15,12 +17,14 @@ import MethodsList from 'lib/components/MethodsList/methods-list';
import SchemaManager from 'lib/utils/SchemaManager';
describe('Redoc components', () => {
describe('ApiInfo Component', () => {
describe('MethodsList Component', () => {
let builder;
let component;
let fixture;
beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()})
provide(SchemaManager, {useValue: new SchemaManager()}),
provide(OptionsManager, {useClass: OptionsManager}),
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter})
]);
beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
builder = tcb;
@ -32,7 +36,7 @@ describe('Redoc components', () => {
component = getChildDebugElement(fixture.debugElement, 'methods-list').componentInstance;
fixture.detectChanges();
done();
}, err => { throw err; });
}, err => done.fail(err) );
});

View File

@ -2,12 +2,16 @@
<div class="params-wrap">
<div *ngFor="#param of data.params" class="param">
<div class="param-name">
<span> {{param.name}} </span>
<span class="param-name-content"> {{param.name}} </span>
</div>
<div class="param-info">
<div>
<span class="param-type" [ngClass]="param.type">{{param.type}}</span>
<span class="param-type {{param.type}}" [ngClass]="{'with-hint': param._displayTypeHint}"
title="{{param._displayTypeHint}}"> {{param._displayType}} {{param._displayFormat}}</span>
<span *ngIf="param.required" class="param-required">Required</span>
<div *ngIf="param.enum" class="param-enum">
<span *ngFor="#enumItem of param.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
</div>
</div>
<div class="param-description" innerHtml="{{param.description | marked}}"></div>
</div>
@ -19,7 +23,7 @@
<div class="body-param-description" innerHtml="{{data.bodyParam.description | marked}}"></div>
<div>
<json-schema pointer="{{data.bodyParam.pointer}}/schema">
</json-schema>
<json-schema-lazy [auto]="true" pointer="{{data.bodyParam.pointer}}/schema">
</json-schema-lazy>
</div>
</div>

View File

@ -2,12 +2,13 @@
import {RedocComponent, BaseComponent} from '../base';
import JsonSchema from '../JsonSchema/json-schema';
import JsonSchemaLazy from '../JsonSchema/json-schema-lazy';
@RedocComponent({
selector: 'params-list',
templateUrl: './lib/components/ParamsList/params-list.html',
styleUrls: ['./lib/components/ParamsList/params-list.css'],
directives: [JsonSchema]
directives: [JsonSchema, JsonSchemaLazy]
})
export default class ParamsList extends BaseComponent {
constructor(schemaMgr) {
@ -26,6 +27,11 @@ export default class ParamsList extends BaseComponent {
this.data.bodyParam = bodyParam;
}
params = params.map((paramData) => {
let propPointer = paramData._pointer;
return JsonSchema.injectPropData(paramData, paramData.name, propPointer);
});
this.data.noParams = !(params.length || this.data.bodyParam);
this.data.params = params;
}

View File

@ -28,23 +28,27 @@
display: table-row;
}
.param:first-of-type .param-name:before {
content: "";
display: block;
position: absolute;
left: -$lines-width;
top: 0;
border-left: $line-border-erase;
height: ($param-name-height/2) + $cell-padding;
.param:last-of-type > .param-name {
border-left: 0;
&:after {
content: "";
display: block;
position: absolute;
left: 0;
border-left: $line-border;
height: ($param-name-height/2) + $cell-padding + $lines-width;
background-color: white;
top: 0;
}
}
.param:last-of-type .param-name:after {
.param:first-of-type .param-name:after {
content: "";
display: block;
position: absolute;
left: -$lines-width;
border-left: $line-border-erase;
top: ($param-name-height/2) + $cell-padding + $lines-width;
height: ($param-name-height/2) + $cell-padding;
background-color: white;
bottom: 0;
top: 0;
}

View File

@ -0,0 +1,37 @@
redoc.loading {
position: relative;
display: block;
min-height:350px;
}
@keyframes move {
0% {transform: translateY(10px)}
25% {transform: translateY(0px)}
50% {transform: translateY(10px)}
75% {transform: translateY(20px)}
100% {transform: translateY(10px)}
}
redoc.loading:before {
content: "Loading...";
font-size: 28px;
text-align: center;
padding-top: 40px;
color: #3F5C9C;
font-weight: bold;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: white;
z-index: 9999;
opacity: 1;
transition: all 0.6s ease-out;
animation: 2s move linear infinite;
}
redoc.loading-remove:before {
opacity: 0;
}

View File

@ -1,5 +1,5 @@
<div class="redoc-wrap">
<div class="menu-content" sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
<div class="menu-content" sticky-sidebar [scrollParent]="options.$scrollParent" [scrollYOffset]="options.scrollYOffset">
<api-logo> </api-logo>
<side-menu> </side-menu>
</div>

View File

@ -1,5 +1,9 @@
'use strict';
import {ChangeDetectionStrategy, provide, enableProdMode} from 'angular2/core';
import {ElementRef} from 'angular2/core';
import {BrowserDomAdapter, bootstrap} from 'angular2/platform/browser';
import detectScollParent from 'scrollparent';
import {RedocComponent, BaseComponent} from '../base';
import SchemaManager from '../../utils/SchemaManager';
@ -11,116 +15,35 @@ import StickySidebar from '../../common/components/StickySidebar/sticky-sidebar'
import OptionsManager from '../../options';
import {redocEvents} from '../../events';
import {ChangeDetectionStrategy} from 'angular2/core';
import {ElementRef} from 'angular2/core';
import {BrowserDomAdapter, bootstrap} from 'angular2/platform/browser';
import detectScollParent from 'scrollparent';
import './redoc-loading-styles.css!css';
import {isFunction, isString} from 'angular2/src/facade/lang';
let optionNames = new Set(['scrollYOffset', 'disableLazySchemas']);
let dom = new BrowserDomAdapter();
var dom = new BrowserDomAdapter();
var _modeLocked = false;
@RedocComponent({
selector: 'redoc',
providers: [SchemaManager, BrowserDomAdapter, OptionsManager],
providers: [
SchemaManager,
BrowserDomAdapter
],
templateUrl: './lib/components/Redoc/redoc.html',
styleUrls: ['./lib/components/Redoc/redoc.css'],
directives: [ApiInfo, ApiLogo, MethodsList, SideMenu, StickySidebar],
changeDetection: ChangeDetectionStrategy.Default
})
@Reflect.metadata('parameters', [
[SchemaManager], [OptionsManager], [ElementRef]])
export default class Redoc extends BaseComponent {
constructor(schemaMgr, optionsMgr, elementRef, dom) {
constructor(schemaMgr, optionsMgr, elementRef) {
super(schemaMgr);
this.element = elementRef.nativeElement;
this.dom = dom;
let el = this.element;
//parse options (top level component doesn't support inputs)
this.scrollParent = detectScollParent(el);
this.parseOptions();
this.options = Object.assign({}, optionsMgr.options, this.options);
this.normalizeOptions();
optionsMgr.options = this.options;
}
parseOptions() {
let attributesMap = this.dom.attributeMap(this.element);
this.options = {};
Array.from(attributesMap.keys())
//camelCasify
.map(k => ({
attrName: k,
name: k.replace(/-(.)/g, (m, $1) => $1.toUpperCase())
})
)
.filter(option => optionNames.has(option.name))
.forEach(option => {
this.options[option.name] = attributesMap.get(option.attrName);
});
}
normalizeOptions() {
// modify scrollYOffset to always be a function
if (!isFunction(this.options.scrollYOffset)) {
if (isFinite(this.options.scrollYOffset)) {
// if number specified create function that returns this value
let numberOffset = parseFloat(this.options.scrollYOffset);
this.options.scrollYOffset = () => numberOffset;
} else {
// if selector or node function that returns bottom offset of this node
let el = this.options.scrollYOffset;
if (!(el instanceof Node)) {
el = this.dom.query(el);
}
if (!el) {
this.options.scrollYOffset = () => 0;
} else {
this.options.scrollYOffset = () => el.offsetTop + el.offsetHeight;
}
}
}
if (isString(this.options.disableLazySchemas)) this.options.disableLazySchemas = true;
optionsMgr.parseOptions( this.element );
optionsMgr.options.$scrollParent = detectScollParent( this.element );
this.options = optionsMgr.options;
}
static showLoadingAnimation() {
if (!dom.query('#redoc-loading-style')) {
let animStyle = dom.createStyleElement(`
redoc.loading {
position: relative;
display: block;
min-height:350px;
}
redoc.loading:before {
content: "Loading...";
font-size: 28px;
text-align: center;
padding-top: 40px;
color: #3F5C9C;
font-weight: bold;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: white;
z-index: 9999;
opacity: 1;
transition: all 0.6s ease-out;
}
redoc.loading-remove:before {
opacity: 0;
}
`);
animStyle.id = 'redoc-loading-style';
dom.appendChild(dom.defaultDoc().head, animStyle);
}
let elem = dom.query('redoc');
dom.addClass(elem, 'loading');
}
@ -135,20 +58,30 @@ export default class Redoc extends BaseComponent {
}
static init(schemaUrl, options) {
var optionsMgr = new OptionsManager();
optionsMgr.options = options;
var providers = [
provide(OptionsManager, {useValue: optionsMgr})
];
if (Redoc.appRef) {
Redoc.dispose();
}
Redoc.showLoadingAnimation();
return SchemaManager.instance().load(schemaUrl)
.then(() => {
(new OptionsManager()).options = options;
return bootstrap(Redoc);
if (!_modeLocked && !optionsMgr.options.debugMode) {
enableProdMode();
_modeLocked = true;
}
return bootstrap(Redoc, providers);
})
.then(
(appRef) => {
Redoc.hideLoadingAnimation();
Redoc.appRef = appRef;
redocEvents.bootstrapped.next();
// setTimeout to allow cached elements to init
setTimeout(() => redocEvents.bootstrapped.next());
console.log('ReDoc bootstrapped!');
},
error => {
@ -160,7 +93,6 @@ export default class Redoc extends BaseComponent {
static autoInit() {
const specUrlAttributeName = 'spec-url';
let dom = new BrowserDomAdapter();
let redocEl = dom.query('redoc');
if (!redocEl) return;
if (dom.hasAttribute(redocEl, specUrlAttributeName)) {
@ -170,7 +102,6 @@ export default class Redoc extends BaseComponent {
}
static dispose() {
let dom = new BrowserDomAdapter();
let el = dom.query('redoc');
let elClone;
let parent;
@ -192,8 +123,3 @@ export default class Redoc extends BaseComponent {
}
}
}
Redoc.parameters = Redoc.parameters.concat([[OptionsManager], [ElementRef], [BrowserDomAdapter]]);
// TODO
// this doesn't work in side-menu.js because of some circular references issue
SideMenu.parameters = SideMenu.parameters.concat([[Redoc]]);

View File

@ -1,12 +1,12 @@
'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {ViewChildren, QueryList, ChangeDetectorRef, ChangeDetectionStrategy} from 'angular2/core';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import JsonPointer from '../../utils/JsonPointer';
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({
@ -18,6 +18,7 @@ import {redocEvents} from '../../events';
pipes: [PrismPipe],
changeDetection: ChangeDetectionStrategy.OnPush
})
@Reflect.metadata('parameters', [[SchemaManager], [new ViewChildren(Tabs), QueryList], [ChangeDetectorRef]])
export default class RequestSamples extends BaseComponent {
constructor(schemaMgr, tabs, changeDetector) {
super(schemaMgr);
@ -46,5 +47,3 @@ export default class RequestSamples extends BaseComponent {
this.data.samples = this.componentSchema['x-code-samples'] || [];
}
}
RequestSamples.parameters = RequestSamples.parameters.concat([ [new ViewChildren(Tabs), QueryList], [ChangeDetectorRef] ]);

View File

@ -14,7 +14,7 @@
<header>
Response schema
</header>
<json-schema *ngIf="response.schema && !enabledLazy" class="schema type" pointer="{{response.pointer}}/schema">
<json-schema *ngIf="response.schema && options.disableLazySchemas" class="schema type" pointer="{{response.pointer}}/schema">
</json-schema>
<json-schema-lazy #lazySchema pointer="{{response.schema ? response.pointer + '/schema' : null}}">
</json-schema-lazy>

View File

@ -1,6 +1,6 @@
'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import JsonPointer from '../../utils/JsonPointer';
import JsonSchema from '../JsonSchema/json-schema';
import JsonSchemaLazy from '../JsonSchema/json-schema-lazy';
@ -18,15 +18,16 @@ function isNumeric(n) {
styleUrls: ['./lib/components/ResponsesList/responses-list.css'],
directives: [JsonSchema, Zippy, JsonSchemaLazy]
})
@Reflect.metadata('parameters', [[SchemaManager], [OptionsManager]])
export default class ResponsesList extends BaseComponent {
constructor(schemaMgr) {
constructor(schemaMgr, optionsMgr) {
super(schemaMgr);
this.options = optionsMgr.options;
}
prepareModel() {
this.data = {};
this.data.responses = [];
this.enabledLazy = !OptionsManager.instance().options.disableLazySchemas;
let responses = this.componentSchema;
if (!responses) return;

View File

@ -1,11 +1,11 @@
'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {ElementRef} from 'angular2/core';
import SchemaSampler from 'json-schema-instantiator';
import {JsonFormatter} from '../../utils/JsonFormatterPipe';
import {ElementRef} from 'angular2/core';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
@RedocComponent({
selector: 'schema-sample',
@ -13,6 +13,7 @@ import {ElementRef} from 'angular2/core';
pipes: [JsonFormatter],
styleUrls: ['./lib/components/SchemaSample/schema-sample.css']
})
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef]])
export default class SchemaSample extends BaseComponent {
constructor(schemaMgr, elementRef) {
super(schemaMgr);
@ -54,4 +55,3 @@ export default class SchemaSample extends BaseComponent {
});
}
}
SchemaSample.parameters = SchemaSample.parameters.concat([[ElementRef]]);

View File

@ -1,13 +1,14 @@
'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {redocEvents} from '../../events';
import {NgZone, ChangeDetectionStrategy, ElementRef} from 'angular2/core';
import {document} from 'angular2/src/facade/browser';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import {global} from 'angular2/src/facade/lang';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import {redocEvents} from '../../events';
import OptionsManager from '../../options';
const CHANGE = {
NEXT : 1,
BACK : -1,
@ -26,20 +27,18 @@ const INVIEW_POSITION = {
styleUrls: ['./lib/components/SideMenu/side-menu.css'],
changeDetection: ChangeDetectionStrategy.Default
})
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef],
[BrowserDomAdapter], [NgZone], OptionsManager])
export default class SideMenu extends BaseComponent {
constructor(schemaMgr, elementRef, adapter, zone, redoc) {
constructor(schemaMgr, elementRef, dom, zone, optionsMgr) {
super(schemaMgr);
this.zone = zone;
this.adapter = adapter;
this.redoc = redoc;
this.scrollParent = this.redoc.scrollParent;
this.mobileNav = adapter.querySelector(elementRef.nativeElement, '.mobile-nav');
this.resourcesNav = adapter.querySelector(elementRef.nativeElement, '#resources-nav');
this.$element = elementRef.nativeElement;
this.dom = dom;
this.options = optionsMgr.options;
this.$scrollParent = this.options.$scrollParent;
// for some reason constructor is not run inside zone
// as workaround running it manually
this.zone.run(() => {
zone.run(() => {
this.bindEvents();
});
this.activeCatIdx = 0;
@ -53,18 +52,26 @@ export default class SideMenu extends BaseComponent {
}
scrollY() {
return (this.scrollParent.pageYOffset != null) ? this.scrollParent.pageYOffset : this.scrollParent.scrollTop;
return (this.$scrollParent.pageYOffset != null) ? this.$scrollParent.pageYOffset : this.$scrollParent.scrollTop;
}
hashScroll(evt) {
let hash = this.adapter.getLocation().hash;
let hash = this.dom.getLocation().hash;
if (!hash) return;
let $el;
hash = hash.substr(1);
let tag = hash.split('/')[0];
let ptr = hash.substr(tag.length);
let el = this.getMethodEl(ptr, tag);
if (el) this.scrollTo(el);
let namespace = hash.split('/')[0];
let ptr = hash.substr(namespace.length + 1);
if (namespace === 'operation') {
$el = this.getMethodElByOperId(ptr);
} else if (namespace === 'tag') {
let tag = ptr.split('/')[0];
ptr = ptr.substr(tag.length);
$el = this.getMethodElByPtr(ptr, tag);
}
if ($el) this.scrollTo($el);
if (evt) evt.preventDefault();
}
@ -73,12 +80,12 @@ export default class SideMenu extends BaseComponent {
//decorate option.scrollYOffset to account mobile nav
this.scrollYOffset = () => {
let mobileNavOffset = this.mobileNav.clientHeight;
return this.redoc.options.scrollYOffset() + mobileNavOffset;
let mobileNavOffset = this.$mobileNav.clientHeight;
return this.options.scrollYOffset() + mobileNavOffset;
};
this._cancel = {};
this._cancel.scroll = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.scrollHandler(); });
this._cancel.hash = this.adapter.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt));
this._cancel.scroll = this.dom.onAndCancel(this.$scrollParent, 'scroll', () => { this.scrollHandler(); });
this._cancel.hash = this.dom.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt));
}
destroy() {
@ -94,14 +101,14 @@ export default class SideMenu extends BaseComponent {
this.scrollToActive();
}
scrollTo(el) {
scrollTo($el) {
// TODO: rewrite this to use offsetTop as more reliable solution
let subjRect = el.getBoundingClientRect();
let subjRect = $el.getBoundingClientRect();
let offset = this.scrollY() + subjRect.top - this.scrollYOffset() + 1;
if (this.scrollParent.scrollTo) {
this.scrollParent.scrollTo(0, offset);
if (this.$scrollParent.scrollTo) {
this.$scrollParent.scrollTo(0, offset);
} else {
this.scrollParent.scrollTop = offset;
this.$scrollParent.scrollTop = offset;
}
}
@ -170,22 +177,27 @@ export default class SideMenu extends BaseComponent {
return (methodIdx === 0 && catIdx === 0);
}
getMethodEl(ptr, tag) {
getMethodElByPtr(ptr, tag) {
let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`;
return document.querySelector(selector);
}
getMethodElByOperId(operationId) {
let selector =`[operation-id="${operationId}"]`;
return document.querySelector(selector);
}
getCurrentMethodEl() {
return this.getMethodEl(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
return this.getMethodElByPtr(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
}
/* returns 1 if element if above the view, 0 if in view and -1 below the view */
getElementInViewPos(el) {
if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset()) {
getElementInViewPos($el) {
if (Math.floor($el.getBoundingClientRect().top) > this.scrollYOffset()) {
return INVIEW_POSITION.ABOVE;
}
if (el.getBoundingClientRect().bottom <= this.scrollYOffset()) {
if ($el.getBoundingClientRect().bottom <= this.scrollYOffset()) {
return INVIEW_POSITION.BELLOW;
}
return INVIEW_POSITION.INVIEW;
@ -196,9 +208,9 @@ export default class SideMenu extends BaseComponent {
this.prevOffsetY = this.scrollY();
let stable = false;
while(!stable) {
let activeMethodHost = this.getCurrentMethodEl();
if (!activeMethodHost) return;
var elementInViewPos = this.getElementInViewPos(activeMethodHost);
let $activeMethodHost = this.getCurrentMethodEl();
if (!$activeMethodHost) return;
var elementInViewPos = this.getElementInViewPos($activeMethodHost);
if(isScrolledDown && elementInViewPos === INVIEW_POSITION.BELLOW) {
stable = this.changeActive(CHANGE.NEXT);
continue;
@ -219,25 +231,26 @@ export default class SideMenu extends BaseComponent {
}
mobileMode() {
return this.mobileNav.clientHeight > 0;
return this.$mobileNav.clientHeight > 0;
}
toggleMobileNav() {
let dom = this.adapter;
let overflowParent = (this.scrollParent === global) ? dom.defaultDoc().body : this.scrollParent;
if (dom.hasStyle(this.resourcesNav, 'height')) {
dom.removeStyle(this.resourcesNav, 'height');
dom.removeStyle(overflowParent, 'overflow-y');
let dom = this.dom;
let $overflowParent = (this.$scrollParent === global) ? dom.defaultDoc().body : this.$scrollParent;
if (dom.hasStyle(this.$resourcesNav, 'height')) {
dom.removeStyle(this.$resourcesNav, 'height');
dom.removeStyle($overflowParent, 'overflow-y');
} else {
let viewportHeight = this.scrollParent.innerHeight || this.scrollParent.clientHeight;
let height = viewportHeight - this.mobileNav.getBoundingClientRect().bottom;
dom.setStyle(overflowParent, 'overflow-y', 'hidden');
dom.setStyle(this.resourcesNav, 'height', height + 'px');
let viewportHeight = this.$scrollParent.innerHeight || this.$scrollParent.clientHeight;
let height = viewportHeight - this.$mobileNav.getBoundingClientRect().bottom;
dom.setStyle($overflowParent, 'overflow-y', 'hidden');
dom.setStyle(this.$resourcesNav, 'height', height + 'px');
}
}
init() {
this.$mobileNav = this.dom.querySelector(this.$element, '.mobile-nav');
this.$resourcesNav = this.dom.querySelector(this.$element, '#resources-nav');
this.changeActive(CHANGE.INITIAL);
}
}
SideMenu.parameters = SideMenu.parameters.concat([[ElementRef], [BrowserDomAdapter], [NgZone]]);

View File

@ -3,6 +3,7 @@
import { getChildDebugElement, mouseclick} from 'tests/helpers';
import {Component, View, provide, ViewMetadata} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import OptionsManager from 'lib/options';
import {
TestComponentBuilder,
@ -15,15 +16,14 @@ import {
import {redocEvents} from 'lib/events';
import MethodsList from 'lib/components/MethodsList/methods-list';
import SideMenu from 'lib/components/SideMenu/side-menu';
import Redoc from 'lib/components/Redoc/redoc';
import SchemaManager from 'lib/utils/SchemaManager';
let _mockRedoc = {
options: {
scrollYOffset: () => 0
},
let testOptions = new OptionsManager();
testOptions.options = {
scrollYOffset: () => 0,
scrollParent: window
};
describe('Redoc components', () => {
describe('SideMenu Component', () => {
let builder;
@ -32,7 +32,7 @@ describe('Redoc components', () => {
beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}),
provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}),
provide(Redoc, {useValue: _mockRedoc})
provide(OptionsManager, {useValue: testOptions})
]);
beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
builder = tcb;
@ -61,7 +61,7 @@ describe('Redoc components', () => {
});
it('should run hashScroll when redoc bootstrapped', (done) => {
spyOn(component.adapter, 'getLocation').and.returnValue({hash: ''});
spyOn(component.dom, 'getLocation').and.returnValue({hash: ''});
spyOn(component, 'hashScroll').and.stub();
spyOn(window, 'scrollTo').and.stub();
redocEvents.bootstrapped.next();
@ -75,9 +75,23 @@ describe('Redoc components', () => {
});
});
it('should scroll to method when location hash is present', (done) => {
let hash = '#pet/paths/~1pet~1findByStatus/get';
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
it('should scroll to method when location hash is present [jp]', (done) => {
let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough();
spyOn(window, 'scrollTo').and.stub();
redocEvents.bootstrapped.next();
setTimeout(() => {
expect(component.hashScroll).toHaveBeenCalled();
let scrollY = window.scrollTo.calls.argsFor(0)[1];
expect(scrollY).toBeGreaterThan(0);
done();
});
});
it('should scroll to method when location hash is present [operation]', (done) => {
let hash = '#operation/getPetById';
spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough();
spyOn(window, 'scrollTo').and.stub();
redocEvents.bootstrapped.next();
@ -90,7 +104,7 @@ describe('Redoc components', () => {
});
});
describe('scollable div parent case', () => {
describe('scrollable div parent case', () => {
let menuNativeEl;
beforeEach((done) => {
let scollableDivTmpl =
@ -100,14 +114,13 @@ describe('Redoc components', () => {
</div>`;
builder = builder.overrideView(
TestApp, new ViewMetadata({template: scollableDivTmpl, directives: [MethodsList, SideMenu]}));
builder.createAsync(TestApp).then(_fixture => {
fixture = _fixture;
component = getChildDebugElement(fixture.debugElement, 'side-menu').componentInstance;
menuNativeEl = getChildDebugElement(fixture.debugElement, 'side-menu').nativeElement;
component.scrollParent = _fixture.nativeElement.children[0];
component.options.scrollParent = _fixture.nativeElement.children[0];
component.$scrollParent = _fixture.nativeElement.children[0];
fixture.detectChanges();
done();
}, err => {
throw err;
@ -121,14 +134,26 @@ describe('Redoc components', () => {
expect(component.data).not.toBeNull();
});
it('should scroll to method when location hash is present', (done) => {
let hash = '#pet/paths/~1pet~1findByStatus/get';
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
it('should scroll to method when location hash is present [jp]', (done) => {
let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough();
redocEvents.bootstrapped.next();
setTimeout(() => {
expect(component.hashScroll).toHaveBeenCalled();
expect(component.scrollParent.scrollTop).toBeGreaterThan(0);
expect(component.$scrollParent.scrollTop).toBeGreaterThan(0);
done();
});
});
it('should scroll to method when location hash is present [operation]', (done) => {
let hash = '#operation/getPetById';
spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough();
redocEvents.bootstrapped.next();
setTimeout(() => {
expect(component.hashScroll).toHaveBeenCalled();
expect(component.$scrollParent.scrollTop).toBeGreaterThan(0);
done();
});
});
@ -138,14 +163,14 @@ describe('Redoc components', () => {
component.activeMethodIdx.should.be.equal(-1);
let elTop = component.getCurrentMethodEl().getBoundingClientRect().bottom;
component.scrollParent.scrollTop = elTop + 1;
component.$scrollParent.scrollTop = elTop + 1;
//simulate scroll down
spyOn(component, 'scrollY').and.returnValue(elTop + 2);
component.scrollHandler();
component.activeCatIdx.should.be.equal(1);
component.scrollParent.scrollTop = elTop - 1;
component.$scrollParent.scrollTop = elTop - 1;
//simulate scroll up
component.scrollY.and.returnValue(elTop - 2);
component.scrollHandler();
@ -161,10 +186,10 @@ describe('Redoc components', () => {
});
it('should scroll to appropriate element when click on menu label', () => {
component.scrollParent.scrollTop.should.be.equal(0);
component.$scrollParent.scrollTop.should.be.equal(0);
let menuItemEl = menuNativeEl.querySelector('li');
mouseclick(menuItemEl);
component.scrollParent.scrollTop.should.be.above(0);
component.$scrollParent.scrollTop.should.be.above(0);
});
});
});

View File

@ -5,6 +5,8 @@ import SchemaManager from '../utils/SchemaManager';
import JsonPointer from '../utils/JsonPointer';
import {MarkedPipe, JsonPointerEscapePipe} from '../utils/pipes';
export { SchemaManager };
// common inputs for all components
let commonInputs = ['pointer']; // json pointer to the schema chunk
@ -77,6 +79,7 @@ export function RedocComponent(options) {
* Generic Component
* @class
*/
@Reflect.metadata('parameters', [[SchemaManager]])
export class BaseComponent {
constructor(schemaMgr) {
this.schemaMgr = schemaMgr;
@ -208,4 +211,3 @@ export class BaseComponent {
*/
destroy() {}
}
BaseComponent.parameters = [[SchemaManager]];

View File

@ -1,10 +1,7 @@
'use strict';
import {Redoc} from './components/index';
import {enableProdMode} from 'angular2/core';
export var init = Redoc.init;
window.Redoc = Redoc;
enableProdMode();
Redoc.autoInit();

View File

@ -1,28 +1,22 @@
'use strict';
export var options = {
scrollYOffset: 0
import {isFunction, isString} from 'angular2/src/facade/lang';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import {global} from 'angular2/src/facade/lang';
var defaults = {
scrollYOffset: 0,
disableLazySchemas: false,
debugMode: global.redocDebugMode
};
// singleton
var OPTION_NAMES = new Set(['scrollYOffset', 'disableLazySchemas']);
@Reflect.metadata('parameters', [[BrowserDomAdapter]])
export default class OptionsManager {
constructor() {
if (OptionsManager.prototype._instance) {
return OptionsManager.prototype._instance;
}
OptionsManager.prototype._instance = this;
this._defaults = {
scrollYOffset: 0,
disableLazySchemas: false
};
this._options = {};
}
static instance() {
return new OptionsManager();
this._options = defaults;
this.dom = new BrowserDomAdapter();
}
get options() {
@ -30,6 +24,50 @@ export default class OptionsManager {
}
set options(opts) {
this._options = Object.assign({}, this._defaults, opts);
this._options = Object.assign(this._options, opts);
}
parseOptions(el) {
let parsedOpts;
let attributesMap = this.dom.attributeMap(el);
parsedOpts = {};
Array.from(attributesMap.keys())
//camelCasify
.map(k => ({
attrName: k,
name: k.replace(/-(.)/g, (m, $1) => $1.toUpperCase())
})
)
.filter(option => OPTION_NAMES.has(option.name))
.forEach(option => {
parsedOpts[option.name] = attributesMap.get(option.attrName);
});
this.options = parsedOpts;
this._normalizeOptions();
}
_normalizeOptions() {
// modify scrollYOffset to always be a function
if (!isFunction(this._options.scrollYOffset)) {
if (isFinite(this._options.scrollYOffset)) {
// if number specified create function that returns this value
let numberOffset = parseFloat(this._options.scrollYOffset);
this.options.scrollYOffset = () => numberOffset;
} else {
// if selector or node function that returns bottom offset of this node
let el = this._options.scrollYOffset;
if (!(el instanceof Node)) {
el = this.dom.query(el);
}
if (!el) {
this._options.scrollYOffset = () => 0;
} else {
this._options.scrollYOffset = () => el.offsetTop + el.offsetHeight;
}
}
}
if (isString(this._options.disableLazySchemas)) this._options.disableLazySchemas = true;
}
}

View File

@ -63,4 +63,6 @@ export class JsonPointer extends JsonPointerLib {
JsonPointerLib._origParse = JsonPointerLib.parse;
JsonPointerLib.parse = JsonPointer.parse;
Object.assign(JsonPointer, JsonPointerLib);
export default JsonPointer;

View File

@ -150,7 +150,11 @@ export default class SchemaManager {
}
if (tagDetails['x-traitTag']) continue;
if (!tagDetails.methods) tagDetails.methods = [];
tagDetails.methods.push({pointer: methodPointer, summary: methodSummary});
tagDetails.methods.push({
pointer: methodPointer,
summary: methodSummary,
operationId: methodInfo.operationId
});
}
}
}

View File

@ -0,0 +1,10 @@
/*eslint no-unused-vars: 0*/
/*eslint strict: 0*/
var $buoop = { vs: {i:9, f:25, o:12.1, s:7}, c:2 };
function $buo_f(){
var e = document.createElement('script');
e.src = '//browser-update.org/update.min.js';
document.body.appendChild(e);
}
try {document.addEventListener('DOMContentLoaded', $buo_f, false);}
catch(e){window.attachEvent('onload', $buo_f);}

View File

@ -4,7 +4,7 @@ import {Pipe} from 'angular2/core';
import {isString, stringify, isBlank} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {JsonPointer} from './JsonPointer';
import marked from 'marked';
import Prism from 'prismjs';
import 'prismjs/components/prism-actionscript.js';
import 'prismjs/components/prism-c.js';
@ -29,6 +29,8 @@ import 'prismjs/components/prism-vim.js';
import 'prismjs/themes/prism-dark.css!css';
import marked from 'marked';
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,

View File

@ -1,7 +1,7 @@
{
"name": "redoc",
"description": "Swagger-generated API Reference Documentation",
"version": "0.5.2",
"version": "0.6.0",
"repository": {
"type": "git",
"url": "git://github.com/Rebilly/ReDoc"
@ -9,7 +9,6 @@
"main": "dist/redoc.min.js",
"scripts": {
"test": "gulp lint && ./build/run_tests.sh",
"prepublish": "gulp build",
"jspm-install": "jspm install",
"start": "gulp serve",
"build-dist": "gulp build",
@ -29,8 +28,9 @@
"jspm": {
"configFile": "system.config.js",
"dependencies": {
"angular2": "npm:angular2@^2.0.0-beta.0",
"angular2": "npm:angular2@2.0.0-beta.6",
"es6-shim": "github:es-shims/es6-shim@^0.33.6",
"json": "github:systemjs/plugin-json@^0.1.0",
"json-formatter-js": "npm:json-formatter-js@^0.2.0",
"json-pointer": "npm:json-pointer@^0.3.0",
"json-schema-instantiator": "npm:json-schema-instantiator@^0.3.0",

View File

@ -37,7 +37,7 @@ System.config({
},
map: {
"angular2": "npm:angular2@2.0.0-beta.0",
"angular2": "npm:angular2@2.0.0-beta.6",
"babel": "npm:babel-core@5.8.34",
"babel-runtime": "npm:babel-runtime@5.8.34",
"clean-css": "npm:clean-css@3.4.6",
@ -135,14 +135,14 @@ System.config({
"path": "github:jspm/nodelibs-path@0.1.0",
"process": "github:jspm/nodelibs-process@0.1.2"
},
"npm:angular2@2.0.0-beta.0": {
"npm:angular2@2.0.0-beta.6": {
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"es6-promise": "npm:es6-promise@3.0.2",
"es6-promise": "npm:es6-promise@3.1.2",
"es6-shim": "npm:es6-shim@0.33.13",
"process": "github:jspm/nodelibs-process@0.1.2",
"reflect-metadata": "npm:reflect-metadata@0.1.2",
"rxjs": "npm:rxjs@5.0.0-beta.0",
"zone.js": "npm:zone.js@0.5.10"
"zone.js": "npm:zone.js@0.5.14"
},
"npm:argparse@1.0.3": {
"assert": "github:jspm/nodelibs-assert@0.1.0",
@ -153,9 +153,9 @@ System.config({
"sprintf-js": "npm:sprintf-js@1.0.3",
"util": "github:jspm/nodelibs-util@0.1.0"
},
"npm:asn1.js@4.3.0": {
"npm:asn1.js@4.4.0": {
"assert": "github:jspm/nodelibs-assert@0.1.0",
"bn.js": "npm:bn.js@4.6.4",
"bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"inherits": "npm:inherits@2.0.1",
"minimalistic-assert": "npm:minimalistic-assert@1.0.0",
@ -199,11 +199,14 @@ System.config({
"readable-stream": "npm:readable-stream@2.0.5",
"util": "github:jspm/nodelibs-util@0.1.0"
},
"npm:bn.js@4.10.3": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0"
},
"npm:boom@2.10.1": {
"hoek": "npm:hoek@2.16.3",
"http": "github:jspm/nodelibs-http@1.7.1"
},
"npm:browserify-aes@1.0.5": {
"npm:browserify-aes@1.0.6": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"buffer-xor": "npm:buffer-xor@1.0.3",
"cipher-base": "npm:cipher-base@1.0.2",
@ -215,7 +218,7 @@ System.config({
"systemjs-json": "github:systemjs/plugin-json@0.1.0"
},
"npm:browserify-cipher@1.0.0": {
"browserify-aes": "npm:browserify-aes@1.0.5",
"browserify-aes": "npm:browserify-aes@1.0.6",
"browserify-des": "npm:browserify-des@1.0.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
@ -229,20 +232,20 @@ System.config({
"inherits": "npm:inherits@2.0.1"
},
"npm:browserify-rsa@4.0.0": {
"bn.js": "npm:bn.js@4.6.4",
"bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"constants": "github:jspm/nodelibs-constants@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"randombytes": "npm:randombytes@2.0.2"
},
"npm:browserify-sign@4.0.0": {
"bn.js": "npm:bn.js@4.6.4",
"bn.js": "npm:bn.js@4.10.3",
"browserify-rsa": "npm:browserify-rsa@4.0.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"create-hash": "npm:create-hash@1.1.2",
"create-hmac": "npm:create-hmac@1.1.4",
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"elliptic": "npm:elliptic@6.0.2",
"elliptic": "npm:elliptic@6.2.3",
"inherits": "npm:inherits@2.0.1",
"parse-asn1": "npm:parse-asn1@5.0.0",
"stream": "github:jspm/nodelibs-stream@0.1.0"
@ -332,10 +335,10 @@ System.config({
"buffer": "github:jspm/nodelibs-buffer@0.1.0"
},
"npm:create-ecdh@4.0.0": {
"bn.js": "npm:bn.js@4.6.4",
"bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"elliptic": "npm:elliptic@6.0.2"
"elliptic": "npm:elliptic@6.2.3"
},
"npm:create-hash@1.1.2": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
@ -363,14 +366,14 @@ System.config({
"create-ecdh": "npm:create-ecdh@4.0.0",
"create-hash": "npm:create-hash@1.1.2",
"create-hmac": "npm:create-hmac@1.1.4",
"diffie-hellman": "npm:diffie-hellman@5.0.1",
"diffie-hellman": "npm:diffie-hellman@5.0.2",
"inherits": "npm:inherits@2.0.1",
"pbkdf2": "npm:pbkdf2@3.0.4",
"public-encrypt": "npm:public-encrypt@4.0.0",
"randombytes": "npm:randombytes@2.0.2"
},
"npm:dashdash@1.12.1": {
"assert-plus": "npm:assert-plus@0.1.5",
"npm:dashdash@1.12.2": {
"assert-plus": "npm:assert-plus@0.2.0",
"fs": "github:jspm/nodelibs-fs@0.1.2",
"path": "github:jspm/nodelibs-path@0.1.0",
"process": "github:jspm/nodelibs-process@0.1.2",
@ -393,8 +396,8 @@ System.config({
"inherits": "npm:inherits@2.0.1",
"minimalistic-assert": "npm:minimalistic-assert@1.0.0"
},
"npm:diffie-hellman@5.0.1": {
"bn.js": "npm:bn.js@4.6.4",
"npm:diffie-hellman@5.0.2": {
"bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"miller-rabin": "npm:miller-rabin@4.0.0",
@ -406,14 +409,14 @@ System.config({
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"jsbn": "npm:jsbn@0.1.0"
},
"npm:elliptic@6.0.2": {
"bn.js": "npm:bn.js@4.6.4",
"npm:elliptic@6.2.3": {
"bn.js": "npm:bn.js@4.10.3",
"brorand": "npm:brorand@1.0.5",
"hash.js": "npm:hash.js@1.0.3",
"inherits": "npm:inherits@2.0.1",
"systemjs-json": "github:systemjs/plugin-json@0.1.0"
},
"npm:es6-promise@3.0.2": {
"npm:es6-promise@3.1.2": {
"process": "github:jspm/nodelibs-process@0.1.2"
},
"npm:es6-shim@0.33.13": {
@ -543,7 +546,7 @@ System.config({
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"call-me-maybe": "npm:call-me-maybe@1.0.1",
"debug": "npm:debug@2.2.0",
"es6-promise": "npm:es6-promise@3.0.2",
"es6-promise": "npm:es6-promise@3.1.2",
"events": "github:jspm/nodelibs-events@0.1.1",
"fs": "github:jspm/nodelibs-fs@0.1.2",
"http": "github:jspm/nodelibs-http@1.7.1",
@ -584,7 +587,7 @@ System.config({
"process": "github:jspm/nodelibs-process@0.1.2"
},
"npm:miller-rabin@4.0.0": {
"bn.js": "npm:bn.js@4.6.4",
"bn.js": "npm:bn.js@4.10.3",
"brorand": "npm:brorand@1.0.5"
},
"npm:mime-db@1.21.0": {
@ -616,8 +619,8 @@ System.config({
"process": "github:jspm/nodelibs-process@0.1.2"
},
"npm:parse-asn1@5.0.0": {
"asn1.js": "npm:asn1.js@4.3.0",
"browserify-aes": "npm:browserify-aes@1.0.5",
"asn1.js": "npm:asn1.js@4.4.0",
"browserify-aes": "npm:browserify-aes@1.0.6",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"create-hash": "npm:create-hash@1.1.2",
"evp_bytestokey": "npm:evp_bytestokey@1.0.0",
@ -637,10 +640,7 @@ System.config({
"systemjs-json": "github:systemjs/plugin-json@0.1.0"
},
"npm:pinkie-promise@2.0.0": {
"pinkie": "npm:pinkie@2.0.1"
},
"npm:pinkie@2.0.1": {
"process": "github:jspm/nodelibs-process@0.1.2"
"pinkie": "npm:pinkie@2.0.4"
},
"npm:prismjs@1.3.0": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
@ -654,7 +654,7 @@ System.config({
"assert": "github:jspm/nodelibs-assert@0.1.0"
},
"npm:public-encrypt@4.0.0": {
"bn.js": "npm:bn.js@4.6.4",
"bn.js": "npm:bn.js@4.10.3",
"browserify-rsa": "npm:browserify-rsa@4.0.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"create-hash": "npm:create-hash@1.1.2",
@ -758,7 +758,7 @@ System.config({
"assert-plus": "npm:assert-plus@0.2.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
"dashdash": "npm:dashdash@1.12.1",
"dashdash": "npm:dashdash@1.12.2",
"ecc-jsbn": "npm:ecc-jsbn@0.1.1",
"jodid25519": "npm:jodid25519@1.0.2",
"jsbn": "npm:jsbn@0.1.0",
@ -793,7 +793,7 @@ System.config({
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
"call-me-maybe": "npm:call-me-maybe@1.0.1",
"debug": "npm:debug@2.2.0",
"es6-promise": "npm:es6-promise@3.0.2",
"es6-promise": "npm:es6-promise@3.1.2",
"events": "github:jspm/nodelibs-events@0.1.1",
"fs": "github:jspm/nodelibs-fs@0.1.2",
"http": "github:jspm/nodelibs-http@1.7.1",
@ -867,8 +867,8 @@ System.config({
"util": "github:jspm/nodelibs-util@0.1.0",
"validator": "npm:validator@4.5.0"
},
"npm:zone.js@0.5.10": {
"es6-promise": "npm:es6-promise@3.0.2",
"npm:zone.js@0.5.14": {
"es6-promise": "npm:es6-promise@3.1.2",
"process": "github:jspm/nodelibs-process@0.1.2"
}
}

View File

@ -1,27 +1,20 @@
'use strict';
import {BrowserDomAdapter} from 'angular2/platform/browser';
BrowserDomAdapter.makeCurrent();
import {By} from 'angular2/platform/browser';
/** Gets a child DebugElement by tag name. */
export function getChildDebugElement(parent, tagName) {
return parent.query(debugEl => {
return debugEl.nativeElement.tagName && debugEl.nativeElement.tagName.toLowerCase() === tagName;
});
return parent.query(By.css(tagName));
}
/** Gets a child DebugElement by Component Type. */
export function getChildDebugElementByType(parent, type) {
return parent.query(debugEl => {
return debugEl.componentInstance instanceof type;
});
return parent.query(By.directive(type));
}
/** Gets a child DebugElements by tag name. */
export function getChildDebugElementAll(parent, tagName) {
return parent.queryAll(debugEl => {
return debugEl.nativeElement.tagName && debugEl.nativeElement.tagName.toLowerCase() === tagName;
});
return parent.queryAll(By.css(tagName));
}
export function mouseclick( element ) {

13
tests/setup.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
import {setBaseTestProviders} from 'angular2/testing';
import {
TEST_BROWSER_PLATFORM_PROVIDERS,
TEST_BROWSER_APPLICATION_PROVIDERS
} from 'angular2/platform/testing/browser';
import {
ELEMENT_PROBE_PROVIDERS_PROD_MODE
} from 'angular2/platform/browser';
setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, [TEST_BROWSER_APPLICATION_PROVIDERS, ELEMENT_PROBE_PROVIDERS_PROD_MODE]);

View File

@ -165,7 +165,7 @@ describe('Utils', () => {
info.should.be.an.Object();
info.methods.should.be.an.Array();
for (let methodInfo of info.methods) {
methodInfo.should.have.keys('pointer', 'summary');
methodInfo.should.have.properties(['pointer', 'summary']);
let methSchema = schemaMgr.byPointer(methodInfo.pointer);
expect(methSchema).not.toBeNull();
if (methSchema.summary) {