mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-13 04:16:34 +03:00
add section menus for tags and object description
This commit is contained in:
parent
7db7d4fc91
commit
4359724434
|
@ -58,9 +58,21 @@ externalDocs:
|
||||||
url: 'https://github.com/Rebilly/generator-openapi-repo'
|
url: 'https://github.com/Rebilly/generator-openapi-repo'
|
||||||
tags:
|
tags:
|
||||||
- name: pet
|
- name: pet
|
||||||
description: Everything about your Pets
|
description: |
|
||||||
|
Everything about your Pets
|
||||||
|
|
||||||
|
## The Pet Object
|
||||||
|
|
||||||
|
<object-description schemaRef="#/components/schemas/Pet" />
|
||||||
|
|
||||||
- name: store
|
- name: store
|
||||||
description: Access to Petstore orders
|
description: |
|
||||||
|
Access to Petstore orders
|
||||||
|
|
||||||
|
## The Order Object
|
||||||
|
|
||||||
|
<object-description schemaRef="#/components/schemas/Order" examplesRef="#/components/examples/Order" />
|
||||||
|
|
||||||
- name: user
|
- name: user
|
||||||
description: Operations about user
|
description: Operations about user
|
||||||
x-tagGroups:
|
x-tagGroups:
|
||||||
|
@ -926,3 +938,10 @@ components:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
name: api_key
|
name: api_key
|
||||||
in: header
|
in: header
|
||||||
|
examples:
|
||||||
|
Order:
|
||||||
|
value:
|
||||||
|
quantity: 1,
|
||||||
|
shipDate: 2018-10-19T16:46:45Z,
|
||||||
|
status: placed,
|
||||||
|
complete: false
|
||||||
|
|
64
src/components/ObjectDescription/ObjectDescription.tsx
Normal file
64
src/components/ObjectDescription/ObjectDescription.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Schema } from '../Schema';
|
||||||
|
|
||||||
|
import { MiddlePanel, Row, Section, DarkRightPanel } from '../../common-elements';
|
||||||
|
import { OpenAPIParser, RedocNormalizedOptions, MediaTypeModel } from '../../services';
|
||||||
|
import { MediaTypeSamples } from '../PayloadSamples/MediaTypeSamples';
|
||||||
|
import { OpenAPIMediaType } from '../../types';
|
||||||
|
|
||||||
|
export interface ObjectDescriptionProps {
|
||||||
|
schemaRef: string;
|
||||||
|
examplesRef?: string;
|
||||||
|
parser: OpenAPIParser;
|
||||||
|
options: RedocNormalizedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ObjectDescription extends React.PureComponent<ObjectDescriptionProps> {
|
||||||
|
private mediaModel: MediaTypeModel;
|
||||||
|
|
||||||
|
constructor(props: ObjectDescriptionProps) {
|
||||||
|
super(props);
|
||||||
|
this.mediaModel = ObjectDescription.getMediaModel(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<Row>
|
||||||
|
<MiddlePanel>
|
||||||
|
<Schema skipWriteOnly={true} key="schema" schema={this.mediaModel.schema} />
|
||||||
|
</MiddlePanel>
|
||||||
|
<DarkRightPanel>
|
||||||
|
<MediaTypeSamples mediaType={this.mediaModel} />
|
||||||
|
</DarkRightPanel>
|
||||||
|
</Row>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getMediaType(schemaRef, examplesRef): OpenAPIMediaType {
|
||||||
|
if (!schemaRef) return {};
|
||||||
|
|
||||||
|
const info: OpenAPIMediaType = {
|
||||||
|
schema: { $ref: schemaRef },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (examplesRef) info.examples = { object: { $ref: examplesRef } };
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getMediaModel({
|
||||||
|
schemaRef,
|
||||||
|
examplesRef,
|
||||||
|
parser,
|
||||||
|
options,
|
||||||
|
}: ObjectDescriptionProps) {
|
||||||
|
return new MediaTypeModel(
|
||||||
|
parser,
|
||||||
|
'json',
|
||||||
|
false,
|
||||||
|
ObjectDescription.getMediaType(schemaRef, examplesRef),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,8 +10,12 @@ import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOption
|
||||||
import { ScrollService } from './ScrollService';
|
import { ScrollService } from './ScrollService';
|
||||||
import { SearchStore } from './SearchStore';
|
import { SearchStore } from './SearchStore';
|
||||||
|
|
||||||
|
import { ObjectDescription } from '../components/ObjectDescription/ObjectDescription';
|
||||||
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
||||||
import { SECURITY_DEFINITIONS_COMPONENT_NAME } from '../utils/openapi';
|
import {
|
||||||
|
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
||||||
|
OBJECT_DEFINTION_COMPONENT_NAME,
|
||||||
|
} from '../utils/openapi';
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
menu: {
|
menu: {
|
||||||
|
@ -151,5 +155,13 @@ const DEFAULT_OPTIONS: RedocRawOptions = {
|
||||||
securitySchemes: store.spec.securitySchemes,
|
securitySchemes: store.spec.securitySchemes,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
[OBJECT_DEFINTION_COMPONENT_NAME]: {
|
||||||
|
component: ObjectDescription,
|
||||||
|
propsSelector: (store: AppStore) => ({
|
||||||
|
securitySchemes: store.spec.securitySchemes,
|
||||||
|
parser: store.spec.parser,
|
||||||
|
options: store.options,
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,7 +42,7 @@ export class MenuBuilder {
|
||||||
|
|
||||||
const items: ContentItemModel[] = [];
|
const items: ContentItemModel[] = [];
|
||||||
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
||||||
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', options));
|
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', undefined, options));
|
||||||
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
||||||
items.push(
|
items.push(
|
||||||
...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
|
...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
|
||||||
|
@ -59,6 +59,7 @@ export class MenuBuilder {
|
||||||
*/
|
*/
|
||||||
static addMarkdownItems(
|
static addMarkdownItems(
|
||||||
description: string,
|
description: string,
|
||||||
|
parent: GroupModel | undefined,
|
||||||
options: RedocNormalizedOptions,
|
options: RedocNormalizedOptions,
|
||||||
): ContentItemModel[] {
|
): ContentItemModel[] {
|
||||||
const renderer = new MarkdownRenderer(options);
|
const renderer = new MarkdownRenderer(options);
|
||||||
|
@ -82,7 +83,7 @@ export class MenuBuilder {
|
||||||
return group;
|
return group;
|
||||||
});
|
});
|
||||||
|
|
||||||
return mapHeadingsDeep(undefined, headings);
|
return mapHeadingsDeep(parent, headings, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,15 +145,22 @@ export class MenuBuilder {
|
||||||
}
|
}
|
||||||
const item = new GroupModel('tag', tag, parent);
|
const item = new GroupModel('tag', tag, parent);
|
||||||
item.depth = GROUP_DEPTH + 1;
|
item.depth = GROUP_DEPTH + 1;
|
||||||
item.items = this.getOperationsItems(parser, item, tag, item.depth + 1, options);
|
|
||||||
|
|
||||||
// don't put empty tag into content, instead put its operations
|
// don't put empty tag into content, instead put its operations
|
||||||
if (tag.name === '') {
|
if (tag.name === '') {
|
||||||
const items = this.getOperationsItems(parser, undefined, tag, item.depth + 1, options);
|
const items = [
|
||||||
|
...MenuBuilder.addMarkdownItems(tag.description || '', item, options),
|
||||||
|
...this.getOperationsItems(parser, undefined, tag, item.depth + 1, options),
|
||||||
|
];
|
||||||
res.push(...items);
|
res.push(...items);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.items = [
|
||||||
|
...MenuBuilder.addMarkdownItems(tag.description || '', item, options),
|
||||||
|
...this.getOperationsItems(parser, item, tag, item.depth + 1, options),
|
||||||
|
];
|
||||||
|
|
||||||
res.push(item);
|
res.push(item);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -40,7 +40,14 @@ export class GroupModel implements IMenuItem {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
||||||
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
||||||
|
|
||||||
|
// remove sections from markdown, same as in ApiInfo
|
||||||
this.description = tagOrGroup.description || '';
|
this.description = tagOrGroup.description || '';
|
||||||
|
const firstHeadingLinePos = this.description.search(/^##?\s+/m);
|
||||||
|
if (firstHeadingLinePos > -1) {
|
||||||
|
this.description = this.description.substring(0, firstHeadingLinePos);
|
||||||
|
}
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
|
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
|
||||||
|
|
||||||
|
|
|
@ -495,6 +495,7 @@ export function normalizeServers(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const OBJECT_DEFINTION_COMPONENT_NAME = 'object-description';
|
||||||
export const SECURITY_DEFINITIONS_COMPONENT_NAME = 'security-definitions';
|
export const SECURITY_DEFINITIONS_COMPONENT_NAME = 'security-definitions';
|
||||||
export let SECURITY_SCHEMES_SECTION_PREFIX = 'section/Authentication/';
|
export let SECURITY_SCHEMES_SECTION_PREFIX = 'section/Authentication/';
|
||||||
export function setSecuritySchemePrefix(prefix: string) {
|
export function setSecuritySchemePrefix(prefix: string) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user