Merge remote-tracking branch 'upstream/master' into master

* upstream/master:
  chore: Release 2.0.0-rc.44 🔖
  feat: new extensions hook PropertyDetailsCell + wrap property name into span
  chore: add .vscode to .gitignore (#1416)
  chore: fix tests
  chore: Release 2.0.0-rc.43 🔖
  fix: fix broken observable after mobx upgrade (#1415)
  chore: Release 2.0.0-rc.42 🔖
  chore: update test snapshot (#1414)
  chore: upgrade to mobx@6 (#1412)
  fix: make schema layout more responsive on small screen (#1411)
  docs: unqualify mobx version for react usage (#1380)
  fix: make samples accessible by keyboard (#1401)
  docs: improvements to vendor extensions topic (#1386)
  fix: hide dropdown input on IE 11 (#1403)
  feat: Dockerfile compatible with OpenShift (#1407) (#1408)
This commit is contained in:
Shelby Sanders 2020-10-19 18:43:58 -07:00
commit b4f3f02f8d
29 changed files with 273 additions and 96 deletions

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ cli/index.js
stats.json stats.json
yarn.lock yarn.lock
.idea .idea
.vscode

View File

@ -1,4 +0,0 @@
{
"editor.formatOnSave": true,
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@ -1,3 +1,32 @@
# [2.0.0-rc.44](https://github.com/Redocly/redoc/compare/v2.0.0-rc.43...v2.0.0-rc.44) (2020-10-16)
### Features
* new extensions hook PropertyDetailsCell + wrap property name into span ([0fae030](https://github.com/Redocly/redoc/commit/0fae03099645bd9d3795709175640583b08dfc3d))
# [2.0.0-rc.43](https://github.com/Redocly/redoc/compare/v2.0.0-rc.42...v2.0.0-rc.43) (2020-10-13)
### Bug Fixes
* fix broken observable after mobx upgrade ([#1415](https://github.com/Redocly/redoc/issues/1415)) ([26c407b](https://github.com/Redocly/redoc/commit/26c407bd0f2bc1ec9881e0a3668e09e645fc0cc0))
# [2.0.0-rc.42](https://github.com/Redocly/redoc/compare/v2.0.0-rc.41...v2.0.0-rc.42) (2020-10-13)
### Bug Fixes
* hide dropdown input on IE 11 ([#1403](https://github.com/Redocly/redoc/issues/1403)) ([6632d84](https://github.com/Redocly/redoc/commit/6632d844536532227cb92290f9fc2b6b2f913270))
* make samples accessible by keyboard ([#1401](https://github.com/Redocly/redoc/issues/1401)) ([146b38c](https://github.com/Redocly/redoc/commit/146b38c9d0b926765d8e00dd37204c30bf3ac4e0))
* make schema layout more responsive on small screen ([#1411](https://github.com/Redocly/redoc/issues/1411)) ([84ab95d](https://github.com/Redocly/redoc/commit/84ab95ddc7b5dc159098aecf82ad922ffd4a3093))
# [2.0.0-rc.41](https://github.com/Redocly/redoc/compare/v2.0.0-rc.40...v2.0.0-rc.41) (2020-09-24) # [2.0.0-rc.41](https://github.com/Redocly/redoc/compare/v2.0.0-rc.40...v2.0.0-rc.41) (2020-09-24)

View File

@ -138,7 +138,7 @@ For npm:
Install peer dependencies required by ReDoc if you don't have them installed already: Install peer dependencies required by ReDoc if you don't have them installed already:
npm i react react-dom mobx@^4.2.0 styled-components core-js npm i react react-dom mobx styled-components core-js
Import `RedocStandalone` component from 'redoc' module: Import `RedocStandalone` component from 'redoc' module:

View File

@ -36,6 +36,18 @@ COPY demo/favicon.png /usr/share/nginx/html/
COPY config/docker/nginx.conf /etc/nginx/ COPY config/docker/nginx.conf /etc/nginx/
COPY config/docker/docker-run.sh /usr/local/bin COPY config/docker/docker-run.sh /usr/local/bin
# Provide rights to the root group to write to nginx repositories (needed to run in OpenShift)
RUN chgrp -R 0 /etc/nginx && \
chgrp -R 0 /usr/share/nginx/html && \
chgrp -R 0 /var/cache/nginx && \
chgrp -R 0 /var/log/nginx && \
chgrp -R 0 /var/run && \
chmod -R g+rwX /etc/nginx && \
chmod -R g+rwX /usr/share/nginx/html && \
chmod -R g+rwX /var/cache/nginx && \
chmod -R g+rwX /var/log/nginx && \
chmod -R g+rwX /var/run
EXPOSE 80 EXPOSE 80
CMD ["sh", "/usr/local/bin/docker-run.sh"] CMD ["sh", "/usr/local/bin/docker-run.sh"]

View File

@ -2,6 +2,8 @@
## Usage ## Usage
### Docker
Serve remote spec by URL: Serve remote spec by URL:
docker run -it --rm -p 80:80 \ docker run -it --rm -p 80:80 \
@ -12,13 +14,33 @@ Serve local file:
docker run -it --rm -p 80:80 \ docker run -it --rm -p 80:80 \
-v $(pwd)/demo/swagger.yaml:/usr/share/nginx/html/swagger.yaml \ -v $(pwd)/demo/swagger.yaml:/usr/share/nginx/html/swagger.yaml \
-e SPEC_URL=swagger.yaml redocly/redoc -e SPEC_URL=swagger.yaml redocly/redoc
Serve local file and watch for updates: Serve local file and watch for updates:
docker run -it --rm -p 80:80 \ docker run -it --rm -p 80:80 \
-v $(pwd)/demo/:/usr/share/nginx/html/swagger/ \ -v $(pwd)/demo/:/usr/share/nginx/html/swagger/ \
-e SPEC_URL=swagger/swagger.yaml redocly/redoc -e SPEC_URL=swagger/swagger.yaml redocly/redoc
### OpenShift
To quote [OpenShift Container Platform-Specific Guidelines](https://docs.openshift.com/container-platform/3.11/creating_images/guidelines.html#openshift-specific-guidelines):
> Support Arbitrary User IDs
>
> By default, OpenShift Container Platform runs containers using an arbitrarily assigned user ID. This provides additional security against processes escaping the container due to a container engine vulnerability and thereby achieving escalated permissions on the host node.
>
> For an image to support running as an arbitrary user, directories and files that may be written to by processes in the image should be owned by the root group and be read/writable by that group. Files to be executed should also have group execute permissions.
To comply with those requirements the `Dockerfile` contains instructions to adapt the rights for the folders:
- `/etc/nginx` because the `docker-run.sh` script modifies it at startup time
- `/usr/share/nginx/html` because the `docker-run.sh` script modifies it at startup time
- `/var/cache/nginx` because the Nginx process writes to it
- `/var/log/nginx` because the Nginx process writes to it
- `/var/run` because the Nginx process writes to it
Another issue with OpenShift is that the default exposed port `80` cannot be used as it is restricted. So one needs to use another port like `8080` (using the `PORT` configuration as described below), and then to configure the `container spec` accordingly.
## Runtime configuration options ## Runtime configuration options
- `PAGE_TITLE` (default `"ReDoc"`) - page title - `PAGE_TITLE` (default `"ReDoc"`) - page title

View File

@ -1,8 +1,34 @@
# ReDoc vendor extensions # ReDoc vendor extensions
ReDoc makes use of the following [vendor extensions](https://swagger.io/specification/#specificationExtensions)
### Swagger Object vendor extensions You can use the following [vendor extensions](https://swagger.io/specification/#specificationExtensions) with Redoc.
Extend OpenAPI root [Swagger Object](https://swagger.io/specification/#oasObject)
- [Swagger Object](#swagger-object)
- [x-servers](#x-servers)
- [x-tagGroups](#x-taggroups)
- [Tag Group Object](#a-nametaggroupobjectatag-group-object)
- [x-ignoredHeaderParameters](#x-ignoredheaderparameters)
- [Info Object](#info-object)
- [x-logo](#x-logo)
- [Logo Object](#a-namelogoobjectalogo-object)
- [Tag Object](#tag-object)
- [x-traitTag](#x-traittag)
- [x-displayName](#x-displayname)
- [Operation Object](#operation-object-vendor-extensions)
- [x-codeSamples](#x-codesamples)
- [Code Sample Object](#a-namecodesampleobjectacode-sample-object)
- [Parameter Object](#parameter-object)
- [x-examples](#x-examples)
- [Response Object vendor extensions](#response-object-vendor-extensions)
- [x-summary](#x-summary)
- [Schema Object](#schema-object)
- [x-nullable](#x-nullable)
- [x-extendedDiscriminator](#x-extendeddiscriminator)
- [x-additionalPropertiesName](#x-additionalpropertiesname)
- [x-explicitMappingOnly](#x-explicitmappingonly)
### Swagger Object
Extends the OpenAPI root [OpenAPI Object](https://swagger.io/specification/#oasObject)
#### x-servers #### x-servers
Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#serverObject). Currently doesn't support templates. Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#serverObject). Currently doesn't support templates.
@ -12,9 +38,9 @@ Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specifica
| :------------- | :-----------: | :---------- | | :------------- | :-----------: | :---------- |
| x-tagGroups | [ [Tag Group Object](#tagGroupObject) ] | A list of tag groups | | x-tagGroups | [ [Tag Group Object](#tagGroupObject) ] | A list of tag groups |
###### Usage in Redoc ###### How to use with Redoc
`x-tagGroups` is used to group tags in the side menu. `x-tagGroups` is used to group tags in the side menu.
If you are going to use `x-tagGroups`, please make sure you **add all tags to a group**, since a tag that is not in a group, **will not be displayed** at all! Before you use `x-tagGroups`, make sure you **add all tags to a group**, since a tag that is not in a group, **will not be displayed** at all!
#### <a name="tagGroupObject"></a>Tag Group Object #### <a name="tagGroupObject"></a>Tag Group Object
Information about tags group Information about tags group
@ -62,8 +88,8 @@ x-tagGroups:
| x-ignoredHeaderParameters | [ string ] | A list of ignored headers | | x-ignoredHeaderParameters | [ string ] | A list of ignored headers |
###### Usage in Redoc ###### How to use with Redoc
`x-ignoredHeaderParameters` is used to specify header parameter names which are ignored by ReDoc Use `x-ignoredHeaderParameters` to specify header parameter names which are ignored by ReDoc.
###### x-ignoredHeaderParameters example ###### x-ignoredHeaderParameters example
```yaml ```yaml
@ -77,19 +103,20 @@ x-ignoredHeaderParameters:
- X-Test-Header - X-Test-Header
``` ```
### Info Object vendor extensions ### Info Object
Extends OpenAPI [Info Object](http://swagger.io/specification/#infoObject) Extends the OpenAPI [Info Object](http://swagger.io/specification/#infoObject)
#### x-logo #### x-logo
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :-----------: | :---------- | | :------------- | :-----------: | :---------- |
| x-logo | [Logo Object](#logoObject) | The information about API logo | | x-logo | [Logo Object](#logoObject) | The information about API logo |
###### Usage in Redoc ###### How to use with Redoc
`x-logo` is used to specify API logo. The corresponding image are displayed just above side-menu. `x-logo` is used to specify API logo. The corresponding image is displayed just above the side-menu.
#### <a name="logoObject"></a>Logo Object #### <a name="logoObject"></a>Logo Object
The information about API logo The information about API logo
###### Fixed fields ###### Fixed fields
| Field Name | Type | Description | | Field Name | Type | Description |
| :-------------- | :------: | :---------- | | :-------------- | :------: | :---------- |
@ -125,17 +152,16 @@ info:
altText: "Petstore logo" altText: "Petstore logo"
``` ```
### Tag Object
Extends the OpenAPI [Tag Object](http://swagger.io/specification/#tagObject)
### Tag Object vendor extensions
Extends OpenAPI [Tag Object](http://swagger.io/specification/#tagObject)
#### x-traitTag #### x-traitTag
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-traitTag | boolean | In Swagger two operations can have multiple tags. This property distinguishes between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) | | x-traitTag | boolean | In Swagger two operations can have multiple tags. This property distinguishes between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) |
###### Usage in Redoc ###### How to use with Redoc
Tags that have `x-traitTag` set to `true` are listed in side-menu but don't have any subitems (operations). Tag `description` is rendered as well. Tags that have `x-traitTag` set to `true` are listed in the side-menu but don't have any subitems (operations). It also renders the `description` tag.
This is useful for handling out common things like Pagination, Rate-Limits, etc. This is useful for handling out common things like Pagination, Rate-Limits, etc.
###### x-traitTag example ###### x-traitTag example
@ -161,17 +187,19 @@ x-traitTag: true
| x-displayName | string | Defines the text that is used for this tag in the menu and in section headings | | x-displayName | string | Defines the text that is used for this tag in the menu and in section headings |
### Operation Object vendor extensions ### Operation Object vendor extensions
Extends OpenAPI [Operation Object](http://swagger.io/specification/#operationObject) Extends the OpenAPI [Operation Object](http://swagger.io/specification/#operationObject)
#### x-codeSamples #### x-codeSamples
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-codeSamples | [ [Code Sample Object](#codeSampleObject) ] | A list of code samples associated with operation | | x-codeSamples | [ [Code Sample Object](#codeSampleObject) ] | A list of code samples associated with operation |
###### Usage in ReDoc ###### How to use with ReDoc
`x-codeSamples` are rendered on the right panel of ReDoc `x-codeSamples` are rendered on the right panel in ReDoc.
#### <a name="codeSampleObject"></a>Code Sample Object #### <a name="codeSampleObject"></a>Code Sample Object
Operation code sample Operation code sample
###### Fixed fields ###### Fixed fields
| Field Name | Type | Description | | Field Name | Type | Description |
| :---------- | :------: | :----------- | | :---------- | :------: | :----------- |
@ -194,49 +222,51 @@ lang: JavaScript
source: console.log('Hello World'); source: console.log('Hello World');
``` ```
### Parameter Object vendor extensions ### Parameter Object
Extends OpenAPI [Parameter Object](http://swagger.io/specification/#parameterObject) Extends the OpenAPI [Parameter Object](http://swagger.io/specification/#parameterObject)
#### x-examples #### x-examples
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-examples | [Example Object](http://swagger.io/specification/#exampleObject) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` | | x-examples | [Example Object](http://swagger.io/specification/#exampleObject) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` |
###### Usage in ReDoc ###### How to use with ReDoc
`x-examples` are rendered in the JSON tab on the right panel of ReDoc. `x-examples` are rendered in the JSON tab on the right panel in ReDoc.
### Response Object vendor extensions ### Response Object vendor extensions
Extends OpenAPI [Response Object](https://swagger.io/specification/#responseObject) Extends the OpenAPI [Response Object](https://swagger.io/specification/#responseObject)
#### x-summary #### x-summary
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-summary | string | a short summary of the response | | x-summary | string | a short summary of the response |
###### Usage in ReDoc ###### How to use with ReDoc
If specified, `x-summary` is used as the response button text. Description is rendered under the button. If specified, you can use `x-summary` as the response button text, with description rendered under the button.
### Schema Object
Extends the OpenAPI [Schema Object](http://swagger.io/specification/#schemaObject)
### Schema Object vendor extensions
Extends OpenAPI [Schema Object](http://swagger.io/specification/#schemaObject)
#### x-nullable #### x-nullable
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-nullable | boolean | marks schema as a nullable | | x-nullable | boolean | marks schema as a nullable |
###### Usage in ReDoc ###### How to use with ReDoc
Schemas marked as `x-nullable` are marked in ReDoc with the label Nullable Schemas marked as `x-nullable` are marked in ReDoc with the label Nullable
#### x-extendedDiscriminator #### x-extendedDiscriminator
**ATTENTION**: This is ReDoc-specific vendor extension. It won't be supported by other tools. **ATTENTION**: This is a ReDoc-specific vendor extension, and is not supported by other tools.
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-extendedDiscriminator | string | specifies extended discriminator | | x-extendedDiscriminator | string | specifies extended discriminator |
###### Usage in ReDoc ###### How to use with ReDoc
ReDoc uses this vendor extension to solve name-clash issues with the standard `discriminator`. ReDoc uses this vendor extension to solve name-clash issues with the standard `discriminator`.
Value of this field specifies the field which will be used as a extended discriminator. Value of this field specifies the field which will be used as a extended discriminator.
ReDoc displays definition with selectpicker using which user can select value of the `x-extendedDiscriminator`-marked field. ReDoc displays definition with selectpicker using which user can select value of the `x-extendedDiscriminator`-marked field.
ReDoc displays the definition which is derived from the current (using `allOf`) and has `enum` with only one value which is the same as the selected value of the field specified as `x-extendedDiscriminator`. ReDoc displays the definition derived from the current (using `allOf`) and has `enum` with only one value which is the same as the selected value of the field specified as `x-extendedDiscriminator`.
###### x-extendedDiscriminator example ###### x-extendedDiscriminator example
@ -276,11 +306,10 @@ PayPalPayment:
type: string type: string
``` ```
In the example above the names of definitions (`PayPalPayment`) are named differently than In the example above, the names of definitions (`PayPalPayment`) are named differently than names in the payload (`paypal`) which is not supported by default `discriminator`.
names in the payload (`paypal`) which is not supported by default `discriminator`.
#### x-additionalPropertiesName #### x-additionalPropertiesName
**ATTENTION**: This is ReDoc-specific vendor extension. It won't be supported by other tools. **ATTENTION**: This is a ReDoc-specific vendor extension, and is not supported by other tools.
Extends the `additionalProperties` property of the schema object. Extends the `additionalProperties` property of the schema object.
@ -288,7 +317,7 @@ Extends the `additionalProperties` property of the schema object.
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-additionalPropertiesName | string | descriptive name of additional properties keys | | x-additionalPropertiesName | string | descriptive name of additional properties keys |
###### Usage in ReDoc ###### How to use with ReDoc
ReDoc uses this extension to display a more descriptive property name in objects with `additionalProperties` when viewing the property list with an `object`. ReDoc uses this extension to display a more descriptive property name in objects with `additionalProperties` when viewing the property list with an `object`.
###### x-additionalPropertiesName example ###### x-additionalPropertiesName example
@ -308,7 +337,7 @@ Player:
``` ```
#### x-explicitMappingOnly #### x-explicitMappingOnly
**ATTENTION**: This is ReDoc-specific vendor extension. It won't be supported by other tools. **ATTENTION**: This is ReDoc-specific vendor extension, and is not supported by other tools.
Extends the `discriminator` property of the schema object. Extends the `discriminator` property of the schema object.
@ -316,10 +345,9 @@ Extends the `discriminator` property of the schema object.
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-explicitMappingOnly | boolean | limit the discriminator selectpicker to the explicit mappings only | | x-explicitMappingOnly | boolean | limit the discriminator selectpicker to the explicit mappings only |
###### Usage in ReDoc ###### How to use with ReDoc
ReDoc uses this extension to filter the `discriminator` mappings shown in the selectpicker. ReDoc uses this extension to filter the `discriminator` mappings shown in the selectpicker.
When set to `true`, the selectpicker will only list the the explicitly defined mappings. When `false`, When set to `true`, the selectpicker will only list the the explicitly defined mappings. When `false`, the default behavior is kept, i.e. explicit and implicit mappings will be shown.
the default behavior is kept, i.e. explicit and implicit mappings will be shown.
###### x-explicitMappingOnly example ###### x-explicitMappingOnly example
@ -338,5 +366,4 @@ Pet:
bee: "#/components/schemas/HoneyBee" bee: "#/components/schemas/HoneyBee"
``` ```
Will show in the selectpicker only the items `cat` and `bee`, even though the `Dog` class inherits from Will show in the selectpicker only the items `cat` and `bee`, even though the `Dog` class inherits from the `Pet` class.
the `Pet` class.

8
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "redoc", "name": "redoc",
"version": "2.0.0-rc.41", "version": "2.0.0-rc.44",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -12998,9 +12998,9 @@
"dev": true "dev": true
}, },
"mobx": { "mobx": {
"version": "5.15.4", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.4.tgz", "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.0.1.tgz",
"integrity": "sha512-xRFJxSU2Im3nrGCdjSuOTFmxVDGeqOHL+TyADCGbT0k4HHqGmx5u2yaHNryvoORpI4DfbzjJ5jPmuv+d7sioFw==", "integrity": "sha512-Pk6uJXZ34yqd661yRmS6z/9avm4FOGXpFpVjnEfiYYOsZXnAxv1fpYjxTCEZ9tuwk0Xe1qnUUlgm+rJtGe0YJA==",
"dev": true "dev": true
}, },
"mobx-react": { "mobx-react": {

View File

@ -1,6 +1,6 @@
{ {
"name": "redoc", "name": "redoc",
"version": "2.0.0-rc.41", "version": "2.0.0-rc.44",
"description": "ReDoc", "description": "ReDoc",
"repository": { "repository": {
"type": "git", "type": "git",
@ -103,7 +103,7 @@
"jest": "^26.1.0", "jest": "^26.1.0",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"mobx": "^5.15.4", "mobx": "^6.0.1",
"prettier": "^2.0.5", "prettier": "^2.0.5",
"raf": "^3.4.1", "raf": "^3.4.1",
"react": "^16.13.1", "react": "^16.13.1",
@ -129,7 +129,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"core-js": "^3.1.4", "core-js": "^3.1.4",
"mobx": "^4.2.0 || ^5.0.0", "mobx": "^6.0.1",
"react": "^16.8.4", "react": "^16.8.4",
"react-dom": "^16.8.4", "react-dom": "^16.8.4",
"styled-components": "^4.1.1 || ^5.1.1" "styled-components": "^4.1.1 || ^5.1.1"

View File

@ -107,6 +107,7 @@ export const StyledDropdown = styled(Dropdown)`
input { input {
cursor: pointer; cursor: pointer;
height: 1px; height: 1px;
background-color: transparent;
} }
} }
`; `;

View File

@ -1,6 +1,6 @@
// import { transparentize } from 'polished'; // import { transparentize } from 'polished';
import styled, { extensionsHook } from '../styled-components'; import styled, { extensionsHook, media } from '../styled-components';
import { deprecatedCss } from './mixins'; import { deprecatedCss } from './mixins';
export const PropertiesTableCaption = styled.caption` export const PropertiesTableCaption = styled.caption`
@ -16,6 +16,11 @@ export const PropertyCell = styled.td<{ kind?: string }>`
position: relative; position: relative;
padding: 10px 10px 10px 0; padding: 10px 10px 10px 0;
${media.lessThan('small')`
display: block;
overflow: hidden;
`}
tr:first-of-type > &, tr:first-of-type > &,
tr.last > & { tr.last > & {
border-left-width: 0; border-left-width: 0;
@ -63,7 +68,7 @@ export const PropertyNameCell = styled(PropertyCell)`
line-height: 20px; line-height: 20px;
white-space: nowrap; white-space: nowrap;
font-size: 13px; font-size: 13px;
font-family: ${props => props.theme.typography.code.fontFamily}; font-family: ${(props) => props.theme.typography.code.fontFamily};
&.deprecated { &.deprecated {
${deprecatedCss}; ${deprecatedCss};
@ -77,12 +82,24 @@ export const PropertyNameCell = styled(PropertyCell)`
export const PropertyDetailsCell = styled.td` export const PropertyDetailsCell = styled.td`
border-bottom: 1px solid #9fb4be; border-bottom: 1px solid #9fb4be;
padding: 10px 0; padding: 10px 0;
width: ${props => props.theme.schema.defaultDetailsWidth}; width: ${(props) => props.theme.schema.defaultDetailsWidth};
box-sizing: border-box; box-sizing: border-box;
tr.expanded & { tr.expanded & {
border-bottom: none; border-bottom: none;
} }
${media.lessThan('small')`
padding: 0 20px;
border-bottom: none;
border-left: 1px solid ${(props) => props.theme.schema.linesColor};
tr.last > & {
border-left: none;
}
`}
${extensionsHook('PropertyDetailsCell')};
`; `;
export const PropertyBullet = styled.span` export const PropertyBullet = styled.span`
@ -125,6 +142,20 @@ export const PropertiesTable = styled.table`
vertical-align: middle; vertical-align: middle;
} }
${media.lessThan('small')`
display: block;
> tr, > tbody > tr {
display: block;
}
`}
${media.lessThan('small', false, ' and (-ms-high-contrast:none)')`
td {
float: left;
width: 100%;
}
`}
& &
${InnerPropertiesWrap}, ${InnerPropertiesWrap},
& &

View File

@ -64,7 +64,7 @@ export class Field extends React.Component<FieldProps> {
onKeyPress={this.handleKeyPress} onKeyPress={this.handleKeyPress}
aria-label="expand properties" aria-label="expand properties"
> >
{name} <span>{name}</span>
<ShelfIcon direction={expanded ? 'down' : 'right'} /> <ShelfIcon direction={expanded ? 'down' : 'right'} />
</button> </button>
{required && <RequiredLabel> required </RequiredLabel>} {required && <RequiredLabel> required </RequiredLabel>}
@ -72,7 +72,7 @@ export class Field extends React.Component<FieldProps> {
) : ( ) : (
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}> <PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
<PropertyBullet /> <PropertyBullet />
{name} <span>{name}</span>
{required && <RequiredLabel> required </RequiredLabel>} {required && <RequiredLabel> required </RequiredLabel>}
</PropertyNameCell> </PropertyNameCell>
); );

View File

@ -34,11 +34,11 @@ class Json extends React.PureComponent<JsonProps> {
<button onClick={this.collapseAll}> Collapse all </button> <button onClick={this.collapseAll}> Collapse all </button>
</SampleControls> </SampleControls>
<OptionsContext.Consumer> <OptionsContext.Consumer>
{options => ( {(options) => (
<PrismDiv <PrismDiv
className={this.props.className} className={this.props.className}
// tslint:disable-next-line // tslint:disable-next-line
ref={node => (this.node = node!)} ref={(node) => (this.node = node!)}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel), __html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
}} }}
@ -65,9 +65,8 @@ class Json extends React.PureComponent<JsonProps> {
} }
}; };
clickListener = (event: MouseEvent) => { collapseElement = (target: HTMLElement) => {
let collapsed; let collapsed;
const target = event.target as HTMLElement;
if (target.className === 'collapser') { if (target.className === 'collapser') {
collapsed = target.parentElement!.getElementsByClassName('collapsible')[0]; collapsed = target.parentElement!.getElementsByClassName('collapsible')[0];
if (collapsed.parentElement.classList.contains('collapsed')) { if (collapsed.parentElement.classList.contains('collapsed')) {
@ -78,12 +77,24 @@ class Json extends React.PureComponent<JsonProps> {
} }
}; };
clickListener = (event: MouseEvent) => {
this.collapseElement(event.target as HTMLElement);
};
focusListener = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
this.collapseElement(event.target as HTMLElement);
}
};
componentDidMount() { componentDidMount() {
this.node!.addEventListener('click', this.clickListener); this.node!.addEventListener('click', this.clickListener);
this.node!.addEventListener('focus', this.focusListener);
} }
componentWillUnmount() { componentWillUnmount() {
this.node!.removeEventListener('click', this.clickListener); this.node!.removeEventListener('click', this.clickListener);
this.node!.removeEventListener('focus', this.focusListener);
} }
} }

View File

@ -1,12 +1,13 @@
import { css } from '../../styled-components'; import { css } from '../../styled-components';
export const jsonStyles = css` export const jsonStyles = css`
.redoc-json > .collapser { .redoc-json code > .collapser {
display: none; display: none;
pointer-events: none;
} }
font-family: ${props => props.theme.typography.code.fontFamily}; font-family: ${(props) => props.theme.typography.code.fontFamily};
font-size: ${props => props.theme.typography.code.fontSize}; font-size: ${(props) => props.theme.typography.code.fontSize};
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')}; white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
contain: content; contain: content;
@ -47,8 +48,32 @@ export const jsonStyles = css`
} }
.collapser { .collapser {
background-color: transparent;
border: 0;
color: #fff;
font-family: ${(props) => props.theme.typography.code.fontFamily};
font-size: ${(props) => props.theme.typography.code.fontSize};
padding-right: 6px; padding-right: 6px;
padding-left: 6px; padding-left: 6px;
padding-top: 0;
padding-bottom: 0;
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
position: absolute;
top: 4px;
left: -1.5em;
cursor: default;
user-select: none;
-webkit-user-select: none;
padding: 2px;
&:focus {
outline-color: #fff;
outline-style: dotted;
outline-width: 1px;
}
} }
ul { ul {
@ -83,13 +108,4 @@ export const jsonStyles = css`
.collapsed > .ellipsis { .collapsed > .ellipsis {
display: inherit; display: inherit;
} }
.collapser {
position: absolute;
top: 1px;
left: -1.5em;
cursor: default;
user-select: none;
-webkit-user-select: none;
}
`; `;

View File

@ -1,7 +1,7 @@
// import { transparentize } from 'polished'; // import { transparentize } from 'polished';
import * as React from 'react'; import * as React from 'react';
import styled from '../../styled-components'; import styled, { media } from '../../styled-components';
import { Link, UnderlinedHeader } from '../../common-elements/'; import { Link, UnderlinedHeader } from '../../common-elements/';
import { SecurityRequirementModel } from '../../services/models/SecurityRequirement'; import { SecurityRequirementModel } from '../../services/models/SecurityRequirement';
@ -85,11 +85,14 @@ export class SecurityRequirement extends React.PureComponent<SecurityRequirement
} }
const AuthHeaderColumn = styled.div` const AuthHeaderColumn = styled.div`
flex: 1; flex: 1 1 auto;
`; `;
const SecuritiesColumn = styled.div` const SecuritiesColumn = styled.div`
width: ${props => props.theme.schema.defaultDetailsWidth}; width: ${props => props.theme.schema.defaultDetailsWidth};
${media.lessThan('small')`
margin-top: 10px;
`}
`; `;
const AuthHeader = styled(UnderlinedHeader)` const AuthHeader = styled(UnderlinedHeader)`
@ -101,6 +104,10 @@ const Wrap = styled.div`
width: 100%; width: 100%;
display: flex; display: flex;
margin: 1em 0; margin: 1em 0;
${media.lessThan('small')`
flex-direction: column;
`}
`; `;
export interface SecurityRequirementsProps { export interface SecurityRequirementsProps {

View File

@ -9,7 +9,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"deprecated": false, "deprecated": false,
"description": "", "description": "",
"example": undefined, "example": undefined,
"expanded": undefined, "expanded": false,
"explode": false, "explode": false,
"in": undefined, "in": undefined,
"kind": "field", "kind": "field",
@ -59,7 +59,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"deprecated": false, "deprecated": false,
"description": "", "description": "",
"example": undefined, "example": undefined,
"expanded": undefined, "expanded": false,
"explode": false, "explode": false,
"in": undefined, "in": undefined,
"kind": "field", "kind": "field",

View File

@ -2,6 +2,8 @@ import 'core-js/es/promise';
import 'core-js/es/array/find'; import 'core-js/es/array/find';
import 'core-js/es/object/assign'; import 'core-js/es/object/assign';
import 'core-js/es/object/entries';
import 'core-js/es/object/is';
import 'core-js/es/string/ends-with'; import 'core-js/es/string/ends-with';
import 'core-js/es/string/starts-with'; import 'core-js/es/string/starts-with';

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { querySelector } from '../utils/dom'; import { querySelector } from '../utils/dom';
import { SpecStore } from './models'; import { SpecStore } from './models';
@ -76,6 +76,8 @@ export class MenuStore {
* @param scroll scroll service instance used by this menu * @param scroll scroll service instance used by this menu
*/ */
constructor(spec: SpecStore, public scroll: ScrollService, public history: HistoryService) { constructor(spec: SpecStore, public scroll: ScrollService, public history: HistoryService) {
makeObservable(this);
this.items = spec.contentItems; this.items = spec.contentItems;
this.flatItems = flattenByProp(this.items || [], 'items'); this.flatItems = flattenByProp(this.items || [], 'items');

View File

@ -20,7 +20,7 @@ describe('Models', () => {
); );
expect(callback.name).toEqual('Test.Callback'); expect(callback.name).toEqual('Test.Callback');
expect(callback.operations.length).toEqual(0); expect(callback.operations.length).toEqual(0);
expect(callback.expanded).toBeUndefined(); expect(callback.expanded).toBeFalsy();
}); });
}); });
}); });

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { OpenAPICallback, Referenced } from '../../types'; import { OpenAPICallback, Referenced } from '../../types';
import { isOperationName, JsonPointer } from '../../utils'; import { isOperationName, JsonPointer } from '../../utils';
@ -8,7 +8,8 @@ import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
export class CallbackModel { export class CallbackModel {
@observable @observable
expanded: boolean; expanded: boolean = false;
name: string; name: string;
operations: OperationModel[] = []; operations: OperationModel[] = [];
@ -19,6 +20,8 @@ export class CallbackModel {
pointer: string, pointer: string,
options: RedocNormalizedOptions, options: RedocNormalizedOptions,
) { ) {
makeObservable(this);
this.name = name; this.name = name;
const paths = parser.deref<OpenAPICallback>(infoOrRef); const paths = parser.deref<OpenAPICallback>(infoOrRef);
parser.exitRef(infoOrRef); parser.exitRef(infoOrRef);

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { import {
OpenAPIParameter, OpenAPIParameter,
@ -39,7 +39,7 @@ const DEFAULT_SERIALIZATION: Record<
*/ */
export class FieldModel { export class FieldModel {
@observable @observable
expanded: boolean | undefined; expanded: boolean | undefined = false;
schema: SchemaModel; schema: SchemaModel;
name: string; name: string;
@ -61,6 +61,8 @@ export class FieldModel {
pointer: string, pointer: string,
options: RedocNormalizedOptions, options: RedocNormalizedOptions,
) { ) {
makeObservable(this);
const info = parser.deref<OpenAPIParameter>(infoOrRef); const info = parser.deref<OpenAPIParameter>(infoOrRef);
this.kind = infoOrRef.kind || 'field'; this.kind = infoOrRef.kind || 'field';
this.name = infoOrRef.name || info.name; this.name = infoOrRef.name || info.name;

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { OpenAPIExternalDocumentation, OpenAPITag } from '../../types'; import { OpenAPIExternalDocumentation, OpenAPITag } from '../../types';
import { safeSlugify } from '../../utils'; import { safeSlugify } from '../../utils';
@ -35,6 +35,8 @@ export class GroupModel implements IMenuItem {
tagOrGroup: OpenAPITag | MarkdownHeading, tagOrGroup: OpenAPITag | MarkdownHeading,
parent?: GroupModel, parent?: GroupModel,
) { ) {
makeObservable(this);
// markdown headings already have ids calculated as they are needed for heading anchors // markdown headings already have ids calculated as they are needed for heading anchors
this.id = (tagOrGroup as MarkdownHeading).id || type + '/' + safeSlugify(tagOrGroup.name); this.id = (tagOrGroup as MarkdownHeading).id || type + '/' + safeSlugify(tagOrGroup.name);
this.type = type; this.type = type;

View File

@ -1,4 +1,4 @@
import { action, computed, observable } from 'mobx'; import { action, computed, observable, makeObservable } from 'mobx';
import { OpenAPIMediaType } from '../../types'; import { OpenAPIMediaType } from '../../types';
import { MediaTypeModel } from './MediaType'; import { MediaTypeModel } from './MediaType';
@ -26,6 +26,8 @@ export class MediaContentModel {
public isRequestType: boolean, public isRequestType: boolean,
options: RedocNormalizedOptions, options: RedocNormalizedOptions,
) { ) {
makeObservable(this);
if (options.unstable_ignoreMimeParameters) { if (options.unstable_ignoreMimeParameters) {
info = mergeSimilarMediaTypes(info); info = mergeSimilarMediaTypes(info);
} }

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { IMenuItem } from '../MenuStore'; import { IMenuItem } from '../MenuStore';
import { GroupModel } from './Group.model'; import { GroupModel } from './Group.model';
@ -84,6 +84,8 @@ export class OperationModel implements IMenuItem {
private options: RedocNormalizedOptions, private options: RedocNormalizedOptions,
isCallback: boolean = false, isCallback: boolean = false,
) { ) {
makeObservable(this);
this.pointer = operationSpec.pointer; this.pointer = operationSpec.pointer;
this.description = operationSpec.description; this.description = operationSpec.description;

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { OpenAPIResponse, Referenced } from '../../types'; import { OpenAPIResponse, Referenced } from '../../types';
@ -13,7 +13,7 @@ import { MediaContentModel } from './MediaContent';
export class ResponseModel { export class ResponseModel {
@observable @observable
expanded: boolean; expanded: boolean = false;
content?: MediaContentModel; content?: MediaContentModel;
code: string; code: string;
@ -31,6 +31,8 @@ export class ResponseModel {
infoOrRef: Referenced<OpenAPIResponse>, infoOrRef: Referenced<OpenAPIResponse>,
options: RedocNormalizedOptions, options: RedocNormalizedOptions,
) { ) {
makeObservable(this);
this.expanded = options.expandResponses === 'all' || options.expandResponses[code]; this.expanded = options.expandResponses === 'all' || options.expandResponses[code];
const info = parser.deref(infoOrRef); const info = parser.deref(infoOrRef);

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx'; import { action, observable, makeObservable } from 'mobx';
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types'; import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';
@ -72,6 +72,8 @@ export class SchemaModel {
private options: RedocNormalizedOptions, private options: RedocNormalizedOptions,
isChild: boolean = false, isChild: boolean = false,
) { ) {
makeObservable(this);
this.pointer = schemaOrRef.$ref || pointer || ''; this.pointer = schemaOrRef.$ref || pointer || '';
this.rawSchema = parser.deref(schemaOrRef); this.rawSchema = parser.deref(schemaOrRef);
this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild); this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild);

View File

@ -1,11 +1,16 @@
import * as React from 'react'; import * as React from 'react';
import { hydrate as hydrateComponent, render } from 'react-dom'; import { hydrate as hydrateComponent, render } from 'react-dom';
import { configure } from "mobx"
import { Redoc, RedocStandalone } from './components/'; import { Redoc, RedocStandalone } from './components/';
import { AppStore, StoreState } from './services/AppStore'; import { AppStore, StoreState } from './services/AppStore';
import { debugTime, debugTimeEnd } from './utils/debug'; import { debugTime, debugTimeEnd } from './utils/debug';
import { querySelector } from './utils/dom'; import { querySelector } from './utils/dom';
configure({
useProxies: 'ifavailable'
})
export { Redoc, AppStore } from '.'; export { Redoc, AppStore } from '.';
export const version = __REDOC_VERSION__; export const version = __REDOC_VERSION__;

View File

@ -13,10 +13,10 @@ const {
} = styledComponents as styledComponents.ThemedStyledComponentsModule<ResolvedThemeInterface>; } = styledComponents as styledComponents.ThemedStyledComponentsModule<ResolvedThemeInterface>;
export const media = { export const media = {
lessThan(breakpoint, print?: boolean) { lessThan(breakpoint, print?: boolean, extra?: string) {
return (...args) => css` return (...args) => css`
@media ${print ? 'print, ' : ''} screen and (max-width: ${props => @media ${print ? 'print, ' : ''} screen and (max-width: ${props =>
props.theme.breakpoints[breakpoint]}) { props.theme.breakpoints[breakpoint]})${extra || ''} {
${(css as any)(...args)}; ${(css as any)(...args)};
} }
`; `;

View File

@ -73,7 +73,7 @@ function valueToHTML(value, maxExpandLevel: number) {
function arrayToHTML(json, maxExpandLevel: number) { function arrayToHTML(json, maxExpandLevel: number) {
const collapsed = level > maxExpandLevel ? 'collapsed' : ''; const collapsed = level > maxExpandLevel ? 'collapsed' : '';
let output = `<div class="collapser"></div>${punctuation( let output = `<button class="collapser"></button>${punctuation(
'[', '[',
)}<span class="ellipsis"></span><ul class="array collapsible">`; )}<span class="ellipsis"></span><ul class="array collapsible">`;
let hasContents = false; let hasContents = false;
@ -98,7 +98,7 @@ function objectToHTML(json, maxExpandLevel: number) {
const collapsed = level > maxExpandLevel ? 'collapsed' : ''; const collapsed = level > maxExpandLevel ? 'collapsed' : '';
const keys = Object.keys(json); const keys = Object.keys(json);
const length = keys.length; const length = keys.length;
let output = `<div class="collapser"></div>${punctuation( let output = `<button class="collapser"></button>${punctuation(
'{', '{',
)}<span class="ellipsis"></span><ul class="obj collapsible">`; )}<span class="ellipsis"></span><ul class="obj collapsible">`;
let hasContents = false; let hasContents = false;