Skip to content

Commit 893c83e

Browse files
m-mohrRomanHotsiy
authored andcommitted
feat: externalDocumentation rendered for tags, operations and schema fields (#595)
The externalDocs were missing for tags, schemata and operations. Added them with this pull requests. Solves #550. Additionally, fixes that the URL in External Documentation Object was specified to be optional, which is not correct according to OpenAPI spec.
1 parent 4b3b5ba commit 893c83e

File tree

13 files changed

+71
-19
lines changed

13 files changed

+71
-19
lines changed

demo/openapi.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,9 @@ components:
767767
bee: '#/components/schemas/HoneyBee'
768768
properties:
769769
id:
770+
externalDocs:
771+
description: "Find more info here"
772+
url: "https://example.com"
770773
description: Pet ID
771774
allOf:
772775
- $ref: '#/components/schemas/Id'

src/components/ApiInfo/ApiInfo.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as React from 'react';
44
import { AppStore } from '../../services/AppStore';
55

66
import { MiddlePanel, Row, Section } from '../../common-elements/';
7+
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
78
import { Markdown } from '../Markdown/Markdown';
89
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
910
import {
@@ -98,15 +99,9 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
9899
</InfoSpanBoxWrap>
99100
)) ||
100101
null}
101-
102-
{(externalDocs && (
103-
<p>
104-
<a href={externalDocs.url}>{externalDocs.description || externalDocs.url}</a>
105-
</p>
106-
)) ||
107-
null}
108102
</StyledMarkdownBlock>
109103
<Markdown source={store.spec.info.description} />
104+
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
110105
</MiddlePanel>
111106
</Row>
112107
</Section>

src/components/ContentItems/ContentItems.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { observer } from 'mobx-react';
22
import * as React from 'react';
33

4+
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
45
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
56

67
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
@@ -64,7 +65,7 @@ const middlePanelWrap = component => <MiddlePanel>{component}</MiddlePanel>;
6465
@observer
6566
export class SectionItem extends React.Component<ContentItemProps> {
6667
render() {
67-
const { name, description, level } = this.props.item as GroupModel;
68+
const { name, description, externalDocs, level } = this.props.item as GroupModel;
6869

6970
const Header = level === 2 ? H2 : H1;
7071
return (
@@ -78,6 +79,13 @@ export class SectionItem extends React.Component<ContentItemProps> {
7879
</MiddlePanel>
7980
</Row>
8081
<AdvancedMarkdown source={description || ''} htmlWrap={middlePanelWrap} />
82+
{externalDocs && (
83+
<Row>
84+
<MiddlePanel>
85+
<ExternalDocumentation externalDocs={externalDocs} />
86+
</MiddlePanel>
87+
</Row>
88+
)}
8189
</>
8290
);
8391
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { observer } from 'mobx-react';
2+
import * as React from 'react';
3+
import styled, { withProps } from '../../styled-components';
4+
import { OpenAPIExternalDocumentation } from '../../types';
5+
import { linksCss } from '../Markdown/styled.elements';
6+
7+
const LinkWrap = withProps<{ compact?: boolean }>(styled.div)`
8+
${linksCss};
9+
${({ compact }) => (!compact ? 'margin: 1em 0' : '')}
10+
`;
11+
12+
@observer
13+
export class ExternalDocumentation extends React.Component<{
14+
externalDocs: OpenAPIExternalDocumentation;
15+
compact?: boolean;
16+
}> {
17+
render() {
18+
const { externalDocs } = this.props;
19+
if (!externalDocs || !externalDocs.url) {
20+
return null;
21+
}
22+
23+
return (
24+
<LinkWrap compact={this.props.compact}>
25+
<a href={externalDocs.url}>{externalDocs.description || externalDocs.url}</a>
26+
</LinkWrap>
27+
);
28+
}
29+
}

src/components/Fields/FieldDetails.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
TypePrefix,
1010
TypeTitle,
1111
} from '../../common-elements/fields';
12+
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
1213
import { Markdown } from '../Markdown/Markdown';
1314
import { EnumValues } from './EnumValues';
1415
import { FieldProps } from './Field';
@@ -51,8 +52,11 @@ export class FieldDetails extends React.PureComponent<FieldProps> {
5152
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
5253
{showExamples && <FieldDetail label={'Example:'} value={example} />}
5354
<div>
54-
<Markdown dense={true} source={description} />
55+
<Markdown compact={true} source={description} />
5556
</div>
57+
{schema.externalDocs && (
58+
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
59+
)}
5660
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
5761
</div>
5862
);

src/components/Markdown/AdvancedMarkdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
3838
return parts.map((part, idx) => {
3939
if (typeof part === 'string') {
4040
return React.cloneElement(
41-
htmlWrap(<SanitizedMarkdownHTML html={part} inline={false} dense={false} />),
41+
htmlWrap(<SanitizedMarkdownHTML html={part} inline={false} compact={false} />),
4242
{ key: idx },
4343
);
4444
}

src/components/Markdown/Markdown.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { MarkdownRenderer } from '../../services';
44
import { SanitizedMarkdownHTML } from './SanitizedMdBlock';
55

66
export interface StylingMarkdownProps {
7-
dense?: boolean;
7+
compact?: boolean;
88
inline?: boolean;
99
}
1010

@@ -21,13 +21,13 @@ export type MarkdownProps = BaseMarkdownProps &
2121

2222
export class Markdown extends React.Component<MarkdownProps> {
2323
render() {
24-
const { source, inline, dense, className } = this.props;
24+
const { source, inline, compact, className } = this.props;
2525
const renderer = new MarkdownRenderer();
2626
return (
2727
<SanitizedMarkdownHTML
2828
html={renderer.renderMd(source)}
2929
inline={inline}
30-
dense={dense}
30+
compact={compact}
3131
className={className}
3232
/>
3333
);

src/components/Operation/Operation.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { OptionsContext } from '../OptionsProvider';
99

1010
import { ShareLink } from '../../common-elements/linkify';
1111
import { Endpoint } from '../Endpoint/Endpoint';
12+
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
1213
import { Markdown } from '../Markdown/Markdown';
1314
import { Parameters } from '../Parameters/Parameters';
1415
import { RequestSamples } from '../RequestSamples/RequestSamples';
@@ -25,7 +26,7 @@ const OperationRow = styled(Row)`
2526
overflow: hidden;
2627
`;
2728

28-
const Description = styled(Markdown)`
29+
const Description = styled.div`
2930
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
3031
`;
3132

@@ -38,7 +39,9 @@ export class Operation extends React.Component<OperationProps> {
3839
render() {
3940
const { operation } = this.props;
4041

41-
const { name: summary, description, deprecated } = operation;
42+
const { name: summary, description, deprecated, externalDocs } = operation;
43+
const hasDescription = !!(description || externalDocs);
44+
4245
return (
4346
<OptionsContext.Consumer>
4447
{options => (
@@ -49,7 +52,12 @@ export class Operation extends React.Component<OperationProps> {
4952
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
5053
</H2>
5154
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
52-
{description !== undefined && <Description source={description} />}
55+
{hasDescription && (
56+
<Description>
57+
{description !== undefined && <Markdown source={description} />}
58+
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
59+
</Description>
60+
)}
5361
<SecurityRequirements securities={operation.security} />
5462
<Parameters parameters={operation.parameters} body={operation.requestBody} />
5563
<ResponsesList responses={operation.responses} />

src/components/Responses/ResponseTitle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class ResponseTitle extends React.PureComponent<ResponseTitleProps> {
2727
/>
2828
)}
2929
<strong>{code} </strong>
30-
<Markdown dense={true} inline={true} source={title} />
30+
<Markdown compact={true} inline={true} source={title} />
3131
</div>
3232
);
3333
}

src/components/Schema/Schema.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
7575
name: '',
7676
required: false,
7777
description: schema.description,
78+
externalDocs: schema.externalDocs,
7879
deprecated: false,
7980
toggle: () => null,
8081
expanded: false,

0 commit comments

Comments
 (0)