Skip to content

Commit 8943a90

Browse files
committed
fix: wip unify redoc config
1 parent 59ee73f commit 8943a90

File tree

12 files changed

+89
-89
lines changed

12 files changed

+89
-89
lines changed

demo/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class DemoApp extends React.Component<
122122
<RedocStandalone
123123
spec={this.state.spec}
124124
specUrl={proxiedUrl}
125-
options={{ scrollYOffset: 'nav', untrustedSpec: true }}
125+
options={{ scrollYOffset: 'nav', sanitize: true }}
126126
/>
127127
</>
128128
);

docs/config.md

+5-25
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,7 @@ Sets the minimum amount of characters that need to be typed into the search dial
2626

2727
_Default: 3_
2828

29-
### expandDefaultServerVariables
30-
31-
Enables or disables expanding default server variables.
32-
33-
### expandResponses
34-
35-
Controls which responses to expand by default. Specify one or more responses by providing their response codes as a comma-separated list without spaces, for example `expandResponses='200,201'`. Special value 'all' expands all responses by default. Be careful: this option can slow down documentation rendering time.
36-
37-
### expandSingleSchemaField
38-
39-
Automatically expands the single field in a schema.
40-
41-
### hideDownloadButton
29+
### hideDownloadButtons
4230

4331
Hides the 'Download' button for saving the API definition source file. **This setting does not make the API definition private**; it just hides the button.
4432

@@ -78,7 +66,7 @@ Hides the request sample tab for requests with only one sample.
7866

7967
Sets the path to the optional HTML file used to modify the layout of the reference docs page.
8068

81-
### jsonSampleExpandLevel
69+
### jsonSamplesExpandLevel
8270

8371
Sets the default expand level for JSON payload samples (response and request body). The default value is 2, and the maximum supported value is '+Infinity'. It can also be configured as a string with the special value `all` that expands all levels.
8472

@@ -102,21 +90,13 @@ If set to `true`, the sidebar uses the native scrollbar instead of perfect-scrol
10290

10391
Shows only required fields in request samples.
10492

105-
### pathInMiddlePanel
106-
107-
Shows the path link and HTTP verb in the middle panel instead of the right panel.
108-
109-
### payloadSampleIdx
110-
111-
If set, the payload sample is inserted at the specified index. If there are `N` payload samples and the value configured here is bigger than `N`, the payload sample is inserted last. Indexes start from 0.
112-
113-
### requiredPropsFirst
93+
### sortRequiredPropsFirst
11494

11595
Shows required properties in schemas first, ordered in the same order as in the required array.
11696

117-
### schemaExpansionLevel
97+
### schemasExpansionLevel
11898

119-
Specifies whether to automatically expand schemas in Reference docs. Set it to `all` to expand all schemas regardless of their level, or set it to a number to expand schemas up to the specified level. For example, `schemaExpansionLevel: 3` expands schemas up to three levels deep. The default value is `0`, meaning no schemas are expanded automatically.
99+
Specifies whether to automatically expand schemas in Reference docs. Set it to `all` to expand all schemas regardless of their level, or set it to a number to expand schemas up to the specified level. For example, `schemasExpansionLevel: 3` expands schemas up to three levels deep. The default value is `0`, meaning no schemas are expanded automatically.
120100

121101
### scrollYOffset
122102

docs/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ theme:
6767
openapi:
6868
disableSearch: true
6969
expandResponses: 200,202
70-
jsonSampleExpandLevel: 1
70+
jsonSamplesExpandLevel: 1
7171

7272
theme:
7373
sidebar:

src/components/ApiInfo/ApiInfo.tsx

+18-19
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,14 @@ export interface ApiInfoProps {
2222

2323
@observer
2424
export class ApiInfo extends React.Component<ApiInfoProps> {
25-
handleDownloadClick = e => {
26-
if (!e.target.href) {
27-
e.target.href = this.props.store.spec.info.downloadLink;
28-
}
29-
};
30-
3125
render() {
3226
const { store } = this.props;
3327
const { info, externalDocs } = store.spec;
34-
const hideDownloadButton = store.options.hideDownloadButton;
35-
36-
const downloadFilename = info.downloadFileName;
37-
const downloadLink = info.downloadLink;
28+
const hideDownloadButtons = store.options.hideDownloadButtons;
3829

30+
// FIXME: use downloadUrls
31+
const downloadUrls = info.downloadUrls;
32+
console.log(downloadUrls);
3933
const license =
4034
(info.license && (
4135
<InfoSpan>
@@ -83,17 +77,22 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
8377
<ApiHeader>
8478
{info.title} {version}
8579
</ApiHeader>
86-
{!hideDownloadButton && (
80+
{!hideDownloadButtons && (
8781
<p>
8882
{l('downloadSpecification')}:
89-
<DownloadButton
90-
download={downloadFilename || true}
91-
target="_blank"
92-
href={downloadLink}
93-
onClick={this.handleDownloadClick}
94-
>
95-
{l('download')}
96-
</DownloadButton>
83+
{downloadUrls?.map(({ title, url }) => {
84+
return (
85+
<DownloadButton
86+
download={title}
87+
target="_blank"
88+
href={url}
89+
rel="noreferrer"
90+
key={title}
91+
>
92+
{downloadUrls.length > 1 ? title : l('download')}
93+
</DownloadButton>
94+
);
95+
})}
9796
</p>
9897
)}
9998
<StyledMarkdownBlock>

src/components/JsonViewer/JsonViewer.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const Json = (props: JsonProps) => {
4545
// tslint:disable-next-line
4646
ref={node => setNode(node!)}
4747
dangerouslySetInnerHTML={{
48-
__html: jsonToHTML(props.data, options.jsonSampleExpandLevel),
48+
__html: jsonToHTML(props.data, options.jsonSamplesExpandLevel),
4949
}}
5050
/>
5151
)}

src/components/Markdown/SanitizedMdBlock.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const StyledMarkdownSpan = styled(StyledMarkdownBlock)`
1010
display: inline;
1111
`;
1212

13-
const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html);
13+
const sanitize = (sanitize, html) => (sanitize ? DOMPurify.sanitize(html) : html);
1414

1515
export function SanitizedMarkdownHTML({
1616
inline,
@@ -25,7 +25,7 @@ export function SanitizedMarkdownHTML({
2525
<Wrap
2626
className={'redoc-markdown ' + (rest.className || '')}
2727
dangerouslySetInnerHTML={{
28-
__html: sanitize(options.untrustedSpec, rest.html),
28+
__html: sanitize(options.sanitize, rest.html),
2929
}}
3030
data-role={rest['data-role']}
3131
{...rest}

src/components/Schema/ObjectSchema.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const ObjectSchema = observer(
2727
skipWriteOnly,
2828
level,
2929
}: ObjectSchemaProps) => {
30-
const { expandSingleSchemaField, showObjectSchemaExamples, schemaExpansionLevel } =
30+
const { expandSingleSchemaField, showObjectSchemaExamples, schemasExpansionLevel } =
3131
React.useContext(OptionsContext);
3232

3333
const filteredFields = React.useMemo(
@@ -45,7 +45,7 @@ export const ObjectSchema = observer(
4545
);
4646

4747
const expandByDefault =
48-
(expandSingleSchemaField && filteredFields.length === 1) || schemaExpansionLevel >= level!;
48+
(expandSingleSchemaField && filteredFields.length === 1) || schemasExpansionLevel >= level!;
4949

5050
return (
5151
<PropertiesTable>

src/services/RedocNormalizedOptions.ts

+35-20
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,47 @@ import { setRedocLabels } from './Labels';
66
import { SideNavStyleEnum } from './types';
77
import type { LabelsConfigRaw, MDXComponentMeta } from './types';
88

9+
export type DownloadUrlsConfig = {
10+
title?: string;
11+
url: string;
12+
}[];
13+
914
export interface RedocRawOptions {
1015
theme?: ThemeInterface;
1116
scrollYOffset?: number | string | (() => number);
1217
hideHostname?: boolean | string;
1318
expandResponses?: string | 'all';
14-
requiredPropsFirst?: boolean | string;
19+
requiredPropsFirst?: boolean | string; // remove in next major release
20+
sortRequiredPropsFirst?: boolean | string;
1521
sortPropsAlphabetically?: boolean | string;
1622
sortEnumValuesAlphabetically?: boolean | string;
1723
sortOperationsAlphabetically?: boolean | string;
1824
sortTagsAlphabetically?: boolean | string;
1925
nativeScrollbars?: boolean | string;
2026
pathInMiddlePanel?: boolean | string;
21-
untrustedSpec?: boolean | string;
27+
untrustedSpec?: boolean | string; // remove in next major release
28+
sanitize?: boolean | string;
2229
hideLoading?: boolean | string;
23-
hideDownloadButton?: boolean | string;
30+
hideDownloadButton?: boolean | string; // remove in next major release
31+
hideDownloadButtons?: boolean | string;
2432
downloadFileName?: string;
2533
downloadDefinitionUrl?: string;
34+
downloadUrls?: DownloadUrlsConfig;
2635
disableSearch?: boolean | string;
2736
onlyRequiredInSamples?: boolean | string;
2837
showExtensions?: boolean | string | string[];
2938
sideNavStyle?: SideNavStyleEnum;
3039
hideSingleRequestSampleTab?: boolean | string;
3140
hideRequestPayloadSample?: boolean;
3241
menuToggle?: boolean | string;
33-
jsonSampleExpandLevel?: number | string | 'all';
42+
jsonSampleExpandLevel?: number | string | 'all'; // remove in next major release
43+
jsonSamplesExpandLevel?: number | string | 'all';
3444
hideSchemaTitles?: boolean | string;
3545
simpleOneOfTypeLabel?: boolean | string;
3646
payloadSampleIdx?: number;
3747
expandSingleSchemaField?: boolean | string;
38-
schemaExpansionLevel?: number | string | 'all';
48+
schemaExpansionLevel?: number | string | 'all'; // remove in next major release
49+
schemasExpansionLevel?: number | string | 'all';
3950
showObjectSchemaExamples?: boolean | string;
4051
showSecuritySchemeType?: boolean;
4152
hideSecuritySection?: boolean;
@@ -216,31 +227,30 @@ export class RedocNormalizedOptions {
216227
scrollYOffset: () => number;
217228
hideHostname: boolean;
218229
expandResponses: { [code: string]: boolean } | 'all';
219-
requiredPropsFirst: boolean;
230+
sortRequiredPropsFirst: boolean;
220231
sortPropsAlphabetically: boolean;
221232
sortEnumValuesAlphabetically: boolean;
222233
sortOperationsAlphabetically: boolean;
223234
sortTagsAlphabetically: boolean;
224235
nativeScrollbars: boolean;
225236
pathInMiddlePanel: boolean;
226-
untrustedSpec: boolean;
227-
hideDownloadButton: boolean;
228-
downloadFileName?: string;
229-
downloadDefinitionUrl?: string;
237+
sanitize: boolean;
238+
hideDownloadButtons: boolean;
239+
downloadUrls?: DownloadUrlsConfig;
230240
disableSearch: boolean;
231241
onlyRequiredInSamples: boolean;
232242
showExtensions: boolean | string[];
233243
sideNavStyle: SideNavStyleEnum;
234244
hideSingleRequestSampleTab: boolean;
235245
hideRequestPayloadSample: boolean;
236246
menuToggle: boolean;
237-
jsonSampleExpandLevel: number;
247+
jsonSamplesExpandLevel: number;
238248
enumSkipQuotes: boolean;
239249
hideSchemaTitles: boolean;
240250
simpleOneOfTypeLabel: boolean;
241251
payloadSampleIdx: number;
242252
expandSingleSchemaField: boolean;
243-
schemaExpansionLevel: number;
253+
schemasExpansionLevel: number;
244254
showObjectSchemaExamples: boolean;
245255
showSecuritySchemeType?: boolean;
246256
hideSecuritySection?: boolean;
@@ -288,33 +298,38 @@ export class RedocNormalizedOptions {
288298
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
289299
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
290300
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
291-
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
301+
this.sortRequiredPropsFirst = argValueToBoolean(
302+
raw.sortRequiredPropsFirst || raw.requiredPropsFirst,
303+
);
292304
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
293305
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
294306
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
295307
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
296308
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
297309
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
298-
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
299-
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
300-
this.downloadFileName = raw.downloadFileName;
301-
this.downloadDefinitionUrl = raw.downloadDefinitionUrl;
310+
this.sanitize = argValueToBoolean(raw.sanitize || raw.untrustedSpec);
311+
this.hideDownloadButtons = argValueToBoolean(raw.hideDownloadButtons || raw.hideDownloadButton);
312+
this.downloadUrls =
313+
raw.downloadUrls ||
314+
([{ title: raw.downloadFileName, url: raw.downloadDefinitionUrl }] as DownloadUrlsConfig);
302315
this.disableSearch = argValueToBoolean(raw.disableSearch);
303316
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
304317
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
305318
this.sideNavStyle = RedocNormalizedOptions.normalizeSideNavStyle(raw.sideNavStyle);
306319
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
307320
this.hideRequestPayloadSample = argValueToBoolean(raw.hideRequestPayloadSample);
308321
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
309-
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
310-
raw.jsonSampleExpandLevel,
322+
this.jsonSamplesExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
323+
raw.jsonSamplesExpandLevel || raw.jsonSampleExpandLevel,
311324
);
312325
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
313326
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
314327
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
315328
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
316329
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
317-
this.schemaExpansionLevel = argValueToExpandLevel(raw.schemaExpansionLevel);
330+
this.schemasExpansionLevel = argValueToExpandLevel(
331+
raw.schemasExpansionLevel || raw.schemaExpansionLevel,
332+
);
318333
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
319334
this.showSecuritySchemeType = argValueToBoolean(raw.showSecuritySchemeType);
320335
this.hideSecuritySection = argValueToBoolean(raw.hideSecuritySection);

src/services/__tests__/models/ApiInfo.test.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ describe('Models', () => {
139139
} as any;
140140

141141
const opts = new RedocNormalizedOptions({
142-
downloadDefinitionUrl: 'https:test.com/filename.yaml',
142+
downloadUrls: [{ title: 'Openapi description', url: 'https:test.com/filename.yaml' }],
143143
});
144144
const info = new ApiInfoModel(parser, opts);
145145
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
@@ -160,6 +160,13 @@ describe('Models', () => {
160160
const info = new ApiInfoModel(parser, opts);
161161
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
162162
expect(info.downloadFileName).toEqual('test.yaml');
163+
164+
const opts2 = new RedocNormalizedOptions({
165+
downloadUrls: [{ title: 'test.yaml', url: 'https:test.com/filename.yaml' }],
166+
});
167+
const info2 = new ApiInfoModel(parser, opts2);
168+
expect(info2.downloadLink).toEqual('https:test.com/filename.yaml');
169+
expect(info2.downloadFileName).toEqual('test.yaml');
163170
});
164171
});
165172
});

src/services/models/ApiInfo.ts

+14-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { OpenAPIContact, OpenAPIInfo, OpenAPILicense } from '../../types';
22
import { IS_BROWSER } from '../../utils/';
33
import type { OpenAPIParser } from '../OpenAPIParser';
4-
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
4+
import { DownloadUrlsConfig, RedocNormalizedOptions } from '../RedocNormalizedOptions';
55

66
export class ApiInfoModel implements OpenAPIInfo {
77
title: string;
@@ -13,8 +13,7 @@ export class ApiInfoModel implements OpenAPIInfo {
1313
contact?: OpenAPIContact;
1414
license?: OpenAPILicense;
1515

16-
downloadLink?: string;
17-
downloadFileName?: string;
16+
downloadUrls?: DownloadUrlsConfig;
1817

1918
constructor(
2019
private parser: OpenAPIParser,
@@ -29,13 +28,20 @@ export class ApiInfoModel implements OpenAPIInfo {
2928
this.description = this.description.substring(0, firstHeadingLinePos);
3029
}
3130

32-
this.downloadLink = this.getDownloadLink();
33-
this.downloadFileName = this.getDownloadFileName();
31+
this.downloadUrls = this.getDownloadUrls();
32+
}
33+
private getDownloadUrls(): DownloadUrlsConfig | undefined {
34+
return this.options.downloadUrls
35+
?.map(({ title, url }) => ({
36+
title: title || 'openapi.json',
37+
url: this.getDownloadLink(url) || '',
38+
}))
39+
.filter(({ title, url }) => title && url);
3440
}
3541

36-
private getDownloadLink(): string | undefined {
37-
if (this.options.downloadDefinitionUrl) {
38-
return this.options.downloadDefinitionUrl;
42+
private getDownloadLink(url?: string): string | undefined {
43+
if (url) {
44+
return url;
3945
}
4046

4147
if (this.parser.specUrl) {
@@ -49,11 +55,4 @@ export class ApiInfoModel implements OpenAPIInfo {
4955
return window.URL.createObjectURL(blob);
5056
}
5157
}
52-
53-
private getDownloadFileName(): string | undefined {
54-
if (!this.parser.specUrl && !this.options.downloadDefinitionUrl) {
55-
return this.options.downloadFileName || 'openapi.json';
56-
}
57-
return this.options.downloadFileName;
58-
}
5958
}

0 commit comments

Comments
 (0)