mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-07 13:44:54 +03:00
Fixed A11Y issues by replacing react-dropdown-aria by react-select component
Fixed: ARIA input fields must have an accessible name Fixed: Ensure interactive controls are not nested Coded formatted with prettier Files changed: dropdown.ts styled.elements.ts PayloadSamples.tsx SchemaDefinition.tsx DiscriminatorDropdown.tsx ResponseDetails.tsx MediaTypeSamples.tsx Parameters.tsx MediaTypesSwitch.tsx DropdownOrLabel.tsx CallbackSamples.tsx GenericChildrenSwitcher.tsx
This commit is contained in:
parent
ebbd256b1f
commit
42f80fc642
18
cli/index.ts
18
cli/index.ts
|
@ -45,7 +45,7 @@ const BUNDLES_DIR = dirname(require.resolve('redoc'));
|
||||||
YargsParser.command(
|
YargsParser.command(
|
||||||
'serve <spec>',
|
'serve <spec>',
|
||||||
'start the server',
|
'start the server',
|
||||||
yargs => {
|
(yargs) => {
|
||||||
yargs.positional('spec', {
|
yargs.positional('spec', {
|
||||||
describe: 'path or URL to your spec',
|
describe: 'path or URL to your spec',
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ YargsParser.command(
|
||||||
yargs.demandOption('spec');
|
yargs.demandOption('spec');
|
||||||
return yargs;
|
return yargs;
|
||||||
},
|
},
|
||||||
async argv => {
|
async (argv) => {
|
||||||
const config: Options = {
|
const config: Options = {
|
||||||
ssr: argv.ssr as boolean,
|
ssr: argv.ssr as boolean,
|
||||||
title: argv.title as string,
|
title: argv.title as string,
|
||||||
|
@ -102,7 +102,7 @@ YargsParser.command(
|
||||||
.command(
|
.command(
|
||||||
'bundle <spec>',
|
'bundle <spec>',
|
||||||
'bundle spec into zero-dependency HTML-file',
|
'bundle spec into zero-dependency HTML-file',
|
||||||
yargs => {
|
(yargs) => {
|
||||||
yargs.positional('spec', {
|
yargs.positional('spec', {
|
||||||
describe: 'path or URL to your spec',
|
describe: 'path or URL to your spec',
|
||||||
});
|
});
|
||||||
|
@ -213,7 +213,7 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
const watcher = watch(pathToSpecDirectory, watchOptions);
|
const watcher = watch(pathToSpecDirectory, watchOptions);
|
||||||
const log = console.log.bind(console);
|
const log = console.log.bind(console);
|
||||||
|
|
||||||
const handlePath = async _path => {
|
const handlePath = async (_path) => {
|
||||||
try {
|
try {
|
||||||
spec = await loadAndBundleSpec(resolve(pathToSpec));
|
spec = await loadAndBundleSpec(resolve(pathToSpec));
|
||||||
pageHTML = await getPageHTML(spec, pathToSpec, options);
|
pageHTML = await getPageHTML(spec, pathToSpec, options);
|
||||||
|
@ -224,18 +224,18 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
};
|
};
|
||||||
|
|
||||||
watcher
|
watcher
|
||||||
.on('change', async path => {
|
.on('change', async (path) => {
|
||||||
log(`${path} changed, updating docs`);
|
log(`${path} changed, updating docs`);
|
||||||
handlePath(path);
|
handlePath(path);
|
||||||
})
|
})
|
||||||
.on('add', async path => {
|
.on('add', async (path) => {
|
||||||
log(`File ${path} added, updating docs`);
|
log(`File ${path} added, updating docs`);
|
||||||
handlePath(path);
|
handlePath(path);
|
||||||
})
|
})
|
||||||
.on('addDir', path => {
|
.on('addDir', (path) => {
|
||||||
log(`↗ Directory ${path} added. Files in here will trigger reload.`);
|
log(`↗ Directory ${path} added. Files in here will trigger reload.`);
|
||||||
})
|
})
|
||||||
.on('error', error => console.error(`Watcher error: ${error}`))
|
.on('error', (error) => console.error(`Watcher error: ${error}`))
|
||||||
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ function escapeClosingScriptTag(str) {
|
||||||
|
|
||||||
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
||||||
function escapeUnicode(str) {
|
function escapeUnicode(str) {
|
||||||
return str.replace(/\u2028|\u2029/g, m => '\\u202' + (m === '\u2028' ? '8' : '9'));
|
return str.replace(/\u2028|\u2029/g, (m) => '\\u202' + (m === '\u2028' ? '8' : '9'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleError(error: Error) {
|
function handleError(error: Error) {
|
||||||
|
|
21207
package-lock.json
generated
21207
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -148,7 +148,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.14.0",
|
"@babel/runtime": "^7.14.0",
|
||||||
"@redocly/openapi-core": "^1.0.0-beta.54",
|
"@redocly/openapi-core": "^1.0.0-beta.54",
|
||||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"decko": "^1.2.0",
|
"decko": "^1.2.0",
|
||||||
"dompurify": "^2.2.8",
|
"dompurify": "^2.2.8",
|
||||||
|
@ -165,6 +164,7 @@
|
||||||
"polished": "^4.1.3",
|
"polished": "^4.1.3",
|
||||||
"prismjs": "^1.24.1",
|
"prismjs": "^1.24.1",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
|
"react-select": "^5.1.0",
|
||||||
"react-tabs": "^3.2.2",
|
"react-tabs": "^3.2.2",
|
||||||
"slugify": "~1.4.7",
|
"slugify": "~1.4.7",
|
||||||
"stickyfill": "^1.1.1",
|
"stickyfill": "^1.1.1",
|
||||||
|
|
|
@ -1,113 +1,102 @@
|
||||||
import Dropdown from '@redocly/react-dropdown-aria';
|
import Dropdown from 'react-select';
|
||||||
|
|
||||||
import styled from '../styled-components';
|
import styled from '../styled-components';
|
||||||
|
|
||||||
export interface DropdownOption {
|
export interface DropdownOption {
|
||||||
idx: number;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropdownProps {
|
export interface DropdownProps {
|
||||||
options: DropdownOption[];
|
options: DropdownOption[];
|
||||||
value: string;
|
value: DropdownOption;
|
||||||
onChange: (option: DropdownOption) => void;
|
onChange: (val: DropdownOption) => void;
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StyledDropdown = styled(Dropdown)`
|
export const StyledDropdown = styled(Dropdown)`
|
||||||
&& {
|
&& {
|
||||||
box-sizing: border-box;
|
|
||||||
min-width: 100px;
|
|
||||||
outline: none;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid rgba(38, 50, 56, 0.5);
|
|
||||||
vertical-align: bottom;
|
|
||||||
padding: 2px 0px 2px 6px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: auto;
|
width: auto;
|
||||||
background: white;
|
min-width: 100px;
|
||||||
color: #263238;
|
border-radius: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
vertical-align: bottom;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
|
|
||||||
|
background: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
|
color: #263238;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
border: 1px solid ${(props) => props.theme.colors.primary.main};
|
border: 1px solid ${(props) => props.theme.colors.border.light};
|
||||||
color: ${(props) => props.theme.colors.primary.main};
|
|
||||||
box-shadow: 0px 0px 0px 1px ${(props) => props.theme.colors.primary.main};
|
box-shadow: 0px 0px 0px 1px ${(props) => props.theme.colors.primary.main};
|
||||||
|
|
||||||
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
}
|
}
|
||||||
.dropdown-selector {
|
|
||||||
display: inline-flex;
|
.react-dropdown__control {
|
||||||
|
min-height: 0;
|
||||||
|
border: 1px solid rgba(38, 50, 56, 0.5);
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
.react-dropdown__value-container {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-dropdown__indicator-separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-dropdown__indicator {
|
||||||
|
margin: 4px 8px 2px 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: auto;
|
|
||||||
padding-right: 20px;
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.dropdown-selector-value {
|
|
||||||
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
|
||||||
position: relative;
|
|
||||||
font-size: 0.929em;
|
|
||||||
width: 100%;
|
|
||||||
line-height: 1;
|
|
||||||
vertical-align: middle;
|
|
||||||
color: #263238;
|
|
||||||
left: 0;
|
|
||||||
transition: color 0.25s ease, text-shadow 0.25s ease;
|
|
||||||
}
|
|
||||||
.dropdown-arrow {
|
|
||||||
position: absolute;
|
|
||||||
right: 3px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
border-color: ${(props) => props.theme.colors.primary.main} transparent transparent;
|
border-color: ${(props) => props.theme.colors.primary.main} transparent transparent;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0.35em 0.35em 0;
|
border-width: 0.35em 0.35em 0;
|
||||||
width: 0;
|
|
||||||
svg {
|
svg {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-selector-content {
|
|
||||||
position: absolute;
|
|
||||||
margin-top: 2px;
|
|
||||||
left: -2px;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
z-index: 10;
|
|
||||||
min-width: 100px;
|
|
||||||
|
|
||||||
background: white;
|
|
||||||
border: 1px solid rgba(38, 50, 56, 0.2);
|
|
||||||
box-shadow: 0px 2px 4px 0px rgba(34, 36, 38, 0.12), 0px 2px 10px 0px rgba(34, 36, 38, 0.08);
|
|
||||||
|
|
||||||
max-height: 220px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-option {
|
.react-dropdown__menu {
|
||||||
font-size: 0.9em;
|
margin: 4px 0 8px;
|
||||||
color: #263238;
|
border-radius: 2px;
|
||||||
cursor: pointer;
|
z-index: 3;
|
||||||
padding: 0.4em;
|
|
||||||
background-color: #ffffff;
|
|
||||||
|
|
||||||
&[aria-selected='true'] {
|
.react-dropdown__menu-list {
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
padding: 2px 0;
|
||||||
}
|
|
||||||
|
.react-dropdown__option {
|
||||||
|
padding: 6px 0px 6px 10px;
|
||||||
|
|
||||||
|
color: rgb(38, 50, 56);
|
||||||
|
background-color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(38, 50, 56, 0.12);
|
background-color: rgba(38, 50, 56, 0.12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
|
||||||
cursor: pointer;
|
.react-dropdown__option--is-selected {
|
||||||
height: 1px;
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
background-color: transparent;
|
}
|
||||||
|
|
||||||
|
.react-dropdown__option--is-focused {
|
||||||
|
background-color: rgba(38, 50, 56, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -115,19 +104,24 @@ export const StyledDropdown = styled(Dropdown)`
|
||||||
export const SimpleDropdown = styled(StyledDropdown)`
|
export const SimpleDropdown = styled(StyledDropdown)`
|
||||||
&& {
|
&& {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
text-transform: none;
|
|
||||||
font-size: 0.969em;
|
|
||||||
|
|
||||||
font-size: 1em;
|
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 1.2em 0 0;
|
|
||||||
|
font-size: 0.969em;
|
||||||
|
text-transform: none;
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
|
.react-dropdown__control {
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.react-dropdown__indicator {
|
||||||
|
margin: 4px 8px 0px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
border: none;
|
.react-dropdown__single-value {
|
||||||
box-shadow: none;
|
|
||||||
.dropdown-selector-value {
|
|
||||||
color: ${(props) => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
text-shadow: 0px 0px 0px ${(props) => props.theme.colors.primary.main};
|
text-shadow: 0px 0px 0px ${(props) => props.theme.colors.primary.main};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@ export const PropertiesTableCaption = styled.caption`
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: ${props => props.theme.colors.text.secondary};
|
color: ${(props) => props.theme.colors.text.secondary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PropertyCell = styled.td<{ kind?: string }>`
|
export const PropertyCell = styled.td<{ kind?: string }>`
|
||||||
border-left: 1px solid ${props => props.theme.schema.linesColor};
|
border-left: 1px solid ${(props) => props.theme.schema.linesColor};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px 10px 10px 0;
|
padding: 10px 10px 10px 0;
|
||||||
|
@ -32,16 +32,16 @@ export const PropertyCell = styled.td<{ kind?: string }>`
|
||||||
to bottom,
|
to bottom,
|
||||||
transparent 0%,
|
transparent 0%,
|
||||||
transparent 22px,
|
transparent 22px,
|
||||||
${props => props.theme.schema.linesColor} 22px,
|
${(props) => props.theme.schema.linesColor} 22px,
|
||||||
${props => props.theme.schema.linesColor} 100%
|
${(props) => props.theme.schema.linesColor} 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.last > & {
|
tr.last > & {
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
${props => props.theme.schema.linesColor} 0%,
|
${(props) => props.theme.schema.linesColor} 0%,
|
||||||
${props => props.theme.schema.linesColor} 22px,
|
${(props) => props.theme.schema.linesColor} 22px,
|
||||||
transparent 22px,
|
transparent 22px,
|
||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
|
@ -101,8 +101,8 @@ export const PropertyDetailsCell = styled.td`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PropertyBullet = styled.span`
|
export const PropertyBullet = styled.span`
|
||||||
color: ${props => props.theme.schema.linesColor};
|
color: ${(props) => props.theme.schema.linesColor};
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
|
@ -111,7 +111,7 @@ export const PropertyBullet = styled.span`
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: ${props => props.theme.schema.linesColor};
|
background: ${(props) => props.theme.schema.linesColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -119,7 +119,7 @@ export const PropertyBullet = styled.span`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background: ${props => props.theme.schema.linesColor};
|
background: ${(props) => props.theme.schema.linesColor};
|
||||||
height: 7px;
|
height: 7px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -131,7 +131,7 @@ export const InnerPropertiesWrap = styled.div`
|
||||||
export const PropertiesTable = styled.table`
|
export const PropertiesTable = styled.table`
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-size: ${props => props.theme.typography.fontSize};
|
font-size: ${(props) => props.theme.typography.fontSize};
|
||||||
|
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -10,10 +10,10 @@ export const ClickablePropertyNameCell = styled(PropertyNameCell)`
|
||||||
border: 0;
|
border: 0;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${(props) => props.theme.colors.text.primary};
|
||||||
&:focus {
|
&:focus {
|
||||||
font-weight: ${({ theme }) => theme.typography.fontWeightBold};
|
font-weight: ${({ theme }) => theme.typography.fontWeightBold};
|
||||||
}
|
}
|
||||||
|
@ -34,23 +34,23 @@ export const FieldLabel = styled.span`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TypePrefix = styled(FieldLabel)`
|
export const TypePrefix = styled(FieldLabel)`
|
||||||
color: ${props => transparentize(0.1, props.theme.schema.typeNameColor)};
|
color: ${(props) => transparentize(0.1, props.theme.schema.typeNameColor)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TypeName = styled(FieldLabel)`
|
export const TypeName = styled(FieldLabel)`
|
||||||
color: ${props => props.theme.schema.typeNameColor};
|
color: ${(props) => props.theme.schema.typeNameColor};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TypeTitle = styled(FieldLabel)`
|
export const TypeTitle = styled(FieldLabel)`
|
||||||
color: ${props => props.theme.schema.typeTitleColor};
|
color: ${(props) => props.theme.schema.typeTitleColor};
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TypeFormat = TypeName;
|
export const TypeFormat = TypeName;
|
||||||
|
|
||||||
export const RequiredLabel = styled(FieldLabel.withComponent('div'))`
|
export const RequiredLabel = styled(FieldLabel.withComponent('div'))`
|
||||||
color: ${props => props.theme.schema.requireLabelColor};
|
color: ${(props) => props.theme.schema.requireLabelColor};
|
||||||
font-size: ${props => props.theme.schema.labelsTextSize};
|
font-size: ${(props) => props.theme.schema.labelsTextSize};
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
|
@ -6,7 +6,7 @@ const headerFontSize = {
|
||||||
3: '1.27em',
|
3: '1.27em',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const headerCommonMixin = level => css`
|
export const headerCommonMixin = (level) => css`
|
||||||
font-family: ${({ theme }) => theme.typography.headings.fontFamily};
|
font-family: ${({ theme }) => theme.typography.headings.fontFamily};
|
||||||
font-weight: ${({ theme }) => theme.typography.headings.fontWeight};
|
font-weight: ${({ theme }) => theme.typography.headings.fontWeight};
|
||||||
font-size: ${headerFontSize[level]};
|
font-size: ${headerFontSize[level]};
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { SECTION_ATTR } from '../services/MenuStore';
|
||||||
import styled, { media } from '../styled-components';
|
import styled, { media } from '../styled-components';
|
||||||
|
|
||||||
export const MiddlePanel = styled.div<{ compact?: boolean }>`
|
export const MiddlePanel = styled.div<{ compact?: boolean }>`
|
||||||
width: calc(100% - ${props => props.theme.rightPanel.width});
|
width: calc(100% - ${(props) => props.theme.rightPanel.width});
|
||||||
padding: 0 ${props => props.theme.spacing.sectionHorizontal}px;
|
padding: 0 ${(props) => props.theme.spacing.sectionHorizontal}px;
|
||||||
|
|
||||||
${({ compact, theme }) =>
|
${({ compact, theme }) =>
|
||||||
media.lessThan('medium', true)`
|
media.lessThan('medium', true)`
|
||||||
|
@ -14,10 +14,10 @@ export const MiddlePanel = styled.div<{ compact?: boolean }>`
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Section = styled.div.attrs(props => ({
|
export const Section = styled.div.attrs((props) => ({
|
||||||
[SECTION_ATTR]: props.id,
|
[SECTION_ATTR]: props.id,
|
||||||
}))<{ underlined?: boolean }>`
|
}))<{ underlined?: boolean }>`
|
||||||
padding: ${props => props.theme.spacing.sectionVertical}px 0;
|
padding: ${(props) => props.theme.spacing.sectionVertical}px 0;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
min-height: calc(100vh + 1px);
|
min-height: calc(100vh + 1px);
|
||||||
|
@ -48,20 +48,20 @@ export const Section = styled.div.attrs(props => ({
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RightPanel = styled.div`
|
export const RightPanel = styled.div`
|
||||||
width: ${props => props.theme.rightPanel.width};
|
width: ${(props) => props.theme.rightPanel.width};
|
||||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
background-color: ${props => props.theme.rightPanel.backgroundColor};
|
background-color: ${(props) => props.theme.rightPanel.backgroundColor};
|
||||||
padding: 0 ${props => props.theme.spacing.sectionHorizontal}px;
|
padding: 0 ${(props) => props.theme.spacing.sectionHorizontal}px;
|
||||||
|
|
||||||
${media.lessThan('medium', true)`
|
${media.lessThan('medium', true)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: ${props =>
|
padding: ${(props) =>
|
||||||
`${props.theme.spacing.sectionVertical}px ${props.theme.spacing.sectionHorizontal}px`};
|
`${props.theme.spacing.sectionVertical}px ${props.theme.spacing.sectionHorizontal}px`};
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DarkRightPanel = styled(RightPanel)`
|
export const DarkRightPanel = styled(RightPanel)`
|
||||||
background-color: ${props => props.theme.rightPanel.backgroundColor};
|
background-color: ${(props) => props.theme.rightPanel.backgroundColor};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Row = styled.div`
|
export const Row = styled.div`
|
||||||
|
|
|
@ -12,7 +12,7 @@ import styled, { createGlobalStyle } from '../styled-components';
|
||||||
* That's why the following ugly fix is required
|
* That's why the following ugly fix is required
|
||||||
*/
|
*/
|
||||||
const PerfectScrollbarConstructor =
|
const PerfectScrollbarConstructor =
|
||||||
PerfectScrollbarNamespace.default || ((PerfectScrollbarNamespace as any) as PerfectScrollbarType);
|
PerfectScrollbarNamespace.default || (PerfectScrollbarNamespace as any as PerfectScrollbarType);
|
||||||
|
|
||||||
const PSStyling = createGlobalStyle`${psStyles && psStyles.toString()}`;
|
const PSStyling = createGlobalStyle`${psStyles && psStyles.toString()}`;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class PerfectScrollbar extends React.Component<PerfectScrollbarProps> {
|
||||||
this.inst.destroy();
|
this.inst.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRef = ref => {
|
handleRef = (ref) => {
|
||||||
this._container = ref;
|
this._container = ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export function PerfectScrollbarWrap(
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options =>
|
{(options) =>
|
||||||
!options.nativeScrollbars ? (
|
!options.nativeScrollbars ? (
|
||||||
<PerfectScrollbar {...props}>{props.children}</PerfectScrollbar>
|
<PerfectScrollbar {...props}>{props.children}</PerfectScrollbar>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -33,8 +33,8 @@ export const SampleControlsWrap = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledPre = styled(PrismDiv.withComponent('pre'))`
|
export const StyledPre = styled(PrismDiv.withComponent('pre'))`
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
font-size: ${(props) => props.theme.typography.code.fontSize};
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ export const OneOfList = styled.div`
|
||||||
export const OneOfLabel = styled.span`
|
export const OneOfLabel = styled.span`
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -20,15 +20,15 @@ export const OneOfButton = styled.button<{ active: boolean }>`
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
border: 1px solid ${(props) => props.theme.colors.primary.main};
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
outline: none;
|
outline: none;
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 0 1px ${props => props.theme.colors.primary.main};
|
box-shadow: 0 0 0 1px ${(props) => props.theme.colors.primary.main};
|
||||||
}
|
}
|
||||||
|
|
||||||
${props => {
|
${(props) => {
|
||||||
if (props.active) {
|
if (props.active) {
|
||||||
return `
|
return `
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -49,7 +49,7 @@ export const OneOfButton = styled.button<{ active: boolean }>`
|
||||||
|
|
||||||
export const ArrayOpenningLabel = styled.div`
|
export const ArrayOpenningLabel = styled.div`
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
&::after {
|
&::after {
|
||||||
content: ' [';
|
content: ' [';
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ export const ArrayOpenningLabel = styled.div`
|
||||||
|
|
||||||
export const ArrayClosingLabel = styled.div`
|
export const ArrayClosingLabel = styled.div`
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
&::after {
|
&::after {
|
||||||
content: ']';
|
content: ']';
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,12 @@ class IntShelfIcon extends React.PureComponent<{
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShelfIcon = styled(IntShelfIcon)`
|
export const ShelfIcon = styled(IntShelfIcon)`
|
||||||
height: ${props => props.size || '18px'};
|
height: ${(props) => props.size || '18px'};
|
||||||
width: ${props => props.size || '18px'};
|
width: ${(props) => props.size || '18px'};
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
float: ${props => props.float || ''};
|
float: ${(props) => props.float || ''};
|
||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
transform: rotateZ(${props => directionMap[props.direction || 'down']});
|
transform: rotateZ(${(props) => directionMap[props.direction || 'down']});
|
||||||
|
|
||||||
polygon {
|
polygon {
|
||||||
fill: ${({ color, theme }) =>
|
fill: ${({ color, theme }) =>
|
||||||
|
@ -52,9 +52,9 @@ export const Badge = styled.span<{ type: string }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: ${props => props.theme.colors[props.type].main};
|
background-color: ${(props) => props.theme.colors[props.type].main};
|
||||||
color: ${props => props.theme.colors[props.type].contrastText};
|
color: ${(props) => props.theme.colors[props.type].contrastText};
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
font-size: ${(props) => props.theme.typography.code.fontSize};
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
|
@ -31,7 +31,7 @@ export const Tabs = styled(ReactTabs)`
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
&.react-tabs__tab--selected {
|
&.react-tabs__tab--selected {
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${(props) => props.theme.colors.text.primary};
|
||||||
background: ${({ theme }) => theme.rightPanel.textColor};
|
background: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: auto;
|
outline: auto;
|
||||||
|
@ -44,19 +44,19 @@ export const Tabs = styled(ReactTabs)`
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-success {
|
&.tab-success {
|
||||||
color: ${props => props.theme.colors.responses.success.tabTextColor};
|
color: ${(props) => props.theme.colors.responses.success.tabTextColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-redirect {
|
&.tab-redirect {
|
||||||
color: ${props => props.theme.colors.responses.redirect.tabTextColor};
|
color: ${(props) => props.theme.colors.responses.redirect.tabTextColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-info {
|
&.tab-info {
|
||||||
color: ${props => props.theme.colors.responses.info.tabTextColor};
|
color: ${(props) => props.theme.colors.responses.info.tabTextColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-error {
|
&.tab-error {
|
||||||
color: ${props => props.theme.colors.responses.error.tabTextColor};
|
color: ${(props) => props.theme.colors.responses.error.tabTextColor};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ export const Tabs = styled(ReactTabs)`
|
||||||
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
||||||
& > div,
|
& > div,
|
||||||
& > pre {
|
& > pre {
|
||||||
padding: ${props => props.theme.spacing.unit * 4}px;
|
padding: ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ export const SmallTabs = styled(Tabs)`
|
||||||
> .react-tabs__tab-panel {
|
> .react-tabs__tab-panel {
|
||||||
& > div,
|
& > div,
|
||||||
& > pre {
|
& > pre {
|
||||||
padding: ${props => props.theme.spacing.unit * 2}px 0;
|
padding: ${(props) => props.theme.spacing.unit * 2}px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface ApiInfoProps {
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ApiInfo extends React.Component<ApiInfoProps> {
|
export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
handleDownloadClick = e => {
|
handleDownloadClick = (e) => {
|
||||||
if (!e.target.href) {
|
if (!e.target.href) {
|
||||||
e.target.href = this.props.store.spec.info.downloadLink;
|
e.target.href = this.props.store.spec.info.downloadLink;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,12 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
const license =
|
const license =
|
||||||
(info.license && (
|
(info.license && (
|
||||||
<InfoSpan>
|
<InfoSpan>
|
||||||
License: {info.license.identifier ? info.license.identifier : (<a href={info.license.url}>{info.license.name}</a>)}
|
License:{' '}
|
||||||
|
{info.license.identifier ? (
|
||||||
|
info.license.identifier
|
||||||
|
) : (
|
||||||
|
<a href={info.license.url}>{info.license.name}</a>
|
||||||
|
)}
|
||||||
</InfoSpan>
|
</InfoSpan>
|
||||||
)) ||
|
)) ||
|
||||||
null;
|
null;
|
||||||
|
|
|
@ -13,8 +13,8 @@ export const ApiHeader = styled(H1)`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DownloadButton = styled.a`
|
export const DownloadButton = styled.a`
|
||||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
border: 1px solid ${(props) => props.theme.colors.primary.main};
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
padding: 4px 8px 4px;
|
padding: 4px 8px 4px;
|
||||||
|
|
|
@ -2,9 +2,9 @@ import * as React from 'react';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
export const LogoImgEl = styled.img`
|
export const LogoImgEl = styled.img`
|
||||||
max-height: ${props => props.theme.logo.maxHeight};
|
max-height: ${(props) => props.theme.logo.maxHeight};
|
||||||
max-width: ${props => props.theme.logo.maxWidth};
|
max-width: ${(props) => props.theme.logo.maxWidth};
|
||||||
padding: ${props => props.theme.logo.gutter};
|
padding: ${(props) => props.theme.logo.gutter};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
`;
|
`;
|
||||||
|
@ -18,4 +18,4 @@ const Link = styled.a`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
export const LinkWrap = url => Component => <Link href={url}>{Component}</Link>;
|
export const LinkWrap = (url) => (Component) => <Link href={url}>{Component}</Link>;
|
||||||
|
|
|
@ -14,7 +14,7 @@ export interface PayloadSampleProps {
|
||||||
|
|
||||||
export class CallbackPayloadSample extends React.Component<PayloadSampleProps> {
|
export class CallbackPayloadSample extends React.Component<PayloadSampleProps> {
|
||||||
render() {
|
render() {
|
||||||
const payloadSample = this.props.callback.codeSamples.find(sample =>
|
const payloadSample = this.props.callback.codeSamples.find((sample) =>
|
||||||
isPayloadSample(sample),
|
isPayloadSample(sample),
|
||||||
) as XPayloadSample | undefined;
|
) as XPayloadSample | undefined;
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,15 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||||
static contextType = OptionsContext;
|
static contextType = OptionsContext;
|
||||||
context: RedocNormalizedOptions;
|
context: RedocNormalizedOptions;
|
||||||
|
|
||||||
private renderDropdown = props => {
|
private renderDropdown = (props) => {
|
||||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
return (
|
||||||
|
<DropdownOrLabel
|
||||||
|
Label={MimeLabel}
|
||||||
|
Dropdown={InvertedSimpleDropdown}
|
||||||
|
aria-label="Callback sample dropdown"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -32,10 +39,10 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const operations = callbacks
|
const operations = callbacks
|
||||||
.map(callback => callback.operations.map(operation => operation))
|
.map((callback) => callback.operations.map((operation) => operation))
|
||||||
.reduce((a, b) => a.concat(b), []);
|
.reduce((a, b) => a.concat(b), []);
|
||||||
|
|
||||||
const hasSamples = operations.some(operation => operation.codeSamples.length > 0);
|
const hasSamples = operations.some((operation) => operation.codeSamples.length > 0);
|
||||||
|
|
||||||
if (!hasSamples) {
|
if (!hasSamples) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -43,8 +50,8 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||||
|
|
||||||
const dropdownOptions = operations.map((callback, idx) => {
|
const dropdownOptions = operations.map((callback, idx) => {
|
||||||
return {
|
return {
|
||||||
value: `${callback.httpVerb.toUpperCase()}: ${callback.name}`,
|
label: `${callback.httpVerb.toUpperCase()}: ${callback.name}`,
|
||||||
idx,
|
value: idx.toString(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,7 +66,7 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||||
label={'Callback'}
|
label={'Callback'}
|
||||||
options={dropdownOptions}
|
options={dropdownOptions}
|
||||||
>
|
>
|
||||||
{callback => (
|
{(callback) => (
|
||||||
<CallbackPayloadSample
|
<CallbackPayloadSample
|
||||||
key="callbackPayloadSample"
|
key="callbackPayloadSample"
|
||||||
callback={callback}
|
callback={callback}
|
||||||
|
@ -75,5 +82,5 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||||
|
|
||||||
export const SamplesWrapper = styled.div`
|
export const SamplesWrapper = styled.div`
|
||||||
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
||||||
padding: ${props => props.theme.spacing.unit * 4}px;
|
padding: ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -19,7 +19,7 @@ export class CallbacksList extends React.PureComponent<CallbacksListProps> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CallbacksHeader> Callbacks </CallbacksHeader>
|
<CallbacksHeader> Callbacks </CallbacksHeader>
|
||||||
{callbacks.map(callback => {
|
{callbacks.map((callback) => {
|
||||||
return callback.operations.map((operation, index) => {
|
return callback.operations.map((operation, index) => {
|
||||||
return (
|
return (
|
||||||
<CallbackOperation key={`${callback.name}_${index}`} callbackOperation={operation} />
|
<CallbackOperation key={`${callback.name}_${index}`} callbackOperation={operation} />
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class ContentItems extends React.Component<{
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return items.map(item => {
|
return items.map((item) => {
|
||||||
return <ContentItem key={item.id} item={item} />;
|
return <ContentItem key={item.id} item={item} />;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ export class ContentItem extends React.Component<ContentItemProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const middlePanelWrap = component => <MiddlePanel compact={true}>{component}</MiddlePanel>;
|
const middlePanelWrap = (component) => <MiddlePanel compact={true}>{component}</MiddlePanel>;
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class SectionItem extends React.Component<ContentItemProps> {
|
export class SectionItem extends React.Component<ContentItemProps> {
|
||||||
|
|
|
@ -10,7 +10,14 @@ export interface DropdownOrLabelProps extends DropdownProps {
|
||||||
export function DropdownOrLabel(props: DropdownOrLabelProps): JSX.Element {
|
export function DropdownOrLabel(props: DropdownOrLabelProps): JSX.Element {
|
||||||
const { Label = MimeLabel, Dropdown = SimpleDropdown } = props;
|
const { Label = MimeLabel, Dropdown = SimpleDropdown } = props;
|
||||||
if (props.options.length === 1) {
|
if (props.options.length === 1) {
|
||||||
return <Label>{props.options[0].value}</Label>;
|
return <Label>{props.options[0].label}</Label>;
|
||||||
}
|
}
|
||||||
return <Dropdown {...props} searchable={false} />;
|
return (
|
||||||
|
<Dropdown
|
||||||
|
className={'react-dropdown-container'}
|
||||||
|
classNamePrefix={'react-dropdown'}
|
||||||
|
isSearchable={false}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
// TODO: highlight server variables, e.g. https://{user}.test.com
|
// TODO: highlight server variables, e.g. https://{user}.test.com
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{(options) => (
|
||||||
<OperationEndpointWrap>
|
<OperationEndpointWrap>
|
||||||
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
||||||
<HttpVerb type={operation.httpVerb} compact={this.props.compact}>
|
<HttpVerb type={operation.httpVerb} compact={this.props.compact}>
|
||||||
|
@ -63,7 +63,7 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
/>
|
/>
|
||||||
</EndpointInfo>
|
</EndpointInfo>
|
||||||
<ServersOverlay expanded={expanded} aria-hidden={!expanded}>
|
<ServersOverlay expanded={expanded} aria-hidden={!expanded}>
|
||||||
{operation.servers.map(server => {
|
{operation.servers.map((server) => {
|
||||||
const normalizedUrl = options.expandDefaultServerVariables
|
const normalizedUrl = options.expandDefaultServerVariables
|
||||||
? expandDefaultServerVariables(server.url, server.variables)
|
? expandDefaultServerVariables(server.url, server.variables)
|
||||||
: server.url;
|
: server.url;
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const OperationEndpointWrap = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ServerRelativeURL = styled.span`
|
export const ServerRelativeURL = styled.span`
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
@ -20,22 +20,22 @@ export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boole
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 10px 30px 10px ${props => (props.inverted ? '10px' : '20px')};
|
padding: 10px 30px 10px ${(props) => (props.inverted ? '10px' : '20px')};
|
||||||
border-radius: ${props => (props.inverted ? '0' : '4px 4px 0 0')};
|
border-radius: ${(props) => (props.inverted ? '0' : '4px 4px 0 0')};
|
||||||
background-color: ${props =>
|
background-color: ${(props) =>
|
||||||
props.inverted ? 'transparent' : props.theme.codeBlock.backgroundColor};
|
props.inverted ? 'transparent' : props.theme.codeBlock.backgroundColor};
|
||||||
display: flex;
|
display: flex;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: ${props => (props.inverted ? '0' : '1px solid transparent')};
|
border: ${(props) => (props.inverted ? '0' : '1px solid transparent')};
|
||||||
border-bottom: ${props => (props.inverted ? '1px solid #ccc' : '0')};
|
border-bottom: ${(props) => (props.inverted ? '1px solid #ccc' : '0')};
|
||||||
transition: border-color 0.25s ease;
|
transition: border-color 0.25s ease;
|
||||||
|
|
||||||
${props =>
|
${(props) =>
|
||||||
(props.expanded && !props.inverted && `border-color: ${props.theme.colors.border.dark};`) || ''}
|
(props.expanded && !props.inverted && `border-color: ${props.theme.colors.border.dark};`) || ''}
|
||||||
|
|
||||||
.${ServerRelativeURL} {
|
.${ServerRelativeURL} {
|
||||||
color: ${props => (props.inverted ? props.theme.colors.text.primary : '#ffffff')}
|
color: ${(props) => (props.inverted ? props.theme.colors.text.primary : '#ffffff')};
|
||||||
}
|
}
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.45), 0 2px 0 rgba(128, 128, 128, 0.25);
|
box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.45), 0 2px 0 rgba(128, 128, 128, 0.25);
|
||||||
|
@ -45,13 +45,13 @@ export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boole
|
||||||
export const HttpVerb = styled.span.attrs((props: { type: string; compact?: boolean }) => ({
|
export const HttpVerb = styled.span.attrs((props: { type: string; compact?: boolean }) => ({
|
||||||
className: `http-verb ${props.type}`,
|
className: `http-verb ${props.type}`,
|
||||||
}))<{ type: string; compact?: boolean }>`
|
}))<{ type: string; compact?: boolean }>`
|
||||||
font-size: ${props => (props.compact ? '0.8em' : '0.929em')};
|
font-size: ${(props) => (props.compact ? '0.8em' : '0.929em')};
|
||||||
line-height: ${props => (props.compact ? '18px' : '20px')};
|
line-height: ${(props) => (props.compact ? '18px' : '20px')};
|
||||||
background-color: ${props => props.theme.colors.http[props.type] || '#999999'};
|
background-color: ${(props) => props.theme.colors.http[props.type] || '#999999'};
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: ${props => (props.compact ? '2px 8px' : '3px 10px')};
|
padding: ${(props) => (props.compact ? '2px 8px' : '3px 10px')};
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ export const ServersOverlay = styled.div<{ expanded: boolean }>`
|
||||||
border-bottom-right-radius: 4px;
|
border-bottom-right-radius: 4px;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.25s ease;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
${props => (props.expanded ? 'visibility: visible;' : 'transform: translateY(-50%) scaleY(0);')}
|
${(props) => (props.expanded ? 'visibility: visible;' : 'transform: translateY(-50%) scaleY(0);')}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ServerItem = styled.div`
|
export const ServerItem = styled.div`
|
||||||
|
@ -80,8 +80,8 @@ export const ServerUrl = styled.div`
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
> span {
|
> span {
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${(props) => props.theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -81,7 +81,7 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToggleButton = styled.span`
|
const ToggleButton = styled.span`
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
|
@ -23,10 +23,10 @@ export class Extensions extends React.PureComponent<ExtensionsProps> {
|
||||||
const exts = this.props.extensions;
|
const exts = this.props.extensions;
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{(options) => (
|
||||||
<>
|
<>
|
||||||
{options.showExtensions &&
|
{options.showExtensions &&
|
||||||
Object.keys(exts).map(key => (
|
Object.keys(exts).map((key) => (
|
||||||
<Extension key={key}>
|
<Extension key={key}>
|
||||||
<FieldLabel> {key.substring(2)}: </FieldLabel>{' '}
|
<FieldLabel> {key.substring(2)}: </FieldLabel>{' '}
|
||||||
<ExtensionValue>
|
<ExtensionValue>
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class Field extends React.Component<FieldProps> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleKeyPress = e => {
|
handleKeyPress = (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.toggle();
|
this.toggle();
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class ConstraintsView extends React.PureComponent<ConstraintsViewProps> {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{' '}
|
{' '}
|
||||||
{this.props.constraints.map(constraint => (
|
{this.props.constraints.map((constraint) => (
|
||||||
<ConstraintItem key={constraint}> {constraint} </ConstraintItem>
|
<ConstraintItem key={constraint}> {constraint} </ConstraintItem>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -59,7 +59,9 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
} else {
|
} else {
|
||||||
const label = l('example') + ':';
|
const label = l('example') + ':';
|
||||||
const raw = !!field.in;
|
const raw = !!field.in;
|
||||||
renderedExamples = <FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />;
|
renderedExamples = (
|
||||||
|
<FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,14 +80,16 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
)}
|
)}
|
||||||
{schema.contentEncoding && (
|
{schema.contentEncoding && (
|
||||||
<TypeFormat>
|
<TypeFormat>
|
||||||
{' '}<
|
{' '}
|
||||||
|
<
|
||||||
{schema.contentEncoding}
|
{schema.contentEncoding}
|
||||||
>{' '}
|
>{' '}
|
||||||
</TypeFormat>
|
</TypeFormat>
|
||||||
)}
|
)}
|
||||||
{schema.contentMediaType && (
|
{schema.contentMediaType && (
|
||||||
<TypeFormat>
|
<TypeFormat>
|
||||||
{' '}<
|
{' '}
|
||||||
|
<
|
||||||
{schema.contentMediaType}
|
{schema.contentMediaType}
|
||||||
>{' '}
|
>{' '}
|
||||||
</TypeFormat>
|
</TypeFormat>
|
||||||
|
@ -124,7 +128,7 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
||||||
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
|
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
|
||||||
)}
|
)}
|
||||||
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
||||||
{field.const && (<FieldDetail label={l('const') + ':'} value={field.const} />) || null}
|
{(field.const && <FieldDetail label={l('const') + ':'} value={field.const} />) || null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -142,7 +146,8 @@ function Examples({ field }: { field: FieldModel }) {
|
||||||
{Object.values(field.examples).map((example, idx) => {
|
{Object.values(field.examples).map((example, idx) => {
|
||||||
return (
|
return (
|
||||||
<li key={idx}>
|
<li key={idx}>
|
||||||
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> - {example.summary || example.description}
|
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> -{' '}
|
||||||
|
{example.summary || example.description}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -160,7 +165,6 @@ function getSerializedValue(field: FieldModel, example: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ExamplesList = styled.ul`
|
const ExamplesList = styled.ul`
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
|
|
@ -32,10 +32,10 @@ export class GenericChildrenSwitcher<T> extends React.Component<
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
switchItem = ({ idx }) => {
|
switchItem = ({ value }) => {
|
||||||
if (this.props.items) {
|
if (this.props.items) {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeItemIdx: idx,
|
activeItemIdx: parseInt(value, 10),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -61,7 +61,7 @@ export class GenericChildrenSwitcher<T> extends React.Component<
|
||||||
<>
|
<>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{this.props.renderDropdown({
|
{this.props.renderDropdown({
|
||||||
value: this.props.options[this.state.activeItemIdx].value,
|
value: this.props.options[this.state.activeItemIdx],
|
||||||
options: this.props.options,
|
options: this.props.options,
|
||||||
onChange: this.switchItem,
|
onChange: this.switchItem,
|
||||||
ariaLabel: this.props.label || 'Callback',
|
ariaLabel: this.props.label || 'Callback',
|
||||||
|
|
|
@ -9,7 +9,7 @@ const LoadingMessage = styled.div<{ color: string }>`
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
margin: 30px 0 20px 0;
|
margin: 30px 0 20px 0;
|
||||||
color: ${props => props.color};
|
color: ${(props) => props.color};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface LoadingProps {
|
export interface LoadingProps {
|
||||||
|
|
|
@ -31,6 +31,6 @@ export const Spinner = styled(_Spinner)`
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
|
|
||||||
path {
|
path {
|
||||||
fill: ${props => props.color};
|
fill: ${(props) => props.color};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -15,15 +15,15 @@ export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<OptionsConsumer>
|
<OptionsConsumer>
|
||||||
{options => (
|
{(options) => (
|
||||||
<StoreConsumer>{store => this.renderWithOptionsAndStore(options, store)}</StoreConsumer>
|
<StoreConsumer>{(store) => this.renderWithOptionsAndStore(options, store)}</StoreConsumer>
|
||||||
)}
|
)}
|
||||||
</OptionsConsumer>
|
</OptionsConsumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWithOptionsAndStore(options: RedocNormalizedOptions, store?: AppStore) {
|
renderWithOptionsAndStore(options: RedocNormalizedOptions, store?: AppStore) {
|
||||||
const { source, htmlWrap = i => i } = this.props;
|
const { source, htmlWrap = (i) => i } = this.props;
|
||||||
if (!store) {
|
if (!store) {
|
||||||
throw new Error('When using components in markdown, store prop must be provided');
|
throw new Error('When using components in markdown, store prop must be provided');
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ export function SanitizedMarkdownHTML(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OptionsConsumer>
|
<OptionsConsumer>
|
||||||
{options => (
|
{(options) => (
|
||||||
<Wrap
|
<Wrap
|
||||||
className={'redoc-markdown ' + (props.className || '')}
|
className={'redoc-markdown ' + (props.className || '')}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
|
|
|
@ -7,14 +7,14 @@ import { StyledComponent } from 'styled-components';
|
||||||
export const linksCss = css`
|
export const linksCss = css`
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: ${props => props.theme.typography.links.color};
|
color: ${(props) => props.theme.typography.links.color};
|
||||||
|
|
||||||
&:visited {
|
&:visited {
|
||||||
color: ${props => props.theme.typography.links.visited};
|
color: ${(props) => props.theme.typography.links.visited};
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: ${props => props.theme.typography.links.hover};
|
color: ${(props) => props.theme.typography.links.hover};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -26,10 +26,9 @@ export const StyledMarkdownBlock = styled(
|
||||||
{ compact?: boolean; inline?: boolean }
|
{ 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};
|
line-height: ${(props) => props.theme.typography.lineHeight};
|
||||||
line-height: ${props => props.theme.typography.lineHeight};
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
&:last-child {
|
&:last-child {
|
||||||
|
@ -56,35 +55,35 @@ export const StyledMarkdownBlock = styled(
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
${headerCommonMixin(1)};
|
${headerCommonMixin(1)};
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
${headerCommonMixin(2)};
|
${headerCommonMixin(2)};
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: ${(props) => props.theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
color: ${({ theme }) => theme.typography.code.color};
|
color: ${({ theme }) => theme.typography.code.color};
|
||||||
background-color: ${({ theme }) => theme.typography.code.backgroundColor};
|
background-color: ${({ theme }) => theme.typography.code.backgroundColor};
|
||||||
|
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid rgba(38, 50, 56, 0.1);
|
border: 1px solid rgba(38, 50, 56, 0.1);
|
||||||
padding: 0 ${({ theme }) => theme.spacing.unit}px;
|
padding: 0 ${({ theme }) => theme.spacing.unit}px;
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
font-size: ${(props) => props.theme.typography.code.fontSize};
|
||||||
font-weight: ${({ theme }) => theme.typography.code.fontWeight};
|
font-weight: ${({ theme }) => theme.typography.code.fontWeight};
|
||||||
|
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')};
|
||||||
background-color: ${({ theme }) => theme.codeBlock.backgroundColor};
|
background-color: ${({ theme }) => theme.codeBlock.backgroundColor};
|
||||||
color: white;
|
color: white;
|
||||||
padding: ${props => props.theme.spacing.unit * 4}px;
|
padding: ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
|
@ -121,7 +120,8 @@ export const StyledMarkdownBlock = styled(
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
ul, ol {
|
ul,
|
||||||
|
ol {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ export interface MediaTypesSwitchProps {
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||||
switchMedia = ({ idx }) => {
|
switchMedia = ({ value }) => {
|
||||||
if (this.props.content) {
|
if (this.props.content) {
|
||||||
this.props.content.activate(idx);
|
this.props.content.activate(parseInt(value, 10));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||||
|
|
||||||
const options = content.mediaTypes.map((mime, idx) => {
|
const options = content.mediaTypes.map((mime, idx) => {
|
||||||
return {
|
return {
|
||||||
value: mime.name,
|
label: mime.name,
|
||||||
idx,
|
value: idx.toString(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||||
<>
|
<>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{this.props.renderDropdown({
|
{this.props.renderDropdown({
|
||||||
value: options[activeMimeIdx].value,
|
value: options[activeMimeIdx],
|
||||||
options,
|
options,
|
||||||
onChange: this.switchMedia,
|
onChange: this.switchMedia,
|
||||||
ariaLabel: 'Content type',
|
ariaLabel: 'Content type',
|
||||||
|
|
|
@ -28,7 +28,7 @@ const PARAM_PLACES = ['path', 'query', 'cookie', 'header'];
|
||||||
export class Parameters extends React.PureComponent<ParametersProps> {
|
export class Parameters extends React.PureComponent<ParametersProps> {
|
||||||
orderParams(params: FieldModel[]): Record<string, FieldModel[]> {
|
orderParams(params: FieldModel[]): Record<string, FieldModel[]> {
|
||||||
const res = {};
|
const res = {};
|
||||||
params.forEach(param => {
|
params.forEach((param) => {
|
||||||
safePush(res, param.in, param);
|
safePush(res, param.in, param);
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
|
@ -50,7 +50,7 @@ export class Parameters extends React.PureComponent<ParametersProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{paramsPlaces.map(place => (
|
{paramsPlaces.map((place) => (
|
||||||
<ParametersGroup key={place} place={place} parameters={paramsMap[place]} />
|
<ParametersGroup key={place} place={place} parameters={paramsMap[place]} />
|
||||||
))}
|
))}
|
||||||
{bodyContent && <BodyContent content={bodyContent} description={bodyDescription} />}
|
{bodyContent && <BodyContent content={bodyContent} description={bodyDescription} />}
|
||||||
|
@ -62,12 +62,15 @@ export class Parameters extends React.PureComponent<ParametersProps> {
|
||||||
function DropdownWithinHeader(props) {
|
function DropdownWithinHeader(props) {
|
||||||
return (
|
return (
|
||||||
<UnderlinedHeader key="header">
|
<UnderlinedHeader key="header">
|
||||||
Request Body schema: <DropdownOrLabel {...props} />
|
Request Body schema: <DropdownOrLabel {...props} aria-label="Request body schema dropdown" />
|
||||||
</UnderlinedHeader>
|
</UnderlinedHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element {
|
export function BodyContent(props: {
|
||||||
|
content: MediaContentModel;
|
||||||
|
description?: string;
|
||||||
|
}): JSX.Element {
|
||||||
const { content, description } = props;
|
const { content, description } = props;
|
||||||
const { isRequestType } = content;
|
const { isRequestType } = content;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -21,9 +21,10 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
|
||||||
state = {
|
state = {
|
||||||
activeIdx: 0,
|
activeIdx: 0,
|
||||||
};
|
};
|
||||||
switchMedia = ({ idx }) => {
|
|
||||||
|
switchMedia = ({ value }) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeIdx: idx,
|
activeIdx: parseInt(value, 10),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
|
@ -41,8 +42,8 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
|
||||||
if (examplesNames.length > 1) {
|
if (examplesNames.length > 1) {
|
||||||
const options = examplesNames.map((name, idx) => {
|
const options = examplesNames.map((name, idx) => {
|
||||||
return {
|
return {
|
||||||
value: examples[name].summary || name,
|
label: examples[name].summary || name,
|
||||||
idx,
|
value: idx.toString(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
|
||||||
<DropdownWrapper>
|
<DropdownWrapper>
|
||||||
<DropdownLabel>Example</DropdownLabel>
|
<DropdownLabel>Example</DropdownLabel>
|
||||||
{this.props.renderDropdown({
|
{this.props.renderDropdown({
|
||||||
value: options[activeIdx].value,
|
value: options[activeIdx],
|
||||||
options,
|
options,
|
||||||
onChange: this.switchMedia,
|
onChange: this.switchMedia,
|
||||||
ariaLabel: 'Example',
|
ariaLabel: 'Example',
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class PayloadSamples extends React.Component<PayloadSamplesProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MediaTypesSwitch content={mimeContent} renderDropdown={this.renderDropdown} withLabel={true}>
|
<MediaTypesSwitch content={mimeContent} renderDropdown={this.renderDropdown} withLabel={true}>
|
||||||
{mediaType => (
|
{(mediaType) => (
|
||||||
<MediaTypeSamples
|
<MediaTypeSamples
|
||||||
key="samples"
|
key="samples"
|
||||||
mediaType={mediaType}
|
mediaType={mediaType}
|
||||||
|
@ -32,7 +32,14 @@ export class PayloadSamples extends React.Component<PayloadSamplesProps> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderDropdown = props => {
|
private renderDropdown = (props) => {
|
||||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
return (
|
||||||
|
<DropdownOrLabel
|
||||||
|
Label={MimeLabel}
|
||||||
|
Dropdown={InvertedSimpleDropdown}
|
||||||
|
aria-label="Payload sample dropdown"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,49 +29,48 @@ export const DropdownWrapper = styled.div`
|
||||||
|
|
||||||
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||||
&& {
|
&& {
|
||||||
margin-left: 10px;
|
|
||||||
text-transform: none;
|
|
||||||
font-size: 0.929em;
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
display: block;
|
display: block;
|
||||||
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
|
||||||
font-size: 1em;
|
|
||||||
border: none;
|
border: none;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
padding: 0.9em 1.6em 0.9em 0.9em;
|
padding: 0.9em 1.6em 0.9em 0.9em;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
|
text-transform: none;
|
||||||
|
|
||||||
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
border: none;
|
border-bottom: 1px solid ${({ theme }) => theme.rightPanel.textColor};
|
||||||
|
border-right: 1px solid ${({ theme }) => theme.rightPanel.textColor};
|
||||||
|
border-left: 1px solid ${({ theme }) => theme.rightPanel.textColor};
|
||||||
|
border-top: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
|
||||||
&:focus-within {
|
|
||||||
background-color: ${({ theme }) => transparentize(0.3, theme.rightPanel.backgroundColor)};
|
background-color: ${({ theme }) => transparentize(0.3, theme.rightPanel.backgroundColor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-arrow {
|
.react-dropdown__control {
|
||||||
border-top-color: ${({ theme }) => theme.rightPanel.textColor};
|
border: transparent;
|
||||||
}
|
background-color: transparent;
|
||||||
.dropdown-selector-value {
|
|
||||||
text-overflow: ellipsis;
|
.react-dropdown__single-value {
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-selector-content {
|
.react-dropdown__indicator {
|
||||||
margin: 0;
|
border-color: ${({ theme }) => theme.rightPanel.textColor} transparent transparent;
|
||||||
margin-top: 2px;
|
|
||||||
.dropdown-option {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.react-dropdown__menu {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const NoSampleLabel = styled.div`
|
export const NoSampleLabel = styled.div`
|
||||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
font-family: ${(props) => props.theme.typography.code.fontFamily};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #ee807f;
|
color: #ee807f;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const ApiContentWrap = styled.div`
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: calc(100% - ${props => props.theme.sidebar.width});
|
width: calc(100% - ${(props) => props.theme.sidebar.width});
|
||||||
${media.lessThan('small', true)`
|
${media.lessThan('small', true)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`};
|
`};
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { argValueToBoolean, RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions';
|
import {
|
||||||
|
argValueToBoolean,
|
||||||
|
RedocNormalizedOptions,
|
||||||
|
RedocRawOptions,
|
||||||
|
} from '../services/RedocNormalizedOptions';
|
||||||
import { ErrorBoundary } from './ErrorBoundary';
|
import { ErrorBoundary } from './ErrorBoundary';
|
||||||
import { Loading } from './Loading/Loading';
|
import { Loading } from './Loading/Loading';
|
||||||
import { Redoc } from './Redoc/Redoc';
|
import { Redoc } from './Redoc/Redoc';
|
||||||
|
@ -32,4 +36,4 @@ export const RedocStandalone = function (props: RedocStandaloneProps) {
|
||||||
</StoreBuilder>
|
</StoreBuilder>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -31,13 +31,13 @@ export class RequestSamples extends React.Component<RequestSamplesProps> {
|
||||||
|
|
||||||
<Tabs defaultIndex={0}>
|
<Tabs defaultIndex={0}>
|
||||||
<TabList hidden={hideTabList}>
|
<TabList hidden={hideTabList}>
|
||||||
{samples.map(sample => (
|
{samples.map((sample) => (
|
||||||
<Tab key={sample.lang + '_' + (sample.label || '')}>
|
<Tab key={sample.lang + '_' + (sample.label || '')}>
|
||||||
{sample.label !== undefined ? sample.label : sample.lang}
|
{sample.label !== undefined ? sample.label : sample.lang}
|
||||||
</Tab>
|
</Tab>
|
||||||
))}
|
))}
|
||||||
</TabList>
|
</TabList>
|
||||||
{samples.map(sample => (
|
{samples.map((sample) => (
|
||||||
<TabPanel key={sample.lang + '_' + (sample.label || '')}>
|
<TabPanel key={sample.lang + '_' + (sample.label || '')}>
|
||||||
{isPayloadSample(sample) ? (
|
{isPayloadSample(sample) ? (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class ResponseSamples extends React.Component<ResponseSamplesProps> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { operation } = this.props;
|
const { operation } = this.props;
|
||||||
const responses = operation.responses.filter(response => {
|
const responses = operation.responses.filter((response) => {
|
||||||
return response.content && response.content.hasSample;
|
return response.content && response.content.hasSample;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28,13 +28,13 @@ export class ResponseSamples extends React.Component<ResponseSamplesProps> {
|
||||||
|
|
||||||
<Tabs defaultIndex={0}>
|
<Tabs defaultIndex={0}>
|
||||||
<TabList>
|
<TabList>
|
||||||
{responses.map(response => (
|
{responses.map((response) => (
|
||||||
<Tab className={'tab-' + response.type} key={response.code}>
|
<Tab className={'tab-' + response.type} key={response.code}>
|
||||||
{response.code}
|
{response.code}
|
||||||
</Tab>
|
</Tab>
|
||||||
))}
|
))}
|
||||||
</TabList>
|
</TabList>
|
||||||
{responses.map(response => (
|
{responses.map((response) => (
|
||||||
<TabPanel key={response.code}>
|
<TabPanel key={response.code}>
|
||||||
<div>
|
<div>
|
||||||
<PayloadSamples content={response.content!} />
|
<PayloadSamples content={response.content!} />
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
|
||||||
render() {
|
render() {
|
||||||
const { headers, type, summary, description, code, expanded, content } = this.props.response;
|
const { headers, type, summary, description, code, expanded, content } = this.props.response;
|
||||||
const mimes =
|
const mimes =
|
||||||
content === undefined ? [] : content.mediaTypes.filter(mime => mime.schema !== undefined);
|
content === undefined ? [] : content.mediaTypes.filter((mime) => mime.schema !== undefined);
|
||||||
|
|
||||||
const empty = headers.length === 0 && mimes.length === 0 && !description;
|
const empty = headers.length === 0 && mimes.length === 0 && !description;
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,10 @@ export class ResponseDetails extends React.PureComponent<{ response: ResponseMod
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderDropdown = props => {
|
private renderDropdown = (props) => {
|
||||||
return (
|
return (
|
||||||
<UnderlinedHeader key="header">
|
<UnderlinedHeader key="header">
|
||||||
Response Schema: <DropdownOrLabel {...props} />
|
Response Schema: <DropdownOrLabel aria-label="Response schema dropdown" {...props} />
|
||||||
</UnderlinedHeader>
|
</UnderlinedHeader>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class ResponsesList extends React.PureComponent<ResponseListProps> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ResponsesHeader>{isCallback ? l('callbackResponses') : l('responses')}</ResponsesHeader>
|
<ResponsesHeader>{isCallback ? l('callbackResponses') : l('responses')}</ResponsesHeader>
|
||||||
{responses.map(response => {
|
{responses.map((response) => {
|
||||||
return <ResponseView key={response.code} response={response} />;
|
return <ResponseView key={response.code} response={response} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,14 +16,17 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
|
||||||
const schema = this.props.schema;
|
const schema = this.props.schema;
|
||||||
const itemsSchema = schema.items;
|
const itemsSchema = schema.items;
|
||||||
|
|
||||||
const minMaxItems = schema.minItems === undefined && schema.maxItems === undefined ?
|
const minMaxItems =
|
||||||
'' :
|
schema.minItems === undefined && schema.maxItems === undefined
|
||||||
`(${humanizeConstraints(schema)})`;
|
? ''
|
||||||
|
: `(${humanizeConstraints(schema)})`;
|
||||||
|
|
||||||
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
||||||
return (<div>
|
return (
|
||||||
|
<div>
|
||||||
<TypeName>{schema.displayType}</TypeName>
|
<TypeName>{schema.displayType}</TypeName>
|
||||||
</div>);
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -33,12 +33,12 @@ export class DiscriminatorDropdown extends React.Component<{
|
||||||
|
|
||||||
const options = parent.oneOf.map((subSchema, idx) => {
|
const options = parent.oneOf.map((subSchema, idx) => {
|
||||||
return {
|
return {
|
||||||
value: subSchema.title,
|
value: idx.toString(),
|
||||||
idx,
|
label: subSchema.title,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeValue = options[parent.activeOneOf].value;
|
const activeValue = options[parent.activeOneOf];
|
||||||
|
|
||||||
this.sortOptions(options, enumValues);
|
this.sortOptions(options, enumValues);
|
||||||
|
|
||||||
|
@ -47,12 +47,16 @@ export class DiscriminatorDropdown extends React.Component<{
|
||||||
value={activeValue}
|
value={activeValue}
|
||||||
options={options}
|
options={options}
|
||||||
onChange={this.changeActiveChild}
|
onChange={this.changeActiveChild}
|
||||||
ariaLabel="Example"
|
className={'react-dropdown-container'}
|
||||||
|
classNamePrefix={'react-dropdown'}
|
||||||
|
isSearchable={false}
|
||||||
|
aria-label="Example"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
changeActiveChild = (option: DropdownOption) => {
|
changeActiveChild = ({ value }) => {
|
||||||
this.props.parent.activateOneOf(option.idx);
|
const idx = parseInt(value, 10);
|
||||||
|
this.props.parent.activateOneOf(idx);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
|
||||||
const needFilter = this.props.skipReadOnly || this.props.skipWriteOnly;
|
const needFilter = this.props.skipReadOnly || this.props.skipWriteOnly;
|
||||||
|
|
||||||
const filteredFields = needFilter
|
const filteredFields = needFilter
|
||||||
? fields.filter(item => {
|
? fields.filter((item) => {
|
||||||
return !(
|
return !(
|
||||||
(this.props.skipReadOnly && item.schema.readOnly) ||
|
(this.props.skipReadOnly && item.schema.readOnly) ||
|
||||||
(this.props.skipWriteOnly && item.schema.writeOnly)
|
(this.props.skipWriteOnly && item.schema.writeOnly)
|
||||||
|
|
|
@ -73,7 +73,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maybe adjust FieldDetails to accept schema
|
// TODO: maybe adjust FieldDetails to accept schema
|
||||||
const field = ({
|
const field = {
|
||||||
schema,
|
schema,
|
||||||
name: '',
|
name: '',
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -82,7 +82,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
deprecated: false,
|
deprecated: false,
|
||||||
toggle: () => null,
|
toggle: () => null,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
} as any) as FieldModel; // cast needed for hot-loader to not fail
|
} as any as FieldModel; // cast needed for hot-loader to not fail
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -74,8 +74,15 @@ export class SchemaDefinition extends React.PureComponent<ObjectDescriptionProps
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderDropdown = props => {
|
private renderDropdown = (props) => {
|
||||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
return (
|
||||||
|
<DropdownOrLabel
|
||||||
|
Label={MimeLabel}
|
||||||
|
Dropdown={InvertedSimpleDropdown}
|
||||||
|
aria-label="Schema definition dropdown"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +90,7 @@ const MediaSamplesWrap = styled.div`
|
||||||
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
background: ${({ theme }) => theme.codeBlock.backgroundColor};
|
||||||
& > div,
|
& > div,
|
||||||
& > pre {
|
& > pre {
|
||||||
padding: ${props => props.theme.spacing.unit * 4}px;
|
padding: ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
setResults(results: SearchResult[], term: string) {
|
setResults(results: SearchResult[], term: string) {
|
||||||
this.setState({
|
this.setState({
|
||||||
results,
|
results,
|
||||||
noResults: results.length === 0
|
noResults: results.length === 0,
|
||||||
});
|
});
|
||||||
this.props.marker.mark(term);
|
this.props.marker.mark(term);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
@bind
|
@bind
|
||||||
@debounce(400)
|
@debounce(400)
|
||||||
searchCallback(searchTerm: string) {
|
searchCallback(searchTerm: string) {
|
||||||
this.props.search.search(searchTerm).then(res => {
|
this.props.search.search(searchTerm).then((res) => {
|
||||||
this.setResults(res, searchTerm);
|
this.setResults(res, searchTerm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { activeItemIdx } = this.state;
|
const { activeItemIdx } = this.state;
|
||||||
const results = this.state.results.map(res => ({
|
const results = this.state.results.map((res) => ({
|
||||||
item: this.props.getItemById(res.meta)!,
|
item: this.props.getItemById(res.meta)!,
|
||||||
score: res.score,
|
score: res.score,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -11,11 +11,11 @@ export const SearchWrap = styled.div`
|
||||||
export const SearchInput = styled.input.attrs(() => ({
|
export const SearchInput = styled.input.attrs(() => ({
|
||||||
className: 'search-input',
|
className: 'search-input',
|
||||||
}))`
|
}))`
|
||||||
width: calc(100% - ${props => props.theme.spacing.unit * 8}px);
|
width: calc(100% - ${(props) => props.theme.spacing.unit * 8}px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0 ${props => props.theme.spacing.unit * 4}px;
|
margin: 0 ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
padding: 5px ${props => props.theme.spacing.unit * 2}px 5px
|
padding: 5px ${(props) => props.theme.spacing.unit * 2}px 5px
|
||||||
${props => props.theme.spacing.unit * 4}px;
|
${(props) => props.theme.spacing.unit * 4}px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-bottom: 1px solid
|
border-bottom: 1px solid
|
||||||
${({ theme }) =>
|
${({ theme }) =>
|
||||||
|
@ -26,7 +26,7 @@ export const SearchInput = styled.input.attrs(() => ({
|
||||||
font-family: ${({ theme }) => theme.typography.fontFamily};
|
font-family: ${({ theme }) => theme.typography.fontFamily};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: ${props => props.theme.sidebar.textColor};
|
color: ${(props) => props.theme.sidebar.textColor};
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
outline: none;
|
outline: none;
|
||||||
`;
|
`;
|
||||||
|
@ -46,19 +46,19 @@ export const SearchIcon = styled((props: { className?: string }) => (
|
||||||
className: 'search-icon',
|
className: 'search-icon',
|
||||||
})`
|
})`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: ${props => props.theme.spacing.unit * 4}px;
|
left: ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
height: 1.8em;
|
height: 1.8em;
|
||||||
width: 0.9em;
|
width: 0.9em;
|
||||||
|
|
||||||
path {
|
path {
|
||||||
fill: ${props => props.theme.sidebar.textColor};
|
fill: ${(props) => props.theme.sidebar.textColor};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
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: ${({ theme }) => darken(0.05, theme.sidebar.backgroundColor)}};
|
background-color: ${({ theme }) => darken(0.05, theme.sidebar.backgroundColor)}};
|
||||||
color: ${props => props.theme.sidebar.textColor};
|
color: ${(props) => props.theme.sidebar.textColor};
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
max-height: 250px;
|
max-height: 250px;
|
||||||
border-top: ${({ theme }) => darken(0.1, theme.sidebar.backgroundColor)}};
|
border-top: ${({ theme }) => darken(0.1, theme.sidebar.backgroundColor)}};
|
||||||
|
@ -89,9 +89,9 @@ export const SearchResultsBox = styled.div`
|
||||||
export const ClearIcon = styled.i`
|
export const ClearIcon = styled.i`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: ${props => props.theme.spacing.unit * 2}px;
|
width: ${(props) => props.theme.spacing.unit * 2}px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
right: ${props => props.theme.spacing.unit * 4}px;
|
right: ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
|
|
|
@ -49,7 +49,7 @@ export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
||||||
<strong> Scopes: </strong>
|
<strong> Scopes: </strong>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
{Object.keys(flow!.scopes || {}).map(scope => (
|
{Object.keys(flow!.scopes || {}).map((scope) => (
|
||||||
<li key={scope}>
|
<li key={scope}>
|
||||||
<code>{scope}</code> - <Markdown inline={true} source={flow!.scopes[scope] || ''} />
|
<code>{scope}</code> - <Markdown inline={true} source={flow!.scopes[scope] || ''} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -67,7 +67,7 @@ export interface SecurityDefsProps {
|
||||||
|
|
||||||
export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
render() {
|
render() {
|
||||||
return this.props.securitySchemes.schemes.map(scheme => (
|
return this.props.securitySchemes.schemes.map((scheme) => (
|
||||||
<Section id={scheme.sectionId} key={scheme.id}>
|
<Section id={scheme.sectionId} key={scheme.id}>
|
||||||
<Row>
|
<Row>
|
||||||
<MiddlePanel>
|
<MiddlePanel>
|
||||||
|
@ -115,7 +115,7 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
) : scheme.flows ? (
|
) : scheme.flows ? (
|
||||||
Object.keys(scheme.flows).map(type => (
|
Object.keys(scheme.flows).map((type) => (
|
||||||
<OAuthFlow key={type} type={type} flow={scheme.flows[type]} />
|
<OAuthFlow key={type} type={type} flow={scheme.flows[type]} />
|
||||||
))
|
))
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class SelectOnClick extends React.PureComponent {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={el => (this.child = el)}
|
ref={(el) => (this.child = el)}
|
||||||
onClick={this.selectElement}
|
onClick={this.selectElement}
|
||||||
onFocus={this.selectElement}
|
onFocus={this.selectElement}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|
|
@ -46,7 +46,7 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private saveScrollUpdate = upd => {
|
private saveScrollUpdate = (upd) => {
|
||||||
this._updateScroll = upd;
|
this._updateScroll = upd;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
|
||||||
}))<{ type: string }>`
|
}))<{ type: string }>`
|
||||||
width: 9ex;
|
width: 9ex;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: ${props => props.theme.typography.code.fontSize};
|
height: ${(props) => props.theme.typography.code.fontSize};
|
||||||
line-height: ${props => props.theme.typography.code.fontSize};
|
line-height: ${(props) => props.theme.typography.code.fontSize};
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
@ -26,43 +26,43 @@ export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|
||||||
&.get {
|
&.get {
|
||||||
background-color: ${props => props.theme.colors.http.get};
|
background-color: ${(props) => props.theme.colors.http.get};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.post {
|
&.post {
|
||||||
background-color: ${props => props.theme.colors.http.post};
|
background-color: ${(props) => props.theme.colors.http.post};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.put {
|
&.put {
|
||||||
background-color: ${props => props.theme.colors.http.put};
|
background-color: ${(props) => props.theme.colors.http.put};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.options {
|
&.options {
|
||||||
background-color: ${props => props.theme.colors.http.options};
|
background-color: ${(props) => props.theme.colors.http.options};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.patch {
|
&.patch {
|
||||||
background-color: ${props => props.theme.colors.http.patch};
|
background-color: ${(props) => props.theme.colors.http.patch};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.delete {
|
&.delete {
|
||||||
background-color: ${props => props.theme.colors.http.delete};
|
background-color: ${(props) => props.theme.colors.http.delete};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.basic {
|
&.basic {
|
||||||
background-color: ${props => props.theme.colors.http.basic};
|
background-color: ${(props) => props.theme.colors.http.basic};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.link {
|
&.link {
|
||||||
background-color: ${props => props.theme.colors.http.link};
|
background-color: ${(props) => props.theme.colors.http.link};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.head {
|
&.head {
|
||||||
background-color: ${props => props.theme.colors.http.head};
|
background-color: ${(props) => props.theme.colors.http.head};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hook {
|
&.hook {
|
||||||
background-color: ${props => props.theme.colors.primary.main};
|
background-color: ${(props) => props.theme.colors.primary.main};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ export const MenuItemUl = styled.ul<{ expanded: boolean }>`
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
}
|
}
|
||||||
|
|
||||||
${props => (props.expanded ? '' : 'display: none;')};
|
${(props) => (props.expanded ? '' : 'display: none;')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const MenuItemLi = styled.li<{ depth: number }>`
|
export const MenuItemLi = styled.li<{ depth: number }>`
|
||||||
|
@ -92,7 +92,7 @@ export const MenuItemLi = styled.li<{ depth: number }>`
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
${props => (props.depth === 0 ? 'margin-top: 15px' : '')};
|
${(props) => (props.depth === 0 ? 'margin-top: 15px' : '')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const menuItemDepth = {
|
export const menuItemDepth = {
|
||||||
|
@ -102,17 +102,17 @@ export const menuItemDepth = {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: ${props => props.theme.sidebar.textColor};
|
color: ${(props) => props.theme.sidebar.textColor};
|
||||||
`,
|
`,
|
||||||
1: css`
|
1: css`
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
text-transform: ${({ theme }) => theme.sidebar.level1Items.textTransform};
|
text-transform: ${({ theme }) => theme.sidebar.level1Items.textTransform};
|
||||||
&:hover {
|
&:hover {
|
||||||
color: ${props => props.theme.sidebar.activeTextColor};
|
color: ${(props) => props.theme.sidebar.activeTextColor};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
2: css`
|
2: css`
|
||||||
color: ${props => props.theme.sidebar.textColor};
|
color: ${(props) => props.theme.sidebar.textColor};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,22 +130,22 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
|
||||||
}),
|
}),
|
||||||
}))<MenuItemLabelType>`
|
}))<MenuItemLabelType>`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: ${props =>
|
color: ${(props) =>
|
||||||
props.active ? props.theme.sidebar.activeTextColor : props.theme.sidebar.textColor};
|
props.active ? props.theme.sidebar.activeTextColor : props.theme.sidebar.textColor};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 12.5px ${props => props.theme.spacing.unit * 4}px;
|
padding: 12.5px ${(props) => props.theme.spacing.unit * 4}px;
|
||||||
${({ depth, type, theme }) =>
|
${({ depth, type, theme }) =>
|
||||||
(type === 'section' && depth > 1 && 'padding-left: ' + theme.spacing.unit * 8 + 'px;') || ''}
|
(type === 'section' && depth > 1 && 'padding-left: ' + theme.spacing.unit * 8 + 'px;') || ''}
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
font-family: ${(props) => props.theme.typography.headings.fontFamily};
|
||||||
${props => menuItemDepth[props.depth]};
|
${(props) => menuItemDepth[props.depth]};
|
||||||
background-color: ${props => (props.active ? menuItemActiveBg(props.depth, props) : '')};
|
background-color: ${(props) => (props.active ? menuItemActiveBg(props.depth, props) : '')};
|
||||||
|
|
||||||
${props => (props.deprecated && deprecatedCss) || ''};
|
${(props) => (props.deprecated && deprecatedCss) || ''};
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${props => menuItemActiveBg(props.depth, props)};
|
background-color: ${(props) => menuItemActiveBg(props.depth, props)};
|
||||||
}
|
}
|
||||||
|
|
||||||
${ShelfIcon} {
|
${ShelfIcon} {
|
||||||
|
@ -160,7 +160,7 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
|
||||||
export const MenuItemTitle = styled.span<{ width?: string }>`
|
export const MenuItemTitle = styled.span<{ width?: string }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: ${props => (props.width ? props.width : 'auto')};
|
width: ${(props) => (props.width ? props.width : 'auto')};
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -62,5 +62,5 @@ const ChevronContainer = styled.div`
|
||||||
align-self: center;
|
align-self: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -26,8 +26,8 @@ export interface StickySidebarState {
|
||||||
const stickyfill = Stickyfill && Stickyfill();
|
const stickyfill = Stickyfill && Stickyfill();
|
||||||
|
|
||||||
const StyledStickySidebar = styled.div<{ open?: boolean }>`
|
const StyledStickySidebar = styled.div<{ open?: boolean }>`
|
||||||
width: ${props => props.theme.sidebar.width};
|
width: ${(props) => props.theme.sidebar.width};
|
||||||
background-color: ${props => props.theme.sidebar.backgroundColor};
|
background-color: ${(props) => props.theme.sidebar.backgroundColor};
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -45,7 +45,7 @@ const StyledStickySidebar = styled.div<{ open?: boolean }>`
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: ${({ theme }) => theme.sidebar.backgroundColor};
|
background: ${({ theme }) => theme.sidebar.backgroundColor};
|
||||||
display: ${props => (props.open ? 'flex' : 'none')};
|
display: ${(props) => (props.open ? 'flex' : 'none')};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
|
@ -57,7 +57,7 @@ const FloatingButton = styled.div`
|
||||||
outline: none;
|
outline: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
color: ${props => props.theme.colors.primary.main};
|
color: ${(props) => props.theme.colors.primary.main};
|
||||||
display: none;
|
display: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -134,7 +134,7 @@ export class StickyResponsiveSidebar extends React.Component<
|
||||||
height: `calc(100vh - ${top})`,
|
height: `calc(100vh - ${top})`,
|
||||||
}}
|
}}
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
ref={el => {
|
ref={(el) => {
|
||||||
this.stickyElement = el as any;
|
this.stickyElement = el as any;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
||||||
setResolvedSpec(resolved);
|
setResolvedSpec(resolved);
|
||||||
}
|
}
|
||||||
load();
|
load();
|
||||||
}, [spec, specUrl])
|
}, [spec, specUrl]);
|
||||||
|
|
||||||
const store = React.useMemo(() => {
|
const store = React.useMemo(() => {
|
||||||
if (!resolvedSpec) return null;
|
if (!resolvedSpec) return null;
|
||||||
|
@ -62,7 +62,7 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
||||||
if (store && onLoaded) {
|
if (store && onLoaded) {
|
||||||
onLoaded();
|
onLoaded();
|
||||||
}
|
}
|
||||||
}, [store, onLoaded])
|
}, [store, onLoaded]);
|
||||||
|
|
||||||
return children({
|
return children({
|
||||||
loading: !store,
|
loading: !store,
|
||||||
|
|
|
@ -10,14 +10,15 @@ const options = new RedocNormalizedOptions({});
|
||||||
describe('Components', () => {
|
describe('Components', () => {
|
||||||
describe('SecurityRequirement', () => {
|
describe('SecurityRequirement', () => {
|
||||||
describe('SecurityRequirement', () => {
|
describe('SecurityRequirement', () => {
|
||||||
it('should render \'None\' when empty object in security open api', () => {
|
it("should render 'None' when empty object in security open api", () => {
|
||||||
const parser = new OpenAPIParser({ openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} },
|
const parser = new OpenAPIParser(
|
||||||
|
{ openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} },
|
||||||
undefined,
|
undefined,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
const securityRequirement = new SecurityRequirementModel({}, parser);
|
const securityRequirement = new SecurityRequirementModel({}, parser);
|
||||||
const securityElement = shallow(
|
const securityElement = shallow(
|
||||||
<SecurityRequirement key={1} security={securityRequirement} />
|
<SecurityRequirement key={1} security={securityRequirement} />,
|
||||||
).getElement();
|
).getElement();
|
||||||
expect(securityElement.props.children.type.target).toEqual('span');
|
expect(securityElement.props.children.type.target).toEqual('span');
|
||||||
expect(securityElement.props.children.props.children).toEqual('None');
|
expect(securityElement.props.children.props.children).toEqual('None');
|
||||||
|
|
|
@ -89,7 +89,7 @@ export class AppStore {
|
||||||
this.search.indexItems(this.menu.items);
|
this.search.indexItems(this.menu.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.disposer = observe(this.menu, 'activeItemIdx', change => {
|
this.disposer = observe(this.menu, 'activeItemIdx', (change) => {
|
||||||
this.updateMarkOnMenu(change.newValue as number);
|
this.updateMarkOnMenu(change.newValue as number);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,10 +121,7 @@ export class MarkdownRenderer {
|
||||||
prevRegexp = regexp;
|
prevRegexp = regexp;
|
||||||
prevPos = currentPos;
|
prevPos = currentPos;
|
||||||
}
|
}
|
||||||
prevHeading.description = rawText
|
prevHeading.description = rawText.substring(prevPos).replace(prevRegexp, '').trim();
|
||||||
.substring(prevPos)
|
|
||||||
.replace(prevRegexp, '')
|
|
||||||
.trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headingRule = (
|
headingRule = (
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class MarkerService {
|
||||||
if (!term && !this.prevTerm) {
|
if (!term && !this.prevTerm) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.map.forEach(val => {
|
this.map.forEach((val) => {
|
||||||
val.unmark();
|
val.unmark();
|
||||||
val.mark(term || this.prevTerm);
|
val.mark(term || this.prevTerm);
|
||||||
});
|
});
|
||||||
|
@ -45,7 +45,7 @@ export class MarkerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
unmark() {
|
unmark() {
|
||||||
this.map.forEach(val => val.unmark());
|
this.map.forEach((val) => val.unmark());
|
||||||
this.prevTerm = '';
|
this.prevTerm = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ export class MenuBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapHeadingsDeep = (_parent, items, depth = 1) =>
|
const mapHeadingsDeep = (_parent, items, depth = 1) =>
|
||||||
items.map(heading => {
|
items.map((heading) => {
|
||||||
const group = new GroupModel('section', heading, _parent);
|
const group = new GroupModel('section', heading, _parent);
|
||||||
group.depth = depth;
|
group.depth = depth;
|
||||||
if (heading.items) {
|
if (heading.items) {
|
||||||
|
@ -149,7 +149,7 @@ export class MenuBuilder {
|
||||||
tagNames = group.tags;
|
tagNames = group.tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = tagNames.map(tagName => {
|
const tags = tagNames.map((tagName) => {
|
||||||
if (!tagsMap[tagName]) {
|
if (!tagsMap[tagName]) {
|
||||||
console.warn(`Non-existing tag "${tagName}" is added to the group "${group!.name}"`);
|
console.warn(`Non-existing tag "${tagName}" is added to the group "${group!.name}"`);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -144,12 +144,12 @@ export class MenuStore {
|
||||||
}
|
}
|
||||||
let item: IMenuItem | undefined;
|
let item: IMenuItem | undefined;
|
||||||
|
|
||||||
item = this.flatItems.find(i => i.id === id);
|
item = this.flatItems.find((i) => i.id === id);
|
||||||
if (item) {
|
if (item) {
|
||||||
this.activateAndScroll(item, false);
|
this.activateAndScroll(item, false);
|
||||||
} else {
|
} else {
|
||||||
if (id.startsWith(SECURITY_SCHEMES_SECTION_PREFIX)) {
|
if (id.startsWith(SECURITY_SCHEMES_SECTION_PREFIX)) {
|
||||||
item = this.flatItems.find(i => SECURITY_SCHEMES_SECTION_PREFIX.startsWith(i.id));
|
item = this.flatItems.find((i) => SECURITY_SCHEMES_SECTION_PREFIX.startsWith(i.id));
|
||||||
this.activate(item);
|
this.activate(item);
|
||||||
}
|
}
|
||||||
this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${id}"]`);
|
this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${id}"]`);
|
||||||
|
@ -185,7 +185,7 @@ export class MenuStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemById = (id: string) => {
|
getItemById = (id: string) => {
|
||||||
return this.flatItems.find(item => item.id === id);
|
return this.flatItems.find((item) => item.id === id);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -193,7 +193,10 @@ export class OpenAPIParser {
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
if (mergeAsAllOf && keys.some((k) => k !== 'description' && k !== 'title' && k !== 'externalDocs')) {
|
if (
|
||||||
|
mergeAsAllOf &&
|
||||||
|
keys.some((k) => k !== 'description' && k !== 'title' && k !== 'externalDocs')
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
allOf: [rest, resolved],
|
allOf: [rest, resolved],
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,8 +23,8 @@ export class SearchStore<T> {
|
||||||
searchWorker = getWorker();
|
searchWorker = getWorker();
|
||||||
|
|
||||||
indexItems(groups: Array<IMenuItem | OperationModel>) {
|
indexItems(groups: Array<IMenuItem | OperationModel>) {
|
||||||
const recurse = items => {
|
const recurse = (items) => {
|
||||||
items.forEach(group => {
|
items.forEach((group) => {
|
||||||
if (group.type !== 'group') {
|
if (group.type !== 'group') {
|
||||||
this.add(group.name, group.description || '', group.id);
|
this.add(group.name, group.description || '', group.id);
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export class SearchStore<T> {
|
||||||
|
|
||||||
fromExternalJS(path?: string, exportName?: string) {
|
fromExternalJS(path?: string, exportName?: string) {
|
||||||
if (path && exportName) {
|
if (path && exportName) {
|
||||||
this.searchWorker.fromExternalJS(path, exportName)
|
this.searchWorker.fromExternalJS(path, exportName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,14 +47,14 @@ function initEmpty() {
|
||||||
|
|
||||||
builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer);
|
builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer);
|
||||||
|
|
||||||
index = new Promise(resolve => {
|
index = new Promise((resolve) => {
|
||||||
resolveIndex = resolve;
|
resolveIndex = resolve;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initEmpty();
|
initEmpty();
|
||||||
|
|
||||||
const expandTerm = term => '*' + lunr.stemmer(new lunr.Token(term, {})) + '*';
|
const expandTerm = (term) => '*' + lunr.stemmer(new lunr.Token(term, {})) + '*';
|
||||||
|
|
||||||
export function add<T>(title: string, description: string, meta?: T) {
|
export function add<T>(title: string, description: string, meta?: T) {
|
||||||
const ref = store.push(meta) - 1;
|
const ref = store.push(meta) - 1;
|
||||||
|
@ -104,11 +104,11 @@ export async function search<Meta = string>(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchResults = (await index).query(t => {
|
let searchResults = (await index).query((t) => {
|
||||||
q.trim()
|
q.trim()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
.forEach(term => {
|
.forEach((term) => {
|
||||||
if (term.length === 1) return;
|
if (term.length === 1) return;
|
||||||
const exp = expandTerm(term);
|
const exp = expandTerm(term);
|
||||||
t.term(exp, {});
|
t.term(exp, {});
|
||||||
|
@ -119,5 +119,5 @@ export async function search<Meta = string>(
|
||||||
searchResults = searchResults.slice(0, limit);
|
searchResults = searchResults.slice(0, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchResults.map(res => ({ meta: store[res.ref], score: res.score }));
|
return searchResults.map((res) => ({ meta: store[res.ref], score: res.score }));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,10 @@ export class SpecStore {
|
||||||
this.externalDocs = this.parser.spec.externalDocs;
|
this.externalDocs = this.parser.spec.externalDocs;
|
||||||
this.contentItems = MenuBuilder.buildStructure(this.parser, this.options);
|
this.contentItems = MenuBuilder.buildStructure(this.parser, this.options);
|
||||||
this.securitySchemes = new SecuritySchemesModel(this.parser);
|
this.securitySchemes = new SecuritySchemesModel(this.parser);
|
||||||
const webhookPath: Referenced<OpenAPIPath> = {...this.parser?.spec?.['x-webhooks'], ...this.parser?.spec.webhooks};
|
const webhookPath: Referenced<OpenAPIPath> = {
|
||||||
|
...this.parser?.spec?.['x-webhooks'],
|
||||||
|
...this.parser?.spec.webhooks,
|
||||||
|
};
|
||||||
this.webhooks = new WebhookModel(this.parser, options, webhookPath);
|
this.webhooks = new WebhookModel(this.parser, options, webhookPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,5 @@ describe('Models', () => {
|
||||||
|
|
||||||
expect(parser.shallowDeref(schemaOrRef)).toMatchSnapshot();
|
expect(parser.shallowDeref(schemaOrRef)).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,8 +54,8 @@ describe('Models', () => {
|
||||||
license: {
|
license: {
|
||||||
name: 'MIT',
|
name: 'MIT',
|
||||||
identifier: 'MIT',
|
identifier: 'MIT',
|
||||||
url: 'https://opensource.org/licenses/MIT'
|
url: 'https://opensource.org/licenses/MIT',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ describe('Models', () => {
|
||||||
expect(contentItems[0].id).toEqual('tag/pet');
|
expect(contentItems[0].id).toEqual('tag/pet');
|
||||||
expect(contentItems[0].name).toEqual('pet');
|
expect(contentItems[0].name).toEqual('pet');
|
||||||
expect(contentItems[0].type).toEqual('tag');
|
expect(contentItems[0].type).toEqual('tag');
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,8 +41,8 @@ export class ExampleModel {
|
||||||
return externalExamplesCache[this.externalValueUrl];
|
return externalExamplesCache[this.externalValueUrl];
|
||||||
}
|
}
|
||||||
|
|
||||||
externalExamplesCache[this.externalValueUrl] = fetch(this.externalValueUrl).then(res => {
|
externalExamplesCache[this.externalValueUrl] = fetch(this.externalValueUrl).then((res) => {
|
||||||
return res.text().then(txt => {
|
return res.text().then((txt) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return Promise.reject(new Error(txt));
|
return Promise.reject(new Error(txt));
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class MediaContentModel {
|
||||||
if (options.unstable_ignoreMimeParameters) {
|
if (options.unstable_ignoreMimeParameters) {
|
||||||
info = mergeSimilarMediaTypes(info);
|
info = mergeSimilarMediaTypes(info);
|
||||||
}
|
}
|
||||||
this.mediaTypes = Object.keys(info).map(name => {
|
this.mediaTypes = Object.keys(info).map((name) => {
|
||||||
const mime = info[name];
|
const mime = info[name];
|
||||||
// reset deref cache just in case something is left there
|
// reset deref cache just in case something is left there
|
||||||
parser.resetVisited();
|
parser.resetVisited();
|
||||||
|
@ -54,6 +54,6 @@ export class MediaContentModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasSample(): boolean {
|
get hasSample(): boolean {
|
||||||
return this.mediaTypes.filter(mime => !!mime.examples).length > 0;
|
return this.mediaTypes.filter((mime) => !!mime.examples).length > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class MediaTypeModel {
|
||||||
if (info.examples !== undefined) {
|
if (info.examples !== undefined) {
|
||||||
this.examples = mapValues(
|
this.examples = mapValues(
|
||||||
info.examples,
|
info.examples,
|
||||||
example => new ExampleModel(parser, example, name, info.encoding),
|
(example) => new ExampleModel(parser, example, name, info.encoding),
|
||||||
);
|
);
|
||||||
} else if (info.example !== undefined) {
|
} else if (info.example !== undefined) {
|
||||||
this.examples = {
|
this.examples = {
|
||||||
|
|
|
@ -173,7 +173,8 @@ export class OperationModel implements IMenuItem {
|
||||||
@memoize
|
@memoize
|
||||||
get requestBody() {
|
get requestBody() {
|
||||||
return (
|
return (
|
||||||
this.operationSpec.requestBody && new RequestBodyModel({
|
this.operationSpec.requestBody &&
|
||||||
|
new RequestBodyModel({
|
||||||
parser: this.parser,
|
parser: this.parser,
|
||||||
infoOrRef: this.operationSpec.requestBody,
|
infoOrRef: this.operationSpec.requestBody,
|
||||||
options: this.options,
|
options: this.options,
|
||||||
|
|
|
@ -9,7 +9,7 @@ type RequestBodyProps = {
|
||||||
infoOrRef: Referenced<OpenAPIRequestBody>;
|
infoOrRef: Referenced<OpenAPIRequestBody>;
|
||||||
options: RedocNormalizedOptions;
|
options: RedocNormalizedOptions;
|
||||||
isEvent: boolean;
|
isEvent: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class RequestBodyModel {
|
export class RequestBodyModel {
|
||||||
description: string;
|
description: string;
|
||||||
|
|
|
@ -9,13 +9,13 @@ import { FieldModel } from './Field';
|
||||||
import { MediaContentModel } from './MediaContent';
|
import { MediaContentModel } from './MediaContent';
|
||||||
|
|
||||||
type ResponseProps = {
|
type ResponseProps = {
|
||||||
parser: OpenAPIParser,
|
parser: OpenAPIParser;
|
||||||
code: string,
|
code: string;
|
||||||
defaultAsError: boolean,
|
defaultAsError: boolean;
|
||||||
infoOrRef: Referenced<OpenAPIResponse>,
|
infoOrRef: Referenced<OpenAPIResponse>;
|
||||||
options: RedocNormalizedOptions,
|
options: RedocNormalizedOptions;
|
||||||
isEvent: boolean,
|
isEvent: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class ResponseModel {
|
export class ResponseModel {
|
||||||
@observable
|
@observable
|
||||||
|
@ -54,7 +54,7 @@ export class ResponseModel {
|
||||||
|
|
||||||
const headers = info.headers;
|
const headers = info.headers;
|
||||||
if (headers !== undefined) {
|
if (headers !== undefined) {
|
||||||
this.headers = Object.keys(headers).map(name => {
|
this.headers = Object.keys(headers).map((name) => {
|
||||||
const header = headers[name];
|
const header = headers[name];
|
||||||
return new FieldModel(parser, { ...header, name }, '', options);
|
return new FieldModel(parser, { ...header, name }, '', options);
|
||||||
});
|
});
|
||||||
|
|
|
@ -134,7 +134,10 @@ export class SchemaModel {
|
||||||
this.maxItems = schema.maxItems;
|
this.maxItems = schema.maxItems;
|
||||||
|
|
||||||
if (!!schema.nullable || schema['x-nullable']) {
|
if (!!schema.nullable || schema['x-nullable']) {
|
||||||
if (Array.isArray(this.type) && !this.type.some((value) => value === null || value === 'null')) {
|
if (
|
||||||
|
Array.isArray(this.type) &&
|
||||||
|
!this.type.some((value) => value === null || value === 'null')
|
||||||
|
) {
|
||||||
this.type = [...this.type, 'null'];
|
this.type = [...this.type, 'null'];
|
||||||
} else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) {
|
} else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) {
|
||||||
this.type = [this.type, 'null'];
|
this.type = [this.type, 'null'];
|
||||||
|
@ -142,7 +145,7 @@ export class SchemaModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.displayType = Array.isArray(this.type)
|
this.displayType = Array.isArray(this.type)
|
||||||
? this.type.map(item => item === null ? 'null' : item).join(' or ')
|
? this.type.map((item) => (item === null ? 'null' : item)).join(' or ')
|
||||||
: this.type;
|
: this.type;
|
||||||
|
|
||||||
if (this.isCircular) {
|
if (this.isCircular) {
|
||||||
|
@ -194,9 +197,8 @@ export class SchemaModel {
|
||||||
this.enum = this.items.enum;
|
this.enum = this.items.enum;
|
||||||
}
|
}
|
||||||
if (Array.isArray(this.type)) {
|
if (Array.isArray(this.type)) {
|
||||||
const filteredType = this.type.filter(item => item !== 'array');
|
const filteredType = this.type.filter((item) => item !== 'array');
|
||||||
if (filteredType.length)
|
if (filteredType.length) this.displayType += ` or ${filteredType.join(' or ')}`;
|
||||||
this.displayType += ` or ${filteredType.join(' or ')}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +217,7 @@ export class SchemaModel {
|
||||||
const title =
|
const title =
|
||||||
isNamedDefinition(variant.$ref) && !merged.title
|
isNamedDefinition(variant.$ref) && !merged.title
|
||||||
? JsonPointer.baseName(variant.$ref)
|
? JsonPointer.baseName(variant.$ref)
|
||||||
: `${(merged.title || '')}${(merged.const && JSON.stringify(merged.const)) || ''}`;
|
: `${merged.title || ''}${(merged.const && JSON.stringify(merged.const)) || ''}`;
|
||||||
|
|
||||||
const schema = new SchemaModel(
|
const schema = new SchemaModel(
|
||||||
parser,
|
parser,
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class SecurityRequirementModel {
|
||||||
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
||||||
|
|
||||||
this.schemes = Object.keys(requirement || {})
|
this.schemes = Object.keys(requirement || {})
|
||||||
.map(id => {
|
.map((id) => {
|
||||||
const scheme = parser.deref(schemes[id]);
|
const scheme = parser.deref(schemes[id]);
|
||||||
const scopes = requirement[id] || [];
|
const scopes = requirement[id] || [];
|
||||||
|
|
||||||
|
@ -31,6 +31,6 @@ export class SecurityRequirementModel {
|
||||||
scopes,
|
scopes,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(scheme => scheme !== undefined) as SecurityScheme[];
|
.filter((scheme) => scheme !== undefined) as SecurityScheme[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ export class SecuritySchemesModel {
|
||||||
constructor(parser: OpenAPIParser) {
|
constructor(parser: OpenAPIParser) {
|
||||||
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
||||||
this.schemes = Object.keys(schemes).map(
|
this.schemes = Object.keys(schemes).map(
|
||||||
name => new SecuritySchemeModel(parser, name, schemes[name]),
|
(name) => new SecuritySchemeModel(parser, name, schemes[name]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { hydrate as hydrateComponent, render } from 'react-dom';
|
import { hydrate as hydrateComponent, render } from 'react-dom';
|
||||||
import { configure } from "mobx"
|
import { configure } from 'mobx';
|
||||||
|
|
||||||
import { Redoc, RedocStandalone } from './components/';
|
import { Redoc, RedocStandalone } from './components/';
|
||||||
import { AppStore, StoreState } from './services/AppStore';
|
import { AppStore, StoreState } from './services/AppStore';
|
||||||
|
@ -8,8 +8,8 @@ import { debugTime, debugTimeEnd } from './utils/debug';
|
||||||
import { querySelector } from './utils/dom';
|
import { querySelector } from './utils/dom';
|
||||||
|
|
||||||
configure({
|
configure({
|
||||||
useProxies: 'ifavailable'
|
useProxies: 'ifavailable',
|
||||||
})
|
});
|
||||||
|
|
||||||
export { Redoc, AppStore } from '.';
|
export { Redoc, AppStore } from '.';
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ const {
|
||||||
export const media = {
|
export const media = {
|
||||||
lessThan(breakpoint, print?: boolean, extra?: string) {
|
lessThan(breakpoint, print?: boolean, extra?: string) {
|
||||||
return (...args) => css`
|
return (...args) => css`
|
||||||
@media ${print ? 'print, ' : ''} screen and (max-width: ${props =>
|
@media ${print ? 'print, ' : ''} screen and (max-width: ${(props) =>
|
||||||
props.theme.breakpoints[breakpoint]}) ${extra || ''} {
|
props.theme.breakpoints[breakpoint]}) ${extra || ''} {
|
||||||
${(css as any)(...args)};
|
${(css as any)(...args)};
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export const media = {
|
||||||
|
|
||||||
greaterThan(breakpoint) {
|
greaterThan(breakpoint) {
|
||||||
return (...args) => css`
|
return (...args) => css`
|
||||||
@media (min-width: ${props => props.theme.breakpoints[breakpoint]}) {
|
@media (min-width: ${(props) => props.theme.breakpoints[breakpoint]}) {
|
||||||
${(css as any)(...args)};
|
${(css as any)(...args)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -32,9 +32,9 @@ export const media = {
|
||||||
|
|
||||||
between(firstBreakpoint, secondBreakpoint) {
|
between(firstBreakpoint, secondBreakpoint) {
|
||||||
return (...args) => css`
|
return (...args) => css`
|
||||||
@media (min-width: ${props =>
|
@media (min-width: ${(props) => props.theme.breakpoints[firstBreakpoint]}) and (max-width: ${(
|
||||||
props.theme.breakpoints[firstBreakpoint]}) and (max-width: ${props =>
|
props,
|
||||||
props.theme.breakpoints[secondBreakpoint]}) {
|
) => props.theme.breakpoints[secondBreakpoint]}) {
|
||||||
${(css as any)(...args)};
|
${(css as any)(...args)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -45,7 +45,7 @@ export { css, createGlobalStyle, keyframes, ThemeProvider };
|
||||||
export default styled;
|
export default styled;
|
||||||
|
|
||||||
export function extensionsHook(styledName: string) {
|
export function extensionsHook(styledName: string) {
|
||||||
return props => {
|
return (props) => {
|
||||||
if (!props.theme.extensionsHook) {
|
if (!props.theme.extensionsHook) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
16
src/theme.ts
16
src/theme.ts
|
@ -84,21 +84,21 @@ const defaultTheme: ThemeInterface = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
linesColor: theme =>
|
linesColor: (theme) =>
|
||||||
lighten(
|
lighten(
|
||||||
theme.colors.tonalOffset,
|
theme.colors.tonalOffset,
|
||||||
desaturate(theme.colors.tonalOffset, theme.colors.primary.main),
|
desaturate(theme.colors.tonalOffset, theme.colors.primary.main),
|
||||||
),
|
),
|
||||||
defaultDetailsWidth: '75%',
|
defaultDetailsWidth: '75%',
|
||||||
typeNameColor: theme => theme.colors.text.secondary,
|
typeNameColor: (theme) => theme.colors.text.secondary,
|
||||||
typeTitleColor: theme => theme.schema.typeNameColor,
|
typeTitleColor: (theme) => theme.schema.typeNameColor,
|
||||||
requireLabelColor: theme => theme.colors.error.main,
|
requireLabelColor: (theme) => theme.colors.error.main,
|
||||||
labelsTextSize: '0.9em',
|
labelsTextSize: '0.9em',
|
||||||
nestingSpacing: '1em',
|
nestingSpacing: '1em',
|
||||||
nestedBackground: '#fafafa',
|
nestedBackground: '#fafafa',
|
||||||
arrow: {
|
arrow: {
|
||||||
size: '1.1em',
|
size: '1.1em',
|
||||||
color: theme => theme.colors.text.secondary,
|
color: (theme) => theme.colors.text.secondary,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
|
@ -134,7 +134,7 @@ const defaultTheme: ThemeInterface = {
|
||||||
width: '260px',
|
width: '260px',
|
||||||
backgroundColor: '#fafafa',
|
backgroundColor: '#fafafa',
|
||||||
textColor: '#333333',
|
textColor: '#333333',
|
||||||
activeTextColor: theme =>
|
activeTextColor: (theme) =>
|
||||||
theme.sidebar.textColor !== defaultTheme.sidebar!.textColor
|
theme.sidebar.textColor !== defaultTheme.sidebar!.textColor
|
||||||
? theme.sidebar.textColor
|
? theme.sidebar.textColor
|
||||||
: theme.colors.primary.main,
|
: theme.colors.primary.main,
|
||||||
|
@ -146,7 +146,7 @@ const defaultTheme: ThemeInterface = {
|
||||||
},
|
},
|
||||||
arrow: {
|
arrow: {
|
||||||
size: '1.5em',
|
size: '1.5em',
|
||||||
color: theme => theme.sidebar.textColor,
|
color: (theme) => theme.sidebar.textColor,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logo: {
|
logo: {
|
||||||
|
@ -170,7 +170,7 @@ export function resolveTheme(theme: ThemeInterface): ResolvedThemeInterface {
|
||||||
const resolvedValues = {};
|
const resolvedValues = {};
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
const setProxy = (obj, path: string) => {
|
const setProxy = (obj, path: string) => {
|
||||||
Object.keys(obj).forEach(k => {
|
Object.keys(obj).forEach((k) => {
|
||||||
const currentPath = (path ? path + '.' : '') + k;
|
const currentPath = (path ? path + '.' : '') + k;
|
||||||
const val = obj[k];
|
const val = obj[k];
|
||||||
if (typeof val === 'function') {
|
if (typeof val === 'function') {
|
||||||
|
|
|
@ -11,7 +11,9 @@ describe('#loadAndBundleSpec', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load And Bundle Spec demo/openapi-3-1.yaml', async () => {
|
it('should load And Bundle Spec demo/openapi-3-1.yaml', async () => {
|
||||||
const spec = yaml.load(readFileSync(resolve(__dirname, '../../../demo/openapi-3-1.yaml'), 'utf-8'));
|
const spec = yaml.load(
|
||||||
|
readFileSync(resolve(__dirname, '../../../demo/openapi-3-1.yaml'), 'utf-8'),
|
||||||
|
);
|
||||||
const bundledSpec = await loadAndBundleSpec(spec);
|
const bundledSpec = await loadAndBundleSpec(spec);
|
||||||
expect(bundledSpec).toMatchSnapshot();
|
expect(bundledSpec).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -103,7 +103,7 @@ describe('Utils', () => {
|
||||||
|
|
||||||
it('Should return pathName if no summary, operationId, description', () => {
|
it('Should return pathName if no summary, operationId, description', () => {
|
||||||
const operation = {
|
const operation = {
|
||||||
pathName: '/sandbox/test'
|
pathName: '/sandbox/test',
|
||||||
};
|
};
|
||||||
expect(getOperationSummary(operation as any)).toBe('/sandbox/test');
|
expect(getOperationSummary(operation as any)).toBe('/sandbox/test');
|
||||||
});
|
});
|
||||||
|
@ -141,9 +141,9 @@ describe('Utils', () => {
|
||||||
object: ['maxProperties', 'minProperties', 'required', 'additionalProperties', 'properties'],
|
object: ['maxProperties', 'minProperties', 'required', 'additionalProperties', 'properties'],
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(tests).forEach(name => {
|
Object.keys(tests).forEach((name) => {
|
||||||
it(`Should detect ${name} if ${name} properties are present`, () => {
|
it(`Should detect ${name} if ${name} properties are present`, () => {
|
||||||
tests[name].forEach(propName => {
|
tests[name].forEach((propName) => {
|
||||||
expect(
|
expect(
|
||||||
detectType({
|
detectType({
|
||||||
[propName]: 0,
|
[propName]: 0,
|
||||||
|
@ -174,7 +174,7 @@ describe('Utils', () => {
|
||||||
expect(isPrimitiveType(schema)).toEqual(false);
|
expect(isPrimitiveType(schema)).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true for array contains object and schema hasn\'t properties', () => {
|
it("should return true for array contains object and schema hasn't properties", () => {
|
||||||
const schema = {
|
const schema = {
|
||||||
type: ['object', 'string'],
|
type: ['object', 'string'],
|
||||||
};
|
};
|
||||||
|
@ -233,7 +233,7 @@ describe('Utils', () => {
|
||||||
items: {
|
items: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'string'
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -415,7 +415,7 @@ describe('Utils', () => {
|
||||||
min?: number,
|
min?: number,
|
||||||
max?: number,
|
max?: number,
|
||||||
multipleOf?: number,
|
multipleOf?: number,
|
||||||
uniqueItems?: boolean
|
uniqueItems?: boolean,
|
||||||
) => ({ type: 'array', minItems: min, maxItems: max, multipleOf, uniqueItems });
|
) => ({ type: 'array', minItems: min, maxItems: max, multipleOf, uniqueItems });
|
||||||
|
|
||||||
it('should not have a humanized constraint without schema constraints', () => {
|
it('should not have a humanized constraint without schema constraints', () => {
|
||||||
|
@ -455,9 +455,9 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have a humanized constraint when uniqueItems is set', () => {
|
it('should have a humanized constraint when uniqueItems is set', () => {
|
||||||
expect(humanizeConstraints(itemConstraintSchema(undefined, undefined, undefined, true))).toContain(
|
expect(
|
||||||
'unique',
|
humanizeConstraints(itemConstraintSchema(undefined, undefined, undefined, true)),
|
||||||
);
|
).toContain('unique');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -656,11 +656,11 @@ describe('Utils', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
testCases.forEach(locationTestGroup => {
|
testCases.forEach((locationTestGroup) => {
|
||||||
describe(locationTestGroup.description, () => {
|
describe(locationTestGroup.description, () => {
|
||||||
locationTestGroup.cases.forEach(valueTypeTestGroup => {
|
locationTestGroup.cases.forEach((valueTypeTestGroup) => {
|
||||||
describe(valueTypeTestGroup.description, () => {
|
describe(valueTypeTestGroup.description, () => {
|
||||||
valueTypeTestGroup.cases.forEach(testCase => {
|
valueTypeTestGroup.cases.forEach((testCase) => {
|
||||||
it(`should serialize correctly when style is ${testCase.style} and explode is ${testCase.explode}`, () => {
|
it(`should serialize correctly when style is ${testCase.style} and explode is ${testCase.explode}`, () => {
|
||||||
const parameter: OpenAPIParameter = {
|
const parameter: OpenAPIParameter = {
|
||||||
name: locationTestGroup.name,
|
name: locationTestGroup.name,
|
||||||
|
|
|
@ -15,10 +15,10 @@ export function querySelector(selector: string): Element | null {
|
||||||
export function html2Str(html: string): string {
|
export function html2Str(html: string): string {
|
||||||
return html
|
return html
|
||||||
.split(/<[^>]+>/)
|
.split(/<[^>]+>/)
|
||||||
.map(chunk => {
|
.map((chunk) => {
|
||||||
return chunk.trim();
|
return chunk.trim();
|
||||||
})
|
})
|
||||||
.filter(trimmedChunk => {
|
.filter((trimmedChunk) => {
|
||||||
return trimmedChunk.length > 0;
|
return trimmedChunk.length > 0;
|
||||||
})
|
})
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
|
@ -50,7 +50,7 @@ export function flattenByProp<T extends object, P extends keyof T>(
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
res.push(item);
|
res.push(item);
|
||||||
if (item[prop]) {
|
if (item[prop]) {
|
||||||
iterate((item[prop] as any) as T[]);
|
iterate(item[prop] as any as T[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,8 +14,8 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi
|
||||||
const config = new Config({});
|
const config = new Config({});
|
||||||
const bundleOpts = {
|
const bundleOpts = {
|
||||||
config,
|
config,
|
||||||
base: IS_BROWSER ? window.location.href : process.cwd()
|
base: IS_BROWSER ? window.location.href : process.cwd(),
|
||||||
}
|
};
|
||||||
|
|
||||||
if (IS_BROWSER) {
|
if (IS_BROWSER) {
|
||||||
config.resolve.http.customFetch = global.fetch;
|
config.resolve.http.customFetch = global.fetch;
|
||||||
|
@ -24,13 +24,15 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi
|
||||||
if (typeof specUrlOrObject === 'object' && specUrlOrObject !== null) {
|
if (typeof specUrlOrObject === 'object' && specUrlOrObject !== null) {
|
||||||
bundleOpts['doc'] = {
|
bundleOpts['doc'] = {
|
||||||
source: { absoluteRef: '' } as Source,
|
source: { absoluteRef: '' } as Source,
|
||||||
parsed: specUrlOrObject
|
parsed: specUrlOrObject,
|
||||||
} as Document
|
} as Document;
|
||||||
} else {
|
} else {
|
||||||
bundleOpts['ref'] = specUrlOrObject;
|
bundleOpts['ref'] = specUrlOrObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bundle: { parsed } } = await bundle(bundleOpts);
|
const {
|
||||||
|
bundle: { parsed },
|
||||||
|
} = await bundle(bundleOpts);
|
||||||
return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
|
return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ const SENTINEL = {};
|
||||||
|
|
||||||
export function memoize<T>(target: any, name: string, descriptor: TypedPropertyDescriptor<T>) {
|
export function memoize<T>(target: any, name: string, descriptor: TypedPropertyDescriptor<T>) {
|
||||||
if (typeof descriptor.value === 'function') {
|
if (typeof descriptor.value === 'function') {
|
||||||
return (_memoizeMethod(target, name, descriptor) as any) as TypedPropertyDescriptor<T>;
|
return _memoizeMethod(target, name, descriptor) as any as TypedPropertyDescriptor<T>;
|
||||||
} else if (typeof descriptor.get === 'function') {
|
} else if (typeof descriptor.get === 'function') {
|
||||||
return _memoizeGetter(target, name, descriptor) as TypedPropertyDescriptor<T>;
|
return _memoizeGetter(target, name, descriptor) as TypedPropertyDescriptor<T>;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -113,7 +113,10 @@ export function detectType(schema: OpenAPISchema): string {
|
||||||
return 'any';
|
return 'any';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] | undefined = schema.type) {
|
export function isPrimitiveType(
|
||||||
|
schema: OpenAPISchema,
|
||||||
|
type: string | string[] | undefined = schema.type,
|
||||||
|
) {
|
||||||
if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
|
if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +125,8 @@ export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] |
|
||||||
const isArray = Array.isArray(type);
|
const isArray = Array.isArray(type);
|
||||||
|
|
||||||
if (type === 'object' || (isArray && type?.includes('object'))) {
|
if (type === 'object' || (isArray && type?.includes('object'))) {
|
||||||
isPrimitive = schema.properties !== undefined
|
isPrimitive =
|
||||||
|
schema.properties !== undefined
|
||||||
? Object.keys(schema.properties).length === 0
|
? Object.keys(schema.properties).length === 0
|
||||||
: schema.additionalProperties === undefined;
|
: schema.additionalProperties === undefined;
|
||||||
}
|
}
|
||||||
|
@ -144,10 +148,10 @@ export function isFormUrlEncoded(contentType: string): boolean {
|
||||||
|
|
||||||
function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: string): string {
|
function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: string): string {
|
||||||
if (Array.isArray(fieldVal)) {
|
if (Array.isArray(fieldVal)) {
|
||||||
return fieldVal.map(v => v.toString()).join(delimiter);
|
return fieldVal.map((v) => v.toString()).join(delimiter);
|
||||||
} else if (typeof fieldVal === 'object') {
|
} else if (typeof fieldVal === 'object') {
|
||||||
return Object.keys(fieldVal)
|
return Object.keys(fieldVal)
|
||||||
.map(k => `${k}${delimiter}${fieldVal[k]}`)
|
.map((k) => `${k}${delimiter}${fieldVal[k]}`)
|
||||||
.join(delimiter);
|
.join(delimiter);
|
||||||
} else {
|
} else {
|
||||||
return fieldName + '=' + fieldVal.toString();
|
return fieldName + '=' + fieldVal.toString();
|
||||||
|
@ -160,7 +164,7 @@ function deepObjectEncodeField(fieldVal: any, fieldName: string): string {
|
||||||
return '';
|
return '';
|
||||||
} else if (typeof fieldVal === 'object') {
|
} else if (typeof fieldVal === 'object') {
|
||||||
return Object.keys(fieldVal)
|
return Object.keys(fieldVal)
|
||||||
.map(k => `${fieldName}[${k}]=${fieldVal[k]}`)
|
.map((k) => `${fieldName}[${k}]=${fieldVal[k]}`)
|
||||||
.join('&');
|
.join('&');
|
||||||
} else {
|
} else {
|
||||||
console.warn('deepObject style cannot be used with non-object value:' + fieldVal.toString());
|
console.warn('deepObject style cannot be used with non-object value:' + fieldVal.toString());
|
||||||
|
@ -192,7 +196,7 @@ export function urlFormEncodePayload(
|
||||||
throw new Error('Payload must have fields: ' + payload.toString());
|
throw new Error('Payload must have fields: ' + payload.toString());
|
||||||
} else {
|
} else {
|
||||||
return Object.keys(payload)
|
return Object.keys(payload)
|
||||||
.map(fieldName => {
|
.map((fieldName) => {
|
||||||
const fieldVal = payload[fieldName];
|
const fieldVal = payload[fieldName];
|
||||||
const { style = 'form', explode = true } = encoding[fieldName] || {};
|
const { style = 'form', explode = true } = encoding[fieldName] || {};
|
||||||
switch (style) {
|
switch (style) {
|
||||||
|
@ -376,7 +380,7 @@ export function isNamedDefinition(pointer?: string): boolean {
|
||||||
export function getDefinitionName(pointer?: string): string | undefined {
|
export function getDefinitionName(pointer?: string): string | undefined {
|
||||||
if (!pointer) return undefined;
|
if (!pointer) return undefined;
|
||||||
const match = pointer.match(/^#\/components\/(schemas|pathItems)\/([^\/]+)$/);
|
const match = pointer.match(/^#\/components\/(schemas|pathItems)\/([^\/]+)$/);
|
||||||
return match === null ? undefined : match[1]
|
return match === null ? undefined : match[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function humanizeMultipleOfConstraint(multipleOf: number | undefined): string | undefined {
|
function humanizeMultipleOfConstraint(multipleOf: number | undefined): string | undefined {
|
||||||
|
@ -452,12 +456,14 @@ export function humanizeConstraints(schema: OpenAPISchema): string[] {
|
||||||
let minimum = 0;
|
let minimum = 0;
|
||||||
let maximum = 0;
|
let maximum = 0;
|
||||||
if (schema.minimum) minimum = schema.minimum;
|
if (schema.minimum) minimum = schema.minimum;
|
||||||
if (typeof schema.exclusiveMinimum === 'number') minimum = minimum <= schema.exclusiveMinimum ? minimum : schema.exclusiveMinimum;
|
if (typeof schema.exclusiveMinimum === 'number')
|
||||||
|
minimum = minimum <= schema.exclusiveMinimum ? minimum : schema.exclusiveMinimum;
|
||||||
|
|
||||||
if (schema.maximum) maximum = schema.maximum;
|
if (schema.maximum) maximum = schema.maximum;
|
||||||
if (typeof schema.exclusiveMaximum === 'number') maximum = maximum > schema.exclusiveMaximum ? maximum : schema.exclusiveMaximum;
|
if (typeof schema.exclusiveMaximum === 'number')
|
||||||
|
maximum = maximum > schema.exclusiveMaximum ? maximum : schema.exclusiveMaximum;
|
||||||
|
|
||||||
numberRange = `[${minimum} .. ${maximum}]`
|
numberRange = `[${minimum} .. ${maximum}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numberRange !== undefined) {
|
if (numberRange !== undefined) {
|
||||||
|
@ -476,7 +482,7 @@ export function sortByRequired(fields: FieldModel[], order: string[] = []) {
|
||||||
const orderedFields: FieldModel[] = [];
|
const orderedFields: FieldModel[] = [];
|
||||||
const unorderedFields: FieldModel[] = [];
|
const unorderedFields: FieldModel[] = [];
|
||||||
|
|
||||||
fields.forEach(field => {
|
fields.forEach((field) => {
|
||||||
if (field.required) {
|
if (field.required) {
|
||||||
order.includes(field.name) ? orderedFields.push(field) : unorderedFields.push(field);
|
order.includes(field.name) ? orderedFields.push(field) : unorderedFields.push(field);
|
||||||
} else {
|
} else {
|
||||||
|
@ -504,13 +510,13 @@ export function mergeParams(
|
||||||
operationParams: Array<Referenced<OpenAPIParameter>> = [],
|
operationParams: Array<Referenced<OpenAPIParameter>> = [],
|
||||||
): Array<Referenced<OpenAPIParameter>> {
|
): Array<Referenced<OpenAPIParameter>> {
|
||||||
const operationParamNames = {};
|
const operationParamNames = {};
|
||||||
operationParams.forEach(param => {
|
operationParams.forEach((param) => {
|
||||||
param = parser.shallowDeref(param);
|
param = parser.shallowDeref(param);
|
||||||
operationParamNames[param.name + '_' + param.in] = true;
|
operationParamNames[param.name + '_' + param.in] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// filter out path params overridden by operation ones with the same name
|
// filter out path params overridden by operation ones with the same name
|
||||||
pathParams = pathParams.filter(param => {
|
pathParams = pathParams.filter((param) => {
|
||||||
param = parser.shallowDeref(param);
|
param = parser.shallowDeref(param);
|
||||||
return !operationParamNames[param.name + '_' + param.in];
|
return !operationParamNames[param.name + '_' + param.in];
|
||||||
});
|
});
|
||||||
|
@ -522,7 +528,7 @@ export function mergeSimilarMediaTypes(
|
||||||
types: Record<string, OpenAPIMediaType>,
|
types: Record<string, OpenAPIMediaType>,
|
||||||
): Record<string, OpenAPIMediaType> {
|
): Record<string, OpenAPIMediaType> {
|
||||||
const mergedTypes = {};
|
const mergedTypes = {};
|
||||||
Object.keys(types).forEach(name => {
|
Object.keys(types).forEach((name) => {
|
||||||
const mime = types[name];
|
const mime = types[name];
|
||||||
// ignore content type parameters (e.g. charset) and merge
|
// ignore content type parameters (e.g. charset) and merge
|
||||||
const normalizedMimeName = name.split(';')[0].trim();
|
const normalizedMimeName = name.split(';')[0].trim();
|
||||||
|
@ -570,7 +576,7 @@ export function normalizeServers(
|
||||||
return resolveUrl(baseUrl, url);
|
return resolveUrl(baseUrl, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return servers.map(server => {
|
return servers.map((server) => {
|
||||||
return {
|
return {
|
||||||
...server,
|
...server,
|
||||||
url: normalizeUrl(server.url),
|
url: normalizeUrl(server.url),
|
||||||
|
@ -588,7 +594,7 @@ export function setSecuritySchemePrefix(prefix: string) {
|
||||||
SECURITY_SCHEMES_SECTION_PREFIX = prefix;
|
SECURITY_SCHEMES_SECTION_PREFIX = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const shortenHTTPVerb = verb =>
|
export const shortenHTTPVerb = (verb) =>
|
||||||
({
|
({
|
||||||
delete: 'del',
|
delete: 'del',
|
||||||
options: 'opts',
|
options: 'opts',
|
||||||
|
@ -619,7 +625,7 @@ export function extractExtensions(
|
||||||
showExtensions: string[] | true,
|
showExtensions: string[] | true,
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
return Object.keys(obj)
|
return Object.keys(obj)
|
||||||
.filter(key => {
|
.filter((key) => {
|
||||||
if (showExtensions === true) {
|
if (showExtensions === true) {
|
||||||
return key.startsWith('x-') && !isRedocExtension(key);
|
return key.startsWith('x-') && !isRedocExtension(key);
|
||||||
}
|
}
|
||||||
|
@ -634,6 +640,6 @@ export function extractExtensions(
|
||||||
export function pluralizeType(displayType: string): string {
|
export function pluralizeType(displayType: string): string {
|
||||||
return displayType
|
return displayType
|
||||||
.split(' or ')
|
.split(' or ')
|
||||||
.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 ');
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ function traverseComponent(root, fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterPropsDeep<T extends object>(component: T, paths: string[]): T {
|
export function filterPropsDeep<T extends object>(component: T, paths: string[]): T {
|
||||||
traverseComponent(component, comp => {
|
traverseComponent(component, (comp) => {
|
||||||
if (comp.props) {
|
if (comp.props) {
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
if (has(comp.props, path)) {
|
if (has(comp.props, path)) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user