diff --git a/README.md b/README.md index 97c760aa4b..cae15bb310 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,7 @@ You can use all of the following options with standalone version on tag * `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button. * `disableSearch` - disable search indexing and search box * `onlyRequiredInSamples` - shows only required fields in request samples. +* `jsonSampleExpandLevel` - set the default expand level for JSON payload samples (responses and request body). Special value 'all' expands all levels. The default value is `2`. * `theme` - ReDoc theme. Not documented yet. For details check source code: [theme.ts](https://github.com/Redocly/redoc/blob/master/src/theme.ts) ## Advanced usage of standalone version diff --git a/src/components/JsonViewer/JsonViewer.tsx b/src/components/JsonViewer/JsonViewer.tsx index 33231ee7e1..bf7352991d 100644 --- a/src/components/JsonViewer/JsonViewer.tsx +++ b/src/components/JsonViewer/JsonViewer.tsx @@ -5,6 +5,7 @@ import { SampleControls } from '../../common-elements'; import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper'; import { PrismDiv } from '../../common-elements/PrismDiv'; import { jsonToHTML } from '../../utils/jsonToHtml'; +import { OptionsContext } from '../OptionsProvider'; import { jsonStyles } from './style'; export interface JsonProps { @@ -32,12 +33,18 @@ class Json extends React.PureComponent { Expand all Collapse all - (this.node = node!)} - dangerouslySetInnerHTML={{ __html: jsonToHTML(this.props.data) }} - /> + + {options => ( + (this.node = node!)} + dangerouslySetInnerHTML={{ + __html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel), + }} + /> + )} + ); diff --git a/src/services/RedocNormalizedOptions.ts b/src/services/RedocNormalizedOptions.ts index 9c7e8452a2..e4bf6751bd 100644 --- a/src/services/RedocNormalizedOptions.ts +++ b/src/services/RedocNormalizedOptions.ts @@ -22,6 +22,7 @@ export interface RedocRawOptions { onlyRequiredInSamples?: boolean | string; showExtensions?: boolean | string | string[]; hideSingleRequestSampleTab?: boolean | string; + jsonSampleExpandLevel?: number | string | 'all'; unstable_ignoreMimeParameters?: boolean; @@ -111,6 +112,16 @@ export class RedocNormalizedOptions { return value; } + private static normalizeJsonSampleExpandLevel(level?: number | string | 'all'): number { + if (level === 'all') { + return +Infinity; + } + if (!isNaN(Number(level))) { + return Math.ceil(Number(level)); + } + return 2; + } + theme: ResolvedThemeInterface; scrollYOffset: () => number; hideHostname: boolean; @@ -126,6 +137,7 @@ export class RedocNormalizedOptions { onlyRequiredInSamples: boolean; showExtensions: boolean | string[]; hideSingleRequestSampleTab: boolean; + jsonSampleExpandLevel: number; enumSkipQuotes: boolean; /* tslint:disable-next-line */ @@ -158,6 +170,9 @@ export class RedocNormalizedOptions { this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples); this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions); this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab); + this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel( + raw.jsonSampleExpandLevel, + ); this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes); this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters); diff --git a/src/utils/jsonToHtml.ts b/src/utils/jsonToHtml.ts index abd4b14afe..bbdc638e55 100644 --- a/src/utils/jsonToHtml.ts +++ b/src/utils/jsonToHtml.ts @@ -1,11 +1,10 @@ let level = 1; -const COLLAPSE_LEVEL = 2; -export function jsonToHTML(json) { +export function jsonToHTML(json, maxExpandLevel) { level = 1; let output = ''; output += '
'; - output += valueToHTML(json); + output += valueToHTML(json, maxExpandLevel); output += '
'; return output; } @@ -33,20 +32,20 @@ function punctuation(val) { return '' + val + ''; } -function valueToHTML(value) { +function valueToHTML(value, maxExpandLevel: number) { const valueType = typeof value; let output = ''; if (value === undefined || value === null) { output += decorateWithSpan('null', 'token keyword'); } else if (value && value.constructor === Array) { level++; - output += arrayToHTML(value); + output += arrayToHTML(value, maxExpandLevel); level--; } else if (value && value.constructor === Date) { output += decorateWithSpan('"' + value.toISOString() + '"', 'token string'); } else if (valueType === 'object') { level++; - output += objectToHTML(value); + output += objectToHTML(value, maxExpandLevel); level--; } else if (valueType === 'number') { output += decorateWithSpan(value, 'token number'); @@ -70,8 +69,8 @@ function valueToHTML(value) { return output; } -function arrayToHTML(json) { - const collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : ''; +function arrayToHTML(json, maxExpandLevel: number) { + const collapsed = level > maxExpandLevel ? 'collapsed' : ''; let output = `
${punctuation( '[', )}
    `; @@ -80,7 +79,7 @@ function arrayToHTML(json) { for (let i = 0; i < length; i++) { hasContents = true; output += '