mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-25 10:03:45 +03:00
feat: display patternProperties (#2008)
This commit is contained in:
parent
58cd3cb782
commit
660cc857bc
|
@ -1,4 +1,4 @@
|
|||
import styled, { extensionsHook, media } from '../styled-components';
|
||||
import styled, { extensionsHook, media, css } from '../styled-components';
|
||||
import { deprecatedCss } from './mixins';
|
||||
|
||||
export const PropertiesTableCaption = styled.caption`
|
||||
|
@ -72,7 +72,26 @@ export const PropertyNameCell = styled(PropertyCell)`
|
|||
${deprecatedCss};
|
||||
}
|
||||
|
||||
${({ kind }) => (kind !== 'field' ? 'font-style: italic' : '')};
|
||||
${({ kind }) =>
|
||||
kind === 'patternProperties' &&
|
||||
css`
|
||||
> span.property-name {
|
||||
display: inline-table;
|
||||
white-space: break-spaces;
|
||||
margin-right: 20px;
|
||||
|
||||
::before,
|
||||
::after {
|
||||
content: '/';
|
||||
filter: opacity(0.2);
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
${({ kind = '' }) =>
|
||||
['field', 'additionalProperties', 'patternProperties'].includes(kind)
|
||||
? ''
|
||||
: 'font-style: italic'};
|
||||
|
||||
${extensionsHook('PropertyNameCell')};
|
||||
`;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { transparentize } from 'polished';
|
||||
|
||||
import styled, { extensionsHook } from '../styled-components';
|
||||
import styled, { extensionsHook, css } from '../styled-components';
|
||||
import { PropertyNameCell } from './fields-layout';
|
||||
import { ShelfIcon } from './shelfs';
|
||||
|
||||
|
@ -17,6 +17,27 @@ export const ClickablePropertyNameCell = styled(PropertyNameCell)`
|
|||
&:focus {
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeightBold};
|
||||
}
|
||||
${({ kind }) =>
|
||||
kind === 'patternProperties' &&
|
||||
css`
|
||||
display: inline-flex;
|
||||
margin-right: 20px;
|
||||
|
||||
> span.property-name {
|
||||
white-space: break-spaces;
|
||||
text-align: left;
|
||||
|
||||
::before,
|
||||
::after {
|
||||
content: '/';
|
||||
filter: opacity(0.2);
|
||||
}
|
||||
}
|
||||
|
||||
> svg {
|
||||
align-self: center;
|
||||
}
|
||||
`}
|
||||
}
|
||||
${ShelfIcon} {
|
||||
height: ${({ theme }) => theme.schema.arrow.size};
|
||||
|
@ -56,6 +77,10 @@ export const RequiredLabel = styled(FieldLabel.withComponent('div'))`
|
|||
line-height: 1;
|
||||
`;
|
||||
|
||||
export const PropertyLabel = styled(RequiredLabel)`
|
||||
color: ${props => props.theme.colors.primary.light};
|
||||
`;
|
||||
|
||||
export const RecursiveLabel = styled(FieldLabel)`
|
||||
color: ${({ theme }) => theme.colors.warning.main};
|
||||
font-size: 13px;
|
||||
|
|
|
@ -37,6 +37,7 @@ class IntShelfIcon extends React.PureComponent<{
|
|||
export const ShelfIcon = styled(IntShelfIcon)`
|
||||
height: ${props => props.size || '18px'};
|
||||
width: ${props => props.size || '18px'};
|
||||
min-width: ${props => props.size || '18px'};
|
||||
vertical-align: middle;
|
||||
float: ${props => props.float || ''};
|
||||
transition: transform 0.2s ease-out;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ClickablePropertyNameCell, RequiredLabel } from '../../common-elements/fields';
|
||||
import {
|
||||
ClickablePropertyNameCell,
|
||||
PropertyLabel,
|
||||
RequiredLabel,
|
||||
} from '../../common-elements/fields';
|
||||
import { FieldDetails } from './FieldDetails';
|
||||
|
||||
import {
|
||||
InnerPropertiesWrap,
|
||||
PropertyBullet,
|
||||
|
@ -11,11 +14,10 @@ import {
|
|||
PropertyDetailsCell,
|
||||
PropertyNameCell,
|
||||
} from '../../common-elements/fields-layout';
|
||||
|
||||
import { ShelfIcon } from '../../common-elements/';
|
||||
|
||||
import { FieldModel } from '../../services/models';
|
||||
import { Schema, SchemaOptions } from '../Schema/Schema';
|
||||
import { Schema } from '../Schema/Schema';
|
||||
import type { SchemaOptions } from '../Schema/Schema';
|
||||
import type { FieldModel } from '../../services/models';
|
||||
|
||||
export interface FieldProps extends SchemaOptions {
|
||||
className?: string;
|
||||
|
@ -52,6 +54,14 @@ export class Field extends React.Component<FieldProps> {
|
|||
|
||||
const expanded = field.expanded === undefined ? expandByDefault : field.expanded;
|
||||
|
||||
const labels = (
|
||||
<>
|
||||
{kind === 'additionalProperties' && <PropertyLabel>additional property</PropertyLabel>}
|
||||
{kind === 'patternProperties' && <PropertyLabel>pattern property</PropertyLabel>}
|
||||
{required && <RequiredLabel>required</RequiredLabel>}
|
||||
</>
|
||||
);
|
||||
|
||||
const paramName = withSubSchema ? (
|
||||
<ClickablePropertyNameCell
|
||||
className={deprecated ? 'deprecated' : ''}
|
||||
|
@ -64,16 +74,16 @@ export class Field extends React.Component<FieldProps> {
|
|||
onKeyPress={this.handleKeyPress}
|
||||
aria-label="expand properties"
|
||||
>
|
||||
<span>{name}</span>
|
||||
<span className="property-name">{name}</span>
|
||||
<ShelfIcon direction={expanded ? 'down' : 'right'} />
|
||||
</button>
|
||||
{required && <RequiredLabel> required </RequiredLabel>}
|
||||
{labels}
|
||||
</ClickablePropertyNameCell>
|
||||
) : (
|
||||
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
|
||||
<PropertyBullet />
|
||||
<span>{name}</span>
|
||||
{required && <RequiredLabel> required </RequiredLabel>}
|
||||
<span className="property-name">{name}</span>
|
||||
{labels}
|
||||
</PropertyNameCell>
|
||||
);
|
||||
|
||||
|
|
32
src/services/__tests__/fixtures/3.1/patternProperties.json
Normal file
32
src/services/__tests__/fixtures/3.1/patternProperties.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "Schema definition with unevaluatedProperties",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "example.com"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Patterns": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^S_\\w+\\.[1-9]{2,4}$": {
|
||||
"type": "string"
|
||||
},
|
||||
"^O_\\w+\\.[1-9]{2,4}$": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"x-nestedProperty": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,5 +76,16 @@ describe('Models', () => {
|
|||
expect(schema.fields![1].kind).toEqual('additionalProperties');
|
||||
expect(schema.fields![1].schema.type).toEqual('boolean');
|
||||
});
|
||||
|
||||
test('schemaDefinition should resolve patternProperties', () => {
|
||||
const spec = require('../fixtures/3.1/patternProperties.json');
|
||||
parser = new OpenAPIParser(spec, undefined, opts);
|
||||
const schema = new SchemaModel(parser, spec.components.schemas.Patterns, '', opts);
|
||||
expect(schema.fields).toHaveLength(2);
|
||||
expect(schema.fields![0].kind).toEqual('patternProperties');
|
||||
expect(schema.fields![0].schema.type).toEqual('string');
|
||||
expect(schema.fields![1].kind).toEqual('patternProperties');
|
||||
expect(schema.fields![1].schema.type).toEqual('object');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -364,6 +364,7 @@ function buildFields(
|
|||
options: RedocNormalizedOptions,
|
||||
): FieldModel[] {
|
||||
const props = schema.properties || {};
|
||||
const patternProps = schema.patternProperties || {};
|
||||
const additionalProps = schema.additionalProperties || schema.unevaluatedProperties;
|
||||
const defaults = schema.default;
|
||||
let fields = Object.keys(props || []).map(fieldName => {
|
||||
|
@ -402,6 +403,31 @@ function buildFields(
|
|||
fields = sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
|
||||
}
|
||||
|
||||
fields.push(
|
||||
...Object.keys(patternProps).map(fieldName => {
|
||||
let field = patternProps[fieldName];
|
||||
|
||||
if (!field) {
|
||||
console.warn(
|
||||
`Field "${fieldName}" is invalid, skipping.\n Field must be an object but got ${typeof field} at "${$ref}"`,
|
||||
);
|
||||
field = {};
|
||||
}
|
||||
|
||||
return new FieldModel(
|
||||
parser,
|
||||
{
|
||||
name: fieldName,
|
||||
required: false,
|
||||
schema: field,
|
||||
kind: 'patternProperties',
|
||||
},
|
||||
`${$ref}/patternProperties/${fieldName}`,
|
||||
options,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
if (typeof additionalProps === 'object' || additionalProps === true) {
|
||||
fields.push(
|
||||
new FieldModel(
|
||||
|
|
|
@ -113,6 +113,7 @@ export interface OpenAPISchema {
|
|||
$ref?: string;
|
||||
type?: string | string[];
|
||||
properties?: { [name: string]: OpenAPISchema };
|
||||
patternProperties?: { [name: string]: OpenAPISchema };
|
||||
additionalProperties?: boolean | OpenAPISchema;
|
||||
unevaluatedProperties?: boolean | OpenAPISchema;
|
||||
description?: string;
|
||||
|
|
|
@ -99,6 +99,7 @@ const schemaKeywordTypes = {
|
|||
additionalProperties: 'object',
|
||||
unevaluatedProperties: 'object',
|
||||
properties: 'object',
|
||||
patternProperties: 'object',
|
||||
};
|
||||
|
||||
export function detectType(schema: OpenAPISchema): string {
|
||||
|
|
Loading…
Reference in New Issue
Block a user