Skip to content

Commit

Permalink
feat: externalDocumentation rendered for tags, operations and schema …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
m-mohr authored and RomanHotsiy committed Sep 10, 2018
1 parent 4b3b5ba commit 893c83e
Show file tree
Hide file tree
Showing 13 changed files with 71 additions and 19 deletions.
3 changes: 3 additions & 0 deletions demo/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,9 @@ components:
bee: '#/components/schemas/HoneyBee'
properties:
id:
externalDocs:
description: "Find more info here"
url: "https://example.com"
description: Pet ID
allOf:
- $ref: '#/components/schemas/Id'
Expand Down
9 changes: 2 additions & 7 deletions src/components/ApiInfo/ApiInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';
import { AppStore } from '../../services/AppStore';

import { MiddlePanel, Row, Section } from '../../common-elements/';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
import {
Expand Down Expand Up @@ -98,15 +99,9 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
</InfoSpanBoxWrap>
)) ||
null}

{(externalDocs && (
<p>
<a href={externalDocs.url}>{externalDocs.description || externalDocs.url}</a>
</p>
)) ||
null}
</StyledMarkdownBlock>
<Markdown source={store.spec.info.description} />
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
</MiddlePanel>
</Row>
</Section>
Expand Down
10 changes: 9 additions & 1 deletion src/components/ContentItems/ContentItems.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { observer } from 'mobx-react';
import * as React from 'react';

import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';

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

const Header = level === 2 ? H2 : H1;
return (
Expand All @@ -78,6 +79,13 @@ export class SectionItem extends React.Component<ContentItemProps> {
</MiddlePanel>
</Row>
<AdvancedMarkdown source={description || ''} htmlWrap={middlePanelWrap} />
{externalDocs && (
<Row>
<MiddlePanel>
<ExternalDocumentation externalDocs={externalDocs} />
</MiddlePanel>
</Row>
)}
</>
);
}
Expand Down
29 changes: 29 additions & 0 deletions src/components/ExternalDocumentation/ExternalDocumentation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import styled, { withProps } from '../../styled-components';
import { OpenAPIExternalDocumentation } from '../../types';
import { linksCss } from '../Markdown/styled.elements';

const LinkWrap = withProps<{ compact?: boolean }>(styled.div)`
${linksCss};
${({ compact }) => (!compact ? 'margin: 1em 0' : '')}
`;

@observer
export class ExternalDocumentation extends React.Component<{
externalDocs: OpenAPIExternalDocumentation;
compact?: boolean;
}> {
render() {
const { externalDocs } = this.props;
if (!externalDocs || !externalDocs.url) {
return null;
}

return (
<LinkWrap compact={this.props.compact}>
<a href={externalDocs.url}>{externalDocs.description || externalDocs.url}</a>
</LinkWrap>
);
}
}
6 changes: 5 additions & 1 deletion src/components/Fields/FieldDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
TypePrefix,
TypeTitle,
} from '../../common-elements/fields';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
import { EnumValues } from './EnumValues';
import { FieldProps } from './Field';
Expand Down Expand Up @@ -51,8 +52,11 @@ export class FieldDetails extends React.PureComponent<FieldProps> {
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
{showExamples && <FieldDetail label={'Example:'} value={example} />}
<div>
<Markdown dense={true} source={description} />
<Markdown compact={true} source={description} />
</div>
{schema.externalDocs && (
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
)}
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Markdown/AdvancedMarkdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
return parts.map((part, idx) => {
if (typeof part === 'string') {
return React.cloneElement(
htmlWrap(<SanitizedMarkdownHTML html={part} inline={false} dense={false} />),
htmlWrap(<SanitizedMarkdownHTML html={part} inline={false} compact={false} />),
{ key: idx },
);
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Markdown/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MarkdownRenderer } from '../../services';
import { SanitizedMarkdownHTML } from './SanitizedMdBlock';

export interface StylingMarkdownProps {
dense?: boolean;
compact?: boolean;
inline?: boolean;
}

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

export class Markdown extends React.Component<MarkdownProps> {
render() {
const { source, inline, dense, className } = this.props;
const { source, inline, compact, className } = this.props;
const renderer = new MarkdownRenderer();
return (
<SanitizedMarkdownHTML
html={renderer.renderMd(source)}
inline={inline}
dense={dense}
compact={compact}
className={className}
/>
);
Expand Down
14 changes: 11 additions & 3 deletions src/components/Operation/Operation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { OptionsContext } from '../OptionsProvider';

import { ShareLink } from '../../common-elements/linkify';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
import { Parameters } from '../Parameters/Parameters';
import { RequestSamples } from '../RequestSamples/RequestSamples';
Expand All @@ -25,7 +26,7 @@ const OperationRow = styled(Row)`
overflow: hidden;
`;

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

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

const { name: summary, description, deprecated } = operation;
const { name: summary, description, deprecated, externalDocs } = operation;
const hasDescription = !!(description || externalDocs);

return (
<OptionsContext.Consumer>
{options => (
Expand All @@ -49,7 +52,12 @@ export class Operation extends React.Component<OperationProps> {
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
</H2>
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
{description !== undefined && <Description source={description} />}
{hasDescription && (
<Description>
{description !== undefined && <Markdown source={description} />}
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
</Description>
)}
<SecurityRequirements securities={operation.security} />
<Parameters parameters={operation.parameters} body={operation.requestBody} />
<ResponsesList responses={operation.responses} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Responses/ResponseTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class ResponseTitle extends React.PureComponent<ResponseTitleProps> {
/>
)}
<strong>{code} </strong>
<Markdown dense={true} inline={true} source={title} />
<Markdown compact={true} inline={true} source={title} />
</div>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/Schema/Schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
name: '',
required: false,
description: schema.description,
externalDocs: schema.externalDocs,
deprecated: false,
toggle: () => null,
expanded: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"displayType": "number",
"enum": Array [],
"example": undefined,
"externalDocs": undefined,
"format": undefined,
"isCircular": undefined,
"isPrimitive": true,
Expand Down Expand Up @@ -72,6 +73,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"displayType": "string",
"enum": Array [],
"example": undefined,
"externalDocs": undefined,
"format": undefined,
"isCircular": undefined,
"isPrimitive": true,
Expand Down
4 changes: 3 additions & 1 deletion src/services/models/Schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { action, observable } from 'mobx';

import { OpenAPISchema, Referenced } from '../../types';
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';

import { OpenAPIParser } from '../OpenAPIParser';
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
Expand All @@ -25,6 +25,7 @@ export class SchemaModel {
typePrefix: string = '';
title: string;
description: string;
externalDocs?: OpenAPIExternalDocumentation;

isPrimitive: boolean;
isCircular: boolean = false;
Expand Down Expand Up @@ -101,6 +102,7 @@ export class SchemaModel {
this.example = schema.example;
this.deprecated = !!schema.deprecated;
this.pattern = schema.pattern;
this.externalDocs = schema.externalDocs;

this.constraints = humanizeConstraints(schema);
this.displayType = this.type;
Expand Down
2 changes: 1 addition & 1 deletion src/types/open-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export interface OpenAPITag {

export interface OpenAPIExternalDocumentation {
description?: string;
url?: string;
url: string;
}

export interface OpenAPIContact {
Expand Down

0 comments on commit 893c83e

Please sign in to comment.