Skip to content

Commit

Permalink
feat: add support x-enumDescriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexVarchuk committed Jan 15, 2025
1 parent c8eb5aa commit 417b934
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 100 deletions.
4 changes: 4 additions & 0 deletions demo/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,10 @@ components:
- available
- pending
- sold
x-enumDescriptions:
available: Available status
pending: Pending status
sold: Sold status
petType:
description: Type of a pet
type: string
Expand Down
114 changes: 83 additions & 31 deletions src/components/Fields/EnumValues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,29 @@ import { l } from '../../services/Labels';
import { OptionsContext } from '../OptionsProvider';
import styled from '../../styled-components';
import { RedocRawOptions } from '../../services/RedocNormalizedOptions';
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
import { Markdown } from '../Markdown/Markdown';

export interface EnumValuesProps {
values: string[];
isArrayType: boolean;
values?: string[] | { [name: string]: string };
type: string | string[];
}

export interface EnumValuesState {
collapsed: boolean;
}

const DescriptionEnumsBlock = styled(StyledMarkdownBlock)`
table {
margin-bottom: 0.2em;
}
`;

export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesState> {
constructor(props: EnumValuesProps) {
super(props);
this.toggle = this.toggle.bind(this);
}
state: EnumValuesState = {
collapsed: true,
};
Expand All @@ -27,54 +39,94 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
}

render() {
const { values, isArrayType } = this.props;
const { values, type } = this.props;
const { collapsed } = this.state;
const isDescriptionEnum = !Array.isArray(values);
const enums =
(Array.isArray(values) && values) ||
Object.entries(values || {}).map(([value, description]) => ({
value,
description,
}));

// TODO: provide context interface in more elegant way
const { enumSkipQuotes, maxDisplayedEnumValues } = this.context as RedocRawOptions;

if (!values.length) {
if (!enums.length) {
return null;
}

const displayedItems =
this.state.collapsed && maxDisplayedEnumValues
? values.slice(0, maxDisplayedEnumValues)
: values;
? enums.slice(0, maxDisplayedEnumValues)
: enums;

const showToggleButton = maxDisplayedEnumValues
? values.length > maxDisplayedEnumValues
: false;
const showToggleButton = maxDisplayedEnumValues ? enums.length > maxDisplayedEnumValues : false;

const toggleButtonText = maxDisplayedEnumValues
? collapsed
? `… ${values.length - maxDisplayedEnumValues} more`
? `… ${enums.length - maxDisplayedEnumValues} more`
: 'Hide'
: '';

return (
<div>
<FieldLabel>
{isArrayType ? l('enumArray') : ''}{' '}
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
</FieldLabel>{' '}
{displayedItems.map((value, idx) => {
const exampleValue = enumSkipQuotes ? String(value) : JSON.stringify(value);
return (
<React.Fragment key={idx}>
<ExampleValue>{exampleValue}</ExampleValue>{' '}
</React.Fragment>
);
})}
{showToggleButton ? (
<ToggleButton
onClick={() => {
this.toggle();
}}
>
{toggleButtonText}
</ToggleButton>
) : null}
{isDescriptionEnum ? (
<>
<DescriptionEnumsBlock>
<table>
<thead>
<tr>
<th>
<FieldLabel>
{type === 'array' ? l('enumArray') : ''}{' '}
{enums.length === 1 ? l('enumSingleValue') : l('enum')}
</FieldLabel>{' '}
</th>
<th>
<strong>Description</strong>
</th>
</tr>
</thead>
<tbody>
{(displayedItems as { value: string; description: string }[]).map(
({ description, value }) => {
return (
<tr key={value}>
<td>{value}</td>
<td>
<Markdown source={description} compact inline />
</td>
</tr>
);
},
)}
</tbody>
</table>
</DescriptionEnumsBlock>
{showToggleButton ? (
<ToggleButton onClick={this.toggle}>{toggleButtonText}</ToggleButton>
) : null}
</>
) : (
<>
<FieldLabel>
{type === 'array' ? l('enumArray') : ''}{' '}
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
</FieldLabel>{' '}
{displayedItems.map((value, idx) => {
const exampleValue = enumSkipQuotes ? String(value) : JSON.stringify(value);
return (
<React.Fragment key={idx}>
<ExampleValue>{exampleValue}</ExampleValue>{' '}
</React.Fragment>
);
})}
{showToggleButton ? (
<ToggleButton onClick={this.toggle}>{toggleButtonText}</ToggleButton>
) : null}
</>
)}
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Fields/FieldDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const FieldDetailsComponent = observer((props: FieldProps) => {
)}
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={defaultValue} />
{!renderDiscriminatorSwitch && (
<EnumValues isArrayType={isArrayType} values={schema.enum} />
<EnumValues type={schema.type} values={schema['x-enumDescriptions'] || schema.enum} />
)}{' '}
{renderedExamples}
<Extensions extensions={{ ...extensions, ...schema.extensions }} />
Expand Down
Loading

0 comments on commit 417b934

Please sign in to comment.