diff --git a/README.md b/README.md index e9e357a0..10e0430a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,15 @@ ## [Live demo](http://rebilly.github.io/ReDoc/) +## Features +- Extremely easy deployment +- It’s free and open-source project under MIT license +- The widest OpenAPI features support (yes, it supports even discriminator) +- Neat documentation for nested objects +- Code samples support (via vendor extension) +- Responsive three-panel design with menu/scrolling synchronization +- Integrate API introduction into side menu - ReDoc takes advantage of markdown headings from OpenAPI description field. It pulls them into side menu and also supports deep linking. + ## Roadmap - [ ] docs pre-rendering (performance and SEO) - [ ] ability to simple customization @@ -90,7 +99,7 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica * [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for handling out common things like Pagination, Rate-Limits, etc * [`x-code-samples`](docs/redoc-vendor-extensions.md#x-code-samples) - specify operation code samples -### Options +### `` tag attributes * `spec-url` - relative or absolute url to your spec file; * `scroll-y-offset` - If set, specifies a vertical scroll-offset. This is often useful when there are fixed positioned elements at the top of the page, such as navbars, headers etc; `scroll-y-offset` can be specified in various ways: @@ -105,7 +114,7 @@ Instead of adding `spec-url` attribute to the `` element you can initiali Redoc.init(specUrl, options) ``` -`options` is javascript object with camel-cased version of options names as the keys, e.g.: +`options` is javascript object with camel-cased version of `` tag attribute names as the keys, e.g.: ```js Redoc.init('http://petstore.swagger.io/v2/swagger.json', { scrollYOffset: 50 diff --git a/demo/index.html b/demo/index.html index 28fb0b47..bda7275f 100644 --- a/demo/index.html +++ b/demo/index.html @@ -26,5 +26,14 @@ + diff --git a/lib/components/ApiInfo/api-info.scss b/lib/components/ApiInfo/api-info.scss index 5ec113ef..67d64281 100644 --- a/lib/components/ApiInfo/api-info.scss +++ b/lib/components/ApiInfo/api-info.scss @@ -8,6 +8,10 @@ width: 60%; padding: 40px; box-sizing: border-box; + + @media (max-width: $right-panel-squash-breakpoint) { + width: 100%; + } } a.openapi-button { diff --git a/lib/components/JsonSchema/json-schema-common.scss b/lib/components/JsonSchema/json-schema-common.scss index e6d24407..26b41559 100644 --- a/lib/components/JsonSchema/json-schema-common.scss +++ b/lib/components/JsonSchema/json-schema-common.scss @@ -77,6 +77,12 @@ $sub-schema-offset: ($bullet-size / 2) + $bullet-margin; font-weight: $base-font-weight; } +.param-type.tuple:before { + content: "Tuple"; + color: $black; + font-weight: $base-font-weight; +} + .param-type.with-hint { display: inline-block; margin-bottom: 0.4em; @@ -86,7 +92,6 @@ $sub-schema-offset: ($bullet-size / 2) + $bullet-margin; } .param-type-trivial { - margin: 10px 10px 0; display: inline-block; } diff --git a/lib/components/JsonSchema/json-schema.html b/lib/components/JsonSchema/json-schema.html index fb532383..dabb35da 100644 --- a/lib/components/JsonSchema/json-schema.html +++ b/lib/components/JsonSchema/json-schema.html @@ -1,82 +1,110 @@ - - file -
-
    -
  • {{type}}
  • -
-
-
-
    -
  • {{type}}
  • -
-
-
- - {{schema._displayType}} {{schema._displayFormat}} - {{schema._range}} - -
- {{enumItem.val | json}} -
-
- - - - -
-
- - + + + + + + + + diff --git a/lib/components/JsonSchema/json-schema.scss b/lib/components/JsonSchema/json-schema.scss index 298beb10..a28431ad 100644 --- a/lib/components/JsonSchema/json-schema.scss +++ b/lib/components/JsonSchema/json-schema.scss @@ -58,11 +58,13 @@ $array-marker-line-height: 1.5; border-top-color: white; } - > .params-wrap > .param:first-of-type > .param-name:before, > .params-wrap > .param:last-of-type > .param-name:after { + > .params-wrap > .param:first-of-type > .param-name:before, + > .params-wrap > .param:last-of-type > .param-name:after { border-color: $side-menu-active-bg-color; } - > .params-wrap > .param:last-of-type, > .params-wrap > .param.last { + > .params-wrap > .param:last-of-type, + > .params-wrap > .param.last { > .param-name:after { border-color: $side-menu-active-bg-color; } @@ -119,11 +121,13 @@ table { .params-wrap.params-array:after { content: "]"; + font-family: monospace; } .params-wrap.params-array:before { content: "Array ["; padding-top: 1em; + font-family: monospace; } .params-wrap.params-array { @@ -201,3 +205,30 @@ li:before { content: "- "; font-weight: bold; } + +.array-tuple > .tuple-item { + margin-top: 1.5em; + display: flex; + + > span { + flex: 0; + padding: 10px 15px 10px 0; + font-family: monospace; + } + + > json-schema { + flex: 1; + &:before, &:after { + display: none; + } + } +} + +.param-enum-value { + padding: 2px; + background-color: #e6ebf6; + + &:before { + content: " = "; + } +} diff --git a/lib/components/JsonSchema/json-schema.spec.ts b/lib/components/JsonSchema/json-schema.spec.ts index 1d633b10..65fed134 100644 --- a/lib/components/JsonSchema/json-schema.spec.ts +++ b/lib/components/JsonSchema/json-schema.spec.ts @@ -44,13 +44,13 @@ describe('Redoc components', () => { component.schema.isTrivial.should.be.true(); }); - it('should use < * > notation for prop without type', () => { + it('should use < anything > notation for prop without type', () => { component.pointer = '#'; (specMgr)._schema = {type: 'object', properties: { test: {} }}; fixture.detectChanges(); - component.schema._properties[0]._displayType.should.be.equal('< * >'); + component.schema._properties[0]._displayType.should.be.equal('< anything >'); }); }); }); diff --git a/lib/components/JsonSchema/json-schema.ts b/lib/components/JsonSchema/json-schema.ts index 83649f80..51f600bd 100644 --- a/lib/components/JsonSchema/json-schema.ts +++ b/lib/components/JsonSchema/json-schema.ts @@ -22,7 +22,6 @@ export class JsonSchema extends BaseComponent { _hasSubSchemas: boolean = false; properties: any; _isArray: boolean; - @Input() isArray: boolean; @Input() final: boolean = false; @Input() nestOdd: boolean; @Input() childFor: string; @@ -89,7 +88,6 @@ export class JsonSchema extends BaseComponent { } this.properties = this.schema._properties; - this._isArray = this.isArray || this.schema._isArray; if (this.isRequestSchema) { this.properties = this.properties && this.properties.filter(prop => !prop.readOnly); } diff --git a/lib/components/Method/method.scss b/lib/components/Method/method.scss index 79b77d57..2fcadd4b 100644 --- a/lib/components/Method/method.scss +++ b/lib/components/Method/method.scss @@ -142,7 +142,7 @@ responses-samples { cursor: pointer; } -@media (max-width: 1100px) { +@media (max-width: $right-panel-squash-breakpoint) { .methods:before { display: none; } diff --git a/lib/components/MethodsList/methods-list.scss b/lib/components/MethodsList/methods-list.scss index deb9b93b..4a5cbf3b 100644 --- a/lib/components/MethodsList/methods-list.scss +++ b/lib/components/MethodsList/methods-list.scss @@ -6,7 +6,7 @@ background-color: white; width: 60%; - @media (max-width: 1100px) { + @media (max-width: $right-panel-squash-breakpoint) { width: 100%; } } diff --git a/lib/components/Redoc/redoc.scss b/lib/components/Redoc/redoc.scss index 81a4d966..50320a81 100644 --- a/lib/components/Redoc/redoc.scss +++ b/lib/components/Redoc/redoc.scss @@ -94,7 +94,7 @@ api-logo { } -@media (max-width: 1100px) { +@media (max-width: $right-panel-squash-breakpoint) { #api-content:before { display: none; } diff --git a/lib/services/schema-helper.service.ts b/lib/services/schema-helper.service.ts index 048bcc78..1e761c93 100644 --- a/lib/services/schema-helper.service.ts +++ b/lib/services/schema-helper.service.ts @@ -49,6 +49,10 @@ const injectors = { injectTo.enum = propertySchema.enum.map((value) => { return {val: value, type: typeof value}; }); + if (propertySchema.enum && propertySchema.enum.length === 1) { + injectTo._enumItem = propertySchema.enum[0]; + injectTo.enum = null; + } } } }, @@ -59,9 +63,9 @@ const injectors = { injectTo.discriminator = propertySchema.discriminator; } }, - array: { + simpleArray: { check: (propertySchema) => { - return propertySchema.type === 'array'; + return propertySchema.type === 'array' && !Array.isArray(propertySchema.items); }, inject: (injectTo, propertySchema = injectTo, propPointer) => { injectTo._isArray = true; @@ -69,6 +73,23 @@ const injectors = { || JsonPointer.join(propertySchema._pointer || propPointer, ['items']); SchemaHelper.runInjectors(injectTo, propertySchema.items, propPointer); + injectTo._widgetType = 'array'; + } + }, + tuple: { + check: (propertySchema) => { + return propertySchema.type === 'array' && Array.isArray(propertySchema.items); + }, + inject: (injectTo, propertySchema = injectTo, propPointer) => { + injectTo._isTuple = true; + injectTo._displayType = ''; + let itemsPtr = JsonPointer.join(propertySchema._pointer || propPointer, ['items']); + for (let i=0; i < propertySchema.items.length; i++) { + let itemSchema = propertySchema.items[i]; + itemSchema._pointer = itemSchema._pointer || JsonPointer.join(itemsPtr, [i.toString()]); + } + injectTo._widgetType = 'tuple'; + // SchemaHelper.runInjectors(injectTo, propertySchema.items, propPointer); } }, object: { @@ -78,14 +99,16 @@ const injectors = { inject: (injectTo, propertySchema = injectTo) => { let baseName = propertySchema._pointer && JsonPointer.baseName(propertySchema._pointer); injectTo._displayType = propertySchema.title || baseName || 'object'; + injectTo._widgetType = 'object'; } }, noType: { check: (propertySchema) => !propertySchema.type, inject: (injectTo) => { - injectTo._displayType = '< * >'; + injectTo._displayType = '< anything >'; injectTo._displayTypeHint = 'This field may contain data of any type'; injectTo.isTrivial = true; + injectTo._widgetType = 'trivial'; } }, simpleType: { @@ -103,6 +126,7 @@ const injectors = { injectTo._displayType = propertySchema.title ? `${propertySchema.title} (${propertySchema.type})` : propertySchema.type; } + injectTo._widgetType = 'trivial'; } }, integer: { @@ -160,7 +184,7 @@ const injectors = { let root = SpecManager.instance().schema; injectTo._produces = parentParam && parentParam.produces || root.produces; injectTo._consumes = parentParam && parentParam.consumes || root.consumes; - + injectTo._widgetType = 'file'; } } }; @@ -240,7 +264,7 @@ export class SchemaHelper { static unwrapArray(schema, pointer) { var res = schema; - if (schema && schema.type === 'array') { + if (schema && schema.type === 'array' && !Array.isArray(schema.items)) { let ptr = schema.items._pointer || JsonPointer.join(pointer, ['items']); res = schema.items; res._isArray = true; diff --git a/lib/shared/styles/_variables.scss b/lib/shared/styles/_variables.scss index f38caf2e..4a87b835 100644 --- a/lib/shared/styles/_variables.scss +++ b/lib/shared/styles/_variables.scss @@ -57,6 +57,7 @@ $sample-panel-color: lighten($black, 80%); $tree-lines-color: rgba($primary-color, .5); $side-menu-mobile-breakpoint: 1000px; +$right-panel-squash-breakpoint: 1100px; // Border Radius // --------------------------- diff --git a/lib/utils/JsonFormatterPipe.ts b/lib/utils/JsonFormatterPipe.ts index a8f7470d..50d8b1d9 100644 --- a/lib/utils/JsonFormatterPipe.ts +++ b/lib/utils/JsonFormatterPipe.ts @@ -82,7 +82,7 @@ function objectToHTML(json) { key = keys[i]; hasContents = true; output += '