Merge branch 'develop' into 1200

# Conflicts:
#	package.json
#	src/components/ApiInfo/ApiInfo.tsx
#	src/components/SearchBox/styled.elements.tsx
#	src/services/RedocNormalizedOptions.ts
#	src/theme.ts
This commit is contained in:
akumarsingh 2020-03-18 14:57:16 -04:00
commit 49aca85e22
52 changed files with 20602 additions and 12892 deletions

View File

@ -9,4 +9,4 @@
!webpack.config.ts !webpack.config.ts
!package.json !package.json
!yarn.lock !package-lock.json

49
.eslintrc.js Normal file
View File

@ -0,0 +1,49 @@
module.exports = {
env: {
browser: true,
},
parser: '@typescript-eslint/parser',
extends: ['plugin:react/recommended', 'plugin:@typescript-eslint/recommended'],
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
createDefaultProgram: true,
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: 'detect',
},
},
plugins: ['@typescript-eslint', 'import'],
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'react/prop-types': 'off',
'import/no-extraneous-dependencies': 'error',
'import/no-internal-modules': [
'error',
{
allow: [
'prismjs/**',
'perfect-scrollbar/**',
'react-dom/*',
'core-js/**',
'memoize-one/**',
'unfetch/**',
'raf/polyfill',
'**/fixtures/**', // for tests
],
},
],
},
};

View File

@ -15,56 +15,56 @@ Hi! We're really excited that you are interested in contributing to ReDoc. Befor
Before submitting a pull request, please make sure the following is done: Before submitting a pull request, please make sure the following is done:
1. Fork the repository and create your branch from master. 1. Fork the repository and create your branch from master.
2. Run `yarn` in the repository root. 2. Run `npm install` in the repository root.
3. If youve fixed a bug or added code that should be tested, add tests! 3. If youve fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 4. Ensure the test suite passes (`npm test`). Tip: `npm test -- --watch TestName` is helpful in development.
5. Format your code with prettier (`yarn prettier`). 5. Format your code with prettier (`npm run prettier`).
## Development Setup ## Development Setup
You will need [Node.js](http://nodejs.org) at `v8.0.0+` and [Yarn](https://yarnpkg.com/en/) at `v1.2.0+` You will need [Node.js](http://nodejs.org) at `v12.0.0+`.
After cloning the repo, run: After cloning the repo, run:
```bash ```bash
$ yarn install # or npm $ npm install # or npm
``` ```
### Commonly used NPM scripts ### Commonly used NPM scripts
``` bash ``` bash
# dev-server, watch and auto reload playground # dev-server, watch and auto reload playground
$ yarn start $ npm start
# start playground app in production environment # start playground app in production environment
$ yarn start:prod $ npm run start:prod
# runt tslint # runt tslint
$ yarn lint $ npm run lint
# try autofix tslint issues # try autofix tslint issues
$ yarn lint --fix $ npm run lint -- --fix
# run unit tests # run unit tests
$ yarn unit $ npm run unit
# run e2e tests # run e2e tests
$ yarn e2e $ npm run e2e
# open cypress UI to debug e2e test # open cypress UI to debug e2e test
$ yarn cy:open $ npm run cy:open
# run the full test suite, include linting / unit / e2e # run the full test suite, include linting / unit / e2e
$ yarn test $ npm test
# prepare bundles # prepare bundles
$ yarn bundle $ npm run bundle
# format the code using prettier # format the code using prettier
$ yarn prettier $ npm run prettier
# auto-generate changelog # auto-generate changelog
$ yarn changelog $ npm run changelog
``` ```
There are some other scripts available in the `scripts` section of the `package.json` file. There are some other scripts available in the `scripts` section of the `package.json` file.

View File

@ -13,9 +13,8 @@ jobs:
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 10.x node-version: 10.x
- name: yarn install, build, and test - name: npm install, build, and test
run: | run: |
npm install -g yarn npm install
yarn install npm run bundle
yarn bundle npm test
yarn test

2
.gitignore vendored
View File

@ -34,7 +34,7 @@ cli/index.js
/coverage /coverage
.ghpages-tmp .ghpages-tmp
stats.json stats.json
/package-lock.json yarn.lock
.idea/ .idea/
*.iml *.iml

View File

@ -2,7 +2,6 @@ language: node_js
node_js: node_js:
- '10' - '10'
cache: cache:
yarn: true
directories: directories:
- "~/.cache" - "~/.cache"
env: env:
@ -35,6 +34,6 @@ deploy:
tags: true tags: true
- provider: script - provider: script
skip_cleanup: true skip_cleanup: true
script: yarn deploy:demo script: npm run deploy:demo
on: on:
tags: true tags: true

View File

@ -1,3 +1,28 @@
# [2.0.0-rc.24](https://github.com/Redocly/redoc/compare/v2.0.0-rc.23...v2.0.0-rc.24) (2020-03-17)
### Bug Fixes
* Add debounce for 300 ms when searching ([#1089](https://github.com/Redocly/redoc/issues/1089)) ([373f018](https://github.com/Redocly/redoc/commit/373f018d0c183f83d07a4dbad4a4e2c9ab159f69))
* do not load SearchWorker if disableSearch is `true` ([#1191](https://github.com/Redocly/redoc/issues/1191)) ([af415e8](https://github.com/Redocly/redoc/commit/af415e89e8c074a3f7c84f76f24020a7bd545483)), closes [#764](https://github.com/Redocly/redoc/issues/764)
* fix major search performance due to wrong marker element ([8c053cc](https://github.com/Redocly/redoc/commit/8c053cc474e88befc3338307317c0702d212d4c3)), closes [#1109](https://github.com/Redocly/redoc/issues/1109)
### Features
* new option expandSingleSchemaField ([7608800](https://github.com/Redocly/redoc/commit/7608800d0acaa2fa0099dc840e17cd5aa90b54ca))
# [2.0.0-rc.23](https://github.com/Redocly/redoc/compare/v2.0.0-rc.22...v2.0.0-rc.23) (2020-02-09)
### Bug Fixes
* fix broken sticky sidebar in Chrome 80 ([1a2a7dd](https://github.com/Redocly/redoc/commit/1a2a7dd8331cedd6ced4c18accf0b417549b3ff3)), closes [#1167](https://github.com/Redocly/redoc/issues/1167)
# [2.0.0-rc.22](https://github.com/Redocly/redoc/compare/v2.0.0-rc.21...v2.0.0-rc.22) (2020-01-15) # [2.0.0-rc.22](https://github.com/Redocly/redoc/compare/v2.0.0-rc.21...v2.0.0-rc.22) (2020-01-15)

View File

@ -64,11 +64,11 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(d
| 1.17.x | 2.0 | | 1.17.x | 2.0 |
## Some Real-life usages ## Some Real-life usages
- [Rebilly](https://rebilly.github.io/RebillyAPI) - [Rebilly](https://rebilly-api.redoc.ly/)
- [Docker Engine](https://docs.docker.com/engine/api/v1.25/) - [Docker Engine](https://docs.docker.com/engine/api/v1.25/)
- [Zuora](https://www.zuora.com/developer/api-reference/) - [Zuora](https://www.zuora.com/developer/api-reference/)
- [Shopify Draft Orders](https://help.shopify.com/api/draft-orders)
- [Discourse](http://docs.discourse.org) - [Discourse](http://docs.discourse.org)
- [Commbox](https://www.commbox.io/api/)
- [APIs.guru](https://apis.guru/api-doc/) - [APIs.guru](https://apis.guru/api-doc/)
- [FastAPI](https://github.com/tiangolo/fastapi) - [FastAPI](https://github.com/tiangolo/fastapi)
@ -107,14 +107,14 @@ That's all folks!
**IMPORTANT NOTE:** if you work with untrusted user spec, use `untrusted-spec` [option](#redoc-options-object) to prevent XSS security risks. **IMPORTANT NOTE:** if you work with untrusted user spec, use `untrusted-spec` [option](#redoc-options-object) to prevent XSS security risks.
### 1. Install ReDoc (skip this step for CDN) ### 1. Install ReDoc (skip this step for CDN)
Install using [yarn](https://yarnpkg.com): Install using [npm](https://docs.npmjs.com/getting-started/what-is-npm):
npm i redoc
or using [yarn](https://yarnpkg.com):
yarn add redoc yarn add redoc
or using [npm](https://docs.npmjs.com/getting-started/what-is-npm):
npm install redoc --save
### 2. Reference redoc script in HTML ### 2. Reference redoc script in HTML
For **CDN**: For **CDN**:
```html ```html
@ -230,6 +230,7 @@ You can use all of the following options with standalone version on <redoc> tag
* `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.
* `hideLoading` - do not show loading animation. Useful for small docs. * `hideLoading` - do not show loading animation. Useful for small docs.
* `hideSingleRequestSampleTab` - do not show the request sample tab for requests with only one sample. * `hideSingleRequestSampleTab` - do not show the request sample tab for requests with only one sample.
* `expandSingleSchemaField` - automatically expand single field in a schema
* `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`. * `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`.
* `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.~~ * `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.~~
* `menuToggle` - if true clicking second time on expanded menu item will collapse it, default `false`. * `menuToggle` - if true clicking second time on expanded menu item will collapse it, default `false`.

View File

@ -17,7 +17,7 @@ const localDistDir = './benchmark/revisions/local/bundles';
sh.rm('-rf', localDistDir); sh.rm('-rf', localDistDir);
console.log(`Building local dist: ${localDistDir}`); console.log(`Building local dist: ${localDistDir}`);
sh.mkdir('-p', localDistDir); sh.mkdir('-p', localDistDir);
exec(`yarn bundle:lib --output-path ${localDistDir}`); exec(`npm run bundle:lib --output-path ${localDistDir}`);
const revisions = []; const revisions = [];
for (const arg of args) { for (const arg of args) {
@ -119,7 +119,7 @@ function buildRevisionDist(revision) {
const pwd = sh.pwd(); const pwd = sh.pwd();
sh.cd(buildDir); sh.cd(buildDir);
exec('yarn remove cypress puppeteer && yarn && yarn bundle:lib'); exec('npm uninstall cypress puppeteer && npm install && npm run bundle:lib');
sh.cd(pwd); sh.cd(pwd);
return distDir; return distDir;
} }

2246
cli/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "redoc-cli", "name": "redoc-cli",
"version": "0.9.5", "version": "0.9.7",
"description": "ReDoc's Command Line Interface", "description": "ReDoc's Command Line Interface",
"main": "index.js", "main": "index.js",
"bin": "index.js", "bin": "index.js",
@ -19,7 +19,7 @@
"node-libs-browser": "^2.2.1", "node-libs-browser": "^2.2.1",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"redoc": "2.0.0-rc.21", "redoc": "2.0.0-rc.24",
"styled-components": "^4.3.2", "styled-components": "^4.3.2",
"tslib": "^1.10.0", "tslib": "^1.10.0",
"yargs": "^13.3.0" "yargs": "^13.3.0"

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,8 @@ RUN apk update && apk add --no-cache git
# Install dependencies # Install dependencies
WORKDIR /build WORKDIR /build
COPY package.json yarn.lock /build/ COPY package.json package-lock.json /build/
RUN yarn install --frozen-lockfile --ignore-optional --ignore-scripts RUN npm ci --no-optional --ignore-scripts
# copy only required for the build files # copy only required for the build files
COPY src /build/src COPY src /build/src

View File

@ -27,6 +27,8 @@ describe('Search', () => {
it('should support arrow navigation', () => { it('should support arrow navigation', () => {
getSearchInput().type('int', { force: true }); getSearchInput().type('int', { force: true });
cy.wait(500);
getSearchInput().type('{downarrow}', { force: true }); getSearchInput().type('{downarrow}', { force: true });
getResult(0).should('have.class', 'active'); getResult(0).should('have.class', 'active');

17990
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "otx-redoc", "name": "otx-redoc",
"version": "20.1.1", "version": "20.1.2",
"description": "ReDoc", "description": "ReDoc",
"repository": { "repository": {
"type": "git", "type": "git",
@ -42,7 +42,7 @@
"stats": "webpack --env.standalone --json --profile --mode=production > stats.json", "stats": "webpack --env.standalone --json --profile --mode=production > stats.json",
"prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"", "prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
"lint": "tslint --project tsconfig.json", "lint": "eslint 'src/**/*.{js,ts,tsx}'",
"benchmark": "node ./benchmark/benchmark.js", "benchmark": "node ./benchmark/benchmark.js",
"start:demo": "webpack-dev-server --hot --config demo/webpack.config.ts --mode=development", "start:demo": "webpack-dev-server --hot --config demo/webpack.config.ts --mode=development",
"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",
@ -52,47 +52,52 @@
"docker:build": "docker build -f config/docker/Dockerfile -t redoc ." "docker:build": "docker build -f config/docker/Dockerfile -t redoc ."
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.7.5", "@babel/core": "7.8.7",
"@babel/plugin-syntax-decorators": "7.7.4", "@babel/plugin-syntax-decorators": "7.8.3",
"@babel/plugin-syntax-dynamic-import": "^7.7.4", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-jsx": "7.7.4", "@babel/plugin-syntax-jsx": "7.8.3",
"@babel/plugin-syntax-typescript": "7.7.4", "@babel/plugin-syntax-typescript": "7.8.3",
"@cypress/webpack-preprocessor": "4.1.1", "@cypress/webpack-preprocessor": "4.1.3",
"@hot-loader/react-dom": "^16.11.0", "@hot-loader/react-dom": "^16.12.0",
"@types/chai": "4.2.7", "@types/chai": "4.2.10",
"@types/dompurify": "^2.0.0", "@types/dompurify": "^2.0.1",
"@types/enzyme": "^3.10.4", "@types/enzyme": "^3.10.5",
"@types/enzyme-to-json": "^1.5.3", "@types/enzyme-to-json": "^1.5.3",
"@types/jest": "^24.0.23", "@types/jest": "^25.1.4",
"@types/json-pointer": "^1.0.30", "@types/json-pointer": "^1.0.30",
"@types/lodash": "^4.14.149", "@types/lodash": "^4.14.149",
"@types/lunr": "^2.3.2", "@types/lunr": "^2.3.2",
"@types/mark.js": "^8.11.5", "@types/mark.js": "^8.11.5",
"@types/marked": "^0.7.2", "@types/marked": "^0.7.3",
"@types/prismjs": "^1.16.0", "@types/prismjs": "^1.16.0",
"@types/prop-types": "^15.7.3", "@types/prop-types": "^15.7.3",
"@types/react": "^16.9.16", "@types/react": "^16.9.23",
"@types/react-dom": "^16.9.4", "@types/react-dom": "^16.9.5",
"@types/react-tabs": "^2.3.1", "@types/react-tabs": "^2.3.1",
"@types/styled-components": "^4.4.1", "@types/styled-components": "^4.4.1",
"@types/tapable": "1.0.4", "@types/tapable": "1.0.5",
"@types/webpack": "^4.41.0", "@types/webpack": "^4.41.7",
"@types/webpack-env": "^1.14.1", "@types/webpack-env": "^1.15.1",
"@types/yargs": "^13.0.3", "@types/yargs": "^13.0.3",
"@typescript-eslint/eslint-plugin": "^2.24.0",
"@typescript-eslint/parser": "^2.24.0",
"babel-loader": "8.0.6", "babel-loader": "8.0.6",
"babel-plugin-styled-components": "^1.10.6", "babel-plugin-styled-components": "^1.10.7",
"beautify-benchmark": "^0.2.4", "beautify-benchmark": "^0.2.4",
"bundlesize": "^0.18.0", "bundlesize": "^0.18.0",
"conventional-changelog-cli": "^2.0.28", "conventional-changelog-cli": "^2.0.31",
"copy-webpack-plugin": "^5.1.1", "copy-webpack-plugin": "^5.1.1",
"core-js": "^3.5.0", "core-js": "^3.5.0",
"coveralls": "^3.0.9", "coveralls": "^3.0.9",
"css-loader": "^3.3.0", "css-loader": "^3.4.2",
"cypress": "~3.7.0", "cypress": "~3.7.0",
"deploy-to-gh-pages": "^1.3.7", "deploy-to-gh-pages": "^1.3.7",
"enzyme": "^3.10.0", "enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.1", "enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.4.3", "enzyme-to-json": "^3.4.4",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-react": "^7.19.0",
"fork-ts-checker-webpack-plugin": "3.1.1", "fork-ts-checker-webpack-plugin": "3.1.1",
"html-webpack-plugin": "^3.1.0", "html-webpack-plugin": "^3.1.0",
"jest": "^24.9.0", "jest": "^24.9.0",
@ -100,27 +105,24 @@
"lodash": "^4.17.15", "lodash": "^4.17.15",
"mobx": "^4.3.1", "mobx": "^4.3.1",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"prettier-eslint": "^9.0.1",
"raf": "^3.4.1", "raf": "^3.4.1",
"react": "^16.12.0", "react": "^16.13.0",
"react-hot-loader": "^4.12.18", "react-dom": "^16.13.0",
"react-dom": "^16.12.0", "react-hot-loader": "^4.12.19",
"rimraf": "^3.0.0", "rimraf": "^3.0.2",
"shelljs": "^0.8.3", "shelljs": "^0.8.3",
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"style-loader": "^1.0.1", "style-loader": "^1.1.3",
"styled-components": "^4.4.1", "styled-components": "^4.4.1",
"ts-jest": "24.2.0", "ts-jest": "24.2.0",
"ts-loader": "6.2.1", "ts-loader": "6.2.1",
"ts-node": "^8.5.4", "ts-node": "^8.6.2",
"tslint": "^5.20.1", "typescript": "^3.8.3",
"tslint-react": "^4.1.0",
"typescript": "^3.7.3",
"unfetch": "^4.1.0", "unfetch": "^4.1.0",
"url-polyfill": "^1.1.7", "url-polyfill": "^1.1.8",
"webpack": "^4.41.2", "webpack": "^4.42.0",
"webpack-cli": "^3.3.10", "webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.9.0", "webpack-dev-server": "^3.10.3",
"webpack-node-externals": "^1.6.0", "webpack-node-externals": "^1.6.0",
"workerize-loader": "^1.1.0", "workerize-loader": "^1.1.0",
"yaml-js": "^0.2.3" "yaml-js": "^0.2.3"
@ -135,7 +137,7 @@
"dependencies": { "dependencies": {
"classnames": "^2.2.6", "classnames": "^2.2.6",
"decko": "^1.2.0", "decko": "^1.2.0",
"dompurify": "^2.0.7", "dompurify": "^2.0.8",
"eventemitter3": "^4.0.0", "eventemitter3": "^4.0.0",
"json-pointer": "^0.6.0", "json-pointer": "^0.6.0",
"json-schema-ref-parser": "^6.1.0", "json-schema-ref-parser": "^6.1.0",
@ -143,18 +145,18 @@
"mark.js": "^8.11.1", "mark.js": "^8.11.1",
"marked": "^0.7.0", "marked": "^0.7.0",
"memoize-one": "~5.1.1", "memoize-one": "~5.1.1",
"mobx-react": "^6.1.4", "mobx-react": "6.1.5",
"openapi-sampler": "1.0.0-beta.15", "openapi-sampler": "1.0.0-beta.15",
"perfect-scrollbar": "^1.4.0", "perfect-scrollbar": "^1.4.0",
"polished": "^3.4.2", "polished": "^3.4.4",
"prismjs": "^1.17.1", "prismjs": "^1.19.0",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-dropdown": "^1.6.4", "react-dropdown": "^1.7.0",
"react-tabs": "^3.0.0", "react-tabs": "^3.1.0",
"slugify": "^1.3.6", "slugify": "^1.4.0",
"stickyfill": "^1.1.1", "stickyfill": "^1.1.1",
"swagger2openapi": "^5.3.1", "swagger2openapi": "^5.3.4",
"tslib": "^1.10.0", "tslib": "^1.11.1",
"url-template": "^2.0.8" "url-template": "^2.0.8"
}, },
"bundlesize": [ "bundlesize": [

View File

@ -1,16 +1,19 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import * as React from 'react'; import * as React from 'react';
import { DarkRightPanel, StyledLink } from '../../../src/common-elements';
import { Constants } from '../../../src/services/Constants';
import { ContentPanel } from '../RightPanelContent/ContentPanel';
import { MiddlePanel, Row, Section } from '../../common-elements/';
import { AppStore } from '../../services/AppStore'; import { AppStore } from '../../services/AppStore';
import { MiddlePanel, Row, Section } from '../../common-elements/';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; 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 { ApiHeader, DownloadButton, InfoSpan, InfoSpanBox, InfoSpanBoxWrap } from './styled.elements'; import {
ApiHeader,
DownloadButton,
InfoSpan,
InfoSpanBox,
InfoSpanBoxWrap,
} from './styled.elements';
export interface ApiInfoProps { export interface ApiInfoProps {
store: AppStore; store: AppStore;
@ -25,8 +28,8 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
}; };
render() { render() {
const {store} = this.props; const { store } = this.props;
const {info, externalDocs} = store.spec; const { info, externalDocs } = store.spec;
const hideDownloadButton = store.options.hideDownloadButton; const hideDownloadButton = store.options.hideDownloadButton;
const downloadFilename = info.downloadFileName; const downloadFilename = info.downloadFileName;
@ -35,34 +38,32 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
const license = const license =
(info.license && ( (info.license && (
<InfoSpan> <InfoSpan>
License: <StyledLink href={info.license.url}>{info.license.name}</StyledLink> License: <a href={info.license.url}>{info.license.name}</a>
</InfoSpan> </InfoSpan>
)) || )) ||
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: <StyledLink href={info.contact.url}>{info.contact.url}</StyledLink> </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>
<StyledLink href={'mailto:' + info.contact.email}>{info.contact.email}</StyledLink> </InfoSpan>
</InfoSpan> )) ||
)) ||
null; null;
const terms = const terms =
(info.termsOfService && ( (info.termsOfService && (
<InfoSpan> <InfoSpan>
<StyledLink href={info.termsOfService}>Terms of Service</StyledLink> <a href={info.termsOfService}>Terms of Service</a>
</InfoSpan> </InfoSpan>
)) || )) ||
null; null;
@ -97,13 +98,11 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
</InfoSpanBox> </InfoSpanBox>
</InfoSpanBoxWrap> </InfoSpanBoxWrap>
)) || )) ||
null} null}
</StyledMarkdownBlock> </StyledMarkdownBlock>
<Markdown source={store.spec.info.description}/> <Markdown source={store.spec.info.description} data-role="redoc-description" />
{externalDocs && <ExternalDocumentation externalDocs={externalDocs}/>} {externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
</MiddlePanel> </MiddlePanel>
{store.options.showOtherInfoPanel &&
<DarkRightPanel><ContentPanel content={info[Constants.OTX_EXTENSION_KEY]}/></DarkRightPanel>}
</Row> </Row>
</Section> </Section>
); );

View File

@ -23,6 +23,7 @@ export interface FieldProps extends SchemaOptions {
showExamples?: boolean; showExamples?: boolean;
field: FieldModel; field: FieldModel;
expandByDefault?: boolean;
renderDiscriminatorSwitch?: (opts: FieldProps) => JSX.Element; renderDiscriminatorSwitch?: (opts: FieldProps) => JSX.Element;
} }
@ -30,13 +31,19 @@ export interface FieldProps extends SchemaOptions {
@observer @observer
export class Field extends React.Component<FieldProps> { export class Field extends React.Component<FieldProps> {
toggle = () => { toggle = () => {
this.props.field.toggle(); if (this.props.field.expanded === undefined && this.props.expandByDefault) {
this.props.field.expanded = false;
} else {
this.props.field.toggle();
}
}; };
render() { render() {
const { className, field, isLast } = this.props; const { className, field, isLast, expandByDefault } = this.props;
const { name, expanded, deprecated, required, kind } = field; const { name, deprecated, required, kind } = field;
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular; const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
const expanded = field.expanded === undefined ? expandByDefault : field.expanded;
const paramName = withSubSchema ? ( const paramName = withSubSchema ? (
<ClickablePropertyNameCell <ClickablePropertyNameCell
onClick={this.toggle} onClick={this.toggle}
@ -65,7 +72,7 @@ export class Field extends React.Component<FieldProps> {
<FieldDetails {...this.props} /> <FieldDetails {...this.props} />
</PropertyDetailsCell> </PropertyDetailsCell>
</tr> </tr>
{field.expanded && withSubSchema && ( {expanded && withSubSchema && (
<tr key={field.name + 'inner'}> <tr key={field.name + 'inner'}>
<PropertyCellWithInner colSpan={2}> <PropertyCellWithInner colSpan={2}>
<InnerPropertiesWrap> <InnerPropertiesWrap>

View File

@ -17,11 +17,12 @@ export type MarkdownProps = BaseMarkdownProps &
StylingMarkdownProps & { StylingMarkdownProps & {
source: string; source: string;
className?: string; className?: string;
'data-role'?: string;
}; };
export class Markdown extends React.Component<MarkdownProps> { export class Markdown extends React.Component<MarkdownProps> {
render() { render() {
const { source, inline, compact, className } = this.props; const { source, inline, compact, className, 'data-role': dataRole } = this.props;
const renderer = new MarkdownRenderer(); const renderer = new MarkdownRenderer();
return ( return (
<SanitizedMarkdownHTML <SanitizedMarkdownHTML
@ -29,6 +30,7 @@ export class Markdown extends React.Component<MarkdownProps> {
inline={inline} inline={inline}
compact={compact} compact={compact}
className={className} className={className}
data-role={dataRole}
/> />
); );
} }

View File

@ -10,7 +10,7 @@ const StyledMarkdownSpan = StyledMarkdownBlock.withComponent('span');
const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html); const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html);
export function SanitizedMarkdownHTML( export function SanitizedMarkdownHTML(
props: StylingMarkdownProps & { html: string; className?: string }, props: StylingMarkdownProps & { html: string; className?: string; 'data-role'?: string },
) { ) {
const Wrap = props.inline ? StyledMarkdownSpan : StyledMarkdownBlock; const Wrap = props.inline ? StyledMarkdownSpan : StyledMarkdownBlock;
@ -22,6 +22,7 @@ export function SanitizedMarkdownHTML(
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: sanitize(options.untrustedSpec, props.html), __html: sanitize(options.untrustedSpec, props.html),
}} }}
data-role={props['data-role']}
{...props} {...props}
/> />
)} )}

View File

@ -19,11 +19,13 @@ export const linksCss = css`
} }
`; `;
export const StyledMarkdownBlock = styled(PrismDiv as StyledComponent< export const StyledMarkdownBlock = styled(
'div', PrismDiv as StyledComponent<
ResolvedThemeInterface, 'div',
{ compact?: boolean; inline?: boolean } 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};

View File

@ -29,7 +29,12 @@ export function ExternalExample({ example, mimeType }: ExampleProps) {
return ( return (
<StyledPre> <StyledPre>
Error loading external example: <br /> Error loading external example: <br />
<a className={'token string'} href={example.externalValueUrl} target="_blank"> <a
className={'token string'}
href={example.externalValueUrl}
target="_blank"
rel="noopener noreferrer"
>
{example.externalValueUrl} {example.externalValueUrl}
</a> </a>
</StyledPre> </StyledPre>

View File

@ -1,3 +1,7 @@
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import ReactDropdown from 'react-dropdown';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import * as React from 'react'; import * as React from 'react';
import { MediaTypeSamples } from './MediaTypeSamples'; import { MediaTypeSamples } from './MediaTypeSamples';

View File

@ -1,5 +1,6 @@
// @ts-ignore // @ts-ignore
import Dropdown from 'react-dropdown'; // eslint-disable-next-line @typescript-eslint/no-unused-vars
import ReactDropdown from 'react-dropdown';
import { transparentize } from 'polished'; import { transparentize } from 'polished';
import styled from '../../styled-components'; import styled from '../../styled-components';

View File

@ -30,8 +30,7 @@ export class OperationPanel extends React.Component<RightPanelContentProps> {
} }
private shouldShowOtherInfoPanel() { private shouldShowOtherInfoPanel() {
return this.props.options.showOtherInfoPanel && return this.props.options.showExtensions &&
this.props.options.showExtensions &&
this.props.operation.extensions[Constants.OTX_EXTENSION_KEY]; this.props.operation.extensions[Constants.OTX_EXTENSION_KEY];
} }
} }

View File

@ -9,6 +9,7 @@ import { DiscriminatorDropdown } from './DiscriminatorDropdown';
import { SchemaProps } from './Schema'; import { SchemaProps } from './Schema';
import { mapWithLast } from '../../utils'; import { mapWithLast } from '../../utils';
import { OptionsContext } from '../OptionsProvider';
export interface ObjectSchemaProps extends SchemaProps { export interface ObjectSchemaProps extends SchemaProps {
discriminator?: { discriminator?: {
@ -19,6 +20,8 @@ export interface ObjectSchemaProps extends SchemaProps {
@observer @observer
export class ObjectSchema extends React.Component<ObjectSchemaProps> { export class ObjectSchema extends React.Component<ObjectSchemaProps> {
static contextType = OptionsContext;
get parentSchema() { get parentSchema() {
return this.props.discriminator!.parentSchema; return this.props.discriminator!.parentSchema;
} }
@ -41,6 +44,8 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
}) })
: fields; : fields;
const expandByDefault = this.context.expandSingleSchemaField && filteredFields.length === 1;
return ( return (
<PropertiesTable> <PropertiesTable>
{showTitle && <PropertiesTableCaption>{this.props.schema.title}</PropertiesTableCaption>} {showTitle && <PropertiesTableCaption>{this.props.schema.title}</PropertiesTableCaption>}
@ -51,6 +56,7 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
key={field.name} key={field.name}
isLast={isLast} isLast={isLast}
field={field} field={field}
expandByDefault={expandByDefault}
renderDiscriminatorSwitch={ renderDiscriminatorSwitch={
(discriminator && (discriminator &&
discriminator.fieldName === field.name && discriminator.fieldName === field.name &&

View File

@ -7,6 +7,7 @@ import { MenuItem } from '../SideMenu/MenuItem';
import { MarkerService } from '../../services/MarkerService'; import { MarkerService } from '../../services/MarkerService';
import { SearchResult } from '../../services/SearchWorker.worker'; import { SearchResult } from '../../services/SearchWorker.worker';
import { bind, debounce } from 'decko';
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar'; import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
import { import {
ClearIcon, ClearIcon,
@ -94,11 +95,18 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
setResults(results: SearchResult[], term: string) { setResults(results: SearchResult[], term: string) {
this.setState({ this.setState({
results, results,
term,
}); });
this.props.marker.mark(term); this.props.marker.mark(term);
} }
@bind
@debounce(400)
searchCallback(searchTerm: string) {
this.props.search.search(searchTerm).then(res => {
this.setResults(res, searchTerm);
});
}
search = (event: React.ChangeEvent<HTMLInputElement>) => { search = (event: React.ChangeEvent<HTMLInputElement>) => {
const q = event.target.value; const q = event.target.value;
if (q.length < 3) { if (q.length < 3) {
@ -106,13 +114,12 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
return; return;
} }
this.setState({ this.setState(
term: q, {
}); term: q,
},
this.props.search.search(event.target.value).then(res => { () => this.searchCallback(this.state.term),
this.setResults(res, q); );
});
}; };
render() { render() {

View File

@ -1,5 +1,6 @@
import { darken } from 'polished';
import * as React from 'react'; import * as React from 'react';
import { darken, getLuminance, lighten } from 'polished';
import styled from '../../styled-components'; import styled from '../../styled-components';
import { MenuItemLabel } from '../SideMenu/styled.elements'; import { MenuItemLabel } from '../SideMenu/styled.elements';
@ -16,9 +17,15 @@ export const SearchInput = styled.input.attrs(() => ({
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 ${({theme}) => darken(0.1, theme.menu.backgroundColor)}; border-bottom: 1px solid
font-family: ${({theme}) => theme.typography.fontFamily}; ${({ theme }) =>
font-size: 1em; (getLuminance(theme.menu.backgroundColor) > 0.5 ? darken : lighten)(
0.1,
theme.menu.backgroundColor,
)};
font-family: ${({ theme }) => theme.typography.fontFamily};
font-weight: bold;
font-size: 13px;
color: ${props => props.theme.menu.textColor}; color: ${props => props.theme.menu.textColor};
background-color: transparent; background-color: transparent;
outline: none; outline: none;
@ -26,23 +33,14 @@ export const SearchInput = styled.input.attrs(() => ({
export const SearchIcon = styled((props: { className?: string }) => ( export const SearchIcon = styled((props: { className?: string }) => (
<svg <svg
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
className={props.className} className={props.className}
viewBox="0 0 24 24" version="1.1"
xmlSpace="preserve" viewBox="0 0 1000 1000"
x="0px"
xmlns="http://www.w3.org/2000/svg"
y="0px"
> >
<g> <path d="M968.2,849.4L667.3,549c83.9-136.5,66.7-317.4-51.7-435.6C477.1-25,252.5-25,113.9,113.4c-138.5,138.3-138.5,362.6,0,501C219.2,730.1,413.2,743,547.6,666.5l301.9,301.4c43.6,43.6,76.9,14.9,104.2-12.4C981,928.3,1011.8,893,968.2,849.4z M524.5,522c-88.9,88.7-233,88.7-321.8,0c-88.9-88.7-88.9-232.6,0-321.3c88.9-88.7,233-88.7,321.8,0C613.4,289.4,613.4,433.3,524.5,522z" />
<path
className="st0"
d="M22.7,21.5l-5.1-5c1.5-1.7,2.4-4,2.4-6.5c0-5.5-4.5-10-10-10S0,4.5,0,10s4.5,10,10,10c2.3,0,4.4-0.8,6.1-2.1
l5.2,5.1c0.2,0.2,0.4,0.3,0.7,0.3c0.3,0,0.5-0.1,0.7-0.3C23.1,22.5,23.1,21.9,22.7,21.5z M10,18c-4.4,0-8-3.6-8-8s3.6-8,8-8
s8,3.6,8,8S14.4,18,10,18z"
/>
</g>
</svg> </svg>
)).attrs({ )).attrs({
className: 'search-icon', className: 'search-icon',
@ -59,11 +57,12 @@ export const SearchIcon = styled((props: { className?: string }) => (
export const SearchResultsBox = styled.div` export const SearchResultsBox = styled.div`
padding: ${props => props.theme.spacing.unit}px 0; padding: ${props => props.theme.spacing.unit}px 0;
background-color: #ededed; background-color: ${({ theme }) => darken(0.05, theme.menu.backgroundColor)}};
color: ${props => props.theme.menu.textColor};
min-height: 150px; min-height: 150px;
max-height: 250px; max-height: 250px;
border-top: 1px solid #e1e1e1; border-top: ${({ theme }) => darken(0.1, theme.menu.backgroundColor)}};
border-bottom: 1px solid #e1e1e1; border-bottom: ${({ theme }) => darken(0.1, theme.menu.backgroundColor)}};
margin-top: 10px; margin-top: 10px;
line-height: 1.4; line-height: 1.4;
font-size: 0.9em; font-size: 0.9em;
@ -72,17 +71,14 @@ export const SearchResultsBox = styled.div`
padding-top: 6px; padding-top: 6px;
padding-bottom: 6px; padding-bottom: 6px;
&:hover { &:hover,
background-color: #e1e1e1; &.active {
background-color: ${({ theme }) => darken(0.1, theme.menu.backgroundColor)};
} }
> svg { > svg {
display: none; display: none;
} }
&.active {
background-color: #e1e1e1;
}
} }
`; `;

View File

@ -97,7 +97,7 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && ( scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
<tr key="bearer"> <tr key="bearer">
<th> Bearer format </th> <th> Bearer format </th>
<td> "{scheme.http.bearerFormat}" </td> <td> &quot;{scheme.http.bearerFormat}&quot; </td>
</tr> </tr>
), ),
] ]
@ -105,7 +105,11 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
<tr> <tr>
<th> Connect URL </th> <th> Connect URL </th>
<td> <td>
<a target="_blank" href={scheme.openId.connectUrl}> <a
target="_blank"
rel="noopener noreferrer"
href={scheme.openId.connectUrl}
>
{scheme.openId.connectUrl} {scheme.openId.connectUrl}
</a> </a>
</td> </td>

View File

@ -1,12 +1,11 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import * as React from 'react'; import * as React from 'react';
import { StyledLink } from '../../../src/common-elements';
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
import { IMenuItem, MenuStore } from '../../services/MenuStore'; import { IMenuItem, MenuStore } from '../../services/MenuStore';
import { OptionsContext } from '../OptionsProvider'; import { OptionsContext } from '../OptionsProvider';
import { MenuItems } from './MenuItems'; import { MenuItems } from './MenuItems';
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
import { RedocAttribution } from './styled.elements'; import { RedocAttribution } from './styled.elements';
@observer @observer
@ -24,11 +23,11 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
wheelPropagation: false, wheelPropagation: false,
}} }}
> >
<MenuItems items={store.items} onActivate={this.activate} root={true}/> <MenuItems items={store.items} onActivate={this.activate} root={true} />
<RedocAttribution> <RedocAttribution>
<StyledLink href="https://www.opentext.com/" target={'_blank'}> <a target="_blank" rel="noopener noreferrer" href="https://www.opentext.com/">
© Copyright 2019 OpenText Corp © Copyright 2019 OpenText Corp
</StyledLink> </a>
</RedocAttribution> </RedocAttribution>
</PerfectScrollbarWrap> </PerfectScrollbarWrap>
); );

View File

@ -33,7 +33,7 @@ const StyledStickySidebar = styled.div<{ open?: boolean }>`
flex-direction: column; flex-direction: column;
backface-visibility: hidden; backface-visibility: hidden;
contain: strict; /* contain: strict; TODO: breaks layout since Chrome 80*/
height: 100vh; height: 100vh;
position: sticky; position: sticky;

View File

@ -1,3 +1,4 @@
/* eslint-disable import/no-internal-modules */
/* tslint:disable:no-implicit-dependencies */ /* tslint:disable:no-implicit-dependencies */
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';

View File

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

View File

@ -18,6 +18,8 @@ import {
SECURITY_DEFINITIONS_JSX_NAME, SECURITY_DEFINITIONS_JSX_NAME,
} from '../utils/openapi'; } from '../utils/openapi';
import { IS_BROWSER } from '../utils';
export interface StoreState { export interface StoreState {
menu: { menu: {
activeItemIdx: number; activeItemIdx: number;
@ -134,16 +136,16 @@ export class AppStore {
const elements: Element[] = []; const elements: Element[] = [];
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
let elem = this.menu.getElementAt(i); const elem = this.menu.getElementAt(i);
if (!elem) { if (!elem) {
continue; continue;
} }
if (this.menu.flatItems[i].type === 'section') { elements.push(elem);
elem = elem.parentElement!.parentElement; }
}
if (elem) { if (idx === -1 && IS_BROWSER) {
elements.push(elem); const $description = document.querySelector('[data-role="redoc-description"]');
} if ($description) elements.push($description);
} }
this.marker.addOnly(elements); this.marker.addOnly(elements);

View File

@ -148,7 +148,7 @@ export class OpenAPIParser {
* @param obj object to dereference * @param obj object to dereference
* @param forceCircular whether to dereference even if it is circular ref * @param forceCircular whether to dereference even if it is circular ref
*/ */
deref<T extends object>(obj: OpenAPIRef | T, forceCircular: boolean = false): T { deref<T extends object>(obj: OpenAPIRef | T, forceCircular = false): T {
if (this.isRef(obj)) { if (this.isRef(obj)) {
const resolved = this.byRef<T>(obj.$ref)!; const resolved = this.byRef<T>(obj.$ref)!;
const visited = this._refCounter.visited(obj.$ref); const visited = this._refCounter.visited(obj.$ref);

View File

@ -21,12 +21,12 @@ export interface RedocRawOptions {
disableSearch?: boolean | string; disableSearch?: boolean | string;
onlyRequiredInSamples?: boolean | string; onlyRequiredInSamples?: boolean | string;
showExtensions?: boolean | string | string[]; showExtensions?: boolean | string | string[];
showOtherInfoPanel?: boolean;
hideSingleRequestSampleTab?: boolean | string; hideSingleRequestSampleTab?: boolean | string;
menuToggle?: boolean | string; menuToggle?: boolean | string;
jsonSampleExpandLevel?: number | string | 'all'; jsonSampleExpandLevel?: number | string | 'all';
hideSchemaTitles?: boolean | string; hideSchemaTitles?: boolean | string;
payloadSampleIdx?: number; payloadSampleIdx?: number;
expandSingleSchemaField?: boolean | string;
unstable_ignoreMimeParameters?: boolean; unstable_ignoreMimeParameters?: boolean;
@ -156,12 +156,12 @@ export class RedocNormalizedOptions {
onlyRequiredInSamples: boolean; onlyRequiredInSamples: boolean;
showExtensions: boolean | string[]; showExtensions: boolean | string[];
hideSingleRequestSampleTab: boolean; hideSingleRequestSampleTab: boolean;
showOtherInfoPanel: boolean;
menuToggle: boolean; menuToggle: boolean;
jsonSampleExpandLevel: number; jsonSampleExpandLevel: number;
enumSkipQuotes: boolean; enumSkipQuotes: boolean;
hideSchemaTitles: boolean; hideSchemaTitles: boolean;
payloadSampleIdx: number; payloadSampleIdx: number;
expandSingleSchemaField: boolean;
/* tslint:disable-next-line */ /* tslint:disable-next-line */
unstable_ignoreMimeParameters: boolean; unstable_ignoreMimeParameters: boolean;
@ -194,7 +194,6 @@ export class RedocNormalizedOptions {
this.disableSearch = argValueToBoolean(raw.disableSearch); this.disableSearch = argValueToBoolean(raw.disableSearch);
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples); this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions); this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
this.showOtherInfoPanel = argValueToBoolean(raw.showOtherInfoPanel);
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab); this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
this.menuToggle = argValueToBoolean(raw.menuToggle, true); this.menuToggle = argValueToBoolean(raw.menuToggle, true);
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel( this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
@ -203,7 +202,9 @@ export class RedocNormalizedOptions {
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes); this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles); this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx); this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
// eslint-disable-next-line @typescript-eslint/camelcase
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters); this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
this.allowedMdComponents = raw.allowedMdComponents || {}; this.allowedMdComponents = raw.allowedMdComponents || {};

View File

@ -9,7 +9,7 @@ const EVENT = 'scroll';
export class ScrollService { export class ScrollService {
private _scrollParent: Window | HTMLElement | undefined; private _scrollParent: Window | HTMLElement | undefined;
private _emiter: EventEmitter; private _emiter: EventEmitter;
private _prevOffsetY: number = 0; private _prevOffsetY = 0;
constructor(private options: RedocNormalizedOptions) { constructor(private options: RedocNormalizedOptions) {
this._scrollParent = IS_BROWSER ? window : undefined; this._scrollParent = IS_BROWSER ? window : undefined;
this._emiter = new EventEmitter(); this._emiter = new EventEmitter();

View File

@ -4,21 +4,23 @@ import { OperationModel } from './models';
import Worker from './SearchWorker.worker'; import Worker from './SearchWorker.worker';
let worker: new () => Worker; function getWorker() {
let worker: new () => Worker;
if (IS_BROWSER) { if (IS_BROWSER) {
try { try {
// tslint:disable-next-line // tslint:disable-next-line
worker = require('workerize-loader?inline&fallback=false!./SearchWorker.worker'); worker = require('workerize-loader?inline&fallback=false!./SearchWorker.worker');
} catch (e) { } catch (e) {
worker = require('./SearchWorker.worker').default;
}
} else {
worker = require('./SearchWorker.worker').default; worker = require('./SearchWorker.worker').default;
} }
} else { return new worker();
worker = require('./SearchWorker.worker').default;
} }
export class SearchStore<T> { export class SearchStore<T> {
searchWorker = new worker(); searchWorker = getWorker();
indexItems(groups: Array<IMenuItem | OperationModel>) { indexItems(groups: Array<IMenuItem | OperationModel>) {
const recurse = items => { const recurse = items => {

View File

@ -8,6 +8,7 @@ describe('Models', () => {
let parser; let parser;
test('should hoist oneOfs when mergin allOf', () => { test('should hoist oneOfs when mergin allOf', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const spec = require('./fixtures/oneOfHoist.json'); const spec = require('./fixtures/oneOfHoist.json');
parser = new OpenAPIParser(spec, undefined, opts); parser = new OpenAPIParser(spec, undefined, opts);
expect(parser.mergeAllOf(spec.components.schemas.test)).toMatchSnapshot(); expect(parser.mergeAllOf(spec.components.schemas.test)).toMatchSnapshot();

View File

@ -6,9 +6,9 @@ const opts = new RedocNormalizedOptions({});
describe('Models', () => { describe('Models', () => {
describe('FieldModel', () => { describe('FieldModel', () => {
let parser; // eslint-disable-next-line @typescript-eslint/no-var-requires
const spec = require('../fixtures/fields.json'); const spec = require('../fixtures/fields.json');
parser = new OpenAPIParser(spec, undefined, opts); const parser = new OpenAPIParser(spec, undefined, opts);
test('basic field details', () => { test('basic field details', () => {
const field = new FieldModel( const field = new FieldModel(

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { SchemaModel } from '../../models/Schema'; import { SchemaModel } from '../../models/Schema';
import { OpenAPIParser } from '../../OpenAPIParser'; import { OpenAPIParser } from '../../OpenAPIParser';
import { RedocNormalizedOptions } from '../../RedocNormalizedOptions'; import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';

View File

@ -30,7 +30,7 @@ function getDefaultStyleValue(parameterLocation: OpenAPIParameterLocation): Open
*/ */
export class FieldModel { export class FieldModel {
@observable @observable
expanded: boolean = false; expanded: boolean | undefined;
schema: SchemaModel; schema: SchemaModel;
name: string; name: string;

View File

@ -3,8 +3,8 @@ import { darken, desaturate, lighten, readableColor, transparentize } from 'poli
const defaultTheme: ThemeInterface = { const defaultTheme: ThemeInterface = {
spacing: { spacing: {
unit: 5, unit: 5,
sectionHorizontal: ({spacing}) => spacing.unit * 8, sectionHorizontal: ({ spacing }) => spacing.unit * 8,
sectionVertical: ({spacing}) => spacing.unit * 8, sectionVertical: ({ spacing }) => spacing.unit * 8,
}, },
breakpoints: { breakpoints: {
small: '50rem', small: '50rem',
@ -15,31 +15,31 @@ const defaultTheme: ThemeInterface = {
tonalOffset: 0.3, tonalOffset: 0.3,
primary: { primary: {
main: '#232E72', main: '#232E72',
light: ({colors}) => lighten(colors.tonalOffset, colors.primary.main), light: ({ colors }) => lighten(colors.tonalOffset, colors.primary.main),
dark: ({colors}) => darken(colors.tonalOffset, colors.primary.main), dark: ({ colors }) => darken(colors.tonalOffset, colors.primary.main),
contrastText: ({colors}) => readableColor(colors.primary.main), contrastText: ({ colors }) => readableColor(colors.primary.main),
}, },
success: { success: {
main: '#00aa13', main: '#00aa13',
light: ({colors}) => lighten(colors.tonalOffset, colors.success.main), light: ({ colors }) => lighten(colors.tonalOffset, colors.success.main),
dark: ({colors}) => darken(colors.tonalOffset, colors.success.main), dark: ({ colors }) => darken(colors.tonalOffset, colors.success.main),
contrastText: ({colors}) => readableColor(colors.success.main), contrastText: ({ colors }) => readableColor(colors.success.main),
}, },
warning: { warning: {
main: '#d4ad03', main: '#d4ad03',
light: ({colors}) => lighten(colors.tonalOffset, colors.warning.main), light: ({ colors }) => lighten(colors.tonalOffset, colors.warning.main),
dark: ({colors}) => darken(colors.tonalOffset, colors.warning.main), dark: ({ colors }) => darken(colors.tonalOffset, colors.warning.main),
contrastText: '#ffffff', contrastText: '#ffffff',
}, },
error: { error: {
main: '#e53935', main: '#e53935',
light: ({colors}) => lighten(colors.tonalOffset, colors.error.main), light: ({ colors }) => lighten(colors.tonalOffset, colors.error.main),
dark: ({colors}) => darken(colors.tonalOffset, colors.error.main), dark: ({ colors }) => darken(colors.tonalOffset, colors.error.main),
contrastText: ({colors}) => readableColor(colors.error.main), contrastText: ({ colors }) => readableColor(colors.error.main),
}, },
text: { text: {
primary: '#333333', primary: '#333333',
secondary: ({colors}) => lighten(colors.tonalOffset, colors.text.primary), secondary: ({ colors }) => lighten(colors.tonalOffset, colors.text.primary),
}, },
border: { border: {
dark: 'rgba(0,0,0, 0.1)', dark: 'rgba(0,0,0, 0.1)',
@ -47,20 +47,20 @@ const defaultTheme: ThemeInterface = {
}, },
responses: { responses: {
success: { success: {
color: ({colors}) => colors.success.main, color: ({ colors }) => colors.success.main,
backgroundColor: ({colors}) => transparentize(0.9, colors.success.main), backgroundColor: ({ colors }) => transparentize(0.9, colors.success.main),
}, },
error: { error: {
color: ({colors}) => colors.error.main, color: ({ colors }) => colors.error.main,
backgroundColor: ({colors}) => transparentize(0.9, colors.error.main), backgroundColor: ({ colors }) => transparentize(0.9, colors.error.main),
}, },
redirect: { redirect: {
color: '#ffa500', color: '#ffa500',
backgroundColor: ({colors}) => transparentize(0.9, colors.responses.redirect.color), backgroundColor: ({ colors }) => transparentize(0.9, colors.responses.redirect.color),
}, },
info: { info: {
color: '#87ceeb', color: '#87ceeb',
backgroundColor: ({colors}) => transparentize(0.9, colors.responses.info.color), backgroundColor: ({ colors }) => transparentize(0.9, colors.responses.info.color),
}, },
}, },
http: { http: {
@ -110,23 +110,26 @@ const defaultTheme: ThemeInterface = {
code: { code: {
fontSize: '13px', fontSize: '13px',
fontFamily: 'Courier, monospace', fontFamily: 'Courier, monospace',
lineHeight: ({typography}) => typography.lineHeight, lineHeight: ({ typography }) => typography.lineHeight,
fontWeight: ({typography}) => typography.fontWeightRegular, fontWeight: ({ typography }) => typography.fontWeightRegular,
color: '#e53935', color: '#e53935',
backgroundColor: 'rgba(38, 50, 56, 0.05)', backgroundColor: 'rgba(38, 50, 56, 0.05)',
wrap: false, wrap: false,
}, },
links: { links: {
color: ({colors}) => colors.primary.main, color: ({ colors }) => colors.primary.main,
visited: ({typography}) => typography.links.color, visited: ({ typography }) => typography.links.color,
hover: ({typography}) => lighten(0.2, typography.links.color), hover: ({ typography }) => lighten(0.2, typography.links.color),
}, },
}, },
menu: { menu: {
width: '260px', width: '260px',
backgroundColor: '#F3F6FB', backgroundColor: '#F3F6FB',
textColor: '#232E72', textColor: '#232E72',
activeTextColor: '#232E72', activeTextColor: theme =>
theme.menu.textColor !== defaultTheme.menu!.textColor
? theme.menu.textColor
: theme.colors.primary.main,
groupItems: { groupItems: {
textTransform: 'uppercase', textTransform: 'uppercase',
}, },
@ -139,8 +142,8 @@ const defaultTheme: ThemeInterface = {
}, },
}, },
logo: { logo: {
maxHeight: ({menu}) => menu.width, maxHeight: ({ menu }) => menu.width,
maxWidth: ({menu}) => menu.width, maxWidth: ({ menu }) => menu.width,
gutter: '2px', gutter: '2px',
}, },
rightPanel: { rightPanel: {
@ -149,7 +152,7 @@ const defaultTheme: ThemeInterface = {
textColor: '#ffffff', textColor: '#ffffff',
}, },
codeSample: { codeSample: {
backgroundColor: ({rightPanel}) => darken(0.1, rightPanel.backgroundColor), backgroundColor: ({ rightPanel }) => darken(0.1, rightPanel.backgroundColor),
}, },
}; };

View File

@ -1,5 +0,0 @@
import { AppStore } from '../services/AppStore';
export interface BaseContainerProps {
store: AppStore;
}

View File

@ -8,7 +8,11 @@ describe('Utils', () => {
const fn = (...args) => args; const fn = (...args) => args;
const actual = mapWithLast(arr, fn); const actual = mapWithLast(arr, fn);
const expected = [[1, false], [2, false], [3, true]]; const expected = [
[1, false],
[2, false],
[3, true],
];
expect(actual).toEqual(expected); expect(actual).toEqual(expected);
}); });

View File

@ -640,7 +640,7 @@ describe('Utils', () => {
describe('OpenAPI sortByRequired', () => { describe('OpenAPI sortByRequired', () => {
it('should equal to the old data when all items have no required props', () => { it('should equal to the old data when all items have no required props', () => {
let fields = [ const fields = [
{ {
name: 'loginName', name: 'loginName',
required: false, required: false,
@ -694,7 +694,7 @@ describe('Utils', () => {
}); });
it('other item should be the same order when some of items are required', () => { it('other item should be the same order when some of items are required', () => {
let fields = [ const fields = [
{ {
name: 'loginName', name: 'loginName',
required: true, required: true,
@ -744,7 +744,7 @@ describe('Utils', () => {
required: false, required: false,
}, },
]; ];
let sortedFields = [ const sortedFields = [
{ {
name: 'loginName', name: 'loginName',
required: true, required: true,
@ -798,7 +798,7 @@ describe('Utils', () => {
}); });
it('should the order of required items is as same as the order parameter ', () => { it('should the order of required items is as same as the order parameter ', () => {
let fields = [ const fields = [
{ {
name: 'loginName', name: 'loginName',
required: true, required: true,

View File

@ -16,6 +16,7 @@ function throttle(func, wait) {
const now = new Date().getTime(); const now = new Date().getTime();
const remaining = wait - (now - previous); const remaining = wait - (now - previous);
context = this; context = this;
// eslint-disable-next-line prefer-rest-params
args = arguments; args = arguments;
if (remaining <= 0 || remaining > wait) { if (remaining <= 0 || remaining > wait) {
if (timeout) { if (timeout) {

View File

@ -189,7 +189,7 @@ export function removeQueryString(serverUrl: string): string {
function parseURL(url: string) { function parseURL(url: string) {
if (typeof URL === 'undefined') { if (typeof URL === 'undefined') {
// node // node
return new (require('url')).URL(url); return new (require('url').URL)(url);
} else { } else {
return new URL(url); return new URL(url);
} }

View File

@ -1,5 +1,5 @@
import { dirname } from 'path'; import { dirname } from 'path';
const URLtemplate = require('url-template'); import * as URLtemplate from 'url-template';
import { FieldModel } from '../services/models'; import { FieldModel } from '../services/models';
import { OpenAPIParser } from '../services/OpenAPIParser'; import { OpenAPIParser } from '../services/OpenAPIParser';

View File

@ -38,6 +38,7 @@
"./custom.d.ts", "./custom.d.ts",
"./demo/playground/hmr-playground.tsx", "./demo/playground/hmr-playground.tsx",
"./src/**/*.ts?", "./src/**/*.ts?",
"demo/*.tsx" "demo/*.tsx",
"src/empty.js"
] ]
} }

View File

@ -1,6 +1,7 @@
{ {
"extends": ["tslint:latest", "tslint-react"], "extends": ["tslint:latest", "tslint-react"],
"rules": { "rules": {
"array-type": false,
"interface-name": false, "interface-name": false,
"object-literal-sort-keys": false, "object-literal-sort-keys": false,
"jsx-no-multiline-js": false, "jsx-no-multiline-js": false,

10572
yarn.lock

File diff suppressed because it is too large Load Diff