feat: add sampleCollapseLevel option (#937)

* feat: add sampleCollapseLevel option

* fix: rename sampleCollapseLevel option to jsonSampleExpandLevel
Change README.md description

* fix: provide maxCollapseLevel option via argument

* fix: rename normalizer function and some vars
This commit is contained in:
Aleksandr Karo 2019-07-29 15:26:16 +03:00 committed by Roman Hotsiy
parent 7c06735c6d
commit d3f1c1677c
4 changed files with 40 additions and 18 deletions

View File

@ -243,6 +243,7 @@ You can use all of the following options with standalone version on <redoc> tag
* `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button. * `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 * `disableSearch` - disable search indexing and search box
* `onlyRequiredInSamples` - shows only required fields in request samples. * `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) * `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 ## Advanced usage of standalone version

View File

@ -5,6 +5,7 @@ import { SampleControls } from '../../common-elements';
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper'; import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
import { PrismDiv } from '../../common-elements/PrismDiv'; import { PrismDiv } from '../../common-elements/PrismDiv';
import { jsonToHTML } from '../../utils/jsonToHtml'; import { jsonToHTML } from '../../utils/jsonToHtml';
import { OptionsContext } from '../OptionsProvider';
import { jsonStyles } from './style'; import { jsonStyles } from './style';
export interface JsonProps { export interface JsonProps {
@ -32,12 +33,18 @@ class Json extends React.PureComponent<JsonProps> {
<span onClick={this.expandAll}> Expand all </span> <span onClick={this.expandAll}> Expand all </span>
<span onClick={this.collapseAll}> Collapse all </span> <span onClick={this.collapseAll}> Collapse all </span>
</SampleControls> </SampleControls>
<PrismDiv <OptionsContext.Consumer>
className={this.props.className} {options => (
// tslint:disable-next-line <PrismDiv
ref={node => (this.node = node!)} className={this.props.className}
dangerouslySetInnerHTML={{ __html: jsonToHTML(this.props.data) }} // tslint:disable-next-line
/> ref={node => (this.node = node!)}
dangerouslySetInnerHTML={{
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
}}
/>
)}
</OptionsContext.Consumer>
</JsonViewerWrap> </JsonViewerWrap>
); );

View File

@ -22,6 +22,7 @@ export interface RedocRawOptions {
onlyRequiredInSamples?: boolean | string; onlyRequiredInSamples?: boolean | string;
showExtensions?: boolean | string | string[]; showExtensions?: boolean | string | string[];
hideSingleRequestSampleTab?: boolean | string; hideSingleRequestSampleTab?: boolean | string;
jsonSampleExpandLevel?: number | string | 'all';
unstable_ignoreMimeParameters?: boolean; unstable_ignoreMimeParameters?: boolean;
@ -111,6 +112,16 @@ export class RedocNormalizedOptions {
return value; 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; theme: ResolvedThemeInterface;
scrollYOffset: () => number; scrollYOffset: () => number;
hideHostname: boolean; hideHostname: boolean;
@ -126,6 +137,7 @@ export class RedocNormalizedOptions {
onlyRequiredInSamples: boolean; onlyRequiredInSamples: boolean;
showExtensions: boolean | string[]; showExtensions: boolean | string[];
hideSingleRequestSampleTab: boolean; hideSingleRequestSampleTab: boolean;
jsonSampleExpandLevel: number;
enumSkipQuotes: boolean; enumSkipQuotes: boolean;
/* tslint:disable-next-line */ /* tslint:disable-next-line */
@ -158,6 +170,9 @@ export class RedocNormalizedOptions {
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples); this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions); this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab); this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
raw.jsonSampleExpandLevel,
);
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes); this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters); this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);

View File

@ -1,11 +1,10 @@
let level = 1; let level = 1;
const COLLAPSE_LEVEL = 2;
export function jsonToHTML(json) { export function jsonToHTML(json, maxExpandLevel) {
level = 1; level = 1;
let output = ''; let output = '';
output += '<div class="redoc-json">'; output += '<div class="redoc-json">';
output += valueToHTML(json); output += valueToHTML(json, maxExpandLevel);
output += '</div>'; output += '</div>';
return output; return output;
} }
@ -33,20 +32,20 @@ function punctuation(val) {
return '<span class="token punctuation">' + val + '</span>'; return '<span class="token punctuation">' + val + '</span>';
} }
function valueToHTML(value) { function valueToHTML(value, maxExpandLevel: number) {
const valueType = typeof value; const valueType = typeof value;
let output = ''; let output = '';
if (value === undefined || value === null) { if (value === undefined || value === null) {
output += decorateWithSpan('null', 'token keyword'); output += decorateWithSpan('null', 'token keyword');
} else if (value && value.constructor === Array) { } else if (value && value.constructor === Array) {
level++; level++;
output += arrayToHTML(value); output += arrayToHTML(value, maxExpandLevel);
level--; level--;
} else if (value && value.constructor === Date) { } else if (value && value.constructor === Date) {
output += decorateWithSpan('"' + value.toISOString() + '"', 'token string'); output += decorateWithSpan('"' + value.toISOString() + '"', 'token string');
} else if (valueType === 'object') { } else if (valueType === 'object') {
level++; level++;
output += objectToHTML(value); output += objectToHTML(value, maxExpandLevel);
level--; level--;
} else if (valueType === 'number') { } else if (valueType === 'number') {
output += decorateWithSpan(value, 'token number'); output += decorateWithSpan(value, 'token number');
@ -70,8 +69,8 @@ function valueToHTML(value) {
return output; return output;
} }
function arrayToHTML(json) { function arrayToHTML(json, maxExpandLevel: number) {
const collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : ''; const collapsed = level > maxExpandLevel ? 'collapsed' : '';
let output = `<div class="collapser"></div>${punctuation( let output = `<div class="collapser"></div>${punctuation(
'[', '[',
)}<span class="ellipsis"></span><ul class="array collapsible">`; )}<span class="ellipsis"></span><ul class="array collapsible">`;
@ -80,7 +79,7 @@ function arrayToHTML(json) {
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
hasContents = true; hasContents = true;
output += '<li><div class="hoverable ' + collapsed + '">'; output += '<li><div class="hoverable ' + collapsed + '">';
output += valueToHTML(json[i]); output += valueToHTML(json[i], maxExpandLevel);
if (i < length - 1) { if (i < length - 1) {
output += ','; output += ',';
} }
@ -93,8 +92,8 @@ function arrayToHTML(json) {
return output; return output;
} }
function objectToHTML(json) { function objectToHTML(json, maxExpandLevel: number) {
const collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : ''; const collapsed = level > maxExpandLevel ? 'collapsed' : '';
const keys = Object.keys(json); const keys = Object.keys(json);
const length = keys.length; const length = keys.length;
let output = `<div class="collapser"></div>${punctuation( let output = `<div class="collapser"></div>${punctuation(
@ -106,7 +105,7 @@ function objectToHTML(json) {
hasContents = true; hasContents = true;
output += '<li><div class="hoverable ' + collapsed + '">'; output += '<li><div class="hoverable ' + collapsed + '">';
output += '<span class="property token string">"' + htmlEncode(key) + '"</span>: '; output += '<span class="property token string">"' + htmlEncode(key) + '"</span>: ';
output += valueToHTML(json[key]); output += valueToHTML(json[key], maxExpandLevel);
if (i < length - 1) { if (i < length - 1) {
output += punctuation(','); output += punctuation(',');
} }