mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-04 04:10:19 +03:00
Merge tag 'v2.0.0-rc.59' into sections-at-the-end
This commit is contained in:
commit
d05cf42ec6
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
||||||
|
# [2.0.0-rc.59](https://github.com/Redocly/redoc/compare/v2.0.0-rc.58...v2.0.0-rc.59) (2021-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix scroll in example dropdown ([#1803](https://github.com/Redocly/redoc/issues/1803)) ([bc2d9a7](https://github.com/Redocly/redoc/commit/bc2d9a7d9cd530274483fecd136db290a5b46ff7))
|
||||||
|
* x-examples for request body param does not display [#1743](https://github.com/Redocly/redoc/issues/1743) ([#1826](https://github.com/Redocly/redoc/issues/1826)) ([aaa3b32](https://github.com/Redocly/redoc/commit/aaa3b3280c8422d450e8849ae02135dde199d6d5))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add option sideNavStyle ([#1805](https://github.com/Redocly/redoc/pull/1805)) ([2e4663b](https://github.com/Redocly/redoc/commit/2e4663b3b7022f25d3dc808afbcb3b3ad9483c41))
|
||||||
|
|
||||||
|
|
||||||
# [2.0.0-rc.58](https://github.com/Redocly/redoc/compare/v2.0.0-rc.57...v2.0.0-rc.58) (2021-11-29)
|
# [2.0.0-rc.58](https://github.com/Redocly/redoc/compare/v2.0.0-rc.57...v2.0.0-rc.58) (2021-11-29)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,9 @@ You can use all of the following options with the standalone version of the <red
|
||||||
* `payloadSampleIdx` - if set, payload sample will be inserted at this index or last. Indexes start from 0.
|
* `payloadSampleIdx` - if set, payload sample will be inserted at this index or last. Indexes start from 0.
|
||||||
* `theme` - ReDoc theme. For details check [theme docs](#redoc-theme-object).
|
* `theme` - ReDoc theme. For details check [theme docs](#redoc-theme-object).
|
||||||
* `untrustedSpec` - if set, the spec is considered untrusted and all HTML/markdown is sanitized to prevent XSS. **Disabled by default** for performance reasons. **Enable this option if you work with untrusted user data!**
|
* `untrustedSpec` - if set, the spec is considered untrusted and all HTML/markdown is sanitized to prevent XSS. **Disabled by default** for performance reasons. **Enable this option if you work with untrusted user data!**
|
||||||
|
* `sideNavStyle` - can be specified in various ways:
|
||||||
|
* **summary-only**: displays a summary in the sidebar navigation item. (**default**)
|
||||||
|
* **path-only**: displays a path in the sidebar navigation item.
|
||||||
|
|
||||||
### `<redoc>` theme object
|
### `<redoc>` theme object
|
||||||
* `spacing`
|
* `spacing`
|
||||||
|
|
10
cli/npm-shrinkwrap.json
generated
10
cli/npm-shrinkwrap.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.13.0",
|
"version": "0.13.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.13.0",
|
"version": "0.13.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
"node-libs-browser": "^2.2.1",
|
"node-libs-browser": "^2.2.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"redoc": "2.0.0-rc.57",
|
"redoc": "2.0.0-rc.58",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1"
|
||||||
},
|
},
|
||||||
|
@ -921,9 +921,7 @@
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": ["darwin"],
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.13.0",
|
"version": "0.13.1",
|
||||||
"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": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"redoc": "2.0.0-rc.57",
|
"redoc": "2.0.0-rc.58",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,14 +33,14 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
},
|
},
|
||||||
|
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: __dirname,
|
static: __dirname,
|
||||||
watchContentBase: true,
|
|
||||||
port: 9090,
|
port: 9090,
|
||||||
disableHostCheck: true,
|
|
||||||
stats: 'minimal',
|
|
||||||
hot: true,
|
hot: true,
|
||||||
|
historyApiFallback: true,
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
children: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||||
fallback: {
|
fallback: {
|
||||||
|
@ -72,7 +72,7 @@ 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: [getBabelLoader({useBuiltIns: true, hot: true} )],
|
use: [getBabelLoader({ useBuiltIns: true, hot: true })],
|
||||||
exclude: {
|
exclude: {
|
||||||
and: [/node_modules/],
|
and: [/node_modules/],
|
||||||
not: {
|
not: {
|
||||||
|
|
7103
package-lock.json
generated
7103
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.0.0-rc.58",
|
"version": "2.0.0-rc.59",
|
||||||
"description": "ReDoc",
|
"description": "ReDoc",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
"unit": "jest --coverage",
|
"unit": "jest --coverage",
|
||||||
"e2e": "cypress run",
|
"e2e": "cypress run",
|
||||||
"e2e-ci": "cypress run --record",
|
"e2e-ci": "cypress run --record",
|
||||||
"bundlesize": "bundlesize",
|
"bundlesize": "size-limit",
|
||||||
"ts-check": "tsc --noEmit --skipLibCheck",
|
"ts-check": "tsc --noEmit --skipLibCheck",
|
||||||
"cy:open": "cypress open",
|
"cy:open": "cypress open",
|
||||||
"bundle:clean": "rimraf bundles",
|
"bundle:clean": "rimraf bundles",
|
||||||
|
@ -75,6 +75,7 @@
|
||||||
"@babel/preset-typescript": "^7.13.0",
|
"@babel/preset-typescript": "^7.13.0",
|
||||||
"@cypress/webpack-preprocessor": "^5.9.0",
|
"@cypress/webpack-preprocessor": "^5.9.0",
|
||||||
"@hot-loader/react-dom": "^17.0.1",
|
"@hot-loader/react-dom": "^17.0.1",
|
||||||
|
"@size-limit/preset-app": "^7.0.4",
|
||||||
"@types/chai": "^4.2.18",
|
"@types/chai": "^4.2.18",
|
||||||
"@types/dompurify": "^2.2.2",
|
"@types/dompurify": "^2.2.2",
|
||||||
"@types/enzyme": "^3.10.5",
|
"@types/enzyme": "^3.10.5",
|
||||||
|
@ -83,9 +84,9 @@
|
||||||
"@types/json-pointer": "^1.0.30",
|
"@types/json-pointer": "^1.0.30",
|
||||||
"@types/lodash": "^4.14.170",
|
"@types/lodash": "^4.14.170",
|
||||||
"@types/lunr": "^2.3.3",
|
"@types/lunr": "^2.3.3",
|
||||||
"@types/node": "^15.6.1",
|
|
||||||
"@types/mark.js": "^8.11.5",
|
"@types/mark.js": "^8.11.5",
|
||||||
"@types/marked": "^1.1.0",
|
"@types/marked": "^1.1.0",
|
||||||
|
"@types/node": "^15.6.1",
|
||||||
"@types/prismjs": "^1.16.5",
|
"@types/prismjs": "^1.16.5",
|
||||||
"@types/prop-types": "^15.7.3",
|
"@types/prop-types": "^15.7.3",
|
||||||
"@types/react": "^17.0.8",
|
"@types/react": "^17.0.8",
|
||||||
|
@ -102,7 +103,6 @@
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"babel-plugin-styled-components": "^1.12.0",
|
"babel-plugin-styled-components": "^1.12.0",
|
||||||
"beautify-benchmark": "^0.2.4",
|
"beautify-benchmark": "^0.2.4",
|
||||||
"bundlesize": "^0.18.1",
|
|
||||||
"conventional-changelog-cli": "^2.0.34",
|
"conventional-changelog-cli": "^2.0.34",
|
||||||
"copy-webpack-plugin": "^9.0.0",
|
"copy-webpack-plugin": "^9.0.0",
|
||||||
"core-js": "^3.13.1",
|
"core-js": "^3.13.1",
|
||||||
|
@ -130,6 +130,7 @@
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
|
"size-limit": "^7.0.4",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^2.0.0",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"ts-jest": "^27.0.2",
|
"ts-jest": "^27.0.2",
|
||||||
|
@ -140,7 +141,7 @@
|
||||||
"url-polyfill": "^1.1.12",
|
"url-polyfill": "^1.1.12",
|
||||||
"webpack": "^5.38.1",
|
"webpack": "^5.38.1",
|
||||||
"webpack-cli": "^4.7.2",
|
"webpack-cli": "^4.7.2",
|
||||||
"webpack-dev-server": "^3.11.2",
|
"webpack-dev-server": "^4.6.0",
|
||||||
"webpack-node-externals": "^3.0.0",
|
"webpack-node-externals": "^3.0.0",
|
||||||
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
|
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
|
||||||
},
|
},
|
||||||
|
@ -163,7 +164,6 @@
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "^0.7.0",
|
"marked": "^0.7.0",
|
||||||
"memoize-one": "^5.2.1",
|
|
||||||
"mobx-react": "^7.2.0",
|
"mobx-react": "^7.2.0",
|
||||||
"openapi-sampler": "^1.0.1",
|
"openapi-sampler": "^1.0.1",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
@ -177,10 +177,10 @@
|
||||||
"swagger2openapi": "^7.0.6",
|
"swagger2openapi": "^7.0.6",
|
||||||
"url-template": "^2.0.8"
|
"url-template": "^2.0.8"
|
||||||
},
|
},
|
||||||
"bundlesize": [
|
"size-limit": [
|
||||||
{
|
{
|
||||||
"path": "./bundles/redoc.standalone.js",
|
"path": "./bundles/redoc.standalone.js",
|
||||||
"maxSize": "350 kB"
|
"limit": "350 kB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"jest": {
|
"jest": {
|
||||||
|
|
|
@ -18,12 +18,6 @@ import { ResponsesList } from '../Responses/ResponsesList';
|
||||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||||
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||||
|
|
||||||
const OperationRow = styled(Row)`
|
|
||||||
backface-visibility: hidden;
|
|
||||||
contain: content;
|
|
||||||
overflow: hidden;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Description = styled.div`
|
const Description = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||||
`;
|
`;
|
||||||
|
@ -43,7 +37,7 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
<OperationRow>
|
<Row>
|
||||||
<MiddlePanel>
|
<MiddlePanel>
|
||||||
<H2>
|
<H2>
|
||||||
<ShareLink to={operation.id} />
|
<ShareLink to={operation.id} />
|
||||||
|
@ -71,7 +65,7 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
<ResponseSamples operation={operation} />
|
<ResponseSamples operation={operation} />
|
||||||
<CallbackSamples callbacks={operation.callbacks} />
|
<CallbackSamples callbacks={operation.callbacks} />
|
||||||
</DarkRightPanel>
|
</DarkRightPanel>
|
||||||
</OperationRow>
|
</Row>
|
||||||
)}
|
)}
|
||||||
</OptionsContext.Consumer>
|
</OptionsContext.Consumer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { shortenHTTPVerb } from '../../utils/openapi';
|
||||||
import { MenuItems } from './MenuItems';
|
import { MenuItems } from './MenuItems';
|
||||||
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
||||||
import { l } from '../../services/Labels';
|
import { l } from '../../services/Labels';
|
||||||
|
import { scrollIntoViewIfNeeded } from '../../utils';
|
||||||
|
|
||||||
export interface MenuItemProps {
|
export interface MenuItemProps {
|
||||||
item: IMenuItem;
|
item: IMenuItem;
|
||||||
|
@ -33,7 +34,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
|
|
||||||
scrollIntoViewIfActive() {
|
scrollIntoViewIfActive() {
|
||||||
if (this.props.item.active && this.ref.current) {
|
if (this.props.item.active && this.ref.current) {
|
||||||
this.ref.current.scrollIntoViewIfNeeded();
|
scrollIntoViewIfNeeded(this.ref.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +51,8 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
||||||
) : (
|
) : (
|
||||||
<MenuItemLabel depth={item.depth} active={item.active} type={item.type} ref={this.ref}>
|
<MenuItemLabel depth={item.depth} active={item.active} type={item.type} ref={this.ref}>
|
||||||
<MenuItemTitle title={item.name}>
|
<MenuItemTitle title={item.sidebarLabel}>
|
||||||
{item.name}
|
{item.sidebarLabel}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
{(item.depth > 0 && item.items.length > 0 && (
|
{(item.depth > 0 && item.items.length > 0 && (
|
||||||
|
@ -82,7 +83,7 @@ export class OperationMenuItemContent extends React.Component<OperationMenuItemC
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.props.item.active && this.ref.current) {
|
if (this.props.item.active && this.ref.current) {
|
||||||
this.ref.current.scrollIntoViewIfNeeded();
|
scrollIntoViewIfNeeded(this.ref.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ export class OperationMenuItemContent extends React.Component<OperationMenuItemC
|
||||||
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
||||||
)}
|
)}
|
||||||
<MenuItemTitle width="calc(100% - 38px)">
|
<MenuItemTitle width="calc(100% - 38px)">
|
||||||
{item.name}
|
{item.sidebarLabel}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
</MenuItemLabel>
|
</MenuItemLabel>
|
||||||
|
|
|
@ -37,7 +37,6 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
||||||
if (item && item.active && this.context.menuToggle) {
|
if (item && item.active && this.context.menuToggle) {
|
||||||
return item.expanded ? item.collapse() : item.expand();
|
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) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ export interface IMenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
absoluteIdx?: number;
|
absoluteIdx?: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
sidebarLabel: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
|
|
@ -5,6 +5,11 @@ import { isNumeric, mergeObjects } from '../utils/helpers';
|
||||||
import { LabelsConfigRaw, setRedocLabels } from './Labels';
|
import { LabelsConfigRaw, setRedocLabels } from './Labels';
|
||||||
import { MDXComponentMeta } from './MarkdownRenderer';
|
import { MDXComponentMeta } from './MarkdownRenderer';
|
||||||
|
|
||||||
|
export enum SideNavStyleEnum {
|
||||||
|
SummaryOnly = 'summary-only',
|
||||||
|
PathOnly = 'path-only',
|
||||||
|
}
|
||||||
|
|
||||||
export interface RedocRawOptions {
|
export interface RedocRawOptions {
|
||||||
theme?: ThemeInterface;
|
theme?: ThemeInterface;
|
||||||
scrollYOffset?: number | string | (() => number);
|
scrollYOffset?: number | string | (() => number);
|
||||||
|
@ -22,6 +27,7 @@ export interface RedocRawOptions {
|
||||||
disableSearch?: boolean | string;
|
disableSearch?: boolean | string;
|
||||||
onlyRequiredInSamples?: boolean | string;
|
onlyRequiredInSamples?: boolean | string;
|
||||||
showExtensions?: boolean | string | string[];
|
showExtensions?: boolean | string | string[];
|
||||||
|
sideNavStyle?: SideNavStyleEnum;
|
||||||
hideSingleRequestSampleTab?: boolean | string;
|
hideSingleRequestSampleTab?: boolean | string;
|
||||||
menuToggle?: boolean | string;
|
menuToggle?: boolean | string;
|
||||||
jsonSampleExpandLevel?: number | string | 'all';
|
jsonSampleExpandLevel?: number | string | 'all';
|
||||||
|
@ -143,6 +149,22 @@ export class RedocNormalizedOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static normalizeSideNavStyle(value: RedocRawOptions['sideNavStyle']): SideNavStyleEnum {
|
||||||
|
const defaultValue = SideNavStyleEnum.SummaryOnly;
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
case defaultValue:
|
||||||
|
return value;
|
||||||
|
case SideNavStyleEnum.PathOnly:
|
||||||
|
return SideNavStyleEnum.PathOnly;
|
||||||
|
default:
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static normalizeSectionsAtTheEnd(value: RedocRawOptions['sectionsAtTheEnd']): string[] {
|
static normalizeSectionsAtTheEnd(value: RedocRawOptions['sectionsAtTheEnd']): string[] {
|
||||||
if (typeof value === 'undefined' || typeof value !== 'string') {
|
if (typeof value === 'undefined' || typeof value !== 'string') {
|
||||||
return new Array(0);
|
return new Array(0);
|
||||||
|
@ -198,6 +220,7 @@ export class RedocNormalizedOptions {
|
||||||
disableSearch: boolean;
|
disableSearch: boolean;
|
||||||
onlyRequiredInSamples: boolean;
|
onlyRequiredInSamples: boolean;
|
||||||
showExtensions: boolean | string[];
|
showExtensions: boolean | string[];
|
||||||
|
sideNavStyle: SideNavStyleEnum;
|
||||||
hideSingleRequestSampleTab: boolean;
|
hideSingleRequestSampleTab: boolean;
|
||||||
menuToggle: boolean;
|
menuToggle: boolean;
|
||||||
jsonSampleExpandLevel: number;
|
jsonSampleExpandLevel: number;
|
||||||
|
@ -257,6 +280,7 @@ 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.sideNavStyle = RedocNormalizedOptions.normalizeSideNavStyle(raw.sideNavStyle);
|
||||||
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(
|
||||||
|
|
|
@ -14,6 +14,7 @@ export class GroupModel implements IMenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
absoluteIdx?: number;
|
absoluteIdx?: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
sidebarLabel: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
type: MenuItemGroupType;
|
type: MenuItemGroupType;
|
||||||
|
|
||||||
|
@ -44,6 +45,8 @@ export class GroupModel implements IMenuItem {
|
||||||
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
||||||
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
||||||
|
|
||||||
|
this.sidebarLabel = this.name;
|
||||||
|
|
||||||
// remove sections from markdown, same as in ApiInfo
|
// remove sections from markdown, same as in ApiInfo
|
||||||
this.description = tagOrGroup.description || '';
|
this.description = tagOrGroup.description || '';
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { FieldModel } from './Field';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
import { RequestBodyModel } from './RequestBody';
|
import { RequestBodyModel } from './RequestBody';
|
||||||
import { ResponseModel } from './Response';
|
import { ResponseModel } from './Response';
|
||||||
|
import { SideNavStyleEnum } from '../RedocNormalizedOptions';
|
||||||
|
|
||||||
export interface XPayloadSample {
|
export interface XPayloadSample {
|
||||||
lang: 'payload';
|
lang: 'payload';
|
||||||
|
@ -49,6 +50,7 @@ export class OperationModel implements IMenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
absoluteIdx?: number;
|
absoluteIdx?: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
sidebarLabel: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
type = 'operation' as const;
|
type = 'operation' as const;
|
||||||
|
|
||||||
|
@ -105,6 +107,8 @@ export class OperationModel implements IMenuItem {
|
||||||
|
|
||||||
this.name = getOperationSummary(operationSpec);
|
this.name = getOperationSummary(operationSpec);
|
||||||
|
|
||||||
|
this.sidebarLabel = options.sideNavStyle === SideNavStyleEnum.PathOnly ? this.path : this.name;
|
||||||
|
|
||||||
if (this.isCallback) {
|
if (this.isCallback) {
|
||||||
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
||||||
// Can be defined individually per-callback in the specification. Defaults to none.
|
// Can be defined individually per-callback in the specification. Defaults to none.
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { OpenAPIRequestBody, Referenced } from '../../types';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
|
import { getContentWithLegacyExamples } from '../../utils';
|
||||||
|
|
||||||
type RequestBodyProps = {
|
type RequestBodyProps = {
|
||||||
parser: OpenAPIParser;
|
parser: OpenAPIParser;
|
||||||
|
@ -18,13 +19,15 @@ export class RequestBodyModel {
|
||||||
|
|
||||||
constructor(props: RequestBodyProps) {
|
constructor(props: RequestBodyProps) {
|
||||||
const { parser, infoOrRef, options, isEvent } = props;
|
const { parser, infoOrRef, options, isEvent } = props;
|
||||||
const isRequest = isEvent ? false : true;
|
const isRequest = !isEvent;
|
||||||
const info = parser.deref(infoOrRef);
|
const info = parser.deref(infoOrRef);
|
||||||
this.description = info.description || '';
|
this.description = info.description || '';
|
||||||
this.required = !!info.required;
|
this.required = !!info.required;
|
||||||
parser.exitRef(infoOrRef);
|
parser.exitRef(infoOrRef);
|
||||||
if (info.content !== undefined) {
|
|
||||||
this.content = new MediaContentModel(parser, info.content, isRequest, options);
|
const mediaContent = getContentWithLegacyExamples(info);
|
||||||
|
if (mediaContent !== undefined) {
|
||||||
|
this.content = new MediaContentModel(parser, mediaContent, isRequest, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,17 +186,20 @@ export interface OpenAPIRequestBody {
|
||||||
description?: string;
|
description?: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
content: { [mime: string]: OpenAPIMediaType };
|
content: { [mime: string]: OpenAPIMediaType };
|
||||||
|
|
||||||
|
'x-examples'?: { [mime: string]: { [name: string]: Referenced<OpenAPIExample> } };
|
||||||
|
'x-example'?: { [mime: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIResponses {
|
export interface OpenAPIResponses {
|
||||||
[code: string]: OpenAPIResponse;
|
[code: string]: OpenAPIResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIResponse {
|
export interface OpenAPIResponse
|
||||||
description?: string;
|
extends Pick<OpenAPIRequestBody, 'description' | 'x-examples' | 'x-example'> {
|
||||||
headers?: { [name: string]: Referenced<OpenAPIHeader> };
|
headers?: { [name: string]: Referenced<OpenAPIHeader> };
|
||||||
content?: { [mime: string]: OpenAPIMediaType };
|
|
||||||
links?: { [name: string]: Referenced<OpenAPILink> };
|
links?: { [name: string]: Referenced<OpenAPILink> };
|
||||||
|
content?: { [mime: string]: OpenAPIMediaType };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPILink {
|
export interface OpenAPILink {
|
||||||
|
|
|
@ -11,10 +11,16 @@ import {
|
||||||
serializeParameterValue,
|
serializeParameterValue,
|
||||||
sortByRequired,
|
sortByRequired,
|
||||||
humanizeNumberRange,
|
humanizeNumberRange,
|
||||||
|
getContentWithLegacyExamples,
|
||||||
} from '../';
|
} from '../';
|
||||||
|
|
||||||
import { FieldModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
import { FieldModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
||||||
import { OpenAPIParameter, OpenAPIParameterLocation, OpenAPIParameterStyle } from '../../types';
|
import {
|
||||||
|
OpenAPIMediaType,
|
||||||
|
OpenAPIParameter,
|
||||||
|
OpenAPIParameterLocation,
|
||||||
|
OpenAPIParameterStyle,
|
||||||
|
} from '../../types';
|
||||||
import { expandDefaultServerVariables } from '../openapi';
|
import { expandDefaultServerVariables } from '../openapi';
|
||||||
|
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
|
@ -1161,4 +1167,106 @@ describe('Utils', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('OpenAPI getContentWithLegacyExamples', () => {
|
||||||
|
it('should return undefined if no x-examples/x-example and no content', () => {
|
||||||
|
expect(getContentWithLegacyExamples({})).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return unmodified object if no x-examples or x-example', () => {
|
||||||
|
const info = {
|
||||||
|
content: {
|
||||||
|
'application/json': {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info);
|
||||||
|
expect(content).toStrictEqual(info.content);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a new content object if no content and x-examples', () => {
|
||||||
|
const info = {
|
||||||
|
'x-examples': {
|
||||||
|
'application/json': {
|
||||||
|
name: {
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info);
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': {
|
||||||
|
examples: {
|
||||||
|
name: {
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a new content object if no content and x-example', () => {
|
||||||
|
const info = {
|
||||||
|
'x-example': {
|
||||||
|
'application/json': 'test',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info);
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': { example: 'test' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return copy of content with injected x-example', () => {
|
||||||
|
const info = {
|
||||||
|
'x-example': {
|
||||||
|
'application/json': 'test',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: { type: 'string' },
|
||||||
|
},
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info) as { [mime: string]: OpenAPIMediaType };
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': { schema: { type: 'string' }, example: 'test' },
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
});
|
||||||
|
expect(content).not.toStrictEqual(info.content);
|
||||||
|
expect(content['application/json']).not.toStrictEqual(info.content['application/json']);
|
||||||
|
expect(content['text/plain']).toStrictEqual(info.content['text/plain']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer x-examples over x-example', () => {
|
||||||
|
const info = {
|
||||||
|
'x-example': {
|
||||||
|
'application/json': 'test',
|
||||||
|
},
|
||||||
|
'x-examples': {
|
||||||
|
'application/json': { name: { value: 'test' } },
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: { type: 'string' },
|
||||||
|
},
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = getContentWithLegacyExamples(info) as { [mime: string]: OpenAPIMediaType };
|
||||||
|
expect(content).toEqual({
|
||||||
|
'application/json': { schema: { type: 'string' }, examples: { name: { value: 'test' } } },
|
||||||
|
'text/plain': { schema: { type: 'string' } },
|
||||||
|
});
|
||||||
|
expect(content).not.toStrictEqual(info.content);
|
||||||
|
expect(content['application/json']).not.toStrictEqual(info.content['application/json']);
|
||||||
|
expect(content['text/plain']).toStrictEqual(info.content['text/plain']);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,13 +24,16 @@ export function html2Str(html: string): string {
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// scrollIntoViewIfNeeded polyfill
|
// Alternate scrollIntoViewIfNeeded implementation.
|
||||||
|
// Used in all cases, since it seems Chrome's implementation is buggy
|
||||||
|
// when "Experimental Web Platform Features" is enabled (at least of version 96).
|
||||||
|
// See #1714, #1742
|
||||||
|
|
||||||
if (typeof Element !== 'undefined' && !(Element as any).prototype.scrollIntoViewIfNeeded) {
|
export function scrollIntoViewIfNeeded(el: HTMLElement, centerIfNeeded = true) {
|
||||||
(Element as any).prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
|
const parent = el.parentNode as HTMLElement | null;
|
||||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
if (!parent) {
|
||||||
|
return;
|
||||||
const parent = this.parentNode;
|
}
|
||||||
const parentComputedStyle = window.getComputedStyle(parent, undefined);
|
const parentComputedStyle = window.getComputedStyle(parent, undefined);
|
||||||
const parentBorderTopWidth = parseInt(
|
const parentBorderTopWidth = parseInt(
|
||||||
parentComputedStyle.getPropertyValue('border-top-width'),
|
parentComputedStyle.getPropertyValue('border-top-width'),
|
||||||
|
@ -40,36 +43,35 @@ if (typeof Element !== 'undefined' && !(Element as any).prototype.scrollIntoView
|
||||||
parentComputedStyle.getPropertyValue('border-left-width'),
|
parentComputedStyle.getPropertyValue('border-left-width'),
|
||||||
10,
|
10,
|
||||||
);
|
);
|
||||||
const overTop = this.offsetTop - parent.offsetTop < parent.scrollTop;
|
const overTop = el.offsetTop - parent.offsetTop < parent.scrollTop;
|
||||||
const overBottom =
|
const overBottom =
|
||||||
this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth >
|
el.offsetTop - parent.offsetTop + el.clientHeight - parentBorderTopWidth >
|
||||||
parent.scrollTop + parent.clientHeight;
|
parent.scrollTop + parent.clientHeight;
|
||||||
const overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft;
|
const overLeft = el.offsetLeft - parent.offsetLeft < parent.scrollLeft;
|
||||||
const overRight =
|
const overRight =
|
||||||
this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth >
|
el.offsetLeft - parent.offsetLeft + el.clientWidth - parentBorderLeftWidth >
|
||||||
parent.scrollLeft + parent.clientWidth;
|
parent.scrollLeft + parent.clientWidth;
|
||||||
const alignWithTop = overTop && !overBottom;
|
const alignWithTop = overTop && !overBottom;
|
||||||
|
|
||||||
if ((overTop || overBottom) && centerIfNeeded) {
|
if ((overTop || overBottom) && centerIfNeeded) {
|
||||||
parent.scrollTop =
|
parent.scrollTop =
|
||||||
this.offsetTop -
|
el.offsetTop -
|
||||||
parent.offsetTop -
|
parent.offsetTop -
|
||||||
parent.clientHeight / 2 -
|
parent.clientHeight / 2 -
|
||||||
parentBorderTopWidth +
|
parentBorderTopWidth +
|
||||||
this.clientHeight / 2;
|
el.clientHeight / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((overLeft || overRight) && centerIfNeeded) {
|
if ((overLeft || overRight) && centerIfNeeded) {
|
||||||
parent.scrollLeft =
|
parent.scrollLeft =
|
||||||
this.offsetLeft -
|
el.offsetLeft -
|
||||||
parent.offsetLeft -
|
parent.offsetLeft -
|
||||||
parent.clientWidth / 2 -
|
parent.clientWidth / 2 -
|
||||||
parentBorderLeftWidth +
|
parentBorderLeftWidth +
|
||||||
this.clientWidth / 2;
|
el.clientWidth / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
||||||
this.scrollIntoView(alignWithTop);
|
el.scrollIntoView(alignWithTop);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
OpenAPIMediaType,
|
OpenAPIMediaType,
|
||||||
OpenAPIParameter,
|
OpenAPIParameter,
|
||||||
OpenAPIParameterStyle,
|
OpenAPIParameterStyle,
|
||||||
|
OpenAPIRequestBody,
|
||||||
|
OpenAPIResponse,
|
||||||
OpenAPISchema,
|
OpenAPISchema,
|
||||||
OpenAPIServer,
|
OpenAPIServer,
|
||||||
Referenced,
|
Referenced,
|
||||||
|
@ -638,3 +640,33 @@ export function pluralizeType(displayType: string): string {
|
||||||
.map(type => type.replace(/^(string|object|number|integer|array|boolean)s?( ?.*)/, '$1s$2'))
|
.map(type => type.replace(/^(string|object|number|integer|array|boolean)s?( ?.*)/, '$1s$2'))
|
||||||
.join(' or ');
|
.join(' or ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getContentWithLegacyExamples(
|
||||||
|
info: OpenAPIRequestBody | OpenAPIResponse,
|
||||||
|
): { [mime: string]: OpenAPIMediaType } | undefined {
|
||||||
|
let mediaContent = info.content;
|
||||||
|
const xExamples = info['x-examples']; // converted from OAS2 body param
|
||||||
|
const xExample = info['x-example']; // converted from OAS2 body param
|
||||||
|
|
||||||
|
if (xExamples) {
|
||||||
|
mediaContent = { ...mediaContent };
|
||||||
|
for (const mime of Object.keys(xExamples)) {
|
||||||
|
const examples = xExamples[mime];
|
||||||
|
mediaContent[mime] = {
|
||||||
|
...mediaContent[mime],
|
||||||
|
examples,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (xExample) {
|
||||||
|
mediaContent = { ...mediaContent };
|
||||||
|
for (const mime of Object.keys(xExample)) {
|
||||||
|
const example = xExample[mime];
|
||||||
|
mediaContent[mime] = {
|
||||||
|
...mediaContent[mime],
|
||||||
|
example,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaContent;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user