mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-24 01:23:43 +03:00
Merge commit '942cd5dc53534c6ff952872ace41ce632888a4f4' into releases
This commit is contained in:
commit
d97374d3c4
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
|||
# 1.8.0 (2017-02-03)
|
||||
### Features/Improvements
|
||||
* In-page search :tada: []#51](https://github.com/Rebilly/ReDoc/issues/51)
|
||||
* Render externalDocs [#103](https://github.com/Rebilly/ReDoc/issues/103)
|
||||
* Undeprecate x-traitTag
|
||||
|
||||
### Bug fixes
|
||||
* Tags with x-traitTag: true are now greyed out in ReDoc output bug [#194](https://github.com/Rebilly/ReDoc/issues/194)
|
||||
* CSS: request body model-tree wrapping problem [#185](https://github.com/Rebilly/ReDoc/issues/185)
|
||||
* Strange request to `example.com` causing CSP error [#178](https://github.com/Rebilly/ReDoc/issues/178)
|
||||
* Fix latest empty menu-items not getting active [#194](https://github.com/Rebilly/ReDoc/issues/194)
|
||||
* Fixed crash when level-2 heading goes before level-1 in description [#179](https://github.com/Rebilly/ReDoc/issues/179) (by [@jsmartfo](https://github.com/jsmartfo))
|
||||
|
||||
# 1.7.0 (2017-01-06)
|
||||
### Features/Improvements
|
||||
* Add support for grouping items in menu via [`x-tagGroups`](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-taggroups)
|
||||
|
|
12
README.md
12
README.md
|
@ -31,8 +31,8 @@
|
|||
- Multiple ReDoc instances on single page ([example](demo/examples/multiple-apis/index.html))
|
||||
|
||||
## Roadmap
|
||||
- [x] performance optimizations
|
||||
- [ ] better navigation (menu improvements + search)
|
||||
- [x] ~~performance optimizations~~
|
||||
- [x] ~~better navigation (menu improvements + search)~~
|
||||
- [ ] ability to simple branding/styling
|
||||
- [ ] built-in API Console
|
||||
- [ ] docs pre-rendering (performance and SEO)
|
||||
|
@ -43,6 +43,14 @@ We host the latest and all the previous ReDoc releases on GitHub Pages-based **C
|
|||
- `v1.x.x` release: https://rebilly.github.io/ReDoc/releases/v1.x.x/redoc.min.js
|
||||
- `latest` release: https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js this file is updated with each release of ReDoc and may introduce breaking changes. **Not recommended to use in production.** Use particular release or `v1.x.x`.
|
||||
|
||||
## Some Real-life usages
|
||||
- [Rebilly](https://rebilly.github.io/RebillyAPI)
|
||||
- [Docker Engine](https://docs.docker.com/engine/api/v1.25/)
|
||||
- [Zuora](https://www.zuora.com/developer/api-reference/)
|
||||
- [Shopify Draft Orders](https://help.shopify.com/api/draft-orders)
|
||||
- [Discourse](https://docs.discourse.org)
|
||||
- [APIs.guru](https://apis.guru/api-doc/)
|
||||
|
||||
## Deployment
|
||||
|
||||
### TL;DR
|
||||
|
|
|
@ -87,7 +87,11 @@ module.exports = function (options) {
|
|||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: ['style-loader', 'css-loader?-import', "sass-loader"],
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader?-import',
|
||||
'sass-loader'
|
||||
],
|
||||
exclude: [/lib[\\\/](?!.*redoc-initial-styles).*\.scss$/]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
</td>
|
||||
<td class="param-info">
|
||||
<div>
|
||||
<span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint, 'tuple': prop._isTuple}"
|
||||
<span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint, 'tuple': prop._isTuple, 'array': prop._isArray}"
|
||||
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}
|
||||
<span class="param-range" *ngIf="prop._range"> {{prop._range}} </span>
|
||||
</span>
|
||||
|
|
|
@ -93,6 +93,7 @@ zippy {
|
|||
|
||||
.param.complex > .param-name svg {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
vertical-align: middle;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Input, HostBinding, Component, OnChanges } from '@angular/core';
|
|||
template: `
|
||||
<span [style.width]='progress + "%"'> </span>
|
||||
`,
|
||||
styleUrls: ['loading-bar.scss']
|
||||
styleUrls: ['loading-bar.css']
|
||||
})
|
||||
export class LoadingBar implements OnChanges {
|
||||
@Input() progress:number = 0;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<header *ngIf="data.responses.length"> Response samples </header>
|
||||
<tabs *ngIf="data.responses.length">
|
||||
<tab *ngFor="let response of data.responses" tabTitle="{{response.code}} {{response.description}}"
|
||||
<tab *ngFor="let response of data.responses" [tabTitle]="response.code + ' ' + response.description | marked"
|
||||
[tabStatus]="response.type">
|
||||
<schema-sample [pointer]="response.pointer"></schema-sample>
|
||||
</tab>
|
||||
|
|
|
@ -51,8 +51,6 @@ export class SideMenu extends BaseComponent implements OnInit, OnDestroy {
|
|||
private $resourcesNav: any;
|
||||
private $scrollParent: any;
|
||||
|
||||
private firstChange = true;
|
||||
|
||||
constructor(specMgr:SpecManager, elementRef:ElementRef,
|
||||
private scrollService:ScrollService, private menuService:MenuService,
|
||||
optionsService:OptionsService, private detectorRef:ChangeDetectorRef, private marker:Marker) {
|
||||
|
@ -84,15 +82,12 @@ export class SideMenu extends BaseComponent implements OnInit, OnDestroy {
|
|||
|
||||
//safari doesn't update bindings if not run changeDetector manually :(
|
||||
this.detectorRef.detectChanges();
|
||||
if (this.firstChange) {
|
||||
this.scrollActiveIntoView();
|
||||
this.firstChange = false;
|
||||
}
|
||||
this.scrollActiveIntoView();
|
||||
}
|
||||
|
||||
scrollActiveIntoView() {
|
||||
let $item = this.$element.querySelector('li.active, label.active');
|
||||
if ($item) $item.scrollIntoView();
|
||||
if ($item) $item.scrollIntoViewIfNeeded();
|
||||
}
|
||||
|
||||
activateAndScroll(item) {
|
||||
|
|
|
@ -28,3 +28,35 @@ if (!IS_PRODUCTION) {
|
|||
Error.stackTraceLimit = Infinity;
|
||||
require('zone.js/dist/long-stack-trace-zone');
|
||||
}
|
||||
|
||||
interface Element {
|
||||
scrollIntoViewIfNeeded(centerIfNeeded?: boolean): void;
|
||||
};
|
||||
|
||||
if (!(<any>Element).prototype.scrollIntoViewIfNeeded) {
|
||||
(<any>Element).prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
|
||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
||||
|
||||
var parent = this.parentNode,
|
||||
parentComputedStyle = window.getComputedStyle(parent, null),
|
||||
parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
|
||||
parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
|
||||
overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
|
||||
overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
|
||||
overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
|
||||
overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
|
||||
alignWithTop = overTop && !overBottom;
|
||||
|
||||
if ((overTop || overBottom) && centerIfNeeded) {
|
||||
parent.scrollTop = this.offsetTop - parent.offsetTop - parent.clientHeight / 2 - parentBorderTopWidth + this.clientHeight / 2;
|
||||
}
|
||||
|
||||
if ((overLeft || overRight) && centerIfNeeded) {
|
||||
parent.scrollLeft = this.offsetLeft - parent.offsetLeft - parent.clientWidth / 2 - parentBorderLeftWidth + this.clientWidth / 2;
|
||||
}
|
||||
|
||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
||||
this.scrollIntoView(alignWithTop);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,4 +17,19 @@ describe('Spec Helper', () => {
|
|||
(<jasmine.Spy>console.warn).and.callThrough();
|
||||
});
|
||||
});
|
||||
|
||||
describe('preprocessProperties', () => {
|
||||
it('should not throw when type array and items are not defined', () => {
|
||||
let schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
prop1: {
|
||||
type: 'array'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(() => SchemaHelper.preprocessProperties(schema, '#/', {})).should.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import { WarningsService } from './warnings.service';
|
|||
import * as slugify from 'slugify';
|
||||
|
||||
interface PropertyPreprocessOptions {
|
||||
childFor: string;
|
||||
childFor?: string;
|
||||
skipReadOnly?: boolean;
|
||||
discriminator?: string;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ const injectors = {
|
|||
return propertySchema.type === 'array' && !Array.isArray(propertySchema.items);
|
||||
},
|
||||
inject: (injectTo, propertySchema = injectTo, propPointer) => {
|
||||
if (!propertySchema.items) propertySchema.items = {};
|
||||
if (!(SchemaHelper.detectType(propertySchema.items) === 'object')) {
|
||||
injectTo._isArray = true;
|
||||
injectTo._pointer = propertySchema.items._pointer
|
||||
|
@ -63,6 +64,7 @@ const injectors = {
|
|||
} else {
|
||||
injectors.object.inject(injectTo, propertySchema.items);
|
||||
}
|
||||
if (!injectTo.description) injectTo.description = propertySchema.items.description;
|
||||
injectTo._widgetType = 'array';
|
||||
}
|
||||
},
|
||||
|
@ -207,7 +209,11 @@ export class SchemaHelper {
|
|||
static preprocessProperties(schema:any, pointer:string, opts: PropertyPreprocessOptions) {
|
||||
let requiredMap = {};
|
||||
if (schema.required) {
|
||||
schema.required.forEach(prop => requiredMap[prop] = true);
|
||||
if (Array.isArray(schema.required)) {
|
||||
schema.required.forEach(prop => requiredMap[prop] = true);
|
||||
} else {
|
||||
WarningsService.warn(`required must be an array: "${typeof schema.required}" found at ${pointer}`);
|
||||
}
|
||||
}
|
||||
|
||||
let props = schema.properties && Object.keys(schema.properties).map(propName => {
|
||||
|
|
|
@ -29,7 +29,7 @@ export class SchemaNormalizer {
|
|||
|
||||
if (opts.childFor) this._dereferencer.visit(opts.childFor);
|
||||
if (schema['x-redoc-normalized']) return schema;
|
||||
let res = SchemaWalker.walk(schema, ptr, (subSchema, ptr) => {
|
||||
let res = SchemaWalker.walk(schema, ptr, (subSchema, ptr) => {
|
||||
let resolved = this._dereferencer.dereference(subSchema, ptr);
|
||||
if (resolved.allOf) {
|
||||
resolved._pointer = resolved._pointer || ptr;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ul>
|
||||
<li *ngFor="let tab of tabs" [ngClass]="{active: tab.active}" (click)="selectTab(tab)"
|
||||
class="tab-{{tab.tabStatus}}">{{tab.tabTitle}}</li>
|
||||
class="tab-{{tab.tabStatus}}" [innerHTML]="tab.tabTitle"></li>
|
||||
</ul>
|
||||
<ng-content></ng-content>
|
||||
|
|
|
@ -16,6 +16,10 @@ li {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
li /deep/ .redoc-markdown-block p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tab-success, .tab-error, .tab-redirect, .tab-info {
|
||||
&:before {
|
||||
content: "";
|
||||
|
|
|
@ -53,6 +53,7 @@ $zippy-redirect-bg-color: rgba($zippy-redirect-color, .08);
|
|||
|
||||
.zippy-indicator svg {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
vertical-align: top;
|
||||
transition: all 0.3s ease;
|
||||
transform: rotateZ(-180deg);
|
||||
|
|
|
@ -8,6 +8,7 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
|||
import { MdRenderer } from './md-renderer';
|
||||
|
||||
import { SwaggerOperation, SwaggerParameter } from './swagger-typings';
|
||||
import { snapshot } from './helpers';
|
||||
|
||||
function getDiscriminator(obj) {
|
||||
return obj.discriminator || obj['x-extendedDiscriminator'];
|
||||
|
@ -37,7 +38,7 @@ export class SpecManager {
|
|||
if (typeof urlOrObject === 'string') {
|
||||
this._url = urlOrObject;
|
||||
}
|
||||
this._schema = schema;
|
||||
this._schema = snapshot(schema);
|
||||
try {
|
||||
this.init();
|
||||
resolve(this._schema);
|
||||
|
@ -67,7 +68,7 @@ export class SpecManager {
|
|||
}
|
||||
|
||||
let host = this._schema.host || urlParts.host;
|
||||
this.basePath = this._schema.basePath || '/';
|
||||
this.basePath = this._schema.basePath || '';
|
||||
this.apiUrl = protocol + '://' + host + this.basePath;
|
||||
if (this.apiUrl.endsWith('/')) {
|
||||
this.apiUrl = this.apiUrl.substr(0, this.apiUrl.length - 1);
|
||||
|
|
83
package.json
83
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "redoc",
|
||||
"description": "Swagger-generated API Reference Documentation",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Rebilly/ReDoc"
|
||||
|
@ -47,32 +47,37 @@
|
|||
"author": "Roman Hotsiy",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@angular/common": "^2.4.5",
|
||||
"@angular/compiler": "^2.4.5",
|
||||
"@angular/compiler-cli": "^2.4.5",
|
||||
"@angular/core": "^2.4.5",
|
||||
"@angular/platform-browser": "^2.4.5",
|
||||
"@angular/platform-browser-dynamic": "^2.4.5",
|
||||
"@angular/platform-server": "^2.4.5",
|
||||
"@angular/common": "^2.4.8",
|
||||
"@angular/compiler": "^2.4.8",
|
||||
"@angular/compiler-cli": "^2.4.8",
|
||||
"@angular/core": "^2.4.8",
|
||||
"@angular/platform-browser": "^2.4.8",
|
||||
"@angular/platform-browser-dynamic": "^2.4.8",
|
||||
"@angular/platform-server": "^2.4.8",
|
||||
"@types/core-js": "^0.9.31",
|
||||
"@types/jasmine": "^2.2.32",
|
||||
"@types/jasmine": "^2.5.43",
|
||||
"@types/requirejs": "^2.1.26",
|
||||
"@types/should": "^8.1.28",
|
||||
"@types/swagger-schema-official": "^2.0.0",
|
||||
"angular2-template-loader": "^0.6.0",
|
||||
"awesome-typescript-loader": "~3.0.0-beta.17",
|
||||
"@types/webpack": "^2.2.6",
|
||||
"angular2-template-loader": "^0.6.2",
|
||||
"awesome-typescript-loader": "^3.0.6",
|
||||
"branch-release": "^1.0.3",
|
||||
"chalk": "^1.1.3",
|
||||
"codelyzer": "^2.0.0-beta.4",
|
||||
"codelyzer": "^2.0.1",
|
||||
"core-js": "^2.4.1",
|
||||
"coveralls": "^2.11.9",
|
||||
"coveralls": "^2.11.16",
|
||||
"css-loader": "^0.26.0",
|
||||
"deploy-to-gh-pages": "^1.1.2",
|
||||
"dropkickjs": "^2.1.10",
|
||||
"hint.css": "^2.3.2",
|
||||
"http-server": "^0.9.0",
|
||||
"istanbul-instrumenter-loader": "^1.2.0",
|
||||
"istanbul-instrumenter-loader": "^2.0.0",
|
||||
"jasmine-core": "^2.4.1",
|
||||
"jasmine-spec-reporter": "^3.1.0",
|
||||
"karma": "^1.4.1",
|
||||
"json-pointer": "^0.6.0",
|
||||
"json-schema-ref-parser": "^3.1.2",
|
||||
"karma": "^1.5.0",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-coveralls": "^1.1.2",
|
||||
|
@ -85,51 +90,35 @@
|
|||
"karma-sinon": "^1.0.4",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"lunr": "^1.0.0",
|
||||
"mark.js": "github:julmot/mark.js",
|
||||
"ngc-webpack": "^1.2.0",
|
||||
"node-sass": "^4.5.0",
|
||||
"openapi-sampler": "^0.4.0",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"protractor": "^5.1.0",
|
||||
"prismjs": "^1.5.1",
|
||||
"protractor": "^5.1.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"rimraf": "^2.5.4",
|
||||
"rxjs": "^5.1.0",
|
||||
"sass-loader": "^4.1.1",
|
||||
"remarkable": "^1.6.2",
|
||||
"rimraf": "^2.6.0",
|
||||
"rxjs": "^5.2.0",
|
||||
"sass-loader": "^6.0.2",
|
||||
"scrollparent": "^1.0.0",
|
||||
"shelljs": "^0.7.0",
|
||||
"should": "^11.1.0",
|
||||
"sinon": "^1.17.2",
|
||||
"slugify": "^1.0.2",
|
||||
"source-map-loader": "^0.1.5",
|
||||
"stream-http": "^2.6.1",
|
||||
"string-replace-webpack-plugin": "0.0.5",
|
||||
"style-loader": "^0.13.1",
|
||||
"ts-helpers": "^1.1.1",
|
||||
"tslint": "^4.3.1",
|
||||
"typescript": "^2.1.5",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.2.0-rc.0",
|
||||
"webpack-merge": "^2.6.1",
|
||||
"zone.js": "^0.7.2"
|
||||
"webpack-dev-server": "^2.4.1",
|
||||
"webpack-merge": "^3.0.0",
|
||||
"zone.js": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"dropkickjs": "^2.1.10",
|
||||
"hint.css": "^2.3.2",
|
||||
"json-pointer": "^0.6.0",
|
||||
"json-schema-ref-parser": "^3.1.2",
|
||||
"lunr": "^0.7.2",
|
||||
"mark.js": "github:julmot/mark.js",
|
||||
"openapi-sampler": "^0.3.3",
|
||||
"prismjs": "^1.5.1",
|
||||
"remarkable": "^1.6.2",
|
||||
"scrollparent": "^1.0.0",
|
||||
"slugify": "^1.0.2",
|
||||
"stream-http": "^2.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^2.4.5",
|
||||
"@angular/compiler": "^2.4.5",
|
||||
"@angular/core": "^2.4.5",
|
||||
"@angular/platform-browser": "^2.4.5",
|
||||
"@angular/platform-browser-dynamic": "^2.4.5",
|
||||
"@angular/platform-server": "^2.4.5",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "^5.0.1",
|
||||
"zone.js": "^0.7.2"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,13 @@ describe('Utils', () => {
|
|||
specMgr.apiUrl.should.be.equal('http://petstore.swagger.io/v2');
|
||||
});
|
||||
|
||||
it('should use empty basePath when basePath is not present', () => {
|
||||
specMgr._schema.basePath = undefined;
|
||||
specMgr._url = 'https://petstore.swagger.io';
|
||||
specMgr.init();
|
||||
specMgr.basePath.should.be.equal('');
|
||||
});
|
||||
|
||||
describe('byPointer method', () => {
|
||||
it('should return correct schema part', ()=> {
|
||||
let part = specMgr.byPointer('/tags/0');
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"noEmitHelpers": true,
|
||||
"strictNullChecks": false,
|
||||
"baseUrl": "./src",
|
||||
"typeRoots": [ "./node_modules/@types" ],
|
||||
"paths": {
|
||||
},
|
||||
"lib": [
|
||||
|
@ -18,7 +19,7 @@
|
|||
"dom"
|
||||
],
|
||||
"types": [
|
||||
"node"
|
||||
"webpack"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
|
|
Loading…
Reference in New Issue
Block a user