mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-06 05:10:20 +03:00
fix: No maxLength label is displayed for arrays of items #1701
This commit is contained in:
parent
b5fb407470
commit
74bcf11514
27
src/components/Fields/ArrayItemDetails.tsx
Normal file
27
src/components/Fields/ArrayItemDetails.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import * as React from 'react';
|
||||
import { TypeFormat, TypeName, TypePrefix } from '../../common-elements/fields';
|
||||
import { ConstraintsView } from './FieldContstraints';
|
||||
import { Pattern } from './Pattern';
|
||||
import { SchemaModel } from '../../services';
|
||||
import styled from '../../styled-components';
|
||||
|
||||
export function ArrayItemDetails({ schema }: { schema: SchemaModel }) {
|
||||
if (!schema || schema.type === 'string' && !schema.constraints.length) return null;
|
||||
|
||||
return (
|
||||
<ArrayItemDetailsStyled>
|
||||
<TypePrefix>{schema.typePrefix}</TypePrefix>
|
||||
<TypeName>{schema.displayType}</TypeName>
|
||||
{schema.displayFormat && (
|
||||
<TypeFormat>{` <${schema.displayFormat}> `}</TypeFormat>
|
||||
)}
|
||||
<ConstraintsView constraints={schema.constraints} />
|
||||
<Pattern schema={schema}/>
|
||||
{schema.items && <ArrayItemDetails schema={schema.items}/> }
|
||||
</ArrayItemDetailsStyled>
|
||||
);
|
||||
}
|
||||
|
||||
const ArrayItemDetailsStyled = styled.div`
|
||||
padding-left: 7px;
|
||||
`;
|
|
@ -8,7 +8,7 @@ import { RedocRawOptions } from '../../services/RedocNormalizedOptions';
|
|||
|
||||
export interface EnumValuesProps {
|
||||
values: string[];
|
||||
type: string | string[];
|
||||
isArrayType: boolean;
|
||||
}
|
||||
|
||||
export interface EnumValuesState {
|
||||
|
@ -27,7 +27,7 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
|
|||
}
|
||||
|
||||
render() {
|
||||
const { values, type } = this.props;
|
||||
const { values, isArrayType } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
// TODO: provide context interface in more elegant way
|
||||
|
@ -55,7 +55,7 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
|
|||
return (
|
||||
<div>
|
||||
<FieldLabel>
|
||||
{type === 'array' ? l('enumArray') : ''}{' '}
|
||||
{isArrayType ? l('enumArray') : ''}{' '}
|
||||
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
||||
</FieldLabel>{' '}
|
||||
{displayedItems.map((value, idx) => {
|
||||
|
|
35
src/components/Fields/Examples.tsx
Normal file
35
src/components/Fields/Examples.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { FieldLabel, ExampleValue } from '../../common-elements/fields';
|
||||
import { getSerializedValue } from '../../utils';
|
||||
|
||||
import { l } from '../../services/Labels';
|
||||
import { FieldModel } from '../../services';
|
||||
import styled from '../../styled-components';
|
||||
|
||||
export function Examples({ field }: { field: FieldModel }) {
|
||||
if (!field.examples) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FieldLabel> {l('examples')}: </FieldLabel>
|
||||
<ExamplesList>
|
||||
{Object.values(field.examples).map((example, idx) => {
|
||||
return (
|
||||
<li key={idx}>
|
||||
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> - {example.summary || example.description}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ExamplesList>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const ExamplesList = styled.ul`
|
||||
margin-top: 1em;
|
||||
padding-left: 0;
|
||||
list-style-position: inside;
|
||||
`;
|
|
@ -1,22 +1,19 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import {
|
||||
PatternLabel,
|
||||
RecursiveLabel,
|
||||
TypeFormat,
|
||||
TypeName,
|
||||
TypePrefix,
|
||||
TypeTitle,
|
||||
ToggleButton,
|
||||
FieldLabel,
|
||||
ExampleValue,
|
||||
} from '../../common-elements/fields';
|
||||
import { serializeParameterValue } from '../../utils/openapi';
|
||||
import { getSerializedValue } from '../../utils';
|
||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { EnumValues } from './EnumValues';
|
||||
import { Extensions } from './Extensions';
|
||||
import { FieldProps } from './Field';
|
||||
import { Examples } from './Examples';
|
||||
import { ConstraintsView } from './FieldContstraints';
|
||||
import { FieldDetail } from './FieldDetail';
|
||||
|
||||
|
@ -24,30 +21,19 @@ import { Badge } from '../../common-elements/';
|
|||
|
||||
import { l } from '../../services/Labels';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { FieldModel } from '../../services/models/Field';
|
||||
import styled from '../../styled-components';
|
||||
|
||||
const MAX_PATTERN_LENGTH = 45;
|
||||
import { Pattern } from './Pattern';
|
||||
import { ArrayItemDetails } from './ArrayItemDetails';
|
||||
|
||||
export class FieldDetails extends React.PureComponent<FieldProps, { patternShown: boolean }> {
|
||||
state = {
|
||||
patternShown: false,
|
||||
};
|
||||
|
||||
static contextType = OptionsContext;
|
||||
|
||||
togglePattern = () => {
|
||||
this.setState({
|
||||
patternShown: !this.state.patternShown,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { showExamples, field, renderDiscriminatorSwitch } = this.props;
|
||||
const { patternShown } = this.state;
|
||||
const { enumSkipQuotes, hideSchemaTitles, hideSchemaPattern } = this.context;
|
||||
const { enumSkipQuotes, hideSchemaTitles } = this.context;
|
||||
|
||||
const { schema, description, example, deprecated, examples } = field;
|
||||
const { type } = schema;
|
||||
const isArrayType = type === 'array';
|
||||
|
||||
const rawDefault = !!enumSkipQuotes || field.in === 'header'; // having quotes around header field default values is confusing and inappropriate
|
||||
|
||||
|
@ -92,20 +78,7 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
|||
)}
|
||||
{schema.title && !hideSchemaTitles && <TypeTitle> ({schema.title}) </TypeTitle>}
|
||||
<ConstraintsView constraints={schema.constraints} />
|
||||
{schema.pattern && !hideSchemaPattern && (
|
||||
<>
|
||||
<PatternLabel>
|
||||
{patternShown || schema.pattern.length < MAX_PATTERN_LENGTH
|
||||
? schema.pattern
|
||||
: `${schema.pattern.substr(0, MAX_PATTERN_LENGTH)}...`}
|
||||
</PatternLabel>
|
||||
{schema.pattern.length > MAX_PATTERN_LENGTH && (
|
||||
<ToggleButton onClick={this.togglePattern}>
|
||||
{patternShown ? 'Hide pattern' : 'Show pattern'}
|
||||
</ToggleButton>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Pattern schema={schema}/>
|
||||
{schema.isCircular && <RecursiveLabel> {l('recursive')} </RecursiveLabel>}
|
||||
</div>
|
||||
{deprecated && (
|
||||
|
@ -114,7 +87,7 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
|||
</div>
|
||||
)}
|
||||
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={schema.default} />
|
||||
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
|
||||
{!renderDiscriminatorSwitch && <EnumValues isArrayType={isArrayType} values={schema.enum} />}{' '}
|
||||
{renderedExamples}
|
||||
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
|
||||
<div>
|
||||
|
@ -125,44 +98,8 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
|
|||
)}
|
||||
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
|
||||
{field.const && (<FieldDetail label={l('const') + ':'} value={field.const} />) || null}
|
||||
{isArrayType && schema.items && <ArrayItemDetails schema={schema.items}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Examples({ field }: { field: FieldModel }) {
|
||||
if (!field.examples) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FieldLabel> {l('examples')}: </FieldLabel>
|
||||
<ExamplesList>
|
||||
{Object.values(field.examples).map((example, idx) => {
|
||||
return (
|
||||
<li key={idx}>
|
||||
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> - {example.summary || example.description}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ExamplesList>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function getSerializedValue(field: FieldModel, example: any) {
|
||||
if (field.in) {
|
||||
// decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
|
||||
return decodeURIComponent(serializeParameterValue(field, example));
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ExamplesList = styled.ul`
|
||||
margin-top: 1em;
|
||||
padding-left: 0;
|
||||
list-style-position: inside;
|
||||
`;
|
||||
|
|
30
src/components/Fields/Pattern.tsx
Normal file
30
src/components/Fields/Pattern.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { PatternLabel, ToggleButton } from '../../common-elements/fields';
|
||||
import * as React from 'react';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
import { SchemaModel } from '../../services';
|
||||
|
||||
const MAX_PATTERN_LENGTH = 45;
|
||||
|
||||
export function Pattern(props: {schema: SchemaModel}) {
|
||||
const pattern = props.schema.pattern;
|
||||
const { hideSchemaPattern } = useContext(OptionsContext);
|
||||
const [patternShown, usePatternShown] = useState(false);
|
||||
const togglePattern = useCallback(() => usePatternShown(!patternShown), [patternShown]);
|
||||
|
||||
if (!pattern || hideSchemaPattern) return null;
|
||||
|
||||
return <>
|
||||
<PatternLabel>
|
||||
{patternShown || pattern.length < MAX_PATTERN_LENGTH
|
||||
? pattern
|
||||
: `${pattern.substr(0, MAX_PATTERN_LENGTH)}...`}
|
||||
</PatternLabel>
|
||||
{pattern.length > MAX_PATTERN_LENGTH && (
|
||||
<ToggleButton onClick={togglePattern}>
|
||||
{patternShown ? 'Hide pattern' : 'Show pattern'}
|
||||
</ToggleButton>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
|
@ -362,6 +362,15 @@ export function serializeParameterValue(
|
|||
}
|
||||
}
|
||||
|
||||
export function getSerializedValue(field: FieldModel, example: any) {
|
||||
if (field.in) {
|
||||
// decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
|
||||
return decodeURIComponent(serializeParameterValue(field, example));
|
||||
} else {
|
||||
return example;
|
||||
}
|
||||
}
|
||||
|
||||
export function langFromMime(contentType: string): string {
|
||||
if (contentType.search(/xml/i) !== -1) {
|
||||
return 'xml';
|
||||
|
|
Loading…
Reference in New Issue
Block a user