mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-09 06:34:53 +03:00
Merge branch 'master' into feature/schema-descriptions
This commit is contained in:
commit
108bb01f27
12
.dockerignore
Normal file
12
.dockerignore
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
*
|
||||||
|
!src/
|
||||||
|
!config
|
||||||
|
!demo/favicon.png
|
||||||
|
|
||||||
|
!custom.d.ts
|
||||||
|
!typings/styled-patch.d.ts
|
||||||
|
!tsconfig.json
|
||||||
|
!webpack.config.ts
|
||||||
|
|
||||||
|
!package.json
|
||||||
|
!yarn.lock
|
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -27,7 +27,7 @@ You will need [Node.js](http://nodejs.org) at `v8.0.0+` and [Yarn](https://yarnp
|
||||||
After cloning the repo, run:
|
After cloning the repo, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ yarn install # or yarn
|
$ yarn install # or npm
|
||||||
```
|
```
|
||||||
|
|
||||||
### Commonly used NPM scripts
|
### Commonly used NPM scripts
|
||||||
|
|
21
.github/workflows/unit-tests.yml
vendored
Normal file
21
.github/workflows/unit-tests.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: Unit Tests
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 10.x
|
||||||
|
- name: yarn install, build, and test
|
||||||
|
run: |
|
||||||
|
npm install -g yarn
|
||||||
|
yarn install
|
||||||
|
yarn bundle
|
||||||
|
yarn test
|
16
.travis.yml
16
.travis.yml
|
@ -1,10 +1,14 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- '8'
|
- '10'
|
||||||
cache: yarn
|
cache:
|
||||||
|
yarn: true
|
||||||
|
directories:
|
||||||
|
# we also need to cache folder with Cypress binary
|
||||||
|
- ~/.cache
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- GH_REF: github.com/Rebilly/ReDoc.git
|
- GH_REF: github.com/Redocly/redoc.git
|
||||||
- GIT_AUTHOR_EMAIL: redoc-bot@users.noreply.github.com
|
- GIT_AUTHOR_EMAIL: redoc-bot@users.noreply.github.com
|
||||||
- GIT_AUTHOR_NAME: RedocBot
|
- GIT_AUTHOR_NAME: RedocBot
|
||||||
- secure: H2GClDJ7TEQaWgnk8d2fIVDpLwG3rTmN8TalUzrCqXGoG6ylCVmlwzKLgfPPWrVgSA7QTdfNV0ab7c2KyPoZBinHmeGMSuKNLVbhOXRc2VFxTBntBTuyJhfCgQEpUKvJesJUuv5RuBn//wC7VZcVVNc06TZDEe8+aDVYQuCRMXZJ4X3e6nSZ64QX2veyVcr+TjnRsZPkeBVcK9hngvuaxLb/hbJ85CvjiseZRt47PGIcrEpMn9n2GMw1m0fNnPoN+MBTSCnIklTmdjPG7t4GUSLmD6H0lNLdXuehYqmQAHgYrLec1aiFlV57QaDoDZrq2hSf4vDmCB/FVydGhD5JunI67pujoV2OnD1V80eUZhYNWOYsJ2Nfp4NxgXsPUcE6zWLYsLfktMPZADhOXInQRACt1cnx8zMYKLnch1RY/ZqjSg0nPtRjLzQ0lNsw5leixvBdBnMjxYHVyAWVwg8WiJMaLO9vog2Qnxg1NTacHO2CsOmm2rw6stpg7ndp/+nOleRlfUKggjt0Tn3FjwCIXeGup2P2EBa+WW2YMAaoMFofYviR5vRlKBgdKo9fsAruaO1r6nm2EdAjOlniyw92bEfU/qOey1nVp/oK2S82uT5In8KB7vl6rF3ak7WAsT9Q5vZUhsrG+eE4PVyIyWNBhs4A7pSwZGHDR/MYtp0E2ug=
|
- secure: H2GClDJ7TEQaWgnk8d2fIVDpLwG3rTmN8TalUzrCqXGoG6ylCVmlwzKLgfPPWrVgSA7QTdfNV0ab7c2KyPoZBinHmeGMSuKNLVbhOXRc2VFxTBntBTuyJhfCgQEpUKvJesJUuv5RuBn//wC7VZcVVNc06TZDEe8+aDVYQuCRMXZJ4X3e6nSZ64QX2veyVcr+TjnRsZPkeBVcK9hngvuaxLb/hbJ85CvjiseZRt47PGIcrEpMn9n2GMw1m0fNnPoN+MBTSCnIklTmdjPG7t4GUSLmD6H0lNLdXuehYqmQAHgYrLec1aiFlV57QaDoDZrq2hSf4vDmCB/FVydGhD5JunI67pujoV2OnD1V80eUZhYNWOYsJ2Nfp4NxgXsPUcE6zWLYsLfktMPZADhOXInQRACt1cnx8zMYKLnch1RY/ZqjSg0nPtRjLzQ0lNsw5leixvBdBnMjxYHVyAWVwg8WiJMaLO9vog2Qnxg1NTacHO2CsOmm2rw6stpg7ndp/+nOleRlfUKggjt0Tn3FjwCIXeGup2P2EBa+WW2YMAaoMFofYviR5vRlKBgdKo9fsAruaO1r6nm2EdAjOlniyw92bEfU/qOey1nVp/oK2S82uT5In8KB7vl6rF3ak7WAsT9Q5vZUhsrG+eE4PVyIyWNBhs4A7pSwZGHDR/MYtp0E2ug=
|
||||||
|
@ -14,6 +18,9 @@ env:
|
||||||
- secure: SEqTg6WoGPPpcWzJ03ZfcSBb3nZ2Mdhug0ec2PszuzYO3libCb9usiqi+jils9z6qyXsL6ecz8HYazDGOUepnubhIpI5otLgfn9XiapjMT06Bj//AjbKpH7eu3TJSpJMzoRHZrKIE1y9ZKIBqKwl9Xs7ko+1oa+MLhrLuxXkoi0JqRB5UzkQtJRDoxVNjysnLQn+hsfnm+yuqPHZd2+Loy++q//WHuf9bwJrlkXn2ICYQIX5oQGlxNO6ui+OZklb0YknvyO5GdQeoKaHYru3MMKKCIS6I7AG9wLmPs5Ou3T0Ia0Xx4/7xazs0rH4NCVpIceSYc3v6evR37pp8MsFTC3BzjL1V3slTnmitC1KSNM8ndGRUg1nsCBkJysnR3HpX6SHuCH+UzOuMxEjwiPdSRnzJPEbTHa1HqMfTkTJMbm4zhp7W4/ozX4TtjUB0ql6NoQE2n0Z3aYgR2C78TmzaPQun8EgredWnCID1FedyexaNcw4HyZ2rXlcvG3rBzSwLHH5PePT9skyqy6KtIaL0MlAP556ilgUeyCZfCNdTmzCvPDZuqaeLRezWDdsKnRfTkxIW80QWlmZ6sW0hynJV5JN2Oghk9Tr+QzgV4ZF68FHwoU9YXCTyX4w5iTYq/GjvfTBqB3VSGPOz3PwU7r47tmaYzPj+I44zqktgxyuxDo=
|
- secure: SEqTg6WoGPPpcWzJ03ZfcSBb3nZ2Mdhug0ec2PszuzYO3libCb9usiqi+jils9z6qyXsL6ecz8HYazDGOUepnubhIpI5otLgfn9XiapjMT06Bj//AjbKpH7eu3TJSpJMzoRHZrKIE1y9ZKIBqKwl9Xs7ko+1oa+MLhrLuxXkoi0JqRB5UzkQtJRDoxVNjysnLQn+hsfnm+yuqPHZd2+Loy++q//WHuf9bwJrlkXn2ICYQIX5oQGlxNO6ui+OZklb0YknvyO5GdQeoKaHYru3MMKKCIS6I7AG9wLmPs5Ou3T0Ia0Xx4/7xazs0rH4NCVpIceSYc3v6evR37pp8MsFTC3BzjL1V3slTnmitC1KSNM8ndGRUg1nsCBkJysnR3HpX6SHuCH+UzOuMxEjwiPdSRnzJPEbTHa1HqMfTkTJMbm4zhp7W4/ozX4TtjUB0ql6NoQE2n0Z3aYgR2C78TmzaPQun8EgredWnCID1FedyexaNcw4HyZ2rXlcvG3rBzSwLHH5PePT9skyqy6KtIaL0MlAP556ilgUeyCZfCNdTmzCvPDZuqaeLRezWDdsKnRfTkxIW80QWlmZ6sW0hynJV5JN2Oghk9Tr+QzgV4ZF68FHwoU9YXCTyX4w5iTYq/GjvfTBqB3VSGPOz3PwU7r47tmaYzPj+I44zqktgxyuxDo=
|
||||||
addons:
|
addons:
|
||||||
chrome: stable
|
chrome: stable
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- libgconf-2-4 # for cypress
|
||||||
before_script: npm run bundle
|
before_script: npm run bundle
|
||||||
script: npm test && ([ "${TRAVIS_PULL_REQUEST}" = "false" ] && npm run e2e-ci || npm
|
script: npm test && ([ "${TRAVIS_PULL_REQUEST}" = "false" ] && npm run e2e-ci || npm
|
||||||
run e2e)
|
run e2e)
|
||||||
|
@ -27,9 +34,6 @@ deploy:
|
||||||
api_key: "$NPM_TOKEN"
|
api_key: "$NPM_TOKEN"
|
||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
- provider: script
|
|
||||||
skip_cleanup: true
|
|
||||||
script: cd cli && yarn install && yarn ci-publish || true
|
|
||||||
- provider: script
|
- provider: script
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
script: yarn deploy:demo
|
script: yarn deploy:demo
|
||||||
|
|
281
CHANGELOG.md
281
CHANGELOG.md
|
@ -1,3 +1,284 @@
|
||||||
|
# [2.0.0-rc.14](https://github.com/Redocly/redoc/compare/v2.0.0-rc.13...v2.0.0-rc.14) (2019-08-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix escaping JSON string values ([58cb20d](https://github.com/Redocly/redoc/commit/58cb20d)), closes [#999](https://github.com/Redocly/redoc/issues/999)
|
||||||
|
* revert expanding default server variables ([7849f7f](https://github.com/Redocly/redoc/commit/7849f7f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.13](https://github.com/Redocly/redoc/compare/v2.0.0-rc.12...v2.0.0-rc.13) (2019-08-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enum list doesn't wrap ([bfbb0c1](https://github.com/Redocly/redoc/commit/bfbb0c1)), closes [#993](https://github.com/Redocly/redoc/issues/993)
|
||||||
|
* incorrect serialization of some parameter samples ([aba45db](https://github.com/Redocly/redoc/commit/aba45db)), closes [#992](https://github.com/Redocly/redoc/issues/992)
|
||||||
|
* support json serialization for parameter examples ([1367380](https://github.com/Redocly/redoc/commit/1367380)), closes [#934](https://github.com/Redocly/redoc/issues/934)
|
||||||
|
* unify accordion icons for responses section ([2afc2e4](https://github.com/Redocly/redoc/commit/2afc2e4)), closes [#975](https://github.com/Redocly/redoc/issues/975)
|
||||||
|
* update to core.js 3 ([9e3375d](https://github.com/Redocly/redoc/commit/9e3375d)), closes [#997](https://github.com/Redocly/redoc/issues/997)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.12](https://github.com/Redocly/redoc/compare/v2.0.0-rc.11...v2.0.0-rc.12) (2019-07-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* rename ObjectDescription to SchemaDefinition as discussed ([4496622](https://github.com/Redocly/redoc/commit/4496622))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.11](https://github.com/Redocly/redoc/compare/v2.0.0-rc.10...v2.0.0-rc.11) (2019-07-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not add extra slashes to pattern ([70d1ee9](https://github.com/Redocly/redoc/commit/70d1ee9)), closes [#983](https://github.com/Redocly/redoc/issues/983)
|
||||||
|
* dropdown fixes related to object description ([0504ad4](https://github.com/Redocly/redoc/commit/0504ad4))
|
||||||
|
* incorrect serialization of parameter sample with hypen ([f7dd658](https://github.com/Redocly/redoc/commit/f7dd658))
|
||||||
|
* redoc-cli: Add missing content type header on compressed responses of `/` path
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* menu items from tags + md extension for Schema Definition ([#681](https://github.com/Redocly/redoc/pull/681))
|
||||||
|
* new option `menuToggle` - fold active MenuItem if clicked ([#963](https://github.com/Redocly/redoc/issues/963))
|
||||||
|
* Add option for skipping quotes in enums `enumSkipQuotes` ([#968](https://github.com/Redocly/redoc/issues/968)) ([afc7e36](https://github.com/Redocly/redoc/commit/afc7e36))
|
||||||
|
* add `sampleCollapseLevel` option ([#937](https://github.com/Redocly/redoc/issues/937)) ([d3f1c16](https://github.com/Redocly/redoc/commit/d3f1c16))
|
||||||
|
|
||||||
|
# [2.0.0-rc.10](https://github.com/Redocly/redoc/compare/v2.0.0-rc.9...v2.0.0-rc.10) (2019-07-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* broken headings with single quote ([51d3b9b](https://github.com/Redocly/redoc/commit/51d3b9b)), closes [#955](https://github.com/Redocly/redoc/issues/955)
|
||||||
|
* fix fields table overflow if deeply nested with long title ([12b7057](https://github.com/Redocly/redoc/commit/12b7057))
|
||||||
|
* hide empty example when it is not defined ([4bd499f](https://github.com/Redocly/redoc/commit/4bd499f))
|
||||||
|
* markdown in examples descriptions + minor ui tweaks ([f52d9e8](https://github.com/Redocly/redoc/commit/f52d9e8))
|
||||||
|
* organize response examples in dropdown and display description ([995e557](https://github.com/Redocly/redoc/commit/995e557))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.9](https://github.com/Redocly/redoc/compare/v2.0.0-rc.8-1...v2.0.0-rc.9) (2019-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix regression double slashes added to full URL display ([f29a4fe](https://github.com/Redocly/redoc/commit/f29a4fe))
|
||||||
|
* IE11, add missing Object.assign polyfill ([888f04e](https://github.com/Redocly/redoc/commit/888f04e))
|
||||||
|
* serialize parameter example values according to the spec ([#917](https://github.com/Redocly/redoc/issues/917)) ([3939286](https://github.com/Redocly/redoc/commit/3939286))
|
||||||
|
* styled-component style error in tabs ([#946](https://github.com/Redocly/redoc/issues/946)) ([c488bbf](https://github.com/Redocly/redoc/commit/c488bbf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add x-additionalPropertiesName ([#622](https://github.com/Redocly/redoc/issues/622)) ([#944](https://github.com/Redocly/redoc/issues/944)) ([0eb1e66](https://github.com/Redocly/redoc/commit/0eb1e66))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.8-1](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.8...v2.0.0-rc.8-1) (2019-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* crash with empty servers with redoc-cli ([3d52b39](https://github.com/Rebilly/ReDoc/commit/3d52b39))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.8](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.7...v2.0.0-rc.8) (2019-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix broken CLI again ([4e12b5d](https://github.com/Rebilly/ReDoc/commit/4e12b5d))
|
||||||
|
* fix logo gutter bg ([81896d3](https://github.com/Rebilly/ReDoc/commit/81896d3))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.7](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.6...v2.0.0-rc.7) (2019-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* crash in node due to broken URL parsing ([8df2b97](https://github.com/Rebilly/ReDoc/commit/8df2b97))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.6](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.5...v2.0.0-rc.6) (2019-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* broken schema talbes with long enums ([3a74b74](https://github.com/Rebilly/ReDoc/commit/3a74b74))
|
||||||
|
* deep linking sometimes not working when sent over messengers ([2491d97](https://github.com/Rebilly/ReDoc/commit/2491d97))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.5](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.4...v2.0.0-rc.5) (2019-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* change fontFamily for EndpointInfo ([#866](https://github.com/Rebilly/ReDoc/issues/866)) ([851b133](https://github.com/Rebilly/ReDoc/commit/851b133))
|
||||||
|
* clean up field values display ([#855](https://github.com/Rebilly/ReDoc/issues/855)) ([5c91590](https://github.com/Rebilly/ReDoc/commit/5c91590))
|
||||||
|
* discriminator and oneOf title fix ([a3d7d7a](https://github.com/Rebilly/ReDoc/commit/a3d7d7a))
|
||||||
|
* encode x-www-form-urlencoded examples correctly ([65930ad](https://github.com/Rebilly/ReDoc/commit/65930ad)), closes [#870](https://github.com/Rebilly/ReDoc/issues/870)
|
||||||
|
* fix redoc-cli broken dependencies ([81a7568](https://github.com/Rebilly/ReDoc/commit/81a7568))
|
||||||
|
* IE11 add missing fetch and URL polyfills ([d2ce1bd](https://github.com/Rebilly/ReDoc/commit/d2ce1bd)), closes [#875](https://github.com/Rebilly/ReDoc/issues/875)
|
||||||
|
* ignore empty x-tagGroups array ([#869](https://github.com/Rebilly/ReDoc/issues/869)) ([4366a0d](https://github.com/Rebilly/ReDoc/commit/4366a0d))
|
||||||
|
* incorrect detected schema title for deeply inherited schemas ([7d7b4e3](https://github.com/Rebilly/ReDoc/commit/7d7b4e3))
|
||||||
|
* pluralize arrray of types ([fdcac30](https://github.com/Rebilly/ReDoc/commit/fdcac30))
|
||||||
|
* remove huge space after Authentication section ([548fae3](https://github.com/Rebilly/ReDoc/commit/548fae3)), closes [#872](https://github.com/Rebilly/ReDoc/issues/872)
|
||||||
|
* remove query string from server URL ([#895](https://github.com/Rebilly/ReDoc/issues/895)) ([64453ff](https://github.com/Rebilly/ReDoc/commit/64453ff))
|
||||||
|
* remove tabs top margin ([5c187f3](https://github.com/Rebilly/ReDoc/commit/5c187f3))
|
||||||
|
* right panel code samples bg color ([de2aed2](https://github.com/Rebilly/ReDoc/commit/de2aed2))
|
||||||
|
* tidy up non-redoc vendor extension presentation ([#847](https://github.com/Rebilly/ReDoc/issues/847)) ([b21cd3d](https://github.com/Rebilly/ReDoc/commit/b21cd3d))
|
||||||
|
* update apiKey in to be titleize ([#902](https://github.com/Rebilly/ReDoc/issues/902)) ([35df477](https://github.com/Rebilly/ReDoc/commit/35df477))
|
||||||
|
* **cli:** add node-libs-browser to the deps ([6c79901](https://github.com/Rebilly/ReDoc/commit/6c79901)), closes [#850](https://github.com/Rebilly/ReDoc/issues/850)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add hideSingleRequestSampleTab option ([4550e4d](https://github.com/Rebilly/ReDoc/commit/4550e4d))
|
||||||
|
* add lineHeight config for headings ([#894](https://github.com/Rebilly/ReDoc/issues/894)) ([5dd5d6d](https://github.com/Rebilly/ReDoc/commit/5dd5d6d))
|
||||||
|
* basic UI labels configuration ([b0e660e](https://github.com/Rebilly/ReDoc/commit/b0e660e)). Can be used for translations later.
|
||||||
|
* add logo gutter to the theme ([82c0cb1a](https://github.com/Rebilly/ReDoc/commit/82c0cb1a)).
|
||||||
|
|
||||||
|
# [2.0.0-rc.4](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.3...v2.0.0-rc.4) (2019-03-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* move swagger2openapi to deps because of missing transitive deps ([ed9b878](https://github.com/Rebilly/ReDoc/commit/ed9b878))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* display requestBody description [#833](https://github.com/Rebilly/ReDoc/issues/833) ([#838](https://github.com/Rebilly/ReDoc/issues/838)) ([56ca371](https://github.com/Rebilly/ReDoc/commit/56ca371))
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.3](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.2...v2.0.0-rc.3) (2019-03-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add extra deref step for anyOf/oneOf variants ([d81b631](https://github.com/Rebilly/ReDoc/commit/d81b631)), closes [#810](https://github.com/Rebilly/ReDoc/issues/810)
|
||||||
|
* duplicate keys in request samples ([3ce5bff](https://github.com/Rebilly/ReDoc/commit/3ce5bff)), closes [#815](https://github.com/Rebilly/ReDoc/issues/815)
|
||||||
|
* escape backslashes in string literals ([#823](https://github.com/Rebilly/ReDoc/issues/823)) ([70faca1](https://github.com/Rebilly/ReDoc/commit/70faca1)), closes [#822](https://github.com/Rebilly/ReDoc/issues/822)
|
||||||
|
* escape quotes in string values ([0473165](https://github.com/Rebilly/ReDoc/commit/0473165)), closes [#882](https://github.com/Rebilly/ReDoc/issues/882)
|
||||||
|
* pin lunr version in ReDoc ([178ff4c](https://github.com/Rebilly/ReDoc/commit/178ff4c)), closes [#844](https://github.com/Rebilly/ReDoc/issues/844)
|
||||||
|
* set last section min-height ([4dd79cd](https://github.com/Rebilly/ReDoc/commit/4dd79cd)), closes [#820](https://github.com/Rebilly/ReDoc/issues/820)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* support externalValue for examples ([2cdfcd2](https://github.com/Rebilly/ReDoc/commit/2cdfcd2)), closes [#551](https://github.com/Rebilly/ReDoc/issues/551) [#840](https://github.com/Rebilly/ReDoc/issues/840)
|
||||||
|
* **cli:** Add templateOptions param to pass additional data to custom template ([#792](https://github.com/Rebilly/ReDoc/issues/792)) ([4e8ee03](https://github.com/Rebilly/ReDoc/commit/4e8ee03))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.2](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2019-01-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* make padding for md code blocks and code samples consistent ([007752d](https://github.com/Rebilly/ReDoc/commit/007752d))
|
||||||
|
* make syntax highlighting for md js code blocks same as for payload samples ([d197c0f](https://github.com/Rebilly/ReDoc/commit/d197c0f))
|
||||||
|
* Only display API version if present ([#773](https://github.com/Rebilly/ReDoc/issues/773)) ([fb3cb36](https://github.com/Rebilly/ReDoc/commit/fb3cb36))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.1](https://github.com/Rebilly/ReDoc/compare/v2.0.0-rc.0...v2.0.0-rc.1) (2019-01-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow docker container serving under non-root URLs ([#731](https://github.com/Rebilly/ReDoc/issues/731)) ([cfb6f0f](https://github.com/Rebilly/ReDoc/commit/cfb6f0f)), closes [#730](https://github.com/Rebilly/ReDoc/issues/730)
|
||||||
|
* make example/defaults badge consistent with code blocks ([fa39ce4](https://github.com/Rebilly/ReDoc/commit/fa39ce4))
|
||||||
|
* pattern constrain spacing ([c7436f2](https://github.com/Rebilly/ReDoc/commit/c7436f2))
|
||||||
|
* sidebar navigation issues when scrollYOffset is float number ([c04f387](https://github.com/Rebilly/ReDoc/commit/c04f387)), closes [#748](https://github.com/Rebilly/ReDoc/issues/748)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.0.0-rc.0](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.41...v2.0.0-rc.0) (2018-11-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* false-positive recursive detection with oneOf ([59eaa8d](https://github.com/Rebilly/ReDoc/commit/59eaa8d)), closes [#723](https://github.com/Rebilly/ReDoc/issues/723) [#585](https://github.com/Rebilly/ReDoc/issues/585)
|
||||||
|
* fix hideHostname also hiding basePath ([b5f3224](https://github.com/Rebilly/ReDoc/commit/b5f3224)), closes [#677](https://github.com/Rebilly/ReDoc/issues/677)
|
||||||
|
* fix spacing with nested markdown lists ([f2f6909](https://github.com/Rebilly/ReDoc/commit/f2f6909)), closes [#718](https://github.com/Rebilly/ReDoc/issues/718)
|
||||||
|
* improve scrolling performance in Chrome with non-wrapped json examples ([a69c402](https://github.com/Rebilly/ReDoc/commit/a69c402))
|
||||||
|
* nested oneOf button spacing ([3673720](https://github.com/Rebilly/ReDoc/commit/3673720)), closes [#719](https://github.com/Rebilly/ReDoc/issues/719)
|
||||||
|
* onLoaded callback not run on spec error ([e77df0c](https://github.com/Rebilly/ReDoc/commit/e77df0c)), closes [#690](https://github.com/Rebilly/ReDoc/issues/690)
|
||||||
|
* theme improvments by [@stasiukanya](https://github.com/stasiukanya) ([e2d0cd5](https://github.com/Rebilly/ReDoc/commit/e2d0cd5))
|
||||||
|
* **cli:** old peer dependency issue with styled-components ([#699](https://github.com/Rebilly/ReDoc/issues/699)) ([9e2853c](https://github.com/Rebilly/ReDoc/commit/9e2853c))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add feature to specify href for logo explicitly ([#645](https://github.com/Rebilly/ReDoc/issues/645)) ([87fd7d7](https://github.com/Rebilly/ReDoc/commit/87fd7d7))
|
||||||
|
* add support for markdown in Server Object ([155d214](https://github.com/Rebilly/ReDoc/commit/155d214))
|
||||||
|
* Add support for minLength and maxLength constraint humanization ([#700](https://github.com/Rebilly/ReDoc/issues/700)) ([f40568b](https://github.com/Rebilly/ReDoc/commit/f40568b)), closes [#42](https://github.com/Rebilly/ReDoc/issues/42) [/github.com/Rebilly/ReDoc/issues/42#issuecomment-371883853](https://github.com//github.com/Rebilly/ReDoc/issues/42/issues/issuecomment-371883853)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0-alpha.41"></a>
|
||||||
|
# [2.0.0-alpha.41](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.40...v2.0.0-alpha.41) (2018-10-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add null check in dispose method ([#675](https://github.com/Rebilly/ReDoc/issues/675)) ([6b7c5b7](https://github.com/Rebilly/ReDoc/commit/6b7c5b7))
|
||||||
|
* extensionHook not being used ([a4a4013](https://github.com/Rebilly/ReDoc/commit/a4a4013)), closes [#665](https://github.com/Rebilly/ReDoc/issues/665)
|
||||||
|
* fix issue with broken markdown caused by marked bug ([70cf293](https://github.com/Rebilly/ReDoc/commit/70cf293))
|
||||||
|
|
||||||
|
### Peer dependencies updates
|
||||||
|
|
||||||
|
* ReDoc now requires `styled-components@^4.0.1` to be installed if used as React component
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0-alpha.40"></a>
|
||||||
|
# [2.0.0-alpha.40](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.39...v2.0.0-alpha.40) (2018-10-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **cli:** add styled-components to dependencies ([2d63fa0](https://github.com/Rebilly/ReDoc/commit/2d63fa0))
|
||||||
|
* allOf inside oneOf overwritten and not rendered ([fe3383d](https://github.com/Rebilly/ReDoc/commit/fe3383d)), closes [#660](https://github.com/Rebilly/ReDoc/issues/660)
|
||||||
|
* fix panel paddings on small screens ([f39fc98](https://github.com/Rebilly/ReDoc/commit/f39fc98))
|
||||||
|
* minor media print improvements ([fbcec82](https://github.com/Rebilly/ReDoc/commit/fbcec82))
|
||||||
|
* remove extra-padding caused by empty group sections ([974bc7d](https://github.com/Rebilly/ReDoc/commit/974bc7d))
|
||||||
|
* server overriding didn't work on Path Item object ([355764d](https://github.com/Rebilly/ReDoc/commit/355764d)), closes [#656](https://github.com/Rebilly/ReDoc/issues/656)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* new option `onlyRequiredInSamples` ([#646](https://github.com/Rebilly/ReDoc/issues/646)) ([10bca66](https://github.com/Rebilly/ReDoc/commit/10bca66))
|
||||||
|
* new option `sortPropsAlphabetically` ([b87cf0d](https://github.com/Rebilly/ReDoc/commit/b87cf0d))
|
||||||
|
* new theme options `spacing.sectionHorizontal` and `spacing.sectionVertical` ([505463f](https://github.com/Rebilly/ReDoc/commit/505463f))
|
||||||
|
* turn off code-blocks wrapping (enable using `theme.typography.code.wrap: true`) ([393681b](https://github.com/Rebilly/ReDoc/commit/393681b)), closes [#658](https://github.com/Rebilly/ReDoc/issues/658)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0-alpha.39"></a>
|
||||||
|
# [2.0.0-alpha.39](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.38...v2.0.0-alpha.39) (2018-09-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Increase badge size slightly so that "PATCH" method fits inside ([#632](https://github.com/Rebilly/ReDoc/issues/632)) ([4b3b5ba](https://github.com/Rebilly/ReDoc/commit/4b3b5ba))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* externalDocumentation rendered for tags, operations and schema fields ([#595](https://github.com/Rebilly/ReDoc/issues/595)) ([893c83e](https://github.com/Rebilly/ReDoc/commit/893c83e)), closes [#550](https://github.com/Rebilly/ReDoc/issues/550)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.0.0-alpha.38"></a>
|
<a name="2.0.0-alpha.38"></a>
|
||||||
# [2.0.0-alpha.38](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.37...v2.0.0-alpha.38) (2018-08-24)
|
# [2.0.0-alpha.38](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.37...v2.0.0-alpha.38) (2018-08-24)
|
||||||
|
|
||||||
|
|
56
README.md
56
README.md
|
@ -1,27 +1,27 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img alt="ReDoc logo" src="https://raw.githubusercontent.com/Rebilly/ReDoc/master/docs/images/redoc-logo.png" width="400px" />
|
<img alt="ReDoc logo" src="https://raw.githubusercontent.com/Redocly/redoc/master/docs/images/redoc-logo.png" width="400px" />
|
||||||
|
|
||||||
**OpenAPI/Swagger-generated API Reference Documentation**
|
**OpenAPI/Swagger-generated API Reference Documentation**
|
||||||
|
|
||||||
[](https://travis-ci.org/Rebilly/ReDoc) [](https://coveralls.io/github/Rebilly/ReDoc?branch=master) [](https://david-dm.org/Rebilly/ReDoc) [](https://david-dm.org/Rebilly/ReDoc#info=devDependencies) [](https://www.npmjs.com/package/redoc) [](https://github.com/Rebilly/ReDoc/blob/master/LICENSE)
|
[](https://travis-ci.org/Redocly/redoc) [](https://coveralls.io/github/Redocly/redoc?branch=master) [](https://david-dm.org/Redocly/redoc) [](https://david-dm.org/Redocly/redoc#info=devDependencies) [](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/master/LICENSE)
|
||||||
|
|
||||||
[](https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc)
|
[](https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc) [](https://hub.docker.com/r/redocly/redoc/)
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
**This is README for `2.0` version of ReDoc (React based). README for `1.x` version is on the branch [v1.x](https://github.com/Rebilly/ReDoc/tree/v1.x)**
|
**This is README for `2.0` version of ReDoc (React based). README for `1.x` version is on the branch [v1.x](https://github.com/Redocly/redoc/tree/v1.x)**
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## [Live demo](http://rebilly.github.io/ReDoc/)
|
## [Live demo](http://redocly.github.io/redoc/)
|
||||||
|
|
||||||
[<img alt="Deploy to Github" src="http://i.imgur.com/YZmaqk3.png" height="60px">](https://github.com/Rebilly/generator-openapi-repo#generator-openapi-repo--) [<img alt="ReDoc as a service" src="http://i.imgur.com/edqdCv6.png" height="60px">](https://redoc.ly) [<img alt="Customization services" src="http://i.imgur.com/c4sUF7M.png" height="60px">](https://redoc.ly/#services)
|
[<img alt="Deploy to Github" src="http://i.imgur.com/YZmaqk3.png" height="60px">](https://github.com/Rebilly/generator-openapi-repo#generator-openapi-repo--) [<img alt="ReDoc as a service" src="http://i.imgur.com/edqdCv6.png" height="60px">](https://redoc.ly) [<img alt="Customization services" src="http://i.imgur.com/c4sUF7M.png" height="60px">](https://redoc.ly/#services)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Extremely easy deployment
|
- Extremely easy deployment
|
||||||
- [redoc-cli](https://github.com/Rebilly/ReDoc/blob/master/cli/README.md) with ability to bundle your docs into **zero-dependency** HTML file
|
- [redoc-cli](https://github.com/Redocly/redoc/blob/master/cli/README.md) with ability to bundle your docs into **zero-dependency** HTML file
|
||||||
- Server Side Rendering ready
|
- Server Side Rendering ready
|
||||||
- The widest OpenAPI v2.0 features support (yes, it supports even `discriminator`) <br>
|
- The widest OpenAPI v2.0 features support (yes, it supports even `discriminator`) <br>
|
||||||

|

|
||||||
|
@ -37,7 +37,7 @@
|
||||||
- Branding/customizations via [`theme` option](#redoc-options-object)
|
- Branding/customizations via [`theme` option](#redoc-options-object)
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
- [x] ~~[OpenAPI v3.0 support](https://github.com/Rebilly/ReDoc/issues/312)~~
|
- [x] ~~[OpenAPI v3.0 support](https://github.com/Redocly/redoc/issues/312)~~
|
||||||
- [x] ~~performance optimizations~~
|
- [x] ~~performance optimizations~~
|
||||||
- [x] ~~better navigation (menu improvements + search)~~
|
- [x] ~~better navigation (menu improvements + search)~~
|
||||||
- [x] ~~React rewrite~~
|
- [x] ~~React rewrite~~
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
- particular release, e.g. `v2.0.0-alpha.15`: https://cdn.jsdelivr.net/npm/redoc@2.0.0-alpha.17/bundles/redoc.standalone.js
|
- particular release, e.g. `v2.0.0-alpha.15`: https://cdn.jsdelivr.net/npm/redoc@2.0.0-alpha.17/bundles/redoc.standalone.js
|
||||||
- `next` release: https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js
|
- `next` release: https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js
|
||||||
|
|
||||||
Additionally, all the 1.x releases are hosted on our GitHub Pages-based **CDN**:
|
Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(deprecated)**:
|
||||||
- particular release, e.g. `v1.2.0`: https://rebilly.github.io/ReDoc/releases/v1.2.0/redoc.min.js
|
- particular release, e.g. `v1.2.0`: https://rebilly.github.io/ReDoc/releases/v1.2.0/redoc.min.js
|
||||||
- `v1.x.x` release: https://rebilly.github.io/ReDoc/releases/v1.x.x/redoc.min.js
|
- `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 - it will point to latest 1.x.x release since 2.x releases are not hosted on this CDN but on unpkg.
|
- `latest` release: https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js - it will point to latest 1.x.x release since 2.x releases are not hosted on this CDN but on unpkg.
|
||||||
|
@ -70,6 +70,7 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based **CDN**:
|
||||||
- [Shopify Draft Orders](https://help.shopify.com/api/draft-orders)
|
- [Shopify Draft Orders](https://help.shopify.com/api/draft-orders)
|
||||||
- [Discourse](http://docs.discourse.org)
|
- [Discourse](http://docs.discourse.org)
|
||||||
- [APIs.guru](https://apis.guru/api-doc/)
|
- [APIs.guru](https://apis.guru/api-doc/)
|
||||||
|
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
|
@ -135,6 +136,10 @@ For npm:
|
||||||
|
|
||||||
## Usage as a React component
|
## Usage as a React component
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
Import `RedocStandalone` component from 'redoc' module:
|
Import `RedocStandalone` component from 'redoc' module:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -157,10 +162,10 @@ Also you can pass options:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
<RedocStandalone
|
<RedocStandalone
|
||||||
specUrl="http://rebilly.github.io/RebillyAPI/swagger.json"
|
specUrl="http://rebilly.github.io/RebillyAPI/openapi.json"
|
||||||
options={{
|
options={{
|
||||||
nativeScrollbars: true,
|
nativeScrollbars: true,
|
||||||
theme: { colors: { main: '#dd5522' } },
|
theme: { colors: { primary: { main: '#dd5522' } } },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
```
|
```
|
||||||
|
@ -171,7 +176,7 @@ You can also specify `onLoaded` callback which will be called each time Redoc ha
|
||||||
|
|
||||||
```js
|
```js
|
||||||
<RedocStandalone
|
<RedocStandalone
|
||||||
specUrl="http://rebilly.github.io/RebillyAPI/swagger.json"
|
specUrl="http://rebilly.github.io/RebillyAPI/openapi.json"
|
||||||
onLoaded={error => {
|
onLoaded={error => {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
console.log('Yay!');
|
console.log('Yay!');
|
||||||
|
@ -180,9 +185,22 @@ You can also specify `onLoaded` callback which will be called each time Redoc ha
|
||||||
/>
|
/>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[**IE11 Support Notes**](docs/usage-with-ie11.md)
|
||||||
|
|
||||||
|
## The Docker way
|
||||||
|
|
||||||
|
ReDoc is available as pre-built Docker image in official [Docker Hub repository](https://hub.docker.com/r/redocly/redoc/). You may simply pull & run it:
|
||||||
|
|
||||||
|
docker pull redocly/redoc
|
||||||
|
docker run -p 8080:80 redocly/redoc
|
||||||
|
|
||||||
|
Also you may rewrite some predefined environment variables defined in [Dockerfile](./config/docker/Dockerfile). By default ReDoc starts with demo Petstore spec located at `http://petstore.swagger.io/v2/swagger.json`, but you may change this URL using environment variable `SPEC_URL`:
|
||||||
|
|
||||||
|
docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.json redocly/redoc
|
||||||
|
|
||||||
## ReDoc CLI
|
## ReDoc CLI
|
||||||
|
|
||||||
[See here](https://github.com/Rebilly/ReDoc/blob/master/cli/README.md)
|
[See here](https://github.com/Redocly/redoc/blob/master/cli/README.md)
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
@ -190,7 +208,7 @@ You can also specify `onLoaded` callback which will be called each time Redoc ha
|
||||||
You can inject Security Definitions widget into any place of your specification `description`. Check out details [here](docs/security-definitions-injection.md).
|
You can inject Security Definitions widget into any place of your specification `description`. Check out details [here](docs/security-definitions-injection.md).
|
||||||
|
|
||||||
### Swagger vendor extensions
|
### Swagger vendor extensions
|
||||||
ReDoc makes use of the following [vendor extensions](http://swagger.io/specification/#vendorExtensions):
|
ReDoc makes use of the following [vendor extensions](https://swagger.io/specification/#specificationExtensions):
|
||||||
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
||||||
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for handling out common things like Pagination, Rate-Limits, etc
|
* [`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
|
* [`x-code-samples`](docs/redoc-vendor-extensions.md#x-code-samples) - specify operation code samples
|
||||||
|
@ -200,6 +218,7 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica
|
||||||
* [`x-tagGroups`](docs/redoc-vendor-extensions.md#x-tagGroups) - group tags by categories in the side menu
|
* [`x-tagGroups`](docs/redoc-vendor-extensions.md#x-tagGroups) - group tags by categories in the side menu
|
||||||
* [`x-servers`](docs/redoc-vendor-extensions.md#x-servers) - ability to specify different servers for API (backported from OpenAPI 3.0)
|
* [`x-servers`](docs/redoc-vendor-extensions.md#x-servers) - ability to specify different servers for API (backported from OpenAPI 3.0)
|
||||||
* [`x-ignoredHeaderParameters`](docs/redoc-vendor-extensions.md#x-ignoredHeaderParameters) - ability to specify header parameter names to ignore
|
* [`x-ignoredHeaderParameters`](docs/redoc-vendor-extensions.md#x-ignoredHeaderParameters) - ability to specify header parameter names to ignore
|
||||||
|
* [`x-additionalPropertiesName`](docs/redoc-vendor-extensions.md#x-additionalPropertiesName) - ability to supply a descriptive name for the additional property keys
|
||||||
|
|
||||||
### `<redoc>` options object
|
### `<redoc>` options object
|
||||||
You can use all of the following options with standalone version on <redoc> tag by kebab-casing them, e.g. `scrollYOffset` becomes `scroll-y-offset` and `expandResponses` becomes `expand-responses`.
|
You can use all of the following options with standalone version on <redoc> tag by kebab-casing them, e.g. `scrollYOffset` becomes `scroll-y-offset` and `expandResponses` becomes `expand-responses`.
|
||||||
|
@ -211,17 +230,22 @@ You can use all of the following options with standalone version on <redoc> tag
|
||||||
* **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset;
|
* **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset;
|
||||||
* **function**: A getter function. Must return a number representing the offset (in pixels);
|
* **function**: A getter function. Must return a number representing the offset (in pixels);
|
||||||
* `suppressWarnings` - if set, warnings are not rendered at the top of documentation (they still are logged to the console).
|
* `suppressWarnings` - if set, warnings are not rendered at the top of documentation (they still are logged to the console).
|
||||||
* `lazyRendering` - _Not implemented yet_ ~~if set, enables lazy rendering mode in ReDoc. This mode is useful for APIs with big number of operations (e.g. > 50). In this mode ReDoc shows initial screen ASAP and then renders the rest operations asynchronously while showing progress bar on the top. Check out the [demo](\\rebilly.github.io/ReDoc) for the example.~~
|
* `lazyRendering` - _Not implemented yet_ ~~if set, enables lazy rendering mode in ReDoc. This mode is useful for APIs with big number of operations (e.g. > 50). In this mode ReDoc shows initial screen ASAP and then renders the rest operations asynchronously while showing progress bar on the top. Check out the [demo](\\redocly.github.io/redoc) for the example.~~
|
||||||
* `hideHostname` - if set, the protocol and hostname is not shown in the operation definition.
|
* `hideHostname` - if set, the protocol and hostname is not shown in the operation definition.
|
||||||
* `expandResponses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expandResponses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
|
* `expandResponses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expandResponses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
|
||||||
* `requiredPropsFirst` - show required properties first ordered in the same order as in `required` array.
|
* `requiredPropsFirst` - show required properties first ordered in the same order as in `required` array.
|
||||||
|
* `sortPropsAlphabetically` - sort properties alphabetically
|
||||||
|
* `showExtensions` - show vendor extensions ("x-" fields). Extensions used by ReDoc are ignored. Can be boolean or an array of `string` with names of extensions to display
|
||||||
* `noAutoAuth` - do not inject Authentication section automatically
|
* `noAutoAuth` - do not inject Authentication section automatically
|
||||||
* `pathInMiddlePanel` - show path link and HTTP verb in the middle panel instead of the right one
|
* `pathInMiddlePanel` - show path link and HTTP verb in the middle panel instead of the right one
|
||||||
* `hideLoading` - do not show loading animation. Useful for small docs
|
* `hideLoading` - do not show loading animation. Useful for small docs
|
||||||
* `nativeScrollbars` - use native scrollbar for sidemenu instead of perfect-scroll (scrolling performance optimization for big specs)
|
* `nativeScrollbars` - use native scrollbar for sidemenu instead of perfect-scroll (scrolling performance optimization for big specs)
|
||||||
* `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button.
|
* `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button.
|
||||||
* `disableSearch` - disable search indexing and search box
|
* `disableSearch` - disable search indexing and search box
|
||||||
* `theme` - ReDoc theme. Not documented yet. For details check source code: [theme.ts](https://github.com/Rebilly/ReDoc/blob/master/src/theme.ts)
|
* `onlyRequiredInSamples` - shows only required fields in request samples.
|
||||||
|
* `jsonSampleExpandLevel` - set the default expand level for JSON payload samples (responses and request body). Special value 'all' expands all levels. The default value is `2`.
|
||||||
|
* `menuToggle` - if true clicking second time on expanded menu item will collapse it, default `false`
|
||||||
|
* `theme` - ReDoc theme. Not documented yet. For details check source code: [theme.ts](https://github.com/Redocly/redoc/blob/master/src/theme.ts)
|
||||||
|
|
||||||
## Advanced usage of standalone version
|
## Advanced usage of standalone version
|
||||||
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
||||||
|
|
23
cli/Dockerfile
Normal file
23
cli/Dockerfile
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Package the 'redoc-cli' as a docker image.
|
||||||
|
#
|
||||||
|
# To build:
|
||||||
|
# $ cd <Redoc project directory>
|
||||||
|
# $ docker build -t redoc-cli -f cli/Dockerfile .
|
||||||
|
#
|
||||||
|
# To run:
|
||||||
|
# To display the command line options:
|
||||||
|
# $ docker run --rm -it redoc-cli --help
|
||||||
|
# .. will display the comand line help
|
||||||
|
#
|
||||||
|
# To turn `swagger.yml` file in the current directory, to html documentation 'redoc-static.html'
|
||||||
|
# $ docker run --rm -it -v $PWD:/data redoc-cli bundle swagger.yml
|
||||||
|
|
||||||
|
FROM node:alpine
|
||||||
|
|
||||||
|
RUN npm install -g redoc-cli
|
||||||
|
|
||||||
|
WORKDIR /data
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT ["redoc-cli"]
|
||||||
|
CMD []
|
|
@ -1,6 +1,6 @@
|
||||||
# redoc-cli
|
# redoc-cli
|
||||||
|
|
||||||
**[ReDoc](https://github.com/Rebilly/ReDoc)'s Command Line Interface**
|
**[ReDoc](https://github.com/Redocly/redoc)'s Command Line Interface**
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
You can use redoc cli by installing `redoc-cli` globally or using [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b).
|
You can use redoc cli by installing `redoc-cli` globally or using [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b).
|
||||||
|
@ -16,6 +16,7 @@ Some examples:
|
||||||
|
|
||||||
- Bundle with main color changed to `orange`: <br> `$ redoc-cli bundle [spec] --options.theme.colors.primary.main=orange`
|
- Bundle with main color changed to `orange`: <br> `$ redoc-cli bundle [spec] --options.theme.colors.primary.main=orange`
|
||||||
- Serve with `nativeScrollbars` option set to true: <br> `$ redoc-cli serve [spec] --options.nativeScrollbars`
|
- Serve with `nativeScrollbars` option set to true: <br> `$ redoc-cli serve [spec] --options.nativeScrollbars`
|
||||||
- Bundle using custom template (check [default template](https://github.com/Rebilly/ReDoc/blob/master/cli/template.hbs) for reference): <br> `$ redoc-cli bundle [spec] -t custom.hbs`
|
- Bundle using custom template (check [default template](https://github.com/Redocly/redoc/blob/master/cli/template.hbs) for reference): <br> `$ redoc-cli bundle [spec] -t custom.hbs`
|
||||||
|
- Bundle using custom template and add custom `templateOptions`: <br> `$ redoc-cli bundle [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"`
|
||||||
|
|
||||||
For more details run `redoc-cli --help`.
|
For more details run `redoc-cli --help`.
|
||||||
|
|
89
cli/index.ts
89
cli/index.ts
|
@ -5,15 +5,16 @@ import { renderToString } from 'react-dom/server';
|
||||||
import { ServerStyleSheet } from 'styled-components';
|
import { ServerStyleSheet } from 'styled-components';
|
||||||
|
|
||||||
import { compile } from 'handlebars';
|
import { compile } from 'handlebars';
|
||||||
import { createServer, ServerRequest, ServerResponse } from 'http';
|
import { createServer, IncomingMessage, ServerResponse } from 'http';
|
||||||
import { dirname, join } from 'path';
|
import { dirname, join, resolve } from 'path';
|
||||||
|
|
||||||
import * as zlib from 'zlib';
|
import * as zlib from 'zlib';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { createStore, loadAndBundleSpec, Redoc } from 'redoc';
|
import { createStore, loadAndBundleSpec, Redoc } from 'redoc';
|
||||||
|
|
||||||
import { createReadStream, existsSync, readFileSync, ReadStream, watch, writeFileSync } from 'fs';
|
import { watch } from 'chokidar';
|
||||||
|
import { createReadStream, existsSync, readFileSync, ReadStream, writeFileSync } from 'fs';
|
||||||
import * as mkdirp from 'mkdirp';
|
import * as mkdirp from 'mkdirp';
|
||||||
|
|
||||||
import * as YargsParser from 'yargs';
|
import * as YargsParser from 'yargs';
|
||||||
|
@ -24,7 +25,9 @@ interface Options {
|
||||||
cdn?: boolean;
|
cdn?: boolean;
|
||||||
output?: string;
|
output?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
port?: number;
|
||||||
templateFileName?: string;
|
templateFileName?: string;
|
||||||
|
templateOptions?: any;
|
||||||
redocOptions?: any;
|
redocOptions?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,15 +63,16 @@ YargsParser.command(
|
||||||
return yargs;
|
return yargs;
|
||||||
},
|
},
|
||||||
async argv => {
|
async argv => {
|
||||||
const config = {
|
const config: Options = {
|
||||||
ssr: argv.ssr,
|
ssr: argv.ssr as boolean,
|
||||||
watch: argv.watch,
|
watch: argv.watch as boolean,
|
||||||
templateFileName: argv.template,
|
templateFileName: argv.template as string,
|
||||||
|
templateOptions: argv.templateOptions || {},
|
||||||
redocOptions: argv.options || {},
|
redocOptions: argv.options || {},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await serve(argv.port, argv.spec, config);
|
await serve(argv.port as number, argv.spec as string, config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
}
|
}
|
||||||
|
@ -105,12 +109,13 @@ YargsParser.command(
|
||||||
return yargs;
|
return yargs;
|
||||||
},
|
},
|
||||||
async argv => {
|
async argv => {
|
||||||
const config = {
|
const config: Options = {
|
||||||
ssr: true,
|
ssr: true,
|
||||||
output: argv.o,
|
output: argv.o as string,
|
||||||
cdn: argv.cdn,
|
cdn: argv.cdn as boolean,
|
||||||
title: argv.title,
|
title: argv.title as string,
|
||||||
templateFileName: argv.template,
|
templateFileName: argv.template as string,
|
||||||
|
templateOptions: argv.templateOptions || {},
|
||||||
redocOptions: argv.options || {},
|
redocOptions: argv.options || {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,6 +132,10 @@ YargsParser.command(
|
||||||
describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example ',
|
describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example ',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
})
|
})
|
||||||
|
.options('templateOptions', {
|
||||||
|
describe:
|
||||||
|
'Additional options that you want pass to template. Use dot notation, e.g. templateOptions.metaDescription',
|
||||||
|
})
|
||||||
.options('options', {
|
.options('options', {
|
||||||
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
||||||
}).argv;
|
}).argv;
|
||||||
|
@ -147,7 +156,9 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (request.url === '/') {
|
} else if (request.url === '/') {
|
||||||
respondWithGzip(pageHTML, request, response);
|
respondWithGzip(pageHTML, request, response, {
|
||||||
|
'Content-Type': 'text/html',
|
||||||
|
});
|
||||||
} else if (request.url === '/spec.json') {
|
} else if (request.url === '/spec.json') {
|
||||||
const specStr = JSON.stringify(spec, null, 2);
|
const specStr = JSON.stringify(spec, null, 2);
|
||||||
respondWithGzip(specStr, request, response, {
|
respondWithGzip(specStr, request, response, {
|
||||||
|
@ -167,28 +178,26 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
server.listen(port, () => console.log(`Server started: http://127.0.0.1:${port}`));
|
server.listen(port, () => console.log(`Server started: http://127.0.0.1:${port}`));
|
||||||
|
|
||||||
if (options.watch && existsSync(pathToSpec)) {
|
if (options.watch && existsSync(pathToSpec)) {
|
||||||
const pathToSpecDirectory = dirname(pathToSpec);
|
const pathToSpecDirectory = resolve(dirname(pathToSpec));
|
||||||
const watchOptions = {
|
const watchOptions = {
|
||||||
recursive: true,
|
ignored: /(^|[\/\\])\../,
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
const watcher = watch(pathToSpecDirectory, watchOptions);
|
||||||
pathToSpecDirectory,
|
const log = console.log.bind(console);
|
||||||
watchOptions,
|
watcher
|
||||||
debounce(async (event, filename) => {
|
.on('change', async path => {
|
||||||
if (event === 'change' || event === 'rename') {
|
log(`${path} changed, updating docs`);
|
||||||
console.log(`${join(pathToSpecDirectory, filename)} changed, updating docs`);
|
try {
|
||||||
try {
|
spec = await loadAndBundleSpec(pathToSpec);
|
||||||
spec = await loadAndBundleSpec(pathToSpec);
|
pageHTML = await getPageHTML(spec, pathToSpec, options);
|
||||||
pageHTML = await getPageHTML(spec, pathToSpec, options);
|
log('Updated successfully');
|
||||||
console.log('Updated successfully');
|
} catch (e) {
|
||||||
} catch (e) {
|
console.error('Error while updating: ', e.message);
|
||||||
console.error('Error while updating: ', e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 2200),
|
})
|
||||||
);
|
.on('error', error => console.error(`Watcher error: ${error}`))
|
||||||
console.log(`👀 Watching ${pathToSpecDirectory} for changes...`);
|
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +218,7 @@ async function bundle(pathToSpec, options: Options = {}) {
|
||||||
async function getPageHTML(
|
async function getPageHTML(
|
||||||
spec: any,
|
spec: any,
|
||||||
pathToSpec: string,
|
pathToSpec: string,
|
||||||
{ ssr, cdn, title, templateFileName, redocOptions = {} }: Options,
|
{ ssr, cdn, title, templateFileName, templateOptions, redocOptions = {} }: Options,
|
||||||
) {
|
) {
|
||||||
let html;
|
let html;
|
||||||
let css;
|
let css;
|
||||||
|
@ -252,13 +261,14 @@ async function getPageHTML(
|
||||||
: `<script>${redocStandaloneSrc}</script>`) + css
|
: `<script>${redocStandaloneSrc}</script>`) + css
|
||||||
: '<script src="redoc.standalone.js"></script>',
|
: '<script src="redoc.standalone.js"></script>',
|
||||||
title,
|
title,
|
||||||
|
templateOptions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// credits: https://stackoverflow.com/a/9238214/1749888
|
// credits: https://stackoverflow.com/a/9238214/1749888
|
||||||
function respondWithGzip(
|
function respondWithGzip(
|
||||||
contents: string | ReadStream,
|
contents: string | ReadStream,
|
||||||
request: ServerRequest,
|
request: IncomingMessage,
|
||||||
response: ServerResponse,
|
response: ServerResponse,
|
||||||
headers = {},
|
headers = {},
|
||||||
) {
|
) {
|
||||||
|
@ -291,17 +301,6 @@ function respondWithGzip(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function debounce(callback: (...args) => void, time: number) {
|
|
||||||
let interval;
|
|
||||||
return (...args) => {
|
|
||||||
clearTimeout(interval);
|
|
||||||
interval = setTimeout(() => {
|
|
||||||
interval = null;
|
|
||||||
callback(...args);
|
|
||||||
}, time);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isURL(str: string): boolean {
|
function isURL(str: string): boolean {
|
||||||
return /^(https?:)\/\//m.test(str);
|
return /^(https?:)\/\//m.test(str);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
{
|
{
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.6.2",
|
"version": "0.8.6",
|
||||||
"description": "ReDoc's Command Line Interface",
|
"description": "ReDoc's Command Line Interface",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"bin": "index.js",
|
"bin": "index.js",
|
||||||
"repository": "https://github.com/Rebilly/ReDoc",
|
"repository": "https://github.com/Redocly/redoc",
|
||||||
"author": "Roman Hotsiy <gotsijroman@gmail.com>",
|
"author": "Roman Hotsiy <gotsijroman@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"handlebars": "^4.0.11",
|
"chokidar": "^3.0.2",
|
||||||
"isarray": "^2.0.4",
|
"handlebars": "^4.1.2",
|
||||||
|
"isarray": "^2.0.5",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mobx": "^4.2.0",
|
"mobx": "^4.2.0",
|
||||||
"react": "^16.4.2",
|
"node-libs-browser": "^2.2.1",
|
||||||
"react-dom": "^16.4.2",
|
"react": "^16.8.6",
|
||||||
"redoc": "^2.0.0-alpha.37",
|
"react-dom": "^16.8.6",
|
||||||
"tslib": "^1.9.3",
|
"redoc": "2.0.0-rc.13",
|
||||||
"yargs": "^12.0.1"
|
"styled-components": "^4.3.2",
|
||||||
},
|
"tslib": "^1.10.0",
|
||||||
"scripts": {
|
"yargs": "^13.3.0"
|
||||||
"ci-publish": "ci-publish"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chokidar": "^2.1.3",
|
||||||
"@types/handlebars": "^4.0.39",
|
"@types/handlebars": "^4.0.39",
|
||||||
"@types/mkdirp": "^0.5.2",
|
"@types/mkdirp": "^0.5.2"
|
||||||
"ci-publish": "^1.3.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3502
cli/yarn.lock
3502
cli/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -9,10 +9,16 @@ FROM node:alpine
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache git
|
RUN apk update && apk add --no-cache git
|
||||||
|
|
||||||
# generate bundle
|
# Install dependencies
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY . /build
|
COPY package.json yarn.lock /build/
|
||||||
RUN yarn install --frozen-lockfile --ignore-optional --ignore-scripts
|
RUN yarn install --frozen-lockfile --ignore-optional --ignore-scripts
|
||||||
|
|
||||||
|
# copy only required for the build files
|
||||||
|
COPY src /build/src
|
||||||
|
COPY webpack.config.ts tsconfig.json custom.d.ts /build/
|
||||||
|
COPY typings/styled-patch.d.ts /build/typings/styled-patch.d.ts
|
||||||
|
|
||||||
RUN npm run bundle:standalone
|
RUN npm run bundle:standalone
|
||||||
|
|
||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
|
@ -1,21 +1,17 @@
|
||||||
# Redoc docker image
|
# Official ReDoc Docker Image
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
docker build -t redoc .
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Serve remote spec by URL:
|
Serve remote spec by URL:
|
||||||
|
|
||||||
docker run -it --rm -p 80:80 \
|
docker run -it --rm -p 80:80 \
|
||||||
-e SPEC_URL='http://localhost:8000/swagger.yaml' redoc
|
-e SPEC_URL='http://localhost:8000/swagger.yaml' redocly/redoc
|
||||||
|
|
||||||
Serve local file:
|
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 redoc
|
-e SPEC_URL=swagger.yaml redocly/redoc
|
||||||
|
|
||||||
## Runtime configuration options
|
## Runtime configuration options
|
||||||
|
|
||||||
|
@ -23,4 +19,8 @@ Serve local file:
|
||||||
- `PAGE_FAVICON` (default `"favicon.png"`) - URL to page favicon
|
- `PAGE_FAVICON` (default `"favicon.png"`) - URL to page favicon
|
||||||
- `SPEC_URL` (default `"http://petstore.swagger.io/v2/swagger.json"`) - URL to spec
|
- `SPEC_URL` (default `"http://petstore.swagger.io/v2/swagger.json"`) - URL to spec
|
||||||
- `PORT` (default `80`) - nginx port
|
- `PORT` (default `80`) - nginx port
|
||||||
- `REDOC_OPTIONS` - [`<redoc>` tag attributes](https://github.com/Rebilly/ReDoc#redoc-tag-attributes)
|
- `REDOC_OPTIONS` - [`<redoc>` tag attributes](https://github.com/Redocly/redoc#redoc-tag-attributes)
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
docker build -t redocly/redoc .
|
||||||
|
|
|
@ -6,6 +6,6 @@ sed -i -e "s|%PAGE_TITLE%|$PAGE_TITLE|g" /usr/share/nginx/html/index.html
|
||||||
sed -i -e "s|%PAGE_FAVICON%|$PAGE_FAVICON|g" /usr/share/nginx/html/index.html
|
sed -i -e "s|%PAGE_FAVICON%|$PAGE_FAVICON|g" /usr/share/nginx/html/index.html
|
||||||
sed -i -e "s|%SPEC_URL%|$SPEC_URL|g" /usr/share/nginx/html/index.html
|
sed -i -e "s|%SPEC_URL%|$SPEC_URL|g" /usr/share/nginx/html/index.html
|
||||||
sed -i -e "s|%REDOC_OPTIONS%|${REDOC_OPTIONS}|g" /usr/share/nginx/html/index.html
|
sed -i -e "s|%REDOC_OPTIONS%|${REDOC_OPTIONS}|g" /usr/share/nginx/html/index.html
|
||||||
sed -i -e "s|80|${PORT}|g" /etc/nginx/nginx.conf
|
sed -i -e "s|\(listen\s*\) [0-9]*|\1 ${PORT}|g" /etc/nginx/nginx.conf
|
||||||
|
|
||||||
exec nginx -g 'daemon off;'
|
exec nginx -g 'daemon off;'
|
||||||
|
|
6
config/docker/hooks/build
Executable file
6
config/docker/hooks/build
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# DockerHub cd into Dockerfile location before buil
|
||||||
|
# So we have to undo this.
|
||||||
|
cd ../..
|
||||||
|
docker build -f config/docker/Dockerfile -t $IMAGE_NAME .
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<redoc spec-url="%SPEC_URL%" %REDOC_OPTIONS%></redoc>
|
<redoc spec-url="%SPEC_URL%" %REDOC_OPTIONS%></redoc>
|
||||||
<script src="/redoc.standalone.js"></script>
|
<script src="redoc.standalone.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
2
custom.d.ts
vendored
2
custom.d.ts
vendored
|
@ -18,6 +18,8 @@ declare module '*.css' {
|
||||||
declare var __REDOC_VERSION__: string;
|
declare var __REDOC_VERSION__: string;
|
||||||
declare var __REDOC_REVISION__: string;
|
declare var __REDOC_REVISION__: string;
|
||||||
|
|
||||||
|
declare var reactHotLoaderGlobal: any;
|
||||||
|
|
||||||
interface Element {
|
interface Element {
|
||||||
scrollIntoViewIfNeeded(centerIfNeeded?: boolean): void;
|
scrollIntoViewIfNeeded(centerIfNeeded?: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled, { StyledFunction } from 'styled-components';
|
import styled from '../src/styled-components';
|
||||||
|
|
||||||
function withProps<T, U extends HTMLElement = HTMLElement>(
|
const DropDownItem = styled.li<{ active?: boolean }>`
|
||||||
styledFunction: StyledFunction<React.HTMLProps<U>>,
|
${(props: any) => (props.active ? 'background-color: #eee' : '')};
|
||||||
): StyledFunction<T & React.HTMLProps<U>> {
|
|
||||||
return styledFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DropDownItem = withProps<{ active: boolean }>(styled.li)`
|
|
||||||
${props => ((props as any).active ? 'background-color: #eee' : '')};
|
|
||||||
padding: 13px 16px;
|
padding: 13px 16px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
|
|
@ -82,7 +82,7 @@ class DemoApp extends React.Component<
|
||||||
<>
|
<>
|
||||||
<Heading>
|
<Heading>
|
||||||
<a href=".">
|
<a href=".">
|
||||||
<Logo src="https://github.com/Rebilly/ReDoc/raw/master/docs/images/redoc-logo.png" />
|
<Logo src="https://github.com/Redocly/redoc/raw/master/docs/images/redoc-logo.png" />
|
||||||
</a>
|
</a>
|
||||||
<ControlsContainer>
|
<ControlsContainer>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
|
@ -97,7 +97,7 @@ class DemoApp extends React.Component<
|
||||||
</CorsCheckbox>
|
</CorsCheckbox>
|
||||||
</ControlsContainer>
|
</ControlsContainer>
|
||||||
<iframe
|
<iframe
|
||||||
src="https://ghbtns.com/github-btn.html?user=Rebilly&repo=ReDoc&type=star&count=true&size=large"
|
src="https://ghbtns.com/github-btn.html?user=Redocly&repo=redoc&type=star&count=true&size=large"
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
scrolling="0"
|
scrolling="0"
|
||||||
width="150px"
|
width="150px"
|
||||||
|
|
|
@ -15,15 +15,15 @@ info:
|
||||||
This API is documented in **OpenAPI format** and is based on
|
This API is documented in **OpenAPI format** and is based on
|
||||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||||
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
|
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
|
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||||
|
|
||||||
# OpenAPI Specification
|
# OpenAPI Specification
|
||||||
This API is documented in **OpenAPI format** and is based on
|
This API is documented in **OpenAPI format** and is based on
|
||||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||||
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
|
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
|
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||||
|
|
||||||
# Cross-Origin Resource Sharing
|
# Cross-Origin Resource Sharing
|
||||||
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
||||||
|
@ -38,7 +38,7 @@ info:
|
||||||
OAuth2 - an open protocol to allow secure authorization in a simple
|
OAuth2 - an open protocol to allow secure authorization in a simple
|
||||||
and standard method from web, mobile and desktop applications.
|
and standard method from web, mobile and desktop applications.
|
||||||
|
|
||||||
<security-definitions />
|
<SecurityDefinitions />
|
||||||
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
title: Swagger Petstore
|
title: Swagger Petstore
|
||||||
|
@ -46,9 +46,9 @@ info:
|
||||||
contact:
|
contact:
|
||||||
name: API Support
|
name: API Support
|
||||||
email: apiteam@swagger.io
|
email: apiteam@swagger.io
|
||||||
url: https://github.com/Rebilly/ReDoc
|
url: https://github.com/Redocly/redoc
|
||||||
x-logo:
|
x-logo:
|
||||||
url: 'https://rebilly.github.io/ReDoc/petstore-logo.png'
|
url: 'https://redocly.github.io/redoc/petstore-logo.png'
|
||||||
altText: Petstore logo
|
altText: Petstore logo
|
||||||
license:
|
license:
|
||||||
name: Apache 2.0
|
name: Apache 2.0
|
||||||
|
@ -63,6 +63,14 @@ tags:
|
||||||
description: Access to Petstore orders
|
description: Access to Petstore orders
|
||||||
- name: user
|
- name: user
|
||||||
description: Operations about user
|
description: Operations about user
|
||||||
|
- name: pet_model
|
||||||
|
x-displayName: The Pet Model
|
||||||
|
description: |
|
||||||
|
<SchemaDefinition schemaRef="#/components/schemas/Pet" />
|
||||||
|
- name: store_model
|
||||||
|
x-displayName: The Order Model
|
||||||
|
description: |
|
||||||
|
<SchemaDefinition schemaRef="#/components/schemas/Order" exampleRef="#/components/examples/Order" showReadOnly={true} showWriteOnly={true} />
|
||||||
x-tagGroups:
|
x-tagGroups:
|
||||||
- name: General
|
- name: General
|
||||||
tags:
|
tags:
|
||||||
|
@ -71,6 +79,10 @@ x-tagGroups:
|
||||||
- name: User Management
|
- name: User Management
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
|
- name: Models
|
||||||
|
tags:
|
||||||
|
- pet_model
|
||||||
|
- store_model
|
||||||
paths:
|
paths:
|
||||||
/pet:
|
/pet:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -296,6 +308,8 @@ paths:
|
||||||
style: form
|
style: form
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 3
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
|
@ -752,6 +766,11 @@ components:
|
||||||
description: Indicates whenever order was completed or not
|
description: Indicates whenever order was completed or not
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
readOnly: true
|
||||||
|
rqeuestId:
|
||||||
|
description: Unique Request Id
|
||||||
|
type: string
|
||||||
|
writeOnly: true
|
||||||
xml:
|
xml:
|
||||||
name: Order
|
name: Order
|
||||||
Pet:
|
Pet:
|
||||||
|
@ -767,6 +786,9 @@ components:
|
||||||
bee: '#/components/schemas/HoneyBee'
|
bee: '#/components/schemas/HoneyBee'
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
|
externalDocs:
|
||||||
|
description: "Find more info here"
|
||||||
|
url: "https://example.com"
|
||||||
description: Pet ID
|
description: Pet ID
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/Id'
|
- $ref: '#/components/schemas/Id'
|
||||||
|
@ -781,6 +803,7 @@ components:
|
||||||
photoUrls:
|
photoUrls:
|
||||||
description: The list of URL to a cute photos featuring pet
|
description: The list of URL to a cute photos featuring pet
|
||||||
type: array
|
type: array
|
||||||
|
maxItems: 20
|
||||||
xml:
|
xml:
|
||||||
name: photoUrl
|
name: photoUrl
|
||||||
wrapped: true
|
wrapped: true
|
||||||
|
@ -793,6 +816,7 @@ components:
|
||||||
tags:
|
tags:
|
||||||
description: Tags attached to the pet
|
description: Tags attached to the pet
|
||||||
type: array
|
type: array
|
||||||
|
minItems: 1
|
||||||
xml:
|
xml:
|
||||||
name: tag
|
name: tag
|
||||||
wrapped: true
|
wrapped: true
|
||||||
|
@ -859,12 +883,12 @@ components:
|
||||||
as well as digits
|
as well as digits
|
||||||
format: password
|
format: password
|
||||||
minLength: 8
|
minLength: 8
|
||||||
pattern: '(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])'
|
pattern: '/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/'
|
||||||
example: drowssaP123
|
example: drowssaP123
|
||||||
phone:
|
phone:
|
||||||
description: User phone number in international format
|
description: User phone number in international format
|
||||||
type: string
|
type: string
|
||||||
pattern: '^\+(?:[0-9]-?){6,14}[0-9]$'
|
pattern: '/^\+(?:[0-9]-?){6,14}[0-9]$/'
|
||||||
example: +1-202-555-0192
|
example: +1-202-555-0192
|
||||||
nullable: true
|
nullable: true
|
||||||
userStatus:
|
userStatus:
|
||||||
|
@ -919,3 +943,10 @@ components:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
name: api_key
|
name: api_key
|
||||||
in: header
|
in: header
|
||||||
|
examples:
|
||||||
|
Order:
|
||||||
|
value:
|
||||||
|
quantity: 1,
|
||||||
|
shipDate: 2018-10-19T16:46:45Z,
|
||||||
|
status: placed,
|
||||||
|
complete: false
|
||||||
|
|
|
@ -17,9 +17,12 @@ const renderRoot = (props: RedocProps) =>
|
||||||
);
|
);
|
||||||
|
|
||||||
const big = window.location.search.indexOf('big') > -1;
|
const big = window.location.search.indexOf('big') > -1;
|
||||||
const swagger = window.location.search.indexOf('swagger') > -1; // compatibility mode ?
|
const swagger = window.location.search.indexOf('swagger') > -1;
|
||||||
|
|
||||||
const specUrl = swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml';
|
const userUrl = window.location.search.match(/url=(.*)$/);
|
||||||
|
|
||||||
|
const specUrl =
|
||||||
|
(userUrl && userUrl[1]) || (swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml');
|
||||||
|
|
||||||
let store;
|
let store;
|
||||||
const options: RedocRawOptions = { nativeScrollbars: false };
|
const options: RedocRawOptions = { nativeScrollbars: false };
|
||||||
|
|
|
@ -14,14 +14,14 @@ info:
|
||||||
This API is documented in **OpenAPI format** and is based on
|
This API is documented in **OpenAPI format** and is based on
|
||||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||||
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
|
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
|
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||||
# OpenAPI Specification
|
# OpenAPI Specification
|
||||||
This API is documented in **OpenAPI format** and is based on
|
This API is documented in **OpenAPI format** and is based on
|
||||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||||
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
|
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
|
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||||
# Cross-Origin Resource Sharing
|
# Cross-Origin Resource Sharing
|
||||||
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
||||||
And that allows cross-domain communication from the browser.
|
And that allows cross-domain communication from the browser.
|
||||||
|
@ -39,9 +39,9 @@ info:
|
||||||
termsOfService: 'http://swagger.io/terms/'
|
termsOfService: 'http://swagger.io/terms/'
|
||||||
contact:
|
contact:
|
||||||
email: apiteam@swagger.io
|
email: apiteam@swagger.io
|
||||||
url: https://github.com/Rebilly/ReDoc
|
url: https://github.com/Redocly/redoc
|
||||||
x-logo:
|
x-logo:
|
||||||
url: 'https://rebilly.github.io/ReDoc/petstore-logo.png'
|
url: 'https://redocly.github.io/redoc/petstore-logo.png'
|
||||||
altText: Petstore logo
|
altText: Petstore logo
|
||||||
license:
|
license:
|
||||||
name: Apache 2.0
|
name: Apache 2.0
|
||||||
|
|
|
@ -22,6 +22,7 @@ const tsLoader = env => ({
|
||||||
options: {
|
options: {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
module: env.bench ? 'esnext' : 'es2015',
|
module: env.bench ? 'esnext' : 'es2015',
|
||||||
|
declaration: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -35,8 +36,8 @@ const babelLoader = mode => ({
|
||||||
plugins: compact([
|
plugins: compact([
|
||||||
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
||||||
['@babel/plugin-syntax-decorators', { legacy: true }],
|
['@babel/plugin-syntax-decorators', { legacy: true }],
|
||||||
|
'@babel/plugin-syntax-dynamic-import',
|
||||||
'@babel/plugin-syntax-jsx',
|
'@babel/plugin-syntax-jsx',
|
||||||
mode !== 'production' ? 'react-hot-loader/babel' : undefined,
|
|
||||||
[
|
[
|
||||||
'babel-plugin-styled-components',
|
'babel-plugin-styled-components',
|
||||||
{
|
{
|
||||||
|
@ -48,6 +49,13 @@ const babelLoader = mode => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const babelHotLoader = {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
plugins: ['react-hot-loader/babel'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) => ({
|
export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) => ({
|
||||||
entry: [
|
entry: [
|
||||||
root('../src/polyfills.ts'),
|
root('../src/polyfills.ts'),
|
||||||
|
@ -55,8 +63,8 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
env.playground
|
env.playground
|
||||||
? 'playground/hmr-playground.tsx'
|
? 'playground/hmr-playground.tsx'
|
||||||
: env.bench
|
: env.bench
|
||||||
? '../benchmark/index.tsx'
|
? '../benchmark/index.tsx'
|
||||||
: 'index.tsx',
|
: 'index.tsx',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
|
@ -75,6 +83,12 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||||
|
alias:
|
||||||
|
mode !== 'production'
|
||||||
|
? {
|
||||||
|
'react-dom': '@hot-loader/react-dom',
|
||||||
|
}
|
||||||
|
: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
node: {
|
node: {
|
||||||
|
@ -86,6 +100,9 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
externals: {
|
externals: {
|
||||||
esprima: 'esprima',
|
esprima: 'esprima',
|
||||||
'node-fetch': 'null',
|
'node-fetch': 'null',
|
||||||
|
'node-fetch-h2': 'null',
|
||||||
|
yaml: 'null',
|
||||||
|
'safe-json-stringify': 'null',
|
||||||
},
|
},
|
||||||
|
|
||||||
module: {
|
module: {
|
||||||
|
@ -94,8 +111,12 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.svg$/, /\.ttf$/], use: 'null-loader' },
|
{ test: [/\.eot$/, /\.gif$/, /\.woff$/, /\.svg$/, /\.ttf$/], use: 'null-loader' },
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
use: [tsLoader(env), babelLoader(mode)],
|
use: compact([
|
||||||
exclude: ['node_modules'],
|
mode !== 'production' ? babelHotLoader : undefined,
|
||||||
|
tsLoader(env),
|
||||||
|
babelLoader(mode),
|
||||||
|
]),
|
||||||
|
exclude: [/node_modules/],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
|
@ -103,12 +124,11 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
minimize: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /node_modules\/(swagger2openapi|reftools)\/.*\.js$/,
|
test: /node_modules\/(swagger2openapi|reftools|oas-resolver|oas-kit-common|oas-schema-walker)\/.*\.js$/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'ts-loader',
|
loader: 'ts-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@ -131,7 +151,11 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
new webpack.NamedModulesPlugin(),
|
new webpack.NamedModulesPlugin(),
|
||||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: env.playground ? 'demo/playground/index.html' : 'demo/index.html',
|
template: env.playground
|
||||||
|
? 'demo/playground/index.html'
|
||||||
|
: env.bench
|
||||||
|
? 'benchmark/index.html'
|
||||||
|
: 'demo/index.html',
|
||||||
}),
|
}),
|
||||||
new ForkTsCheckerWebpackPlugin(),
|
new ForkTsCheckerWebpackPlugin(),
|
||||||
ignore(/js-yaml\/dumper\.js$/),
|
ignore(/js-yaml\/dumper\.js$/),
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# ReDoc vendor extensions
|
# ReDoc vendor extensions
|
||||||
ReDoc makes use of the following [vendor extensions](http://swagger.io/specification/#vendorExtensions)
|
ReDoc makes use of the following [vendor extensions](https://swagger.io/specification/#specificationExtensions)
|
||||||
|
|
||||||
### Swagger Object vendor extensions
|
### Swagger Object vendor extensions
|
||||||
Extend OpenAPI root [Swagger Object](http://swagger.io/specification/#swaggerObject)
|
Extend OpenAPI root [Swagger Object](https://swagger.io/specification/#oasObject)
|
||||||
#### x-servers
|
#### x-servers
|
||||||
Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#server-object). Currently doesn't support templates.
|
Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#server-object). Currently doesn't support templates.
|
||||||
|
|
||||||
#### x-tagGroups
|
#### x-tagGroups
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ The information about API logo
|
||||||
| url | string | The URL pointing to the spec logo. MUST be in the format of a URL. It SHOULD be an absolute URL so your API definition is usable from any location
|
| url | string | The URL pointing to the spec logo. MUST be in the format of a URL. It SHOULD be an absolute URL so your API definition is usable from any location
|
||||||
| backgroundColor | string | background color to be used. MUST be RGB color in [hexadecimal format] (https://en.wikipedia.org/wiki/Web_colors#Hex_triplet)
|
| backgroundColor | string | background color to be used. MUST be RGB color in [hexadecimal format] (https://en.wikipedia.org/wiki/Web_colors#Hex_triplet)
|
||||||
| altText | string | Text to use for alt tag on the logo. Defaults to 'logo' if nothing is provided.
|
| altText | string | Text to use for alt tag on the logo. Defaults to 'logo' if nothing is provided.
|
||||||
|
| href | string | The URL pointing to the contact page. Default to 'info.contact.url' field of the OAS.
|
||||||
|
|
||||||
|
|
||||||
###### x-logo example
|
###### x-logo example
|
||||||
|
@ -106,7 +107,7 @@ json
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"title": "Swagger Petstore",
|
"title": "Swagger Petstore",
|
||||||
"x-logo": {
|
"x-logo": {
|
||||||
"url": "https://rebilly.github.io/ReDoc/petstore-logo.png",
|
"url": "https://redocly.github.io/redoc/petstore-logo.png",
|
||||||
"backgroundColor": "#FFFFFF",
|
"backgroundColor": "#FFFFFF",
|
||||||
"altText": "Petstore logo"
|
"altText": "Petstore logo"
|
||||||
}
|
}
|
||||||
|
@ -119,7 +120,7 @@ info:
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
title: "Swagger Petstore"
|
title: "Swagger Petstore"
|
||||||
x-logo:
|
x-logo:
|
||||||
url: "https://rebilly.github.io/ReDoc/petstore-logo.png"
|
url: "https://redocly.github.io/redoc/petstore-logo.png"
|
||||||
backgroundColor: "#FFFFFF"
|
backgroundColor: "#FFFFFF"
|
||||||
altText: "Petstore logo"
|
altText: "Petstore logo"
|
||||||
```
|
```
|
||||||
|
@ -131,14 +132,7 @@ 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 multiply tags. This property distinguish between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) |
|
| 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-displayName
|
|
||||||
|
|
||||||
| Field Name | Type | Description |
|
|
||||||
| :------------- | :------: | :---------- |
|
|
||||||
| x-displayName | string | Define the text that is used for this tag in the menu and in section headings |
|
|
||||||
|
|
||||||
|
|
||||||
###### Usage in Redoc
|
###### Usage in Redoc
|
||||||
Tags that have `x-traitTag` set to `true` are listed in side-menu but don't have any subitems (operations). Tag `description` is rendered as well.
|
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.
|
||||||
|
@ -160,6 +154,12 @@ description: Pagination description (can use markdown syntax)
|
||||||
x-traitTag: true
|
x-traitTag: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### x-displayName
|
||||||
|
|
||||||
|
| Field Name | Type | Description |
|
||||||
|
| :------------- | :------: | :---------- |
|
||||||
|
| 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 OpenAPI [Operation Object](http://swagger.io/specification/#operationObject)
|
||||||
#### x-code-samples
|
#### x-code-samples
|
||||||
|
@ -278,3 +278,31 @@ PayPalPayment:
|
||||||
|
|
||||||
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
|
||||||
|
**ATTENTION**: This is ReDoc-specific vendor extension. It won't be supported by other tools.
|
||||||
|
|
||||||
|
Extends the `additionalProperties` property of the schema object.
|
||||||
|
|
||||||
|
| Field Name | Type | Description |
|
||||||
|
| :------------- | :------: | :---------- |
|
||||||
|
| x-additionalPropertiesName | string | descriptive name of additional properties keys |
|
||||||
|
|
||||||
|
###### Usage in ReDoc
|
||||||
|
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
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Player:
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
additionalProperties:
|
||||||
|
x-additionalPropertiesName: attribute-name
|
||||||
|
type: string
|
||||||
|
```
|
||||||
|
|
24
docs/usage-with-ie11.md
Normal file
24
docs/usage-with-ie11.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Usage With IE11
|
||||||
|
|
||||||
|
|
||||||
|
## Standalone package
|
||||||
|
|
||||||
|
IE11 is supported by default if you use ReDoc as a standalone package.
|
||||||
|
|
||||||
|
## Usage as a React component
|
||||||
|
|
||||||
|
If you use ReDoc as a React component you should include the following polyfills in your project:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import 'core-js/es6/promise';
|
||||||
|
import 'core-js/fn/array/find';
|
||||||
|
import 'core-js/fn/object/assign';
|
||||||
|
import 'core-js/fn/string/ends-with';
|
||||||
|
import 'core-js/fn/string/starts-with';
|
||||||
|
|
||||||
|
import 'core-js/es6/map';
|
||||||
|
import 'core-js/es6/symbol';
|
||||||
|
|
||||||
|
import 'unfetch/polyfill/index'; // or any other fetch polyfill
|
||||||
|
import 'url-polyfill';
|
||||||
|
```
|
6
e2e/e2e.html
Normal file
6
e2e/e2e.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<script src="../bundles/redoc.standalone.js">{}</script>
|
||||||
|
<div id="redoc" />
|
||||||
|
</body>
|
||||||
|
</html>;
|
6
e2e/index.html
Normal file
6
e2e/index.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<script src="../bundles/redoc.standalone.js">{}</script>
|
||||||
|
<div id="redoc" />
|
||||||
|
</body>
|
||||||
|
</html>;
|
|
@ -6,7 +6,7 @@ describe('Menu', () => {
|
||||||
it('should have valid items count', () => {
|
it('should have valid items count', () => {
|
||||||
cy.get('.menu-content')
|
cy.get('.menu-content')
|
||||||
.find('li')
|
.find('li')
|
||||||
.should('have.length', 6 + (2 + 8 + 4) + (1 + 8));
|
.should('have.length', 6 + (2 + 8 + 1 + 4 + 2) + (1 + 8));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync active menu items while scroll', () => {
|
it('should sync active menu items while scroll', () => {
|
||||||
|
|
64
e2e/integration/misc.e2e.ts
Normal file
64
e2e/integration/misc.e2e.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// tslint:disable:no-implicit-dependencies
|
||||||
|
import * as yaml from 'yaml-js';
|
||||||
|
|
||||||
|
async function loadSpec(url: string): Promise<any> {
|
||||||
|
const spec = await (await fetch(url)).text();
|
||||||
|
return yaml.load(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initReDoc(win, spec, options = {}) {
|
||||||
|
(win as any).Redoc.init(spec, options, win.document.getElementById('redoc'));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Servers', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('e2e/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have valid server', () => {
|
||||||
|
cy.window().then(async win => {
|
||||||
|
const spec = await loadSpec('/demo/openapi.yaml');
|
||||||
|
initReDoc(win, spec, {});
|
||||||
|
|
||||||
|
// TODO add cy-data attributes
|
||||||
|
cy.get('[data-section-id="operation/addPet"]').should(
|
||||||
|
'contain',
|
||||||
|
'http://petstore.swagger.io/v2/pet',
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get('[data-section-id="operation/addPet"]').should(
|
||||||
|
'contain',
|
||||||
|
'http://petstore.swagger.io/sandbox/pet',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have valid server for when servers not provided', () => {
|
||||||
|
cy.window().then(async win => {
|
||||||
|
const spec = await loadSpec('/demo/openapi.yaml');
|
||||||
|
delete spec.servers;
|
||||||
|
initReDoc(win, spec, {});
|
||||||
|
|
||||||
|
// TODO add cy-data attributes
|
||||||
|
cy.get('[data-section-id="operation/addPet"]').should(
|
||||||
|
'contain',
|
||||||
|
'http://localhost:' + win.location.port + '/e2e/pet',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have valid server for when servers not provided at .html pages', () => {
|
||||||
|
cy.visit('e2e/e2e.html');
|
||||||
|
cy.window().then(async win => {
|
||||||
|
const spec = await loadSpec('/demo/openapi.yaml');
|
||||||
|
delete spec.servers;
|
||||||
|
initReDoc(win, spec, {});
|
||||||
|
|
||||||
|
// TODO add cy-data attributes
|
||||||
|
cy.get('[data-section-id="operation/addPet"]').should(
|
||||||
|
'contain',
|
||||||
|
'http://localhost:' + win.location.port + '/e2e/pet',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
218
package.json
218
package.json
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.0.0-alpha.38",
|
"version": "2.0.0-rc.14",
|
||||||
"description": "ReDoc",
|
"description": "ReDoc",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/Rebilly/ReDoc"
|
"url": "git://github.com/Redocly/redoc"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9",
|
"node": ">=6.9",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
"start": "webpack-dev-server --mode=development --env.playground --hot --config demo/webpack.config.ts",
|
"start": "webpack-dev-server --mode=development --env.playground --hot --config demo/webpack.config.ts",
|
||||||
"start:prod": "webpack-dev-server --env.playground --mode=production --config demo/webpack.config.ts",
|
"start:prod": "webpack-dev-server --env.playground --mode=production --config demo/webpack.config.ts",
|
||||||
"start:benchmark": "webpack-dev-server --mode=production --env.bench --config demo/webpack.config.ts",
|
"start:benchmark": "webpack-dev-server --mode=production --env.bench --config demo/webpack.config.ts",
|
||||||
"test": "npm run lint && npm run unit && npm run bundlesize && npm run license-check",
|
"test": "npm run lint && npm run unit && npm run license-check",
|
||||||
"unit": "jest --coverage",
|
"unit": "jest --coverage",
|
||||||
"e2e": "cypress run",
|
"e2e": "cypress run",
|
||||||
"e2e-ci": "cypress run --record",
|
"e2e-ci": "cypress run --record",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"cy:open": "cypress open",
|
"cy:open": "cypress open",
|
||||||
"bundle:clean": "rimraf bundles",
|
"bundle:clean": "rimraf bundles",
|
||||||
"bundle:standalone": "webpack --env.standalone --mode=production",
|
"bundle:standalone": "webpack --env.standalone --mode=production",
|
||||||
"bundle:lib": "webpack --mode=production",
|
"bundle:lib": "webpack --mode=production && npm run declarations",
|
||||||
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:standalone",
|
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:standalone",
|
||||||
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
|
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
|
||||||
"stats": "webpack --env.standalone --json --profile --mode=production > stats.json",
|
"stats": "webpack --env.standalone --json --profile --mode=production > stats.json",
|
||||||
|
@ -48,111 +48,115 @@
|
||||||
"compile:cli": "tsc custom.d.ts cli/index.ts --target es6 --module commonjs --types yargs",
|
"compile:cli": "tsc custom.d.ts cli/index.ts --target es6 --module commonjs --types yargs",
|
||||||
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
||||||
"deploy:demo": "npm run build:demo && deploy-to-gh-pages --update demo/dist",
|
"deploy:demo": "npm run build:demo && deploy-to-gh-pages --update demo/dist",
|
||||||
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause' --summary"
|
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause' --summary",
|
||||||
|
"docker:build": "docker build -f config/docker/Dockerfile -t redoc ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.0.0-rc.2",
|
"@babel/core": "7.5.5",
|
||||||
"@babel/plugin-syntax-decorators": "7.0.0-rc.2",
|
"@babel/plugin-syntax-decorators": "7.2.0",
|
||||||
"@babel/plugin-syntax-jsx": "7.0.0-rc.2",
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
"@babel/plugin-syntax-typescript": "7.0.0-rc.2",
|
"@babel/plugin-syntax-jsx": "7.2.0",
|
||||||
"@cypress/webpack-preprocessor": "2.0.1",
|
"@babel/plugin-syntax-typescript": "7.3.3",
|
||||||
"@types/chai": "4.1.4",
|
"@cypress/webpack-preprocessor": "4.1.0",
|
||||||
"@types/dompurify": "^0.0.31",
|
"@hot-loader/react-dom": "^16.8.6",
|
||||||
"@types/enzyme": "^3.1.13",
|
"@types/chai": "4.1.7",
|
||||||
"@types/enzyme-to-json": "^1.5.2",
|
"@types/dompurify": "^0.0.33",
|
||||||
"@types/jest": "^23.3.1",
|
"@types/enzyme": "^3.10.3",
|
||||||
|
"@types/enzyme-to-json": "^1.5.3",
|
||||||
|
"@types/jest": "^24.0.15",
|
||||||
"@types/json-pointer": "^1.0.30",
|
"@types/json-pointer": "^1.0.30",
|
||||||
"@types/lodash": "^4.14.116",
|
"@types/lodash": "^4.14.136",
|
||||||
"@types/lunr": "^2.1.6",
|
"@types/lunr": "^2.3.2",
|
||||||
"@types/mark.js": "^8.11.1",
|
"@types/mark.js": "^8.11.4",
|
||||||
"@types/marked": "^0.4.1",
|
"@types/marked": "^0.6.5",
|
||||||
"@types/prismjs": "^1.6.4",
|
"@types/prismjs": "^1.16.0",
|
||||||
"@types/prop-types": "^15.5.5",
|
"@types/prop-types": "^15.7.1",
|
||||||
"@types/react": "^16.4.11",
|
"@types/react": "^16.8.23",
|
||||||
"@types/react-dom": "^16.0.7",
|
"@types/react-dom": "^16.8.5",
|
||||||
"@types/react-hot-loader": "^4.1.0",
|
"@types/react-hot-loader": "^4.1.0",
|
||||||
"@types/react-tabs": "^1.0.5",
|
"@types/react-tabs": "^2.3.1",
|
||||||
|
"@types/styled-components": "^4.1.18",
|
||||||
"@types/tapable": "1.0.4",
|
"@types/tapable": "1.0.4",
|
||||||
"@types/webpack": "^4.4.11",
|
"@types/webpack": "^4.32.1",
|
||||||
"@types/webpack-env": "^1.13.0",
|
"@types/webpack-env": "^1.14.0",
|
||||||
"@types/yargs": "^11.1.1",
|
"@types/yargs": "^13.0.0",
|
||||||
"babel-loader": "8.0.0-beta.2",
|
"babel-loader": "8.0.6",
|
||||||
"babel-plugin-styled-components": "^1.5.1",
|
"babel-plugin-styled-components": "^1.10.6",
|
||||||
"beautify-benchmark": "^0.2.4",
|
"beautify-benchmark": "^0.2.4",
|
||||||
"bundlesize": "^0.17.0",
|
"bundlesize": "^0.18.0",
|
||||||
"conventional-changelog-cli": "^2.0.5",
|
"conventional-changelog-cli": "^2.0.23",
|
||||||
"copy-webpack-plugin": "^4.5.2",
|
"copy-webpack-plugin": "^5.0.4",
|
||||||
"core-js": "^2.5.7",
|
"core-js": "^3.1.4",
|
||||||
"coveralls": "^3.0.2",
|
"coveralls": "^3.0.5",
|
||||||
"css-loader": "^1.0.0",
|
"css-loader": "^3.1.0",
|
||||||
"cypress": "~3.1.0",
|
"cypress": "~3.4.0",
|
||||||
"deploy-to-gh-pages": "^1.3.6",
|
"deploy-to-gh-pages": "^1.3.7",
|
||||||
"enzyme": "^3.4.4",
|
"enzyme": "^3.10.0",
|
||||||
"enzyme-adapter-react-16": "^1.2.0",
|
"enzyme-adapter-react-16": "^1.14.0",
|
||||||
"enzyme-to-json": "^3.3.4",
|
"enzyme-to-json": "^3.3.5",
|
||||||
"fork-ts-checker-webpack-plugin": "0.4.3",
|
"fork-ts-checker-webpack-plugin": "1.4.3",
|
||||||
"html-webpack-plugin": "^3.1.0",
|
"html-webpack-plugin": "^3.1.0",
|
||||||
"jest": "^23.5.0",
|
"jest": "^24.8.0",
|
||||||
"license-checker": "^20.2.0",
|
"license-checker": "^25.0.1",
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.15",
|
||||||
"mobx": "^4.3.1",
|
"mobx": "^4.3.1",
|
||||||
"prettier": "^1.14.2",
|
"prettier": "^1.18.2",
|
||||||
"prettier-eslint": "^8.8.2",
|
"prettier-eslint": "^9.0.0",
|
||||||
"puppeteer": "^1.7.0",
|
"raf": "^3.4.1",
|
||||||
"raf": "^3.4.0",
|
"react": "^16.8.6",
|
||||||
"react": "^16.4.2",
|
"react-dom": "^16.8.6",
|
||||||
"react-dom": "^16.4.2",
|
"rimraf": "^2.6.3",
|
||||||
"rimraf": "^2.6.2",
|
"shelljs": "^0.8.3",
|
||||||
"shelljs": "^0.8.1",
|
|
||||||
"source-map-loader": "^0.2.4",
|
"source-map-loader": "^0.2.4",
|
||||||
"style-loader": "^0.22.1",
|
"style-loader": "^0.23.1",
|
||||||
"swagger2openapi": "^3.2.8",
|
"styled-components": "^4.3.2",
|
||||||
"ts-jest": "23.0.1",
|
"ts-jest": "24.0.2",
|
||||||
"ts-loader": "4.5.0",
|
"ts-loader": "6.0.4",
|
||||||
"ts-node": "^7.0.1",
|
"ts-node": "^8.3.0",
|
||||||
"tslint": "^5.11.0",
|
"tslint": "^5.18.0",
|
||||||
"tslint-react": "^3.4.0",
|
"tslint-react": "^4.0.0",
|
||||||
"typescript": "^3.0.1",
|
"typescript": "^3.5.3",
|
||||||
"webpack": "^4.17.1",
|
"unfetch": "^4.1.0",
|
||||||
"webpack-cli": "^3.1.0",
|
"url-polyfill": "^1.1.7",
|
||||||
"webpack-dev-server": "^3.1.5",
|
"webpack": "^4.38.0",
|
||||||
|
"webpack-cli": "^3.3.6",
|
||||||
|
"webpack-dev-server": "^3.7.2",
|
||||||
"webpack-node-externals": "^1.6.0",
|
"webpack-node-externals": "^1.6.0",
|
||||||
"workerize-loader": "^1.0.3",
|
"workerize-loader": "^1.0.4",
|
||||||
"yaml-js": "^0.2.3"
|
"yaml-js": "^0.2.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"mobx": "^4.2.0",
|
"core-js": "^3.1.4",
|
||||||
"react": "^16.2.0",
|
"mobx": "^4.2.0 || ^5.0.0",
|
||||||
"react-dom": "^16.2.0"
|
"react": "^16.8.4",
|
||||||
|
"react-dom": "^16.8.4",
|
||||||
|
"styled-components": "^4.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"decko": "^1.2.0",
|
"decko": "^1.2.0",
|
||||||
"dompurify": "^1.0.7",
|
"dompurify": "^1.0.11",
|
||||||
"eventemitter3": "^3.0.0",
|
"eventemitter3": "^4.0.0",
|
||||||
"json-pointer": "^0.6.0",
|
"json-pointer": "^0.6.0",
|
||||||
"json-schema-ref-parser": "^5.1.2",
|
"json-schema-ref-parser": "^6.1.0",
|
||||||
"lunr": "^2.3.2",
|
"lunr": "2.3.6",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "0.3.18",
|
"marked": "^0.7.0",
|
||||||
"memoize-one": "^4.0.0",
|
"memoize-one": "^5.0.5",
|
||||||
"mobx-react": "^5.2.5",
|
"mobx-react": "^6.1.1",
|
||||||
"openapi-sampler": "1.0.0-beta.14",
|
"openapi-sampler": "1.0.0-beta.15",
|
||||||
"perfect-scrollbar": "^1.4.0",
|
"perfect-scrollbar": "^1.4.0",
|
||||||
"polished": "^2.0.2",
|
"polished": "^3.4.1",
|
||||||
"prismjs": "^1.15.0",
|
"prismjs": "^1.17.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.7.2",
|
||||||
"react-dropdown": "^1.6.2",
|
"react-dropdown": "^1.6.4",
|
||||||
"react-hot-loader": "^4.3.5",
|
"react-hot-loader": "^4.12.10",
|
||||||
"react-tabs": "^2.0.0",
|
"react-tabs": "^3.0.0",
|
||||||
"slugify": "^1.3.1",
|
"slugify": "^1.3.4",
|
||||||
"stickyfill": "^1.1.1",
|
"stickyfill": "^1.1.1",
|
||||||
"styled-components": "^3.4.5",
|
"swagger2openapi": "^5.3.1",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.10.0",
|
||||||
},
|
"uri-template-lite": "^19.4.0"
|
||||||
"resolutions": {
|
|
||||||
"@types/chai": "4.0.8",
|
|
||||||
"@types/tapable": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"bundlesize": [
|
"bundlesize": [
|
||||||
{
|
{
|
||||||
|
@ -161,30 +165,10 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"jest": {
|
"jest": {
|
||||||
"transform": {
|
"setupFilesAfterEnv": [
|
||||||
"^.+\\.tsx?$": "ts-jest"
|
"<rootDir>/src/setupTests.ts"
|
||||||
},
|
|
||||||
"setupTestFrameworkScriptFile": "<rootDir>/src/setupTests.ts",
|
|
||||||
"testPathIgnorePatterns": [
|
|
||||||
"/node_modules/",
|
|
||||||
"/benchmark/"
|
|
||||||
],
|
],
|
||||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
"preset": "ts-jest",
|
||||||
"moduleFileExtensions": [
|
|
||||||
"ts",
|
|
||||||
"tsx",
|
|
||||||
"js",
|
|
||||||
"jsx",
|
|
||||||
"json"
|
|
||||||
],
|
|
||||||
"moduleNameMapper": {
|
|
||||||
"\\.(css|less)$": "<rootDir>/src/empty.js"
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"ts-jest": {
|
|
||||||
"skipBabel": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
"src/**/*.{ts,tsx}"
|
"src/**/*.{ts,tsx}"
|
||||||
],
|
],
|
||||||
|
@ -194,8 +178,16 @@
|
||||||
"text-summary"
|
"text-summary"
|
||||||
],
|
],
|
||||||
"coveragePathIgnorePatterns": [
|
"coveragePathIgnorePatterns": [
|
||||||
"\\.d\\.ts$"
|
"\\.d\\.ts$",
|
||||||
]
|
"/benchmark/",
|
||||||
|
"/node_modules/"
|
||||||
|
],
|
||||||
|
"modulePathIgnorePatterns": [
|
||||||
|
"/benchmark/"
|
||||||
|
],
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"\\.(css|less)$": "<rootDir>/src/empty.js"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
|
|
|
@ -5,11 +5,7 @@ import { ClipboardService } from '../services/ClipboardService';
|
||||||
|
|
||||||
export interface CopyButtonWrapperProps {
|
export interface CopyButtonWrapperProps {
|
||||||
data: any;
|
data: any;
|
||||||
children: (
|
children: (props: { renderCopyButton: () => React.ReactNode }) => React.ReactNode;
|
||||||
props: {
|
|
||||||
renderCopyButton: (() => React.ReactNode);
|
|
||||||
},
|
|
||||||
) => React.ReactNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CopyButtonWrapper extends React.PureComponent<
|
export class CopyButtonWrapper extends React.PureComponent<
|
||||||
|
|
|
@ -19,6 +19,7 @@ const Tip = styled.div`
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 0.3em 0.6em;
|
padding: 0.3em 0.6em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
box-shadow: 0px 0px 5px 0px rgba(204, 204, 204, 1);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Content = styled.div`
|
const Content = styled.div`
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import Dropdown from 'react-dropdown';
|
import Dropdown from 'react-dropdown';
|
||||||
|
|
||||||
import { StyledComponentClass } from 'styled-components';
|
import styled from '../styled-components';
|
||||||
import styled, { withProps } from '../styled-components';
|
|
||||||
|
|
||||||
export interface DropdownOption {
|
export interface DropdownOption {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -14,17 +13,17 @@ export interface DropdownProps {
|
||||||
onChange: (val: DropdownOption) => void;
|
onChange: (val: DropdownOption) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StyledDropdown = withProps<DropdownProps>(styled(Dropdown))`
|
export const StyledDropdown = styled(Dropdown)`
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: auto;
|
width: auto;
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||||
|
|
||||||
.Dropdown-control {
|
.Dropdown-control {
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: .929em;
|
font-size: 0.929em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@ -39,6 +38,8 @@ export const StyledDropdown = withProps<DropdownProps>(styled(Dropdown))`
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
background: white;
|
background: white;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: ${props => props.theme.colors.primary.main};
|
border-color: ${props => props.theme.colors.primary.main};
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${props => props.theme.colors.primary.main};
|
||||||
|
@ -54,7 +55,7 @@ export const StyledDropdown = withProps<DropdownProps>(styled(Dropdown))`
|
||||||
display: block;
|
display: block;
|
||||||
height: 0;
|
height: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.35em;
|
right: 0.6em;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -0.125em;
|
margin-top: -0.125em;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
@ -84,14 +85,14 @@ export const StyledDropdown = withProps<DropdownProps>(styled(Dropdown))`
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
|
|
||||||
&.is-selected {
|
&.is-selected {
|
||||||
background-color: rgba(0, 0, 0, 0.05)
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(38, 50, 56, 0.12)
|
background-color: rgba(38, 50, 56, 0.12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
` as StyledComponentClass<any, DropdownProps>;
|
`;
|
||||||
|
|
||||||
export const SimpleDropdown = styled(StyledDropdown)`
|
export const SimpleDropdown = styled(StyledDropdown)`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
@ -105,7 +106,7 @@ export const SimpleDropdown = styled(StyledDropdown)`
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: ${props => props.theme.colors.main};
|
color: ${props => props.theme.colors.primary.main};
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
// import { transparentize } from 'polished';
|
// import { transparentize } from 'polished';
|
||||||
|
|
||||||
import styled, { extensionsHook, withProps } from '../styled-components';
|
import styled, { extensionsHook } from '../styled-components';
|
||||||
import { deprecatedCss } from './mixins';
|
import { deprecatedCss } from './mixins';
|
||||||
|
|
||||||
export const PropertyCell = styled.td`
|
export const PropertiesTableCaption = styled.caption`
|
||||||
|
text-align: right;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-weight: normal;
|
||||||
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PropertyCell = styled.td<{ kind?: string }>`
|
||||||
border-left: 1px solid ${props => props.theme.schema.linesColor};
|
border-left: 1px solid ${props => props.theme.schema.linesColor};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -51,7 +58,7 @@ export const PropertyCellWithInner = styled(PropertyCell)`
|
||||||
padding: 0;
|
padding: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PropertyNameCell = withProps<{ kind?: string }>(styled(PropertyCell))`
|
export const PropertyNameCell = styled(PropertyCell)`
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -18,7 +18,7 @@ export const ClickablePropertyNameCell = styled(PropertyNameCell)`
|
||||||
|
|
||||||
export const FieldLabel = styled.span`
|
export const FieldLabel = styled.span`
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
font-size: 0.929em;
|
font-size: ${({ theme }) => theme.typography.code.fontSize};
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ export const TypeName = styled(FieldLabel)`
|
||||||
|
|
||||||
export const TypeTitle = styled(FieldLabel)`
|
export const TypeTitle = styled(FieldLabel)`
|
||||||
color: ${props => props.theme.schema.typeTitleColor};
|
color: ${props => props.theme.schema.typeTitleColor};
|
||||||
|
word-break: break-word;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TypeFormat = TypeName;
|
export const TypeFormat = TypeName;
|
||||||
|
@ -58,7 +59,6 @@ export const PatternLabel = styled(FieldLabel)`
|
||||||
color: #3195a6;
|
color: #3195a6;
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
content: '/';
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -69,9 +69,10 @@ export const ExampleValue = styled(FieldLabel)`
|
||||||
background-color: ${transparentize(0.95, theme.colors.text.primary)};
|
background-color: ${transparentize(0.95, theme.colors.text.primary)};
|
||||||
color: ${transparentize(0.1, theme.colors.text.primary)};
|
color: ${transparentize(0.1, theme.colors.text.primary)};
|
||||||
|
|
||||||
margin: ${theme.spacing.unit}px;
|
|
||||||
padding: 0 ${theme.spacing.unit}px;
|
padding: 0 ${theme.spacing.unit}px;
|
||||||
border: 1px solid ${transparentize(0.9, theme.colors.text.primary)};
|
border: 1px solid ${transparentize(0.9, theme.colors.text.primary)};
|
||||||
|
font-family: ${theme.typography.code.fontFamily};
|
||||||
|
color: ${theme.typography.code.color};
|
||||||
}`};
|
}`};
|
||||||
& + & {
|
& + & {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
@ -79,6 +80,8 @@ export const ExampleValue = styled(FieldLabel)`
|
||||||
${extensionsHook('ExampleValue')};
|
${extensionsHook('ExampleValue')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ExtensionValue = styled(ExampleValue)``;
|
||||||
|
|
||||||
export const ConstraintItem = styled(FieldLabel)`
|
export const ConstraintItem = styled(FieldLabel)`
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
${({ theme }) => `
|
${({ theme }) => `
|
||||||
|
|
|
@ -7,14 +7,15 @@ const headerFontSize = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const headerCommonMixin = level => css`
|
export const headerCommonMixin = level => css`
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${({ theme }) => theme.typography.headings.fontFamily};
|
||||||
font-weight: 400;
|
font-weight: ${({ theme }) => theme.typography.headings.fontWeight};
|
||||||
font-size: ${headerFontSize[level]};
|
font-size: ${headerFontSize[level]};
|
||||||
|
line-height: ${({ theme }) => theme.typography.headings.lineHeight};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const H1 = styled.h1`
|
export const H1 = styled.h1`
|
||||||
${headerCommonMixin(1)};
|
${headerCommonMixin(1)};
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${({ theme }) => theme.colors.primary.main};
|
||||||
|
|
||||||
${extensionsHook('H1')};
|
${extensionsHook('H1')};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,23 +1,34 @@
|
||||||
import { SECTION_ATTR } from '../services/MenuStore';
|
import { SECTION_ATTR } from '../services/MenuStore';
|
||||||
import styled, { media, withProps } from '../styled-components';
|
import styled, { media } from '../styled-components';
|
||||||
|
|
||||||
export const MiddlePanel = styled.div`
|
export const MiddlePanel = styled.div`
|
||||||
width: calc(100% - ${props => props.theme.rightPanel.width});
|
width: calc(100% - ${props => props.theme.rightPanel.width});
|
||||||
padding: 0 ${props => props.theme.spacing.unit * 8}px;
|
padding: 0 ${props => props.theme.spacing.sectionHorizontal}px;
|
||||||
|
|
||||||
${media.lessThan('medium')`
|
${media.lessThan('medium', true)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: ${props =>
|
||||||
|
`${props.theme.spacing.sectionVertical}px ${props.theme.spacing.sectionHorizontal}px`};
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Section = withProps<{ underlined?: boolean }>(
|
export const Section = styled.div.attrs(props => ({
|
||||||
styled.div.attrs({
|
[SECTION_ATTR]: props.id,
|
||||||
[SECTION_ATTR]: props => props.id,
|
}))<{ underlined?: boolean }>`
|
||||||
} as any),
|
padding: ${props => props.theme.spacing.sectionVertical}px 0;
|
||||||
)`
|
|
||||||
padding: ${props => props.theme.spacing.unit * 8}px 0;
|
|
||||||
|
|
||||||
${props =>
|
&:last-child {
|
||||||
|
min-height: calc(100vh + 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > &:last-child {
|
||||||
|
min-height: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
${media.lessThan('medium', true)`
|
||||||
|
padding: 0;
|
||||||
|
`}
|
||||||
|
${(props: any) =>
|
||||||
(props.underlined &&
|
(props.underlined &&
|
||||||
`
|
`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -36,12 +47,14 @@ export const Section = withProps<{ underlined?: boolean }>(
|
||||||
|
|
||||||
export const RightPanel = styled.div`
|
export const RightPanel = styled.div`
|
||||||
width: ${props => props.theme.rightPanel.width};
|
width: ${props => props.theme.rightPanel.width};
|
||||||
color: #fafbfc;
|
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
background-color: ${props => props.theme.rightPanel.backgroundColor};
|
background-color: ${props => props.theme.rightPanel.backgroundColor};
|
||||||
padding: 0 ${props => props.theme.spacing.unit * 8}px;
|
padding: 0 ${props => props.theme.spacing.sectionHorizontal}px;
|
||||||
|
|
||||||
${media.lessThan('medium')`
|
${media.lessThan('medium', true)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: ${props =>
|
||||||
|
`${props.theme.spacing.sectionVertical}px ${props.theme.spacing.sectionHorizontal}px`};
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -54,7 +67,7 @@ export const Row = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
${media.lessThan('medium')`
|
${media.lessThan('medium', true)`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import PerfectScrollbarType, * as PerfectScrollbarNamespace from 'perfect-scroll
|
||||||
import psStyles from 'perfect-scrollbar/css/perfect-scrollbar.css';
|
import psStyles from 'perfect-scrollbar/css/perfect-scrollbar.css';
|
||||||
|
|
||||||
import { OptionsContext } from '../components/OptionsProvider';
|
import { OptionsContext } from '../components/OptionsProvider';
|
||||||
import styled, { injectGlobal } from '../styled-components';
|
import styled, { createGlobalStyle } from '../styled-components';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* perfect scrollbar umd bundle uses exports assignment while module uses default export
|
* perfect scrollbar umd bundle uses exports assignment while module uses default export
|
||||||
|
@ -14,7 +14,7 @@ import styled, { injectGlobal } from '../styled-components';
|
||||||
const PerfectScrollbarConstructor =
|
const PerfectScrollbarConstructor =
|
||||||
PerfectScrollbarNamespace.default || ((PerfectScrollbarNamespace as any) as PerfectScrollbarType);
|
PerfectScrollbarNamespace.default || ((PerfectScrollbarNamespace as any) as PerfectScrollbarType);
|
||||||
|
|
||||||
injectGlobal`${psStyles && psStyles.toString()}`;
|
const PSStyling = createGlobalStyle`${psStyles && psStyles.toString()}`;
|
||||||
|
|
||||||
const StyledScrollWrapper = styled.div`
|
const StyledScrollWrapper = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -58,9 +58,12 @@ export class PerfectScrollbar extends React.Component<PerfectScrollbarProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledScrollWrapper className={`scrollbar-container ${className}`} innerRef={this.handleRef}>
|
<>
|
||||||
{children}
|
<PSStyling />
|
||||||
</StyledScrollWrapper>
|
<StyledScrollWrapper className={`scrollbar-container ${className}`} ref={this.handleRef}>
|
||||||
|
{children}
|
||||||
|
</StyledScrollWrapper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import styled from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
import { PrismDiv } from './PrismDiv';
|
||||||
|
|
||||||
export const SampleControls = styled.div`
|
export const SampleControls = styled.div`
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
@ -21,3 +22,12 @@ export const SampleControlsWrap = styled.div`
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const StyledPre = styled(PrismDiv.withComponent('pre'))`
|
||||||
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
|
`;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import styled, { withProps } from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
|
||||||
export const OneOfList = styled.ul`
|
export const OneOfList = styled.ul`
|
||||||
margin: 0;
|
margin: 0 0 3px 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -15,7 +15,7 @@ export const OneOfLabel = styled.span`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const OneOfButton = withProps<{ active: boolean }>(styled.li)`
|
export const OneOfButton = styled.li<{ active: boolean }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled, { withProps } from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
|
||||||
const directionMap = {
|
const directionMap = {
|
||||||
left: '90deg',
|
left: '90deg',
|
||||||
|
@ -48,7 +48,7 @@ export const ShelfIcon = styled(IntShelfIcon)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Badge = withProps<{ type: string }>(styled.span)`
|
export const Badge = styled.span<{ type: string }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { darken } from 'polished';
|
||||||
import { Tabs as ReactTabs } from 'react-tabs';
|
import { Tabs as ReactTabs } from 'react-tabs';
|
||||||
|
|
||||||
import styled from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
|
||||||
export { Tab, TabList, TabPanel } from 'react-tabs';
|
export { Tab, TabList, TabPanel } from 'react-tabs';
|
||||||
|
@ -14,14 +16,15 @@ export const Tabs = styled(ReactTabs)`
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: ${({ theme }) => theme.codeSample.backgroundColor};
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.5);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.5);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
outline: none;
|
outline: none;
|
||||||
color: #ccc;
|
color: ${({ theme }) => darken(theme.colors.tonalOffset, theme.rightPanel.textColor)};
|
||||||
margin: 5px;
|
margin: 0
|
||||||
border: 1px solid #181f22;
|
${({ theme }) => `${theme.spacing.unit}px ${theme.spacing.unit}px ${theme.spacing.unit}px`};
|
||||||
|
border: 1px solid ${({ theme }) => darken(0.05, theme.codeSample.backgroundColor)};
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
@ -29,7 +32,7 @@ export const Tabs = styled(ReactTabs)`
|
||||||
|
|
||||||
&.react-tabs__tab--selected {
|
&.react-tabs__tab--selected {
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${props => props.theme.colors.text.primary};
|
||||||
background: #e2e2e2;
|
background: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
&:only-child {
|
&:only-child {
|
||||||
|
@ -55,12 +58,16 @@ export const Tabs = styled(ReactTabs)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> .react-tabs__tab-panel {
|
> .react-tabs__tab-panel {
|
||||||
background: #171e21;
|
background: ${({ theme }) => theme.codeSample.backgroundColor};
|
||||||
& > div,
|
& > div,
|
||||||
& > pre {
|
& > pre {
|
||||||
padding: 20px;
|
padding: ${props => props.theme.spacing.unit * 4}px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > div > pre {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -74,7 +81,7 @@ export const SmallTabs = styled(Tabs)`
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
border-bottom: 1px dashed;
|
border-bottom: 1px dashed;
|
||||||
color: #787b7d;
|
color: ${({ theme }) => darken(theme.colors.tonalOffset, theme.rightPanel.textColor)};
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
background: none;
|
background: none;
|
||||||
|
|
||||||
|
@ -83,7 +90,7 @@ export const SmallTabs = styled(Tabs)`
|
||||||
}
|
}
|
||||||
|
|
||||||
&.react-tabs__tab--selected {
|
&.react-tabs__tab--selected {
|
||||||
color: #babcbf;
|
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,8 +98,7 @@ export const SmallTabs = styled(Tabs)`
|
||||||
> .react-tabs__tab-panel {
|
> .react-tabs__tab-panel {
|
||||||
& > div,
|
& > div,
|
||||||
& > pre {
|
& > pre {
|
||||||
padding: 10px 0;
|
padding: ${props => props.theme.spacing.unit * 2}px 0;
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import * as React from 'react';
|
||||||
import { AppStore } from '../../services/AppStore';
|
import { AppStore } from '../../services/AppStore';
|
||||||
|
|
||||||
import { MiddlePanel, Row, Section } from '../../common-elements/';
|
import { MiddlePanel, Row, Section } from '../../common-elements/';
|
||||||
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
||||||
import {
|
import {
|
||||||
|
@ -43,22 +44,20 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const website =
|
const website =
|
||||||
(info.contact &&
|
(info.contact && info.contact.url && (
|
||||||
info.contact.url && (
|
<InfoSpan>
|
||||||
<InfoSpan>
|
URL: <a href={info.contact.url}>{info.contact.url}</a>
|
||||||
URL: <a href={info.contact.url}>{info.contact.url}</a>
|
</InfoSpan>
|
||||||
</InfoSpan>
|
)) ||
|
||||||
)) ||
|
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const email =
|
const email =
|
||||||
(info.contact &&
|
(info.contact && info.contact.email && (
|
||||||
info.contact.email && (
|
<InfoSpan>
|
||||||
<InfoSpan>
|
{info.contact.name || 'E-mail'}:{' '}
|
||||||
{info.contact.name || 'E-mail'}:{' '}
|
<a href={'mailto:' + info.contact.email}>{info.contact.email}</a>
|
||||||
<a href={'mailto:' + info.contact.email}>{info.contact.email}</a>
|
</InfoSpan>
|
||||||
</InfoSpan>
|
)) ||
|
||||||
)) ||
|
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const terms =
|
const terms =
|
||||||
|
@ -69,12 +68,14 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
)) ||
|
)) ||
|
||||||
null;
|
null;
|
||||||
|
|
||||||
|
const version = (info.version && <span>({info.version})</span>) || null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
<Row>
|
<Row>
|
||||||
<MiddlePanel className="api-info">
|
<MiddlePanel className="api-info">
|
||||||
<ApiHeader>
|
<ApiHeader>
|
||||||
{info.title} <span>({info.version})</span>
|
{info.title} {version}
|
||||||
</ApiHeader>
|
</ApiHeader>
|
||||||
{!hideDownloadButton && (
|
{!hideDownloadButton && (
|
||||||
<p>
|
<p>
|
||||||
|
@ -98,15 +99,9 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
</InfoSpanBoxWrap>
|
</InfoSpanBoxWrap>
|
||||||
)) ||
|
)) ||
|
||||||
null}
|
null}
|
||||||
|
|
||||||
{(externalDocs && (
|
|
||||||
<p>
|
|
||||||
<a href={externalDocs.url}>{externalDocs.description || externalDocs.url}</a>
|
|
||||||
</p>
|
|
||||||
)) ||
|
|
||||||
null}
|
|
||||||
</StyledMarkdownBlock>
|
</StyledMarkdownBlock>
|
||||||
<Markdown source={store.spec.info.description} />
|
<Markdown source={store.spec.info.description} />
|
||||||
|
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
</Row>
|
</Row>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
|
@ -12,19 +12,15 @@ export class ApiLogo extends React.Component<{ info: OpenAPIInfo }> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logoHref = logoInfo.href || (info.contact && info.contact.url);
|
||||||
|
|
||||||
// Use the english word logo if no alt text is provided
|
// Use the english word logo if no alt text is provided
|
||||||
const altText = logoInfo.altText ? logoInfo.altText : 'logo';
|
const altText = logoInfo.altText ? logoInfo.altText : 'logo';
|
||||||
|
|
||||||
const logo = (
|
const logo = <LogoImgEl src={logoInfo.url} alt={altText} />;
|
||||||
<LogoImgEl
|
|
||||||
src={logoInfo.url}
|
|
||||||
style={{ backgroundColor: logoInfo.backgroundColor }}
|
|
||||||
alt={altText}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<LogoWrap>
|
<LogoWrap style={{ backgroundColor: logoInfo.backgroundColor }}>
|
||||||
{info.contact && info.contact.url ? LinkWrap(info.contact.url)(logo) : logo}{' '}
|
{logoHref ? LinkWrap(logoHref)(logo) : logo}
|
||||||
</LogoWrap>
|
</LogoWrap>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import styled from '../../styled-components';
|
||||||
export const LogoImgEl = styled.img`
|
export const LogoImgEl = styled.img`
|
||||||
max-height: ${props => props.theme.logo.maxHeight};
|
max-height: ${props => props.theme.logo.maxHeight};
|
||||||
max-width: ${props => props.theme.logo.maxWidth};
|
max-width: ${props => props.theme.logo.maxWidth};
|
||||||
|
padding: ${props => props.theme.logo.gutter};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
||||||
|
|
||||||
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||||
|
@ -36,8 +37,6 @@ export class ContentItem extends React.Component<ContentItemProps> {
|
||||||
content = null;
|
content = null;
|
||||||
break;
|
break;
|
||||||
case 'tag':
|
case 'tag':
|
||||||
content = <SectionItem {...this.props} />;
|
|
||||||
break;
|
|
||||||
case 'section':
|
case 'section':
|
||||||
content = <SectionItem {...this.props} />;
|
content = <SectionItem {...this.props} />;
|
||||||
break;
|
break;
|
||||||
|
@ -45,14 +44,16 @@ export class ContentItem extends React.Component<ContentItemProps> {
|
||||||
content = <OperationItem item={item as any} />;
|
content = <OperationItem item={item as any} />;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown item type');
|
content = <SectionItem {...this.props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Section id={item.id} underlined={item.type === 'operation'}>
|
{content && (
|
||||||
{content}
|
<Section id={item.id} underlined={item.type === 'operation'}>
|
||||||
</Section>
|
{content}
|
||||||
|
</Section>
|
||||||
|
)}
|
||||||
{item.items && <ContentItems items={item.items} />}
|
{item.items && <ContentItems items={item.items} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -64,7 +65,7 @@ const middlePanelWrap = component => <MiddlePanel>{component}</MiddlePanel>;
|
||||||
@observer
|
@observer
|
||||||
export class SectionItem extends React.Component<ContentItemProps> {
|
export class SectionItem extends React.Component<ContentItemProps> {
|
||||||
render() {
|
render() {
|
||||||
const { name, description, level } = this.props.item as GroupModel;
|
const { name, description, externalDocs, level } = this.props.item as GroupModel;
|
||||||
|
|
||||||
const Header = level === 2 ? H2 : H1;
|
const Header = level === 2 ? H2 : H1;
|
||||||
return (
|
return (
|
||||||
|
@ -78,6 +79,13 @@ export class SectionItem extends React.Component<ContentItemProps> {
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
</Row>
|
</Row>
|
||||||
<AdvancedMarkdown source={description || ''} htmlWrap={middlePanelWrap} />
|
<AdvancedMarkdown source={description || ''} htmlWrap={middlePanelWrap} />
|
||||||
|
{externalDocs && (
|
||||||
|
<Row>
|
||||||
|
<MiddlePanel>
|
||||||
|
<ExternalDocumentation externalDocs={externalDocs} />
|
||||||
|
</MiddlePanel>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ShelfIcon } from '../../common-elements';
|
import { ShelfIcon } from '../../common-elements';
|
||||||
import { OperationModel } from '../../services';
|
import { OperationModel } from '../../services';
|
||||||
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
||||||
|
|
||||||
|
import { getBasePath } from '../../utils';
|
||||||
import {
|
import {
|
||||||
EndpointInfo,
|
EndpointInfo,
|
||||||
HttpVerb,
|
HttpVerb,
|
||||||
|
@ -58,17 +60,23 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
/>
|
/>
|
||||||
</EndpointInfo>
|
</EndpointInfo>
|
||||||
<ServersOverlay expanded={expanded}>
|
<ServersOverlay expanded={expanded}>
|
||||||
{operation.servers.map(server => (
|
{operation.servers.map(server => {
|
||||||
<ServerItem key={server.url}>
|
return (
|
||||||
<div>{server.description}</div>
|
<ServerItem key={server.url}>
|
||||||
<SelectOnClick>
|
<Markdown source={server.description || ''} compact={true} />
|
||||||
<ServerUrl>
|
<SelectOnClick>
|
||||||
{!(hideHostname || options.hideHostname) && <span>{server.url}</span>}
|
<ServerUrl>
|
||||||
{operation.path}
|
<span>
|
||||||
</ServerUrl>
|
{hideHostname || options.hideHostname
|
||||||
</SelectOnClick>
|
? getBasePath(server.url)
|
||||||
</ServerItem>
|
: server.url}
|
||||||
))}
|
</span>
|
||||||
|
{operation.path}
|
||||||
|
</ServerUrl>
|
||||||
|
</SelectOnClick>
|
||||||
|
</ServerItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ServersOverlay>
|
</ServersOverlay>
|
||||||
</OperationEndpointWrap>
|
</OperationEndpointWrap>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import styled, { withProps } from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
export const OperationEndpointWrap = styled.div`
|
export const OperationEndpointWrap = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -7,17 +7,18 @@ export const OperationEndpointWrap = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ServerRelativeURL = styled.span`
|
export const ServerRelativeURL = styled.span`
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const EndpointInfo = withProps<{ expanded?: boolean; inverted?: boolean }>(styled.div)`
|
export const EndpointInfo = styled.div<{ expanded?: boolean; inverted?: boolean }>`
|
||||||
padding: 10px 30px 10px ${props => (props.inverted ? '10px' : '20px')};
|
padding: 10px 30px 10px ${props => (props.inverted ? '10px' : '20px')};
|
||||||
border-radius: ${props => (props.inverted ? '0' : '4px 4px 0 0')};
|
border-radius: ${props => (props.inverted ? '0' : '4px 4px 0 0')};
|
||||||
background-color: ${props => (props.inverted ? 'transparent' : '#222d32')};
|
background-color: ${props =>
|
||||||
|
props.inverted ? 'transparent' : props.theme.codeSample.backgroundColor};
|
||||||
display: flex;
|
display: flex;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -33,12 +34,12 @@ export const EndpointInfo = withProps<{ expanded?: boolean; inverted?: boolean }
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const HttpVerb = withProps<{ type: string }>(styled.span).attrs({
|
export const HttpVerb = styled.span.attrs((props: { type: string }) => ({
|
||||||
className: props => `http-verb ${props.type}`,
|
className: `http-verb ${props.type}`,
|
||||||
})`
|
}))<{ type: string }>`
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
background-color: ${props => props.theme.colors.http[props.type] || '#999999'};
|
background-color: ${(props: any) => props.theme.colors.http[props.type] || '#999999'};
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -46,7 +47,7 @@ export const HttpVerb = withProps<{ type: string }>(styled.span).attrs({
|
||||||
margin: 0;
|
margin: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ServersOverlay = withProps<{ expanded: boolean }>(styled.div)`
|
export const ServersOverlay = styled.div<{ expanded: boolean }>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { OpenAPIExternalDocumentation } from '../../types';
|
||||||
|
import { linksCss } from '../Markdown/styled.elements';
|
||||||
|
|
||||||
|
const LinkWrap = styled.div<{ compact?: boolean }>`
|
||||||
|
${linksCss};
|
||||||
|
${({ compact }) => (!compact ? 'margin: 1em 0' : '')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class ExternalDocumentation extends React.Component<{
|
||||||
|
externalDocs: OpenAPIExternalDocumentation;
|
||||||
|
compact?: boolean;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
const { externalDocs } = this.props;
|
||||||
|
if (!externalDocs || !externalDocs.url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LinkWrap compact={this.props.compact}>
|
||||||
|
<a href={externalDocs.url}>{externalDocs.description || externalDocs.url}</a>
|
||||||
|
</LinkWrap>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,19 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ExampleValue, FieldLabel } from '../../common-elements/fields';
|
import { ExampleValue, FieldLabel } from '../../common-elements/fields';
|
||||||
|
|
||||||
|
import { l } from '../../services/Labels';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
export interface EnumValuesProps {
|
export interface EnumValuesProps {
|
||||||
values: string[];
|
values: string[];
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EnumValues extends React.PureComponent<EnumValuesProps> {
|
export class EnumValues extends React.PureComponent<EnumValuesProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
render() {
|
render() {
|
||||||
const { values, type } = this.props;
|
const { values, type } = this.props;
|
||||||
|
const { enumSkipQuotes } = this.context;
|
||||||
if (!values.length) {
|
if (!values.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -16,11 +21,17 @@ export class EnumValues extends React.PureComponent<EnumValuesProps> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FieldLabel>
|
<FieldLabel>
|
||||||
{type === 'array' ? 'Items' : ''} {values.length === 1 ? 'Value' : 'Enum'}:
|
{type === 'array' ? l('enumArray') : ''}{' '}
|
||||||
</FieldLabel>
|
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
||||||
{values.map((value, idx) => (
|
</FieldLabel>{' '}
|
||||||
<ExampleValue key={idx}>{JSON.stringify(value)} </ExampleValue>
|
{values.map((value, idx) => {
|
||||||
))}
|
const exampleValue = enumSkipQuotes ? value : JSON.stringify(value);
|
||||||
|
return (
|
||||||
|
<React.Fragment key={idx}>
|
||||||
|
<ExampleValue>{exampleValue}</ExampleValue>{' '}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
42
src/components/Fields/Extensions.tsx
Normal file
42
src/components/Fields/Extensions.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ExtensionValue, FieldLabel } from '../../common-elements/fields';
|
||||||
|
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
|
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
||||||
|
|
||||||
|
const Extension = styled(StyledMarkdownBlock)`
|
||||||
|
margin: 2px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export interface ExtensionsProps {
|
||||||
|
extensions: {
|
||||||
|
[k: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Extensions extends React.PureComponent<ExtensionsProps> {
|
||||||
|
render() {
|
||||||
|
const exts = this.props.extensions;
|
||||||
|
return (
|
||||||
|
<OptionsContext.Consumer>
|
||||||
|
{options => (
|
||||||
|
<>
|
||||||
|
{options.showExtensions &&
|
||||||
|
Object.keys(exts).map(key => (
|
||||||
|
<Extension key={key}>
|
||||||
|
<FieldLabel> {key.substring(2)}: </FieldLabel>{' '}
|
||||||
|
<ExtensionValue>
|
||||||
|
{typeof exts[key] === 'string' ? exts[key] : JSON.stringify(exts[key])}
|
||||||
|
</ExtensionValue>
|
||||||
|
</Extension>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</OptionsContext.Consumer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,20 +65,20 @@ export class Field extends React.Component<FieldProps> {
|
||||||
<FieldDetails {...this.props} />
|
<FieldDetails {...this.props} />
|
||||||
</PropertyDetailsCell>
|
</PropertyDetailsCell>
|
||||||
</tr>
|
</tr>
|
||||||
{field.expanded &&
|
{field.expanded && withSubSchema && (
|
||||||
withSubSchema && (
|
<tr key={field.name + 'inner'}>
|
||||||
<tr key={field.name + 'inner'}>
|
<PropertyCellWithInner colSpan={2}>
|
||||||
<PropertyCellWithInner colSpan={2}>
|
<InnerPropertiesWrap>
|
||||||
<InnerPropertiesWrap>
|
<Schema
|
||||||
<Schema
|
schema={field.schema}
|
||||||
schema={field.schema}
|
skipReadOnly={this.props.skipReadOnly}
|
||||||
skipReadOnly={this.props.skipReadOnly}
|
skipWriteOnly={this.props.skipWriteOnly}
|
||||||
skipWriteOnly={this.props.skipWriteOnly}
|
showTitle={this.props.showTitle}
|
||||||
/>
|
/>
|
||||||
</InnerPropertiesWrap>
|
</InnerPropertiesWrap>
|
||||||
</PropertyCellWithInner>
|
</PropertyCellWithInner>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ExampleValue, FieldLabel } from '../../common-elements/fields';
|
||||||
export interface FieldDetailProps {
|
export interface FieldDetailProps {
|
||||||
value?: any;
|
value?: any;
|
||||||
label: string;
|
label: string;
|
||||||
|
raw?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FieldDetail extends React.PureComponent<FieldDetailProps> {
|
export class FieldDetail extends React.PureComponent<FieldDetailProps> {
|
||||||
|
@ -11,10 +12,12 @@ export class FieldDetail extends React.PureComponent<FieldDetailProps> {
|
||||||
if (this.props.value === undefined) {
|
if (this.props.value === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const value = this.props.raw ? this.props.value : JSON.stringify(this.props.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FieldLabel> {this.props.label} </FieldLabel>{' '}
|
<FieldLabel> {this.props.label} </FieldLabel> <ExampleValue>{value}</ExampleValue>
|
||||||
<ExampleValue> {JSON.stringify(this.props.value)} </ExampleValue>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,40 @@ import {
|
||||||
TypePrefix,
|
TypePrefix,
|
||||||
TypeTitle,
|
TypeTitle,
|
||||||
} from '../../common-elements/fields';
|
} from '../../common-elements/fields';
|
||||||
|
import { serializeParameterValue } from '../../utils/openapi';
|
||||||
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { EnumValues } from './EnumValues';
|
import { EnumValues } from './EnumValues';
|
||||||
|
import { Extensions } from './Extensions';
|
||||||
import { FieldProps } from './Field';
|
import { FieldProps } from './Field';
|
||||||
import { ConstraintsView } from './FieldContstraints';
|
import { ConstraintsView } from './FieldContstraints';
|
||||||
import { FieldDetail } from './FieldDetail';
|
import { FieldDetail } from './FieldDetail';
|
||||||
|
|
||||||
import { Badge } from '../../common-elements/';
|
import { Badge } from '../../common-elements/';
|
||||||
|
|
||||||
|
import { l } from '../../services/Labels';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
export class FieldDetails extends React.PureComponent<FieldProps> {
|
export class FieldDetails extends React.PureComponent<FieldProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
render() {
|
render() {
|
||||||
const { showExamples, field, renderDiscriminatorSwitch } = this.props;
|
const { showExamples, field, renderDiscriminatorSwitch } = this.props;
|
||||||
|
const { enumSkipQuotes } = this.context;
|
||||||
|
|
||||||
const { schema, description, example, deprecated } = field;
|
const { schema, description, example, deprecated } = field;
|
||||||
|
|
||||||
|
let exampleField: JSX.Element | null = null;
|
||||||
|
|
||||||
|
if (showExamples && example !== undefined) {
|
||||||
|
const label = l('example') + ':';
|
||||||
|
if (field.in && (field.style || field.serializationMime)) {
|
||||||
|
const serializedValue = serializeParameterValue(field, example);
|
||||||
|
exampleField = <FieldDetail label={label} value={serializedValue} raw={true} />;
|
||||||
|
} else {
|
||||||
|
exampleField = <FieldDetail label={label} value={example} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -38,21 +58,25 @@ export class FieldDetails extends React.PureComponent<FieldProps> {
|
||||||
)}
|
)}
|
||||||
{schema.title && <TypeTitle> ({schema.title}) </TypeTitle>}
|
{schema.title && <TypeTitle> ({schema.title}) </TypeTitle>}
|
||||||
<ConstraintsView constraints={schema.constraints} />
|
<ConstraintsView constraints={schema.constraints} />
|
||||||
{schema.nullable && <NullableLabel> Nullable </NullableLabel>}
|
{schema.nullable && <NullableLabel> {l('nullable')} </NullableLabel>}
|
||||||
{schema.pattern && <PatternLabel>{schema.pattern}</PatternLabel>}
|
{schema.pattern && <PatternLabel>{schema.pattern}</PatternLabel>}
|
||||||
{schema.isCircular && <RecursiveLabel> Recursive </RecursiveLabel>}
|
{schema.isCircular && <RecursiveLabel> {l('recursive')} </RecursiveLabel>}
|
||||||
</div>
|
</div>
|
||||||
{deprecated && (
|
{deprecated && (
|
||||||
<div>
|
<div>
|
||||||
<Badge type="warning"> Deprecated </Badge>
|
<Badge type="warning"> {l('deprecated')} </Badge>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FieldDetail label={'Default:'} value={schema.default} />
|
<FieldDetail raw={enumSkipQuotes} label={l('default') + ':'} value={schema.default} />
|
||||||
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
|
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
|
||||||
{showExamples && <FieldDetail label={'Example:'} value={example} />}
|
{exampleField}
|
||||||
|
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
|
||||||
<div>
|
<div>
|
||||||
<Markdown dense={true} source={description} />
|
<Markdown compact={true} source={description} />
|
||||||
</div>
|
</div>
|
||||||
|
{schema.externalDocs && (
|
||||||
|
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
|
||||||
|
)}
|
||||||
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { SampleControls } from '../../common-elements';
|
||||||
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
||||||
import { PrismDiv } from '../../common-elements/PrismDiv';
|
import { PrismDiv } from '../../common-elements/PrismDiv';
|
||||||
import { jsonToHTML } from '../../utils/jsonToHtml';
|
import { jsonToHTML } from '../../utils/jsonToHtml';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { jsonStyles } from './style';
|
import { jsonStyles } from './style';
|
||||||
|
|
||||||
export interface JsonProps {
|
export interface JsonProps {
|
||||||
|
@ -32,12 +33,18 @@ class Json extends React.PureComponent<JsonProps> {
|
||||||
<span onClick={this.expandAll}> Expand all </span>
|
<span onClick={this.expandAll}> Expand all </span>
|
||||||
<span onClick={this.collapseAll}> Collapse all </span>
|
<span onClick={this.collapseAll}> Collapse all </span>
|
||||||
</SampleControls>
|
</SampleControls>
|
||||||
<PrismDiv
|
<OptionsContext.Consumer>
|
||||||
className={this.props.className}
|
{options => (
|
||||||
// tslint:disable-next-line
|
<PrismDiv
|
||||||
innerRef={node => (this.node = node!)}
|
className={this.props.className}
|
||||||
dangerouslySetInnerHTML={{ __html: jsonToHTML(this.props.data) }}
|
// tslint:disable-next-line
|
||||||
/>
|
ref={node => (this.node = node!)}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</OptionsContext.Consumer>
|
||||||
</JsonViewerWrap>
|
</JsonViewerWrap>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ export const jsonStyles = css`
|
||||||
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: pre-wrap;
|
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
|
contain: content;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|
||||||
.callback-function {
|
.callback-function {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled, { withProps } from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
import { Spinner } from './Spinner.svg';
|
import { Spinner } from './Spinner.svg';
|
||||||
|
|
||||||
const LoadingMessage = withProps<{ color: string }>(styled.div)`
|
const LoadingMessage = styled.div<{ color: string }>`
|
||||||
font-family: helvetica, sans;
|
font-family: helvetica, sans;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -38,11 +38,11 @@ export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
|
||||||
return parts.map((part, idx) => {
|
return parts.map((part, idx) => {
|
||||||
if (typeof part === 'string') {
|
if (typeof part === 'string') {
|
||||||
return React.cloneElement(
|
return React.cloneElement(
|
||||||
htmlWrap(<SanitizedMarkdownHTML html={part} inline={false} dense={false} />),
|
htmlWrap(<SanitizedMarkdownHTML html={part} inline={false} compact={false} />),
|
||||||
{ key: idx },
|
{ key: idx },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <part.component key={idx} {...{ ...part.attrs, ...part.propsSelector(store) }} />;
|
return <part.component key={idx} {...{ ...part.props, ...part.propsSelector(store) }} />;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { MarkdownRenderer } from '../../services';
|
||||||
import { SanitizedMarkdownHTML } from './SanitizedMdBlock';
|
import { SanitizedMarkdownHTML } from './SanitizedMdBlock';
|
||||||
|
|
||||||
export interface StylingMarkdownProps {
|
export interface StylingMarkdownProps {
|
||||||
dense?: boolean;
|
compact?: boolean;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,13 +21,13 @@ export type MarkdownProps = BaseMarkdownProps &
|
||||||
|
|
||||||
export class Markdown extends React.Component<MarkdownProps> {
|
export class Markdown extends React.Component<MarkdownProps> {
|
||||||
render() {
|
render() {
|
||||||
const { source, inline, dense, className } = this.props;
|
const { source, inline, compact, className } = this.props;
|
||||||
const renderer = new MarkdownRenderer();
|
const renderer = new MarkdownRenderer();
|
||||||
return (
|
return (
|
||||||
<SanitizedMarkdownHTML
|
<SanitizedMarkdownHTML
|
||||||
html={renderer.renderMd(source)}
|
html={renderer.renderMd(source)}
|
||||||
inline={inline}
|
inline={inline}
|
||||||
dense={dense}
|
compact={compact}
|
||||||
className={className}
|
className={className}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { headerCommonMixin, linkifyMixin } from '../../common-elements';
|
import { headerCommonMixin, linkifyMixin } from '../../common-elements';
|
||||||
import { PrismDiv } from '../../common-elements/PrismDiv';
|
import { PrismDiv } from '../../common-elements/PrismDiv';
|
||||||
import styled, { css, extensionsHook, withProps } from '../../styled-components';
|
import styled, { css, extensionsHook, ResolvedThemeInterface } from '../../styled-components';
|
||||||
|
|
||||||
|
import { StyledComponent } from 'styled-components';
|
||||||
|
|
||||||
export const linksCss = css`
|
export const linksCss = css`
|
||||||
a {
|
a {
|
||||||
|
@ -17,9 +19,11 @@ export const linksCss = css`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean }>(
|
export const StyledMarkdownBlock = styled(PrismDiv as StyledComponent<
|
||||||
styled(PrismDiv),
|
'div',
|
||||||
)`
|
ResolvedThemeInterface,
|
||||||
|
{ compact?: boolean; inline?: boolean }
|
||||||
|
>)`
|
||||||
|
|
||||||
font-family: ${props => props.theme.typography.fontFamily};
|
font-family: ${props => props.theme.typography.fontFamily};
|
||||||
font-weight: ${props => props.theme.typography.fontWeightRegular};
|
font-weight: ${props => props.theme.typography.fontWeightRegular};
|
||||||
|
@ -31,8 +35,8 @@ export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${({ dense }) =>
|
${({ compact }) =>
|
||||||
dense &&
|
compact &&
|
||||||
`
|
`
|
||||||
p:first-child {
|
p:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -66,18 +70,19 @@ export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid rgba(38, 50, 56, 0.1);
|
border: 1px solid rgba(38, 50, 56, 0.1);
|
||||||
padding: 0.1em 0.25em 0.2em;
|
padding: 0 ${({ theme }) => theme.spacing.unit}px;
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
|
font-weight: ${({ theme }) => theme.typography.code.fontWeight};
|
||||||
|
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
white-space: pre-wrap;
|
white-space:${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
background-color: #263238;
|
background-color: #263238;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 12px 14px 15px 14px;
|
padding: ${props => props.theme.spacing.unit * 4}px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
border-radius: 0px
|
border-radius: 0px
|
||||||
|
@ -113,9 +118,11 @@ export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
// > li {
|
|
||||||
// margin: 0.5em 0;
|
ul, ol {
|
||||||
// }
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
|
@ -135,7 +142,7 @@ export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
|
|
||||||
&:nth-child(2n) {
|
&:nth-child(2n) {
|
||||||
background-color: #f8f8f8;
|
background-color: ${({ theme }) => theme.schema.nestedBackground};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { DropdownProps } from '../../common-elements/dropdown';
|
import { DropdownProps } from '../../common-elements/dropdown';
|
||||||
import { MediaContentModel, MediaTypeModel, SchemaModel } from '../../services/models';
|
import { MediaContentModel, MediaTypeModel, SchemaModel } from '../../services/models';
|
||||||
|
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||||
|
|
||||||
export interface MediaTypeChildProps {
|
export interface MediaTypeChildProps {
|
||||||
schema: SchemaModel;
|
schema: SchemaModel;
|
||||||
|
@ -11,6 +12,8 @@ export interface MediaTypeChildProps {
|
||||||
|
|
||||||
export interface MediaTypesSwitchProps {
|
export interface MediaTypesSwitchProps {
|
||||||
content?: MediaContentModel;
|
content?: MediaContentModel;
|
||||||
|
withLabel?: boolean;
|
||||||
|
|
||||||
renderDropdown: (props: DropdownProps) => JSX.Element;
|
renderDropdown: (props: DropdownProps) => JSX.Element;
|
||||||
children: (activeMime: MediaTypeModel) => JSX.Element;
|
children: (activeMime: MediaTypeModel) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
@ -37,13 +40,25 @@ export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Wrapper = ({ children }) =>
|
||||||
|
this.props.withLabel ? (
|
||||||
|
<DropdownWrapper>
|
||||||
|
<DropdownLabel>Content type</DropdownLabel>
|
||||||
|
{children}
|
||||||
|
</DropdownWrapper>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.props.renderDropdown({
|
<Wrapper>
|
||||||
value: options[activeMimeIdx],
|
{this.props.renderDropdown({
|
||||||
options,
|
value: options[activeMimeIdx],
|
||||||
onChange: this.switchMedia,
|
options,
|
||||||
})}
|
onChange: this.switchMedia,
|
||||||
|
})}
|
||||||
|
</Wrapper>
|
||||||
{this.props.children(content.active)}
|
{this.props.children(content.active)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
import { ShareLink } from '../../common-elements/linkify';
|
import { ShareLink } from '../../common-elements/linkify';
|
||||||
import { Endpoint } from '../Endpoint/Endpoint';
|
import { Endpoint } from '../Endpoint/Endpoint';
|
||||||
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { Parameters } from '../Parameters/Parameters';
|
import { Parameters } from '../Parameters/Parameters';
|
||||||
import { RequestSamples } from '../RequestSamples/RequestSamples';
|
import { RequestSamples } from '../RequestSamples/RequestSamples';
|
||||||
|
@ -17,6 +18,7 @@ import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||||
|
|
||||||
import { OperationModel as OperationType } from '../../services/models';
|
import { OperationModel as OperationType } from '../../services/models';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
import { Extensions } from '../Fields/Extensions';
|
||||||
|
|
||||||
const OperationRow = styled(Row)`
|
const OperationRow = styled(Row)`
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
|
@ -25,7 +27,7 @@ const OperationRow = styled(Row)`
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Description = styled(Markdown)`
|
const Description = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -38,7 +40,9 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
render() {
|
render() {
|
||||||
const { operation } = this.props;
|
const { operation } = this.props;
|
||||||
|
|
||||||
const { name: summary, description, deprecated } = operation;
|
const { name: summary, description, deprecated, externalDocs } = operation;
|
||||||
|
const hasDescription = !!(description || externalDocs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
|
@ -49,7 +53,13 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
||||||
</H2>
|
</H2>
|
||||||
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
||||||
{description !== undefined && <Description source={description} />}
|
{hasDescription && (
|
||||||
|
<Description>
|
||||||
|
{description !== undefined && <Markdown source={description} />}
|
||||||
|
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||||
|
</Description>
|
||||||
|
)}
|
||||||
|
<Extensions extensions={operation.extensions} />
|
||||||
<SecurityRequirements securities={operation.security} />
|
<SecurityRequirements securities={operation.security} />
|
||||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||||
<ResponsesList responses={operation.responses} />
|
<ResponsesList responses={operation.responses} />
|
||||||
|
|
|
@ -4,11 +4,12 @@ import { ParametersGroup } from './ParametersGroup';
|
||||||
|
|
||||||
import { UnderlinedHeader } from '../../common-elements';
|
import { UnderlinedHeader } from '../../common-elements';
|
||||||
|
|
||||||
|
import { MediaContentModel } from '../../services';
|
||||||
import { FieldModel, RequestBodyModel } from '../../services/models';
|
import { FieldModel, RequestBodyModel } from '../../services/models';
|
||||||
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
||||||
import { Schema } from '../Schema';
|
import { Schema } from '../Schema';
|
||||||
|
|
||||||
import { MediaContentModel } from '../../services';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
|
||||||
function safePush(obj, prop, item) {
|
function safePush(obj, prop, item) {
|
||||||
if (!obj[prop]) {
|
if (!obj[prop]) {
|
||||||
|
@ -45,13 +46,15 @@ export class Parameters extends React.PureComponent<ParametersProps> {
|
||||||
|
|
||||||
const bodyContent = body && body.content;
|
const bodyContent = body && body.content;
|
||||||
|
|
||||||
|
const bodyDescription = body && body.description;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{paramsPlaces.map(place => (
|
{paramsPlaces.map(place => (
|
||||||
<ParametersGroup key={place} place={place} parameters={paramsMap[place]} />
|
<ParametersGroup key={place} place={place} parameters={paramsMap[place]} />
|
||||||
))}
|
))}
|
||||||
{bodyContent && <BodyContent content={bodyContent} />}
|
{bodyContent && <BodyContent content={bodyContent} description={bodyDescription} />}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,12 +67,17 @@ function DropdownWithinHeader(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BodyContent(props: { content: MediaContentModel }): JSX.Element {
|
function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element {
|
||||||
const { content } = props;
|
const { content, description } = props;
|
||||||
return (
|
return (
|
||||||
<MediaTypesSwitch content={content} renderDropdown={DropdownWithinHeader}>
|
<MediaTypesSwitch content={content} renderDropdown={DropdownWithinHeader}>
|
||||||
{({ schema }) => {
|
{({ schema }) => {
|
||||||
return <Schema skipReadOnly={true} key="schema" schema={schema} />;
|
return (
|
||||||
|
<>
|
||||||
|
{description !== undefined && <Markdown source={description} />}
|
||||||
|
<Schema skipReadOnly={true} key="schema" schema={schema} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
</MediaTypesSwitch>
|
</MediaTypesSwitch>
|
||||||
);
|
);
|
||||||
|
|
40
src/components/PayloadSamples/Example.tsx
Normal file
40
src/components/PayloadSamples/Example.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { StyledPre } from '../../common-elements/samples';
|
||||||
|
import { ExampleModel } from '../../services/models';
|
||||||
|
import { ExampleValue } from './ExampleValue';
|
||||||
|
import { useExternalExample } from './exernalExampleHook';
|
||||||
|
|
||||||
|
export interface ExampleProps {
|
||||||
|
example: ExampleModel;
|
||||||
|
mimeType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Example({ example, mimeType }: ExampleProps) {
|
||||||
|
if (example.value === undefined && example.externalValueUrl) {
|
||||||
|
return <ExternalExample example={example} mimeType={mimeType} />;
|
||||||
|
} else {
|
||||||
|
return <ExampleValue value={example.value} mimeType={mimeType} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExternalExample({ example, mimeType }: ExampleProps) {
|
||||||
|
const value = useExternalExample(example, mimeType);
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
return <span>Loading...</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Error) {
|
||||||
|
return (
|
||||||
|
<StyledPre>
|
||||||
|
Error loading external example: <br />
|
||||||
|
<a className={'token string'} href={example.externalValueUrl} target="_blank">
|
||||||
|
{example.externalValueUrl}
|
||||||
|
</a>
|
||||||
|
</StyledPre>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ExampleValue value={value} mimeType={mimeType} />;
|
||||||
|
}
|
22
src/components/PayloadSamples/ExampleValue.tsx
Normal file
22
src/components/PayloadSamples/ExampleValue.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { isJsonLike, langFromMime } from '../../utils/openapi';
|
||||||
|
import { JsonViewer } from '../JsonViewer/JsonViewer';
|
||||||
|
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
||||||
|
|
||||||
|
export interface ExampleValueProps {
|
||||||
|
value: any;
|
||||||
|
mimeType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExampleValue({ value, mimeType }: ExampleValueProps) {
|
||||||
|
if (isJsonLike(mimeType)) {
|
||||||
|
return <JsonViewer data={value} />;
|
||||||
|
} else {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
// just in case example was cached as json but used as non-json
|
||||||
|
value = JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
return <SourceCodeWithCopy lang={langFromMime(mimeType)} source={value} />;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,51 +1,82 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { SmallTabs, Tab, TabList, TabPanel } from '../../common-elements';
|
import styled from '../../styled-components';
|
||||||
import { MediaTypeModel } from '../../services/models';
|
|
||||||
import { JsonViewer } from '../JsonViewer/JsonViewer';
|
|
||||||
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
|
||||||
import { NoSampleLabel } from './styled.elements';
|
|
||||||
|
|
||||||
import { isJsonLike, langFromMime } from '../../utils';
|
import { DropdownProps } from '../../common-elements';
|
||||||
|
import { MediaTypeModel } from '../../services/models';
|
||||||
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
import { Example } from './Example';
|
||||||
|
import { DropdownLabel, DropdownWrapper, NoSampleLabel } from './styled.elements';
|
||||||
|
|
||||||
export interface PayloadSamplesProps {
|
export interface PayloadSamplesProps {
|
||||||
mediaType: MediaTypeModel;
|
mediaType: MediaTypeModel;
|
||||||
|
renderDropdown: (props: DropdownProps) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MediaTypeSamples extends React.Component<PayloadSamplesProps> {
|
interface MediaTypeSamplesState {
|
||||||
|
activeIdx: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MediaTypeSamples extends React.Component<PayloadSamplesProps, MediaTypeSamplesState> {
|
||||||
|
state = {
|
||||||
|
activeIdx: 0,
|
||||||
|
};
|
||||||
|
switchMedia = ({ value }) => {
|
||||||
|
this.setState({
|
||||||
|
activeIdx: parseInt(value, 10),
|
||||||
|
});
|
||||||
|
};
|
||||||
render() {
|
render() {
|
||||||
|
const { activeIdx } = this.state;
|
||||||
const examples = this.props.mediaType.examples || {};
|
const examples = this.props.mediaType.examples || {};
|
||||||
const mimeType = this.props.mediaType.name;
|
const mimeType = this.props.mediaType.name;
|
||||||
|
|
||||||
const noSample = <NoSampleLabel>No sample</NoSampleLabel>;
|
const noSample = <NoSampleLabel>No sample</NoSampleLabel>;
|
||||||
const sampleView = isJsonLike(mimeType)
|
|
||||||
? sample => <JsonViewer data={sample} />
|
|
||||||
: sample =>
|
|
||||||
(sample !== undefined && (
|
|
||||||
<SourceCodeWithCopy lang={langFromMime(mimeType)} source={sample} />
|
|
||||||
)) ||
|
|
||||||
noSample;
|
|
||||||
|
|
||||||
const examplesNames = Object.keys(examples);
|
const examplesNames = Object.keys(examples);
|
||||||
if (examplesNames.length === 0) {
|
if (examplesNames.length === 0) {
|
||||||
return noSample;
|
return noSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examplesNames.length > 1) {
|
if (examplesNames.length > 1) {
|
||||||
|
const options = examplesNames.map((name, idx) => {
|
||||||
|
return {
|
||||||
|
label: examples[name].summary || name,
|
||||||
|
value: idx.toString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const example = examples[examplesNames[activeIdx]];
|
||||||
|
const description = example.description;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SmallTabs>
|
<SamplesWrapper>
|
||||||
<TabList>
|
<DropdownWrapper>
|
||||||
{examplesNames.map(name => (
|
<DropdownLabel>Example</DropdownLabel>
|
||||||
<Tab key={name}> {examples[name].summary || name} </Tab>
|
{this.props.renderDropdown({
|
||||||
))}
|
value: options[activeIdx],
|
||||||
</TabList>
|
options,
|
||||||
{examplesNames.map(name => (
|
onChange: this.switchMedia,
|
||||||
<TabPanel key={name}>{sampleView(examples[name].value)}</TabPanel>
|
})}
|
||||||
))}
|
</DropdownWrapper>
|
||||||
</SmallTabs>
|
<div>
|
||||||
|
{description && <Markdown source={description} />}
|
||||||
|
<Example example={example} mimeType={mimeType} />
|
||||||
|
</div>
|
||||||
|
</SamplesWrapper>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const name = examplesNames[0];
|
const example = examples[examplesNames[0]];
|
||||||
return <div>{sampleView(examples[name].value)}</div>;
|
return (
|
||||||
|
<SamplesWrapper>
|
||||||
|
{example.description && <Markdown source={example.description} />}
|
||||||
|
<Example example={example} mimeType={mimeType} />
|
||||||
|
</SamplesWrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SamplesWrapper = styled.div`
|
||||||
|
margin-top: 15px;
|
||||||
|
`;
|
||||||
|
|
|
@ -2,10 +2,9 @@ import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { MediaTypeSamples } from './MediaTypeSamples';
|
import { MediaTypeSamples } from './MediaTypeSamples';
|
||||||
|
|
||||||
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
|
||||||
|
|
||||||
import { MediaContentModel } from '../../services/models';
|
import { MediaContentModel } from '../../services/models';
|
||||||
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
||||||
|
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
||||||
import { InvertedSimpleDropdown, MimeLabel } from './styled.elements';
|
import { InvertedSimpleDropdown, MimeLabel } from './styled.elements';
|
||||||
|
|
||||||
export interface PayloadSamplesProps {
|
export interface PayloadSamplesProps {
|
||||||
|
@ -21,8 +20,14 @@ export class PayloadSamples extends React.Component<PayloadSamplesProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MediaTypesSwitch content={mimeContent} renderDropdown={this.renderDropdown}>
|
<MediaTypesSwitch content={mimeContent} renderDropdown={this.renderDropdown} withLabel={true}>
|
||||||
{mediaType => <MediaTypeSamples key="samples" mediaType={mediaType} />}
|
{mediaType => (
|
||||||
|
<MediaTypeSamples
|
||||||
|
key="samples"
|
||||||
|
mediaType={mediaType}
|
||||||
|
renderDropdown={this.renderDropdown}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</MediaTypesSwitch>
|
</MediaTypesSwitch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
31
src/components/PayloadSamples/exernalExampleHook.ts
Normal file
31
src/components/PayloadSamples/exernalExampleHook.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { ExampleModel } from '../../services/models/Example';
|
||||||
|
|
||||||
|
export function useExternalExample(example: ExampleModel, mimeType: string) {
|
||||||
|
const [, setIsLoading] = useState(true); // to trigger component reload
|
||||||
|
|
||||||
|
const value = useRef<any>(undefined);
|
||||||
|
const prevRef = useRef<ExampleModel | undefined>(undefined);
|
||||||
|
|
||||||
|
if (prevRef.current !== example) {
|
||||||
|
value.current = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRef.current = example;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const load = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
value.current = await example.getExternalValue(mimeType);
|
||||||
|
} catch (e) {
|
||||||
|
value.current = e;
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
load();
|
||||||
|
}, [example, mimeType]);
|
||||||
|
|
||||||
|
return value.current;
|
||||||
|
}
|
|
@ -1,37 +1,59 @@
|
||||||
|
// @ts-ignore
|
||||||
|
import Dropdown from 'react-dropdown';
|
||||||
|
|
||||||
|
import { transparentize } from 'polished';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
import { StyledDropdown } from '../../common-elements';
|
import { StyledDropdown } from '../../common-elements';
|
||||||
|
|
||||||
export const MimeLabel = styled.div`
|
export const MimeLabel = styled.div`
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.9);
|
padding: 12px;
|
||||||
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
display: block;
|
display: block;
|
||||||
color: rgba(255, 255, 255, 0.8);
|
`;
|
||||||
|
|
||||||
|
export const DropdownLabel = styled.span`
|
||||||
|
font-family: ${({ theme }) => theme.typography.headings.fontFamily};
|
||||||
|
font-size: 12px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: -11px;
|
||||||
|
left: 12px;
|
||||||
|
font-weight: ${({ theme }) => theme.typography.fontWeightBold};
|
||||||
|
color: ${({ theme }) => transparentize(0.6, theme.rightPanel.textColor)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DropdownWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.9);
|
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
display: block;
|
display: block;
|
||||||
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
|
.Dropdown-control {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
.Dropdown-control,
|
.Dropdown-control,
|
||||||
.Dropdown-control:hover {
|
.Dropdown-control:hover {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 1.2em 0 0;
|
padding: 0.9em 1.6em 0.9em 0.9em;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
.Dropdown-arrow {
|
.Dropdown-arrow {
|
||||||
border-top-color: rgba(255, 255, 255, 0.9);
|
border-top-color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.Dropdown-menu {
|
.Dropdown-menu {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,6 @@ export const RedocWrap = styled.div`
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.redoc-markdown h1 {
|
|
||||||
padding-top: ${theme.spacing.unit * 16}px;
|
|
||||||
}
|
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -34,9 +30,10 @@ export const ApiContentWrap = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: calc(100% - ${props => props.theme.menu.width});
|
width: calc(100% - ${props => props.theme.menu.width});
|
||||||
${media.lessThan('small')`
|
${media.lessThan('small', true)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
contain: layout;
|
contain: layout;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -46,8 +43,15 @@ export const BackgroundStub = styled.div`
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: calc((100% - ${({ theme }) => theme.menu.width}) * 0.4);
|
width: ${({ theme }) => {
|
||||||
${media.lessThan('medium')`
|
if (theme.rightPanel.width.endsWith('%')) {
|
||||||
|
const percents = parseInt(theme.rightPanel.width, 10);
|
||||||
|
return `calc((100% - ${theme.menu.width}) * ${percents / 100})`;
|
||||||
|
} else {
|
||||||
|
return theme.rightPanel.width;
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
${media.lessThan('medium', true)`
|
||||||
display: none;
|
display: none;
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { OperationModel } from '../../services/models';
|
import { OperationModel, RedocNormalizedOptions } from '../../services';
|
||||||
import { PayloadSamples } from '../PayloadSamples/PayloadSamples';
|
import { PayloadSamples } from '../PayloadSamples/PayloadSamples';
|
||||||
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
||||||
|
|
||||||
import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements';
|
import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
export interface RequestSamplesProps {
|
export interface RequestSamplesProps {
|
||||||
operation: OperationModel;
|
operation: OperationModel;
|
||||||
|
@ -12,6 +13,8 @@ export interface RequestSamplesProps {
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class RequestSamples extends React.Component<RequestSamplesProps> {
|
export class RequestSamples extends React.Component<RequestSamplesProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
|
context: RedocNormalizedOptions;
|
||||||
operation: OperationModel;
|
operation: OperationModel;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -21,16 +24,20 @@ export class RequestSamples extends React.Component<RequestSamplesProps> {
|
||||||
const samples = operation.codeSamples;
|
const samples = operation.codeSamples;
|
||||||
|
|
||||||
const hasSamples = hasBodySample || samples.length > 0;
|
const hasSamples = hasBodySample || samples.length > 0;
|
||||||
|
const hideTabList =
|
||||||
|
samples.length + (hasBodySample ? 1 : 0) === 1
|
||||||
|
? this.context.hideSingleRequestSampleTab
|
||||||
|
: false;
|
||||||
return (
|
return (
|
||||||
(hasSamples && (
|
(hasSamples && (
|
||||||
<div>
|
<div>
|
||||||
<RightPanelHeader> Request samples </RightPanelHeader>
|
<RightPanelHeader> Request samples </RightPanelHeader>
|
||||||
|
|
||||||
<Tabs defaultIndex={0}>
|
<Tabs defaultIndex={0}>
|
||||||
<TabList>
|
<TabList hidden={hideTabList}>
|
||||||
{hasBodySample && <Tab key="payload"> Payload </Tab>}
|
{hasBodySample && <Tab key="payload"> Payload </Tab>}
|
||||||
{samples.map(sample => (
|
{samples.map(sample => (
|
||||||
<Tab key={sample.lang}>
|
<Tab key={sample.lang + '_' + (sample.label || '')}>
|
||||||
{sample.label !== undefined ? sample.label : sample.lang}
|
{sample.label !== undefined ? sample.label : sample.lang}
|
||||||
</Tab>
|
</Tab>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -28,12 +28,11 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
|
||||||
code={code}
|
code={code}
|
||||||
opened={expanded}
|
opened={expanded}
|
||||||
/>
|
/>
|
||||||
{expanded &&
|
{expanded && !empty && (
|
||||||
!empty && (
|
<ResponseDetailsWrap>
|
||||||
<ResponseDetailsWrap>
|
<ResponseDetails response={this.props.response} />
|
||||||
<ResponseDetails response={this.props.response} />
|
</ResponseDetailsWrap>
|
||||||
</ResponseDetailsWrap>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,12 @@ export class ResponseTitle extends React.PureComponent<ResponseTitleProps> {
|
||||||
<ShelfIcon
|
<ShelfIcon
|
||||||
size={'1.5em'}
|
size={'1.5em'}
|
||||||
color={type}
|
color={type}
|
||||||
direction={opened ? 'up' : 'down'}
|
direction={opened ? 'down' : 'right'}
|
||||||
float={'left'}
|
float={'left'}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<strong>{code} </strong>
|
<strong>{code} </strong>
|
||||||
<Markdown dense={true} inline={true} source={title} />
|
<Markdown compact={true} inline={true} source={title} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,9 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
|
||||||
|
|
||||||
const filteredFields = needFilter
|
const filteredFields = needFilter
|
||||||
? fields.filter(item => {
|
? fields.filter(item => {
|
||||||
return (
|
return !(
|
||||||
(this.props.skipReadOnly && !item.schema.readOnly) ||
|
(this.props.skipReadOnly && item.schema.readOnly) ||
|
||||||
(this.props.skipWriteOnly && !item.schema.writeOnly)
|
(this.props.skipWriteOnly && item.schema.writeOnly)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
: fields;
|
: fields;
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { ArraySchema } from './ArraySchema';
|
||||||
import { ObjectSchema } from './ObjectSchema';
|
import { ObjectSchema } from './ObjectSchema';
|
||||||
import { OneOfSchema } from './OneOfSchema';
|
import { OneOfSchema } from './OneOfSchema';
|
||||||
|
|
||||||
|
import { l } from '../../services/Labels';
|
||||||
|
|
||||||
export interface SchemaOptions {
|
export interface SchemaOptions {
|
||||||
skipReadOnly?: boolean;
|
skipReadOnly?: boolean;
|
||||||
skipWriteOnly?: boolean;
|
skipWriteOnly?: boolean;
|
||||||
|
@ -33,7 +35,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
<div>
|
<div>
|
||||||
<TypeName>{schema.displayType}</TypeName>
|
<TypeName>{schema.displayType}</TypeName>
|
||||||
{schema.title && <TypeTitle> {schema.title} </TypeTitle>}
|
{schema.title && <TypeTitle> {schema.title} </TypeTitle>}
|
||||||
<RecursiveLabel> Recursive </RecursiveLabel>
|
<RecursiveLabel> {l('recursive')} </RecursiveLabel>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -74,6 +76,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
name: '',
|
name: '',
|
||||||
required: false,
|
required: false,
|
||||||
description: schema.description,
|
description: schema.description,
|
||||||
|
externalDocs: schema.externalDocs,
|
||||||
deprecated: false,
|
deprecated: false,
|
||||||
toggle: () => null,
|
toggle: () => null,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
|
|
93
src/components/SchemaDefinition/SchemaDefinition.tsx
Normal file
93
src/components/SchemaDefinition/SchemaDefinition.tsx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { DarkRightPanel, MiddlePanel, MimeLabel, Row, Section } from '../../common-elements';
|
||||||
|
import { MediaTypeModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { OpenAPIMediaType } from '../../types';
|
||||||
|
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
||||||
|
import { MediaTypeSamples } from '../PayloadSamples/MediaTypeSamples';
|
||||||
|
import { InvertedSimpleDropdown } from '../PayloadSamples/styled.elements';
|
||||||
|
import { Schema } from '../Schema';
|
||||||
|
|
||||||
|
export interface ObjectDescriptionProps {
|
||||||
|
schemaRef: string;
|
||||||
|
exampleRef?: string;
|
||||||
|
showReadOnly?: boolean;
|
||||||
|
showWriteOnly?: boolean;
|
||||||
|
parser: OpenAPIParser;
|
||||||
|
options: RedocNormalizedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SchemaDefinition extends React.PureComponent<ObjectDescriptionProps> {
|
||||||
|
private static getMediaType(schemaRef: string, exampleRef?: string): OpenAPIMediaType {
|
||||||
|
if (!schemaRef) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const info: OpenAPIMediaType = {
|
||||||
|
schema: { $ref: schemaRef },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (exampleRef) {
|
||||||
|
info.examples = { example: { $ref: exampleRef } };
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _mediaModel: MediaTypeModel;
|
||||||
|
|
||||||
|
private get mediaModel() {
|
||||||
|
const { parser, schemaRef, exampleRef, options } = this.props;
|
||||||
|
if (!this._mediaModel) {
|
||||||
|
this._mediaModel = new MediaTypeModel(
|
||||||
|
parser,
|
||||||
|
'json',
|
||||||
|
false,
|
||||||
|
SchemaDefinition.getMediaType(schemaRef, exampleRef),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._mediaModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { showReadOnly = true, showWriteOnly = false } = this.props;
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<Row>
|
||||||
|
<MiddlePanel>
|
||||||
|
<Schema
|
||||||
|
skipWriteOnly={!showWriteOnly}
|
||||||
|
skipReadOnly={!showReadOnly}
|
||||||
|
schema={this.mediaModel.schema}
|
||||||
|
/>
|
||||||
|
</MiddlePanel>
|
||||||
|
<DarkRightPanel>
|
||||||
|
<MediaSamplesWrap>
|
||||||
|
<MediaTypeSamples renderDropdown={this.renderDropdown} mediaType={this.mediaModel} />
|
||||||
|
</MediaSamplesWrap>
|
||||||
|
</DarkRightPanel>
|
||||||
|
</Row>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderDropdown = props => {
|
||||||
|
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const MediaSamplesWrap = styled.div`
|
||||||
|
background: ${({ theme }) => theme.codeSample.backgroundColor};
|
||||||
|
& > div,
|
||||||
|
& > pre {
|
||||||
|
padding: ${props => props.theme.spacing.unit * 4}px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div > pre {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`;
|
|
@ -1,5 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { darken } from 'polished';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
import { MenuItemLabel } from '../SideMenu/styled.elements';
|
import { MenuItemLabel } from '../SideMenu/styled.elements';
|
||||||
|
|
||||||
|
@ -7,19 +8,20 @@ export const SearchWrap = styled.div`
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SearchInput = styled.input.attrs({
|
export const SearchInput = styled.input.attrs(() => ({
|
||||||
className: 'search-input',
|
className: 'search-input',
|
||||||
})`
|
}))`
|
||||||
width: calc(100% - ${props => props.theme.spacing.unit * 8}px);
|
width: calc(100% - ${props => props.theme.spacing.unit * 8}px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0 ${props => props.theme.spacing.unit * 4}px;
|
margin: 0 ${props => props.theme.spacing.unit * 4}px;
|
||||||
padding: 5px ${props => props.theme.spacing.unit * 2}px 5px
|
padding: 5px ${props => props.theme.spacing.unit * 2}px 5px
|
||||||
${props => props.theme.spacing.unit * 4}px;
|
${props => props.theme.spacing.unit * 4}px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-bottom: 1px solid #e1e1e1;
|
border-bottom: 1px solid ${({ theme }) => darken(0.1, theme.menu.backgroundColor)};
|
||||||
|
font-family: ${({ theme }) => theme.typography.fontFamily};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: ${props => props.theme.colors.text};
|
color: ${props => props.theme.menu.textColor};
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
outline: none;
|
outline: none;
|
||||||
`;
|
`;
|
||||||
|
@ -44,7 +46,7 @@ export const SearchIcon = styled((props: { className?: string }) => (
|
||||||
width: 0.9em;
|
width: 0.9em;
|
||||||
|
|
||||||
path {
|
path {
|
||||||
fill: ${props => props.theme.colors.text};
|
fill: ${props => props.theme.menu.textColor};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { SecuritySchemesModel } from '../../services/models';
|
||||||
|
|
||||||
import { H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
import { H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||||
import { OpenAPISecurityScheme } from '../../types';
|
import { OpenAPISecurityScheme } from '../../types';
|
||||||
|
import { titleize } from '../../utils/helpers';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
</tr>
|
</tr>
|
||||||
{scheme.apiKey ? (
|
{scheme.apiKey ? (
|
||||||
<tr>
|
<tr>
|
||||||
<th> {scheme.apiKey.in} parameter name:</th>
|
<th> {titleize(scheme.apiKey.in || '')} parameter name:</th>
|
||||||
<td> {scheme.apiKey.name} </td>
|
<td> {scheme.apiKey.name} </td>
|
||||||
</tr>
|
</tr>
|
||||||
) : scheme.http ? (
|
) : scheme.http ? (
|
||||||
|
@ -93,13 +94,12 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
<th> HTTP Authorization Scheme </th>
|
<th> HTTP Authorization Scheme </th>
|
||||||
<td> {scheme.http.scheme} </td>
|
<td> {scheme.http.scheme} </td>
|
||||||
</tr>,
|
</tr>,
|
||||||
scheme.http.scheme === 'bearer' &&
|
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
|
||||||
scheme.http.bearerFormat && (
|
<tr key="bearer">
|
||||||
<tr key="bearer">
|
<th> Bearer format </th>
|
||||||
<th> Bearer format </th>
|
<td> "{scheme.http.bearerFormat}" </td>
|
||||||
<td> "{scheme.http.bearerFormat}" </td>
|
</tr>
|
||||||
</tr>
|
),
|
||||||
),
|
|
||||||
]
|
]
|
||||||
) : scheme.openId ? (
|
) : scheme.openId ? (
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -46,7 +46,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
<MenuItemLi
|
<MenuItemLi
|
||||||
onClick={this.activate}
|
onClick={this.activate}
|
||||||
depth={item.depth}
|
depth={item.depth}
|
||||||
innerRef={this.saveRef}
|
ref={this.saveRef}
|
||||||
data-item-id={item.id}
|
data-item-id={item.id}
|
||||||
>
|
>
|
||||||
{item.type === 'operation' ? (
|
{item.type === 'operation' ? (
|
||||||
|
@ -57,22 +57,19 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
{item.name}
|
{item.name}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
{(item.depth > 0 &&
|
{(item.depth > 0 && item.items.length > 0 && (
|
||||||
item.items.length > 0 && (
|
<ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} />
|
||||||
<ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} />
|
)) ||
|
||||||
)) ||
|
|
||||||
null}
|
null}
|
||||||
</MenuItemLabel>
|
</MenuItemLabel>
|
||||||
)}
|
)}
|
||||||
{!withoutChildren &&
|
{!withoutChildren && item.items && item.items.length > 0 && (
|
||||||
item.items &&
|
<MenuItems
|
||||||
item.items.length > 0 && (
|
expanded={item.expanded}
|
||||||
<MenuItems
|
items={item.items}
|
||||||
expanded={item.expanded}
|
onActivate={this.props.onActivate}
|
||||||
items={item.items}
|
/>
|
||||||
onActivate={this.props.onActivate}
|
)}
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</MenuItemLi>
|
</MenuItemLi>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -80,22 +77,16 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
|
|
||||||
export interface OperationMenuItemContentProps {
|
export interface OperationMenuItemContentProps {
|
||||||
item: OperationModel;
|
item: OperationModel;
|
||||||
className?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class OperationMenuItemContent extends React.Component<OperationMenuItemContentProps> {
|
export class OperationMenuItemContent extends React.Component<OperationMenuItemContentProps> {
|
||||||
render() {
|
render() {
|
||||||
const { item, className } = this.props;
|
const { item } = this.props;
|
||||||
return (
|
return (
|
||||||
<MenuItemLabel
|
<MenuItemLabel depth={item.depth} active={item.active} deprecated={item.deprecated}>
|
||||||
className={className}
|
|
||||||
depth={item.depth}
|
|
||||||
active={item.active}
|
|
||||||
deprecated={item.deprecated}
|
|
||||||
>
|
|
||||||
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
||||||
<MenuItemTitle width="calc(100% - 32px)">
|
<MenuItemTitle width="calc(100% - 38px)">
|
||||||
{item.name}
|
{item.name}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { IMenuItem, MenuStore } from '../../services/MenuStore';
|
import { IMenuItem, MenuStore } from '../../services/MenuStore';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { MenuItems } from './MenuItems';
|
import { MenuItems } from './MenuItems';
|
||||||
|
|
||||||
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
|
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
|
||||||
|
@ -9,6 +10,7 @@ import { RedocAttribution } from './styled.elements';
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class SideMenu extends React.Component<{ menu: MenuStore; className?: string }> {
|
export class SideMenu extends React.Component<{ menu: MenuStore; className?: string }> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
private _updateScroll?: () => void;
|
private _updateScroll?: () => void;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -23,7 +25,7 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
||||||
>
|
>
|
||||||
<MenuItems items={store.items} onActivate={this.activate} root={true} />
|
<MenuItems items={store.items} onActivate={this.activate} root={true} />
|
||||||
<RedocAttribution>
|
<RedocAttribution>
|
||||||
<a target="_blank" href="https://github.com/Rebilly/ReDoc">
|
<a target="_blank" href="https://github.com/Redocly/redoc">
|
||||||
Documentation Powered by ReDoc
|
Documentation Powered by ReDoc
|
||||||
</a>
|
</a>
|
||||||
</RedocAttribution>
|
</RedocAttribution>
|
||||||
|
@ -32,6 +34,10 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
||||||
}
|
}
|
||||||
|
|
||||||
activate = (item: IMenuItem) => {
|
activate = (item: IMenuItem) => {
|
||||||
|
if (item && item.active && this.context.menuToggle) {
|
||||||
|
return item.expanded ? item.collapse() : item.expand();
|
||||||
|
}
|
||||||
|
|
||||||
this.props.menu.activateAndScroll(item, true);
|
this.props.menu.activateAndScroll(item, true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this._updateScroll) {
|
if (this._updateScroll) {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import * as classnames from 'classnames';
|
import * as classnames from 'classnames';
|
||||||
|
import { darken } from 'polished';
|
||||||
|
|
||||||
import { deprecatedCss, ShelfIcon } from '../../common-elements';
|
import { deprecatedCss, ShelfIcon } from '../../common-elements';
|
||||||
import styled, { css, withProps } from '../../styled-components';
|
import styled, { css } from '../../styled-components';
|
||||||
|
|
||||||
export const OperationBadge = withProps<{ type: string }>(styled.span).attrs({
|
export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
|
||||||
className: props => `operation-type ${props.type}`,
|
className: `operation-type ${props.type}`,
|
||||||
})`
|
}))<{ type: string }>`
|
||||||
width: 26px;
|
width: 32px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: ${props => props.theme.typography.code.fontSize};
|
height: ${props => props.theme.typography.code.fontSize};
|
||||||
line-height: ${props => props.theme.typography.code.fontSize};
|
line-height: ${props => props.theme.typography.code.fontSize};
|
||||||
|
@ -57,21 +58,21 @@ export const OperationBadge = withProps<{ type: string }>(styled.span).attrs({
|
||||||
}
|
}
|
||||||
|
|
||||||
&.head {
|
&.head {
|
||||||
background-color: ${props => props.theme.colors.http.head};
|
background-color: ${props => props.theme.colors.http.head};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function menuItemActiveBg(depth): string {
|
function menuItemActiveBg(depth, { theme }): string {
|
||||||
if (depth > 1) {
|
if (depth > 1) {
|
||||||
return '#e1e1e1';
|
return darken(0.1, theme.menu.backgroundColor);
|
||||||
} else if (depth === 1) {
|
} else if (depth === 1) {
|
||||||
return '#f0f0f0';
|
return darken(0.05, theme.menu.backgroundColor);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MenuItemUl = withProps<{ expanded: boolean }>(styled.ul)`
|
export const MenuItemUl = styled.ul<{ expanded: boolean }>`
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ export const MenuItemUl = withProps<{ expanded: boolean }>(styled.ul)`
|
||||||
${props => (props.expanded ? '' : 'display: none;')};
|
${props => (props.expanded ? '' : 'display: none;')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const MenuItemLi = withProps<{ depth: number }>(styled.li)`
|
export const MenuItemLi = styled.li<{ depth: number }>`
|
||||||
list-style: none inside none;
|
list-style: none inside none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -97,7 +98,7 @@ export const menuItemDepth = {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${props => props.theme.menu.textColor};
|
||||||
`,
|
`,
|
||||||
1: css`
|
1: css`
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
|
@ -107,27 +108,25 @@ export const menuItemDepth = {
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
2: css`
|
2: css`
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${props => props.theme.menu.textColor};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MenuItemLabel = withProps<{
|
export interface MenuItemLabelType {
|
||||||
depth: number;
|
depth: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
deprecated?: boolean;
|
deprecated?: boolean;
|
||||||
type?: string;
|
type?: string;
|
||||||
}>(
|
}
|
||||||
styled.label.attrs({
|
|
||||||
role: 'menuitem',
|
export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
|
||||||
className: props =>
|
role: 'menuitem',
|
||||||
classnames('-depth' + props.depth, {
|
className: classnames('-depth' + props.depth, {
|
||||||
active: props.active,
|
active: props.active,
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
)`
|
}))<MenuItemLabelType>`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: ${props =>
|
color: ${props => (props.active ? props.theme.colors.primary.main : props.theme.menu.textColor)};
|
||||||
props.active ? props.theme.colors.primary.main : props.theme.colors.text.primary};
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 12.5px ${props => props.theme.spacing.unit * 4}px;
|
padding: 12.5px ${props => props.theme.spacing.unit * 4}px;
|
||||||
${({ depth, type, theme }) =>
|
${({ depth, type, theme }) =>
|
||||||
|
@ -136,12 +135,12 @@ export const MenuItemLabel = withProps<{
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||||
${props => menuItemDepth[props.depth]};
|
${props => menuItemDepth[props.depth]};
|
||||||
background-color: ${props => (props.active ? menuItemActiveBg(props.depth) : '')};
|
background-color: ${props => (props.active ? menuItemActiveBg(props.depth, props) : '')};
|
||||||
|
|
||||||
${props => (props.deprecated && deprecatedCss) || ''};
|
${props => (props.deprecated && deprecatedCss) || ''};
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${props => menuItemActiveBg(props.depth)};
|
background-color: ${props => menuItemActiveBg(props.depth, props)};
|
||||||
}
|
}
|
||||||
|
|
||||||
${ShelfIcon} {
|
${ShelfIcon} {
|
||||||
|
@ -153,7 +152,7 @@ export const MenuItemLabel = withProps<{
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const MenuItemTitle = withProps<{ width?: string }>(styled.span)`
|
export const MenuItemTitle = styled.span<{ width?: string }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: ${props => (props.width ? props.width : 'auto')};
|
width: ${props => (props.width ? props.width : 'auto')};
|
||||||
|
@ -173,8 +172,8 @@ export const RedocAttribution = styled.div`
|
||||||
a,
|
a,
|
||||||
a:visited,
|
a:visited,
|
||||||
a:hover {
|
a:hover {
|
||||||
color: ${theme.colors.text.primary} !important;
|
color: ${theme.menu.textColor} !important;
|
||||||
border-top: 1px solid #e1e1e1;
|
border-top: 1px solid ${darken(0.1, theme.menu.backgroundColor)};
|
||||||
padding: ${theme.spacing.unit}px 0;
|
padding: ${theme.spacing.unit}px 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { highlight } from '../../utils';
|
import { highlight } from '../../utils';
|
||||||
|
|
||||||
import { SampleControls, SampleControlsWrap } from '../../common-elements';
|
import { SampleControls, SampleControlsWrap, StyledPre } from '../../common-elements';
|
||||||
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
||||||
import { PrismDiv } from '../../common-elements/PrismDiv';
|
|
||||||
import styled from '../../styled-components';
|
|
||||||
|
|
||||||
const StyledPre = styled(PrismDiv.withComponent('pre'))`
|
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
|
||||||
overflow-x: auto;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
white-space: pre-wrap;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export interface SourceCodeProps {
|
export interface SourceCodeProps {
|
||||||
source: string;
|
source: string;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { MenuStore } from '../../services/MenuStore';
|
import { MenuStore } from '../../services/MenuStore';
|
||||||
import { RedocNormalizedOptions, RedocRawOptions } from '../../services/RedocNormalizedOptions';
|
import { RedocNormalizedOptions, RedocRawOptions } from '../../services/RedocNormalizedOptions';
|
||||||
import styled, { media, withProps } from '../../styled-components';
|
import styled, { media } from '../../styled-components';
|
||||||
import { IS_BROWSER } from '../../utils/index';
|
import { IS_BROWSER } from '../../utils/index';
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { AnimatedChevronButton } from './ChevronSvg';
|
import { AnimatedChevronButton } from './ChevronSvg';
|
||||||
|
@ -21,7 +21,7 @@ export interface StickySidebarProps {
|
||||||
|
|
||||||
const stickyfill = Stickyfill && Stickyfill();
|
const stickyfill = Stickyfill && Stickyfill();
|
||||||
|
|
||||||
const StyledStickySidebar = withProps<{ open?: boolean }>(styled.div)`
|
const StyledStickySidebar = styled.div<{ open?: boolean }>`
|
||||||
width: ${props => props.theme.menu.width};
|
width: ${props => props.theme.menu.width};
|
||||||
background-color: ${props => props.theme.menu.backgroundColor};
|
background-color: ${props => props.theme.menu.backgroundColor};
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -43,6 +43,10 @@ const StyledStickySidebar = withProps<{ open?: boolean }>(styled.div)`
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
display: ${props => (props.open ? 'flex' : 'none')};
|
display: ${props => (props.open ? 'flex' : 'none')};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FloatingButton = styled.div`
|
const FloatingButton = styled.div`
|
||||||
|
@ -60,11 +64,16 @@ const FloatingButton = styled.div`
|
||||||
${media.lessThan('small')`
|
${media.lessThan('small')`
|
||||||
display: flex;
|
display: flex;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
bottom: 44px;
|
bottom: 44px;
|
||||||
|
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
@ -113,7 +122,7 @@ export class StickyResponsiveSidebar extends React.Component<StickySidebarProps>
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
style={style(options)}
|
style={style(options)}
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
innerRef={el => {
|
ref={el => {
|
||||||
this.stickyElement = el as any;
|
this.stickyElement = el as any;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -52,7 +52,14 @@ export class StoreBuilder extends Component<StoreBuilderProps, StoreBuilderState
|
||||||
if (!spec) {
|
if (!spec) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return new AppStore(spec, specUrl, options);
|
try {
|
||||||
|
return new AppStore(spec, specUrl, options);
|
||||||
|
} catch (e) {
|
||||||
|
if (this.props.onLoaded) {
|
||||||
|
this.props.onLoaded(e);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
|
@ -1,34 +1,36 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Components SchemaView discriminator should correctly render discriminator dropdown 1`] = `
|
exports[`Components SchemaView discriminator should correctly render discriminator dropdown 1`] = `
|
||||||
<div>
|
<styled.table>
|
||||||
<styled.div>
|
<tbody>
|
||||||
<Styled(styled.h2)>
|
<Field
|
||||||
Dog
|
field={
|
||||||
</Styled(styled.h2)>
|
FieldModel {
|
||||||
<styled.div>
|
"deprecated": false,
|
||||||
<Markdown
|
"description": "",
|
||||||
dense={true}
|
"example": undefined,
|
||||||
source=""
|
"expanded": false,
|
||||||
/>
|
"explode": false,
|
||||||
</styled.div>
|
"in": undefined,
|
||||||
</styled.div>
|
"kind": "field",
|
||||||
<styled.table>
|
"name": "packSize",
|
||||||
<tbody>
|
"required": false,
|
||||||
<Field
|
"schema": SchemaModel {
|
||||||
field={
|
"activeOneOf": 0,
|
||||||
FieldModel {
|
"constraints": Array [],
|
||||||
|
"default": undefined,
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"example": undefined,
|
"example": undefined,
|
||||||
"expanded": false,
|
"externalDocs": undefined,
|
||||||
"in": undefined,
|
"format": undefined,
|
||||||
"kind": "field",
|
"isCircular": undefined,
|
||||||
"name": "packSize",
|
"isPrimitive": true,
|
||||||
"required": false,
|
"nullable": false,
|
||||||
"schema": SchemaModel {
|
"options": "<<<filtered>>>",
|
||||||
"activeOneOf": 0,
|
"pattern": undefined,
|
||||||
"constraints": Array [],
|
"pointer": "#/components/schemas/Dog/properties/packSize",
|
||||||
|
"rawSchema": Object {
|
||||||
"default": undefined,
|
"default": undefined,
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
|
@ -59,24 +61,44 @@ exports[`Components SchemaView discriminator should correctly render discriminat
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isLast={false}
|
}
|
||||||
key="packSize"
|
isLast={false}
|
||||||
showExamples={false}
|
key="packSize"
|
||||||
/>
|
showExamples={false}
|
||||||
<Field
|
/>
|
||||||
field={
|
<Field
|
||||||
FieldModel {
|
field={
|
||||||
|
FieldModel {
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "",
|
||||||
|
"example": undefined,
|
||||||
|
"expanded": false,
|
||||||
|
"explode": false,
|
||||||
|
"in": undefined,
|
||||||
|
"kind": "field",
|
||||||
|
"name": "type",
|
||||||
|
"required": true,
|
||||||
|
"schema": SchemaModel {
|
||||||
|
"activeOneOf": 0,
|
||||||
|
"constraints": Array [],
|
||||||
|
"default": undefined,
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"example": undefined,
|
"example": undefined,
|
||||||
"expanded": false,
|
"externalDocs": undefined,
|
||||||
"in": undefined,
|
"format": undefined,
|
||||||
"kind": "field",
|
"isCircular": undefined,
|
||||||
"name": "type",
|
"isPrimitive": true,
|
||||||
"required": true,
|
"nullable": false,
|
||||||
"schema": SchemaModel {
|
"options": "<<<filtered>>>",
|
||||||
"activeOneOf": 0,
|
"pattern": undefined,
|
||||||
"constraints": Array [],
|
"pointer": "#/components/schemas/Dog/properties/type",
|
||||||
|
"rawSchema": Object {
|
||||||
|
"default": undefined,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"readOnly": false,
|
||||||
|
"schema": Object {
|
||||||
"default": undefined,
|
"default": undefined,
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
|
@ -8,7 +8,6 @@ export * from './Schema/';
|
||||||
export * from './SearchBox/SearchBox';
|
export * from './SearchBox/SearchBox';
|
||||||
export * from './Operation/Operation';
|
export * from './Operation/Operation';
|
||||||
export * from './Loading/Loading';
|
export * from './Loading/Loading';
|
||||||
export * from './RedocStandalone';
|
|
||||||
export * from './JsonViewer';
|
export * from './JsonViewer';
|
||||||
export * from './Markdown/Markdown';
|
export * from './Markdown/Markdown';
|
||||||
export { StyledMarkdownBlock } from './Markdown/styled.elements';
|
export { StyledMarkdownBlock } from './Markdown/styled.elements';
|
||||||
|
@ -29,3 +28,5 @@ export * from './OptionsProvider';
|
||||||
export * from './SideMenu/';
|
export * from './SideMenu/';
|
||||||
export * from './StickySidebar/StickyResponsiveSidebar';
|
export * from './StickySidebar/StickyResponsiveSidebar';
|
||||||
export * from './SearchBox/SearchBox';
|
export * from './SearchBox/SearchBox';
|
||||||
|
export * from './SchemaDefinition/SchemaDefinition';
|
||||||
|
export * from './SourceCode/SourceCode';
|
||||||
|
|
|
@ -4,7 +4,11 @@ import defaultTheme from '../theme';
|
||||||
|
|
||||||
export default class TestThemeProvider extends React.Component {
|
export default class TestThemeProvider extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return <ThemeProvider theme={defaultTheme}>{this.props.children}</ThemeProvider>;
|
return (
|
||||||
|
<ThemeProvider theme={defaultTheme}>
|
||||||
|
{React.Children.only(this.props.children as any)}
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import 'core-js/es6/promise';
|
import 'core-js/es/promise';
|
||||||
import 'core-js/fn/array/find';
|
|
||||||
import 'core-js/fn/object/assign';
|
|
||||||
import 'core-js/fn/string/ends-with';
|
|
||||||
import 'core-js/fn/string/starts-with';
|
|
||||||
|
|
||||||
import 'core-js/es6/map';
|
import 'core-js/es/array/find';
|
||||||
import 'core-js/es6/symbol';
|
import 'core-js/es/object/assign';
|
||||||
|
import 'core-js/es/string/ends-with';
|
||||||
|
import 'core-js/es/string/starts-with';
|
||||||
|
|
||||||
|
import 'core-js/es/map';
|
||||||
|
import 'core-js/es/symbol';
|
||||||
|
|
||||||
|
import 'unfetch/polyfill/index';
|
||||||
|
import 'url-polyfill';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { observe } from 'mobx';
|
import { Lambda, observe } from 'mobx';
|
||||||
|
|
||||||
import { OpenAPISpec } from '../types';
|
import { OpenAPISpec } from '../types';
|
||||||
import { loadAndBundleSpec } from '../utils/loadAndBundleSpec';
|
import { loadAndBundleSpec } from '../utils/loadAndBundleSpec';
|
||||||
|
@ -10,8 +10,13 @@ import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOption
|
||||||
import { ScrollService } from './ScrollService';
|
import { ScrollService } from './ScrollService';
|
||||||
import { SearchStore } from './SearchStore';
|
import { SearchStore } from './SearchStore';
|
||||||
|
|
||||||
|
import { SchemaDefinition } from '../components/SchemaDefinition/SchemaDefinition';
|
||||||
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
||||||
import { SECURITY_DEFINITIONS_COMPONENT_NAME } from '../utils/openapi';
|
import {
|
||||||
|
SCHEMA_DEFINITION_JSX_NAME,
|
||||||
|
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
||||||
|
SECURITY_DEFINITIONS_JSX_NAME,
|
||||||
|
} from '../utils/openapi';
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
menu: {
|
menu: {
|
||||||
|
@ -58,7 +63,7 @@ export class AppStore {
|
||||||
marker = new MarkerService();
|
marker = new MarkerService();
|
||||||
|
|
||||||
private scroll: ScrollService;
|
private scroll: ScrollService;
|
||||||
private disposer;
|
private disposer: Lambda | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
spec: OpenAPISpec,
|
spec: OpenAPISpec,
|
||||||
|
@ -96,7 +101,9 @@ export class AppStore {
|
||||||
dispose() {
|
dispose() {
|
||||||
this.scroll.dispose();
|
this.scroll.dispose();
|
||||||
this.menu.dispose();
|
this.menu.dispose();
|
||||||
this.disposer();
|
if (this.disposer != null) {
|
||||||
|
this.disposer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,5 +156,18 @@ const DEFAULT_OPTIONS: RedocRawOptions = {
|
||||||
securitySchemes: store.spec.securitySchemes,
|
securitySchemes: store.spec.securitySchemes,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
[SECURITY_DEFINITIONS_JSX_NAME]: {
|
||||||
|
component: SecurityDefs,
|
||||||
|
propsSelector: (store: AppStore) => ({
|
||||||
|
securitySchemes: store.spec.securitySchemes,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[SCHEMA_DEFINITION_JSX_NAME]: {
|
||||||
|
component: SchemaDefinition,
|
||||||
|
propsSelector: (store: AppStore) => ({
|
||||||
|
parser: store.spec.parser,
|
||||||
|
options: store.options,
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,10 @@ export class ClipboardService {
|
||||||
if ((document as any).selection) {
|
if ((document as any).selection) {
|
||||||
(document as any).selection.empty();
|
(document as any).selection.empty();
|
||||||
} else if (window.getSelection) {
|
} else if (window.getSelection) {
|
||||||
window.getSelection().removeAllRanges();
|
const selection = window.getSelection();
|
||||||
|
if (selection) {
|
||||||
|
selection.removeAllRanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class HistoryService {
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentId(): string {
|
get currentId(): string {
|
||||||
return IS_BROWSER ? window.location.hash.substring(1) : '';
|
return IS_BROWSER ? decodeURIComponent(window.location.hash.substring(1)) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
linkForId(id: string) {
|
linkForId(id: string) {
|
||||||
|
|
37
src/services/Labels.ts
Normal file
37
src/services/Labels.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
export interface LabelsConfig {
|
||||||
|
enum: string;
|
||||||
|
enumSingleValue: string;
|
||||||
|
enumArray: string;
|
||||||
|
default: string;
|
||||||
|
deprecated: string;
|
||||||
|
example: string;
|
||||||
|
nullable: string;
|
||||||
|
recursive: string;
|
||||||
|
arrayOf: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LabelsConfigRaw = Partial<LabelsConfig>;
|
||||||
|
|
||||||
|
const labels: LabelsConfig = {
|
||||||
|
enum: 'Enum',
|
||||||
|
enumSingleValue: 'Value',
|
||||||
|
enumArray: 'Items',
|
||||||
|
default: 'Default',
|
||||||
|
deprecated: 'Deprecated',
|
||||||
|
example: 'Example',
|
||||||
|
nullable: 'Nullable',
|
||||||
|
recursive: 'Recursive',
|
||||||
|
arrayOf: 'Array of ',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setRedocLabels(_labels?: LabelsConfigRaw) {
|
||||||
|
Object.assign(labels, _labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function l(key: keyof LabelsConfig, idx?: number): string {
|
||||||
|
const label = labels[key];
|
||||||
|
if (idx !== undefined) {
|
||||||
|
return label[idx];
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import * as marked from 'marked';
|
import * as marked from 'marked';
|
||||||
|
|
||||||
import { highlight, safeSlugify } from '../utils';
|
import { highlight, safeSlugify, unescapeHTMLChars } from '../utils';
|
||||||
import { AppStore } from './AppStore';
|
import { AppStore } from './AppStore';
|
||||||
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
||||||
|
|
||||||
|
@ -13,14 +13,18 @@ marked.setOptions({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const LEGACY_REGEXP = '^\\s*<!-- ReDoc-Inject:\\s+?<{component}\\s*?/?>\\s+?-->\\s*$';
|
export const LEGACY_REGEXP = '^ {0,3}<!-- ReDoc-Inject:\\s+?<({component}).*?/?>\\s+?-->\\s*$';
|
||||||
export const MDX_COMPONENT_REGEXP = '^\\s*<{component}\\s*?/>\\s*$';
|
|
||||||
|
// prettier-ignore
|
||||||
|
export const MDX_COMPONENT_REGEXP = '(?:^ {0,3}<({component})([\\s\\S]*?)>([\\s\\S]*?)</\\2>' // with children
|
||||||
|
+ '|^ {0,3}<({component})([\\s\\S]*?)(?:/>|\\n{2,}))'; // self-closing
|
||||||
|
|
||||||
export const COMPONENT_REGEXP = '(?:' + LEGACY_REGEXP + '|' + MDX_COMPONENT_REGEXP + ')';
|
export const COMPONENT_REGEXP = '(?:' + LEGACY_REGEXP + '|' + MDX_COMPONENT_REGEXP + ')';
|
||||||
|
|
||||||
export interface MDXComponentMeta {
|
export interface MDXComponentMeta {
|
||||||
component: React.ComponentType;
|
component: React.ComponentType;
|
||||||
propsSelector: (store?: AppStore) => any;
|
propsSelector: (store?: AppStore) => any;
|
||||||
attrs?: object;
|
props?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkdownHeading {
|
export interface MarkdownHeading {
|
||||||
|
@ -37,11 +41,8 @@ export function buildComponentComment(name: string) {
|
||||||
|
|
||||||
export class MarkdownRenderer {
|
export class MarkdownRenderer {
|
||||||
static containsComponent(rawText: string, componentName: string) {
|
static containsComponent(rawText: string, componentName: string) {
|
||||||
const anyCompRegexp = new RegExp(
|
const compRegexp = new RegExp(COMPONENT_REGEXP.replace(/{component}/g, componentName), 'gmi');
|
||||||
COMPONENT_REGEXP.replace(/{component}/g, componentName),
|
return compRegexp.test(rawText);
|
||||||
'gmi',
|
|
||||||
);
|
|
||||||
return anyCompRegexp.test(rawText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headings: MarkdownHeading[] = [];
|
headings: MarkdownHeading[] = [];
|
||||||
|
@ -64,6 +65,7 @@ export class MarkdownRenderer {
|
||||||
container: MarkdownHeading[] = this.headings,
|
container: MarkdownHeading[] = this.headings,
|
||||||
parentId?: string,
|
parentId?: string,
|
||||||
): MarkdownHeading {
|
): MarkdownHeading {
|
||||||
|
name = unescapeHTMLChars(name);
|
||||||
const item = {
|
const item = {
|
||||||
id: parentId ? `${parentId}/${safeSlugify(name)}` : `section/${safeSlugify(name)}`,
|
id: parentId ? `${parentId}/${safeSlugify(name)}` : `section/${safeSlugify(name)}`,
|
||||||
name,
|
name,
|
||||||
|
@ -87,7 +89,7 @@ export class MarkdownRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
attachHeadingsDescriptions(rawText: string) {
|
attachHeadingsDescriptions(rawText: string) {
|
||||||
const buildRegexp = heading => {
|
const buildRegexp = (heading: MarkdownHeading) => {
|
||||||
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}`);
|
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ export class MarkdownRenderer {
|
||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
headingRule = (text: string, level: number, raw: string) => {
|
headingRule = (text: string, level: number, raw: string, slugger: marked.Slugger) => {
|
||||||
if (level === 1) {
|
if (level === 1) {
|
||||||
this.currentTopHeading = this.saveHeading(text, level);
|
this.currentTopHeading = this.saveHeading(text, level);
|
||||||
} else if (level === 2) {
|
} else if (level === 2) {
|
||||||
|
@ -128,7 +130,7 @@ export class MarkdownRenderer {
|
||||||
this.currentTopHeading && this.currentTopHeading.id,
|
this.currentTopHeading && this.currentTopHeading.id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return this.originalHeadingRule(text, level, raw);
|
return this.originalHeadingRule(text, level, raw, slugger);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderMd(rawText: string, extractHeadings: boolean = false): string {
|
renderMd(rawText: string, extractHeadings: boolean = false): string {
|
||||||
|
@ -147,32 +149,41 @@ export class MarkdownRenderer {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rewrite this completelly! Regexp-based 👎
|
// regexp-based 👎: remark is slow and too big so for now using marked + regexps soup
|
||||||
// Use marked ecosystem
|
|
||||||
renderMdWithComponents(rawText: string): Array<string | MDXComponentMeta> {
|
renderMdWithComponents(rawText: string): Array<string | MDXComponentMeta> {
|
||||||
const components = this.options && this.options.allowedMdComponents;
|
const components = this.options && this.options.allowedMdComponents;
|
||||||
if (!components || Object.keys(components).length === 0) {
|
if (!components || Object.keys(components).length === 0) {
|
||||||
return [this.renderMd(rawText)];
|
return [this.renderMd(rawText)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const componentDefs: string[] = [];
|
const names = Object.keys(components).join('|');
|
||||||
const names = '(?:' + Object.keys(components).join('|') + ')';
|
const componentsRegexp = new RegExp(COMPONENT_REGEXP.replace(/{component}/g, names), 'mig');
|
||||||
|
|
||||||
const anyCompRegexp = new RegExp(
|
const htmlParts: string[] = [];
|
||||||
COMPONENT_REGEXP.replace(/{component}/g, '(' + names + '.*?)'),
|
const componentDefs: MDXComponentMeta[] = [];
|
||||||
'gmi',
|
|
||||||
);
|
let match = componentsRegexp.exec(rawText);
|
||||||
let match = anyCompRegexp.exec(rawText);
|
let lasxtIdx = 0;
|
||||||
while (match) {
|
while (match) {
|
||||||
componentDefs.push(match[1] || match[2]);
|
htmlParts.push(rawText.substring(lasxtIdx, match.index));
|
||||||
match = anyCompRegexp.exec(rawText);
|
lasxtIdx = componentsRegexp.lastIndex;
|
||||||
}
|
const compName = match[1] || match[2] || match[5];
|
||||||
|
const componentMeta = components[compName];
|
||||||
|
|
||||||
|
const props = match[3] || match[6];
|
||||||
|
const children = match[4];
|
||||||
|
|
||||||
|
if (componentMeta) {
|
||||||
|
componentDefs.push({
|
||||||
|
component: componentMeta.component,
|
||||||
|
propsSelector: componentMeta.propsSelector,
|
||||||
|
props: { ...parseProps(props), ...componentMeta.props, children },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
match = componentsRegexp.exec(rawText);
|
||||||
|
}
|
||||||
|
htmlParts.push(rawText.substring(lasxtIdx));
|
||||||
|
|
||||||
const splitCompRegexp = new RegExp(
|
|
||||||
COMPONENT_REGEXP.replace(/{component}/g, names + '.*?'),
|
|
||||||
'mi',
|
|
||||||
);
|
|
||||||
const htmlParts = rawText.split(splitCompRegexp);
|
|
||||||
const res: any[] = [];
|
const res: any[] = [];
|
||||||
for (let i = 0; i < htmlParts.length; i++) {
|
for (let i = 0; i < htmlParts.length; i++) {
|
||||||
const htmlPart = htmlParts[i];
|
const htmlPart = htmlParts[i];
|
||||||
|
@ -180,46 +191,37 @@ export class MarkdownRenderer {
|
||||||
res.push(this.renderMd(htmlPart));
|
res.push(this.renderMd(htmlPart));
|
||||||
}
|
}
|
||||||
if (componentDefs[i]) {
|
if (componentDefs[i]) {
|
||||||
const { componentName, attrs } = parseComponent(componentDefs[i]);
|
res.push(componentDefs[i]);
|
||||||
if (!componentName) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
res.push({
|
|
||||||
...components[componentName],
|
|
||||||
attrs,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseComponent(
|
function parseProps(props: string): object {
|
||||||
htmlTag: string,
|
if (!props) {
|
||||||
): {
|
return {};
|
||||||
componentName?: string;
|
|
||||||
attrs: any;
|
|
||||||
} {
|
|
||||||
const match = /([\w_-]+)(\s+[\w_-]+\s*={[^}]*?})*/.exec(htmlTag);
|
|
||||||
if (match === null || match.length <= 1) {
|
|
||||||
return { componentName: undefined, attrs: {} };
|
|
||||||
}
|
}
|
||||||
const componentName = match[1];
|
|
||||||
const attrs = {};
|
|
||||||
for (let i = 2; i < match.length; i++) {
|
|
||||||
if (!match[i]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const [name, value] = match[i]
|
|
||||||
.trim()
|
|
||||||
.split('=')
|
|
||||||
.map(p => p.trim());
|
|
||||||
|
|
||||||
// tslint:disable-next-line
|
const regex = /([\w-]+)\s*=\s*(?:{([^}]+?)}|"([^"]+?)")/gim;
|
||||||
attrs[name] = value.startsWith('{') ? eval(value.substr(1, value.length - 2)) : eval(value);
|
const parsed = {};
|
||||||
|
let match;
|
||||||
|
// tslint:disable-next-line
|
||||||
|
while ((match = regex.exec(props)) !== null) {
|
||||||
|
if (match[3]) {
|
||||||
|
// string prop match (in double quotes)
|
||||||
|
parsed[match[1]] = match[3];
|
||||||
|
} else if (match[2]) {
|
||||||
|
// jsx prop match (in curly braces)
|
||||||
|
let val;
|
||||||
|
try {
|
||||||
|
val = JSON.parse(match[2]);
|
||||||
|
} catch (e) {
|
||||||
|
/* noop */
|
||||||
|
}
|
||||||
|
parsed[match[1]] = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
componentName,
|
return parsed;
|
||||||
attrs,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,8 @@ export class MenuBuilder {
|
||||||
|
|
||||||
const items: ContentItemModel[] = [];
|
const items: ContentItemModel[] = [];
|
||||||
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
||||||
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', options));
|
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', undefined, 1, options));
|
||||||
if (spec['x-tagGroups']) {
|
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
||||||
items.push(
|
items.push(
|
||||||
...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
|
...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
|
||||||
);
|
);
|
||||||
|
@ -59,14 +59,16 @@ export class MenuBuilder {
|
||||||
*/
|
*/
|
||||||
static addMarkdownItems(
|
static addMarkdownItems(
|
||||||
description: string,
|
description: string,
|
||||||
|
parent: GroupModel | undefined,
|
||||||
|
initialDepth: number,
|
||||||
options: RedocNormalizedOptions,
|
options: RedocNormalizedOptions,
|
||||||
): ContentItemModel[] {
|
): ContentItemModel[] {
|
||||||
const renderer = new MarkdownRenderer(options);
|
const renderer = new MarkdownRenderer(options);
|
||||||
const headings = renderer.extractHeadings(description || '');
|
const headings = renderer.extractHeadings(description || '');
|
||||||
|
|
||||||
const mapHeadingsDeep = (parent, items, depth = 1) =>
|
const mapHeadingsDeep = (_parent, items, depth = 1) =>
|
||||||
items.map(heading => {
|
items.map(heading => {
|
||||||
const group = new GroupModel('section', heading, parent);
|
const group = new GroupModel('section', heading, _parent);
|
||||||
group.depth = depth;
|
group.depth = depth;
|
||||||
if (heading.items) {
|
if (heading.items) {
|
||||||
group.items = mapHeadingsDeep(group, heading.items, depth + 1);
|
group.items = mapHeadingsDeep(group, heading.items, depth + 1);
|
||||||
|
@ -82,7 +84,7 @@ export class MenuBuilder {
|
||||||
return group;
|
return group;
|
||||||
});
|
});
|
||||||
|
|
||||||
return mapHeadingsDeep(undefined, headings);
|
return mapHeadingsDeep(parent, headings, initialDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,15 +146,22 @@ export class MenuBuilder {
|
||||||
}
|
}
|
||||||
const item = new GroupModel('tag', tag, parent);
|
const item = new GroupModel('tag', tag, parent);
|
||||||
item.depth = GROUP_DEPTH + 1;
|
item.depth = GROUP_DEPTH + 1;
|
||||||
item.items = this.getOperationsItems(parser, item, tag, item.depth + 1, options);
|
|
||||||
|
|
||||||
// don't put empty tag into content, instead put its operations
|
// don't put empty tag into content, instead put its operations
|
||||||
if (tag.name === '') {
|
if (tag.name === '') {
|
||||||
const items = this.getOperationsItems(parser, undefined, tag, item.depth + 1, options);
|
const items = [
|
||||||
|
...MenuBuilder.addMarkdownItems(tag.description || '', item, item.depth + 1, options),
|
||||||
|
...this.getOperationsItems(parser, undefined, tag, item.depth + 1, options),
|
||||||
|
];
|
||||||
res.push(...items);
|
res.push(...items);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.items = [
|
||||||
|
...MenuBuilder.addMarkdownItems(tag.description || '', item, item.depth + 1, options),
|
||||||
|
...this.getOperationsItems(parser, item, tag, item.depth + 1, options),
|
||||||
|
];
|
||||||
|
|
||||||
res.push(item);
|
res.push(item);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -187,6 +187,7 @@ export class OpenAPIParser {
|
||||||
...schema,
|
...schema,
|
||||||
allOf: undefined,
|
allOf: undefined,
|
||||||
parentRefs: [],
|
parentRefs: [],
|
||||||
|
title: schema.title || (isNamedDefinition($ref) ? JsonPointer.baseName($ref) : undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
// avoid mutating inner objects
|
// avoid mutating inner objects
|
||||||
|
@ -257,17 +258,12 @@ export class OpenAPIParser {
|
||||||
receiver.parentRefs!.push(subSchemaRef);
|
receiver.parentRefs!.push(subSchemaRef);
|
||||||
if (receiver.title === undefined && isNamedDefinition(subSchemaRef)) {
|
if (receiver.title === undefined && isNamedDefinition(subSchemaRef)) {
|
||||||
// this is not so correct behaviour. comented out for now
|
// this is not so correct behaviour. comented out for now
|
||||||
// ref: https://github.com/Rebilly/ReDoc/issues/601
|
// ref: https://github.com/Redocly/redoc/issues/601
|
||||||
// receiver.title = JsonPointer.baseName(subSchemaRef);
|
// receiver.title = JsonPointer.baseName(subSchemaRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// name of definition or title on top level
|
|
||||||
if (schema.title === undefined && isNamedDefinition($ref)) {
|
|
||||||
receiver.title = JsonPointer.baseName($ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
return receiver;
|
return receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,6 +287,12 @@ export class OpenAPIParser {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exitParents(shema: MergedOpenAPISchema) {
|
||||||
|
for (const parent$ref of shema.parentRefs || []) {
|
||||||
|
this.exitRef({ $ref: parent$ref });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private hoistOneOfs(schema: OpenAPISchema) {
|
private hoistOneOfs(schema: OpenAPISchema) {
|
||||||
if (schema.allOf === undefined) {
|
if (schema.allOf === undefined) {
|
||||||
return schema;
|
return schema;
|
||||||
|
@ -304,9 +306,14 @@ export class OpenAPIParser {
|
||||||
const afterAllOf = allOf.slice(i + 1);
|
const afterAllOf = allOf.slice(i + 1);
|
||||||
return {
|
return {
|
||||||
oneOf: sub.oneOf.map(part => {
|
oneOf: sub.oneOf.map(part => {
|
||||||
return this.mergeAllOf({
|
const merged = this.mergeAllOf({
|
||||||
allOf: [...beforeAllOf, part, ...afterAllOf],
|
allOf: [...beforeAllOf, part, ...afterAllOf],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// each oneOf should be independent so exiting all the parent refs
|
||||||
|
// otherwise it will cause false-positive recursive detection
|
||||||
|
this.exitParents(merged);
|
||||||
|
return merged;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import defaultTheme, { ResolvedThemeInterface, resolveTheme, ThemeInterface } fr
|
||||||
import { querySelector } from '../utils/dom';
|
import { querySelector } from '../utils/dom';
|
||||||
import { isNumeric, mergeObjects } from '../utils/helpers';
|
import { isNumeric, mergeObjects } from '../utils/helpers';
|
||||||
|
|
||||||
|
import { LabelsConfigRaw, setRedocLabels } from './Labels';
|
||||||
import { MDXComponentMeta } from './MarkdownRenderer';
|
import { MDXComponentMeta } from './MarkdownRenderer';
|
||||||
|
|
||||||
export interface RedocRawOptions {
|
export interface RedocRawOptions {
|
||||||
|
@ -10,6 +11,7 @@ export interface RedocRawOptions {
|
||||||
hideHostname?: boolean | string;
|
hideHostname?: boolean | string;
|
||||||
expandResponses?: string | 'all';
|
expandResponses?: string | 'all';
|
||||||
requiredPropsFirst?: boolean | string;
|
requiredPropsFirst?: boolean | string;
|
||||||
|
sortPropsAlphabetically?: boolean | string;
|
||||||
noAutoAuth?: boolean | string;
|
noAutoAuth?: boolean | string;
|
||||||
nativeScrollbars?: boolean | string;
|
nativeScrollbars?: boolean | string;
|
||||||
pathInMiddlePanel?: boolean | string;
|
pathInMiddlePanel?: boolean | string;
|
||||||
|
@ -17,10 +19,18 @@ export interface RedocRawOptions {
|
||||||
hideLoading?: boolean | string;
|
hideLoading?: boolean | string;
|
||||||
hideDownloadButton?: boolean | string;
|
hideDownloadButton?: boolean | string;
|
||||||
disableSearch?: boolean | string;
|
disableSearch?: boolean | string;
|
||||||
|
onlyRequiredInSamples?: boolean | string;
|
||||||
|
showExtensions?: boolean | string | string[];
|
||||||
|
hideSingleRequestSampleTab?: boolean | string;
|
||||||
|
menuToggle?: boolean | string;
|
||||||
|
jsonSampleExpandLevel?: number | string | 'all';
|
||||||
|
|
||||||
unstable_ignoreMimeParameters?: boolean;
|
unstable_ignoreMimeParameters?: boolean;
|
||||||
|
|
||||||
allowedMdComponents?: Dict<MDXComponentMeta>;
|
allowedMdComponents?: Dict<MDXComponentMeta>;
|
||||||
|
|
||||||
|
labels?: LabelsConfigRaw;
|
||||||
|
enumSkipQuotes?: boolean | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function argValueToBoolean(val?: string | boolean): boolean {
|
function argValueToBoolean(val?: string | boolean): boolean {
|
||||||
|
@ -88,42 +98,85 @@ export class RedocNormalizedOptions {
|
||||||
return () => 0;
|
return () => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static normalizeShowExtensions(value: RedocRawOptions['showExtensions']): string[] | boolean {
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value.split(',').map(ext => ext.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static normalizeJsonSampleExpandLevel(level?: number | string | 'all'): number {
|
||||||
|
if (level === 'all') {
|
||||||
|
return +Infinity;
|
||||||
|
}
|
||||||
|
if (!isNaN(Number(level))) {
|
||||||
|
return Math.ceil(Number(level));
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
theme: ResolvedThemeInterface;
|
theme: ResolvedThemeInterface;
|
||||||
scrollYOffset: () => number;
|
scrollYOffset: () => number;
|
||||||
hideHostname: boolean;
|
hideHostname: boolean;
|
||||||
expandResponses: { [code: string]: boolean } | 'all';
|
expandResponses: { [code: string]: boolean } | 'all';
|
||||||
requiredPropsFirst: boolean;
|
requiredPropsFirst: boolean;
|
||||||
|
sortPropsAlphabetically: boolean;
|
||||||
noAutoAuth: boolean;
|
noAutoAuth: boolean;
|
||||||
nativeScrollbars: boolean;
|
nativeScrollbars: boolean;
|
||||||
pathInMiddlePanel: boolean;
|
pathInMiddlePanel: boolean;
|
||||||
untrustedSpec: boolean;
|
untrustedSpec: boolean;
|
||||||
hideDownloadButton: boolean;
|
hideDownloadButton: boolean;
|
||||||
disableSearch: boolean;
|
disableSearch: boolean;
|
||||||
|
onlyRequiredInSamples: boolean;
|
||||||
|
showExtensions: boolean | string[];
|
||||||
|
hideSingleRequestSampleTab: boolean;
|
||||||
|
menuToggle: boolean;
|
||||||
|
jsonSampleExpandLevel: number;
|
||||||
|
enumSkipQuotes: boolean;
|
||||||
|
|
||||||
/* tslint:disable-next-line */
|
/* tslint:disable-next-line */
|
||||||
unstable_ignoreMimeParameters: boolean;
|
unstable_ignoreMimeParameters: boolean;
|
||||||
allowedMdComponents: Dict<MDXComponentMeta>;
|
allowedMdComponents: Dict<MDXComponentMeta>;
|
||||||
|
|
||||||
constructor(raw: RedocRawOptions, defaults: RedocRawOptions = {}) {
|
constructor(raw: RedocRawOptions, defaults: RedocRawOptions = {}) {
|
||||||
let hook;
|
|
||||||
raw = { ...defaults, ...raw };
|
raw = { ...defaults, ...raw };
|
||||||
if (raw.theme && raw.theme.extensionsHook) {
|
const hook = raw.theme && raw.theme.extensionsHook;
|
||||||
hook = raw.theme.extensionsHook;
|
this.theme = resolveTheme(
|
||||||
raw.theme.extensionsHook = undefined;
|
mergeObjects({} as any, defaultTheme, { ...raw.theme, extensionsHook: undefined }),
|
||||||
}
|
);
|
||||||
this.theme = resolveTheme(mergeObjects({} as any, defaultTheme, raw.theme || {}));
|
|
||||||
this.theme.extensionsHook = hook;
|
this.theme.extensionsHook = hook as any;
|
||||||
|
|
||||||
|
// do not support dynamic labels changes. Labels should be configured before
|
||||||
|
setRedocLabels(raw.labels);
|
||||||
|
|
||||||
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
|
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
|
||||||
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
|
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
|
||||||
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
|
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
|
||||||
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
|
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
|
||||||
|
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
|
||||||
this.noAutoAuth = argValueToBoolean(raw.noAutoAuth);
|
this.noAutoAuth = argValueToBoolean(raw.noAutoAuth);
|
||||||
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
|
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
|
||||||
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
|
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
|
||||||
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
|
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
|
||||||
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
|
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
|
||||||
this.disableSearch = argValueToBoolean(raw.disableSearch);
|
this.disableSearch = argValueToBoolean(raw.disableSearch);
|
||||||
|
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
|
||||||
|
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
||||||
|
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
||||||
|
this.menuToggle = argValueToBoolean(raw.menuToggle);
|
||||||
|
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
|
||||||
|
raw.jsonSampleExpandLevel,
|
||||||
|
);
|
||||||
|
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
|
||||||
|
|
||||||
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
|
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,8 @@ export class ScrollService {
|
||||||
}
|
}
|
||||||
element.scrollIntoView();
|
element.scrollIntoView();
|
||||||
if (this._scrollParent && this._scrollParent.scrollBy) {
|
if (this._scrollParent && this._scrollParent.scrollBy) {
|
||||||
(this._scrollParent.scrollBy as any)(0, -this.options.scrollYOffset());
|
// adding 1 account rounding errors in case scrollYOffset is float-number
|
||||||
|
(this._scrollParent.scrollBy as any)(0, -this.options.scrollYOffset() + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as lunr from 'lunr';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
require('core-js/es6/promise'); // bundle into worker
|
require('core-js/es/promise'); // bundle into worker
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// nope
|
// nope
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,11 +53,47 @@ describe('Markdown renderer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renderMdWithComponents should parse attribute names', () => {
|
test('renderMdWithComponents should parse attribute names', () => {
|
||||||
const source = '<security-definitions pointer={"test"}/>';
|
const source = '<security-definitions pointer={"test"} />';
|
||||||
const parts = renderer.renderMdWithComponents(source);
|
const parts = renderer.renderMdWithComponents(source);
|
||||||
expect(parts).toHaveLength(1);
|
expect(parts).toHaveLength(1);
|
||||||
const part = parts[0] as MDXComponentMeta;
|
const part = parts[0] as MDXComponentMeta;
|
||||||
expect(part.component).toBe(TestComponent);
|
expect(part.component).toBe(TestComponent);
|
||||||
expect(part.attrs).toEqual({ pointer: 'test' });
|
expect(part.props).toEqual({ pointer: 'test' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renderMdWithComponents should parse string attribute names', () => {
|
||||||
|
const source = '<security-definitions pointer="test" />';
|
||||||
|
const parts = renderer.renderMdWithComponents(source);
|
||||||
|
expect(parts).toHaveLength(1);
|
||||||
|
const part = parts[0] as MDXComponentMeta;
|
||||||
|
expect(part.component).toBe(TestComponent);
|
||||||
|
expect(part.props).toEqual({ pointer: 'test' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renderMdWithComponents should parse string attribute with spaces new-lines', () => {
|
||||||
|
const source = '<security-definitions \n pointer = "test" \n flag-dash={ \nfalse } />';
|
||||||
|
const parts = renderer.renderMdWithComponents(source);
|
||||||
|
expect(parts).toHaveLength(1);
|
||||||
|
const part = parts[0] as MDXComponentMeta;
|
||||||
|
expect(part.component).toBe(TestComponent);
|
||||||
|
expect(part.props).toEqual({ pointer: 'test', 'flag-dash': false });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renderMdWithComponents should parse children', () => {
|
||||||
|
const source = '<security-definitions> Test Test </security-definitions>';
|
||||||
|
const parts = renderer.renderMdWithComponents(source);
|
||||||
|
expect(parts).toHaveLength(1);
|
||||||
|
const part = parts[0] as MDXComponentMeta;
|
||||||
|
expect(part.component).toBe(TestComponent);
|
||||||
|
expect(part.props).toEqual({ children: ' Test Test ' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renderMdWithComponents should parse children', () => {
|
||||||
|
const source = '<security-definitions> Test Test </security-definitions>';
|
||||||
|
const parts = renderer.renderMdWithComponents(source);
|
||||||
|
expect(parts).toHaveLength(1);
|
||||||
|
const part = parts[0] as MDXComponentMeta;
|
||||||
|
expect(part.component).toBe(TestComponent);
|
||||||
|
expect(part.props).toEqual({ children: ' Test Test ' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,6 +21,7 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"title": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
|
@ -38,6 +39,7 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"title": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -59,6 +61,7 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"title": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
|
@ -76,6 +79,7 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"title": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user