From e5ed542fed08750d9346b355b156962f352febe9 Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Mon, 6 Aug 2018 17:20:24 +0200 Subject: [PATCH 1/5] External Documentation rendered for groups, operations and schemata. URL for External Documentation is required. --- src/components/ApiInfo/ApiInfo.tsx | 13 +++++---- src/components/ContentItems/ContentItems.tsx | 8 +++++- .../ExternalDocumentation.tsx | 27 +++++++++++++++++++ src/components/Fields/FieldDetails.tsx | 6 +++++ src/components/Operation/Operation.tsx | 8 +++++- src/components/Schema/Schema.tsx | 1 + .../DiscriminatorDropdown.test.tsx.snap | 2 ++ src/services/models/Schema.ts | 4 ++- src/types/open-api.d.ts | 2 +- 9 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 src/components/ExternalDocumentation/ExternalDocumentation.tsx diff --git a/src/components/ApiInfo/ApiInfo.tsx b/src/components/ApiInfo/ApiInfo.tsx index e3df998b51..9728410dee 100644 --- a/src/components/ApiInfo/ApiInfo.tsx +++ b/src/components/ApiInfo/ApiInfo.tsx @@ -5,6 +5,7 @@ import { AppStore } from '../../services/AppStore'; import { MiddlePanel, Row } from '../../common-elements/'; +import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; import { StyledMarkdownBlock } from '../Markdown/styled.elements'; import { ApiHeader, @@ -97,14 +98,12 @@ export class ApiInfo extends React.Component { )) || null} - - {(externalDocs && ( -

- {externalDocs.description || externalDocs.url} -

- )) || - null} + {externalDocs && ( +

+ +

+ )} ); diff --git a/src/components/ContentItems/ContentItems.tsx b/src/components/ContentItems/ContentItems.tsx index f3ecc1e1ce..028398b563 100644 --- a/src/components/ContentItems/ContentItems.tsx +++ b/src/components/ContentItems/ContentItems.tsx @@ -2,6 +2,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { SECTION_ATTR } from '../../services/MenuStore'; +import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; import { Markdown } from '../Markdown/Markdown'; import { H1, MiddlePanel, Row, ShareLink } from '../../common-elements'; @@ -79,7 +80,7 @@ export class ContentItem extends React.Component { @observer export class SectionItem extends React.Component { render() { - const { name, description } = this.props.item; + const { name, description, externalDocs } = this.props.item; const components = this.props.allowedMdComponents; return ( @@ -97,6 +98,11 @@ export class SectionItem extends React.Component { )} )} + {externalDocs && ( +

+ +

+ )}
); diff --git a/src/components/ExternalDocumentation/ExternalDocumentation.tsx b/src/components/ExternalDocumentation/ExternalDocumentation.tsx new file mode 100644 index 0000000000..d46bda55e9 --- /dev/null +++ b/src/components/ExternalDocumentation/ExternalDocumentation.tsx @@ -0,0 +1,27 @@ +import { observer } from 'mobx-react'; +import * as React from 'react'; +import styled from '../../styled-components'; +import { OpenAPIExternalDocumentation } from '../../types'; +import { linksCss } from '../Markdown/styled.elements'; + +const Link = styled.span` + ${linksCss}; +`; + +@observer +export class ExternalDocumentation extends React.Component<{ + externalDocs: OpenAPIExternalDocumentation; +}> { + render() { + const { externalDocs } = this.props; + if (!externalDocs || !externalDocs.url) { + return null; + } + + return ( + + {externalDocs.description || externalDocs.url} + + ); + } +} diff --git a/src/components/Fields/FieldDetails.tsx b/src/components/Fields/FieldDetails.tsx index 87aebdac44..d09194dff8 100644 --- a/src/components/Fields/FieldDetails.tsx +++ b/src/components/Fields/FieldDetails.tsx @@ -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'; @@ -46,6 +47,11 @@ export class FieldDetails extends React.PureComponent {
+ {schema.externalDocs && ( +
+ +
+ )} {(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null} ); diff --git a/src/components/Operation/Operation.tsx b/src/components/Operation/Operation.tsx index 6c9a9a7228..2df4508761 100644 --- a/src/components/Operation/Operation.tsx +++ b/src/components/Operation/Operation.tsx @@ -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'; @@ -43,7 +44,7 @@ export class Operation extends React.Component { render() { const { operation } = this.props; - const { name: summary, description, deprecated } = operation; + const { name: summary, description, deprecated, externalDocs } = operation; return ( {options => ( @@ -55,6 +56,11 @@ export class Operation extends React.Component { {options.pathInMiddlePanel && } {description !== undefined && } + {externalDocs && ( +

+ +

+ )} diff --git a/src/components/Schema/Schema.tsx b/src/components/Schema/Schema.tsx index d875071029..22e1eabc03 100644 --- a/src/components/Schema/Schema.tsx +++ b/src/components/Schema/Schema.tsx @@ -75,6 +75,7 @@ export class Schema extends React.Component> { name: '', required: false, description: schema.description, + externalDocs: schema.externalDocs, deprecated: false, toggle: () => null, expanded: false, diff --git a/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap b/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap index ab1a367c03..5aa3ccfec8 100644 --- a/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap @@ -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, @@ -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, diff --git a/src/services/models/Schema.ts b/src/services/models/Schema.ts index f8a196ab67..0a5a9319e3 100644 --- a/src/services/models/Schema.ts +++ b/src/services/models/Schema.ts @@ -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'; @@ -25,6 +25,7 @@ export class SchemaModel { typePrefix: string = ''; title: string; description: string; + externalDocs?: OpenAPIExternalDocumentation; isPrimitive: boolean; isCircular: boolean = false; @@ -100,6 +101,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; diff --git a/src/types/open-api.d.ts b/src/types/open-api.d.ts index 2d6287363f..4fb04a83d1 100644 --- a/src/types/open-api.d.ts +++ b/src/types/open-api.d.ts @@ -255,7 +255,7 @@ export interface OpenAPITag { export interface OpenAPIExternalDocumentation { description?: string; - url?: string; + url: string; } export interface OpenAPIContact { From 0dee3a8907e31ddac497ca7e83df52f46378bea1 Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Wed, 29 Aug 2018 16:52:23 +0200 Subject: [PATCH 2/5] Fixed issues that arose from latest changes / merge --- src/components/ApiInfo/ApiInfo.tsx | 2 +- src/components/ContentItems/ContentItems.tsx | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/ApiInfo/ApiInfo.tsx b/src/components/ApiInfo/ApiInfo.tsx index b820612761..019a1cec49 100644 --- a/src/components/ApiInfo/ApiInfo.tsx +++ b/src/components/ApiInfo/ApiInfo.tsx @@ -3,8 +3,8 @@ import * as React from 'react'; import { AppStore } from '../../services/AppStore'; -import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; import { MiddlePanel, Row, Section } from '../../common-elements/'; +import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; import { Markdown } from '../Markdown/Markdown'; import { StyledMarkdownBlock } from '../Markdown/styled.elements'; import { diff --git a/src/components/ContentItems/ContentItems.tsx b/src/components/ContentItems/ContentItems.tsx index b05c5a493b..293d7e6ae9 100644 --- a/src/components/ContentItems/ContentItems.tsx +++ b/src/components/ContentItems/ContentItems.tsx @@ -1,8 +1,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; -import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown'; import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; +import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown'; import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements'; import { ContentItemModel } from '../../services/MenuBuilder'; @@ -80,9 +80,13 @@ export class SectionItem extends React.Component { {externalDocs && ( -

- -

+ + +

+ +

+
+
)} ); From b90a5a000c725e5fb5c666db135357ec532a15e8 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Mon, 10 Sep 2018 18:07:43 +0300 Subject: [PATCH 3/5] chore: clean up --- src/components/ApiInfo/ApiInfo.tsx | 6 +----- src/components/ContentItems/ContentItems.tsx | 4 +--- .../ExternalDocumentation.tsx | 10 ++++++---- src/components/Fields/FieldDetails.tsx | 4 +--- src/components/Operation/Operation.tsx | 14 ++++++++------ 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/components/ApiInfo/ApiInfo.tsx b/src/components/ApiInfo/ApiInfo.tsx index 019a1cec49..e1fcaaea3f 100644 --- a/src/components/ApiInfo/ApiInfo.tsx +++ b/src/components/ApiInfo/ApiInfo.tsx @@ -101,11 +101,7 @@ export class ApiInfo extends React.Component { null} - {externalDocs && ( -

- -

- )} + {externalDocs && } diff --git a/src/components/ContentItems/ContentItems.tsx b/src/components/ContentItems/ContentItems.tsx index 293d7e6ae9..02efc38975 100644 --- a/src/components/ContentItems/ContentItems.tsx +++ b/src/components/ContentItems/ContentItems.tsx @@ -82,9 +82,7 @@ export class SectionItem extends React.Component { {externalDocs && ( -

- -

+
)} diff --git a/src/components/ExternalDocumentation/ExternalDocumentation.tsx b/src/components/ExternalDocumentation/ExternalDocumentation.tsx index d46bda55e9..eb76b41452 100644 --- a/src/components/ExternalDocumentation/ExternalDocumentation.tsx +++ b/src/components/ExternalDocumentation/ExternalDocumentation.tsx @@ -1,16 +1,18 @@ import { observer } from 'mobx-react'; import * as React from 'react'; -import styled from '../../styled-components'; +import styled, { withProps } from '../../styled-components'; import { OpenAPIExternalDocumentation } from '../../types'; import { linksCss } from '../Markdown/styled.elements'; -const Link = styled.span` +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; @@ -19,9 +21,9 @@ export class ExternalDocumentation extends React.Component<{ } return ( - + {externalDocs.description || externalDocs.url} - + ); } } diff --git a/src/components/Fields/FieldDetails.tsx b/src/components/Fields/FieldDetails.tsx index 246f3ae180..2651d54bfa 100644 --- a/src/components/Fields/FieldDetails.tsx +++ b/src/components/Fields/FieldDetails.tsx @@ -55,9 +55,7 @@ export class FieldDetails extends React.PureComponent { {schema.externalDocs && ( -
- -
+ )} {(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null} diff --git a/src/components/Operation/Operation.tsx b/src/components/Operation/Operation.tsx index da647bde2d..320a6621d6 100644 --- a/src/components/Operation/Operation.tsx +++ b/src/components/Operation/Operation.tsx @@ -26,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; `; @@ -40,6 +40,8 @@ export class Operation extends React.Component { const { operation } = this.props; const { name: summary, description, deprecated, externalDocs } = operation; + const hasDescription = !!(description || externalDocs); + return ( {options => ( @@ -50,11 +52,11 @@ export class Operation extends React.Component { {summary} {deprecated && Deprecated } {options.pathInMiddlePanel && } - {description !== undefined && } - {externalDocs && ( -

- -

+ {hasDescription && ( + + {description !== undefined && } + {externalDocs && } + )} From cd5d213a7f79920f077a9c9778680c45126ed3b9 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Mon, 10 Sep 2018 18:08:17 +0300 Subject: [PATCH 4/5] chore: add externalDocs in the field in the demo spec --- demo/openapi.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/demo/openapi.yaml b/demo/openapi.yaml index 94ca4d11b5..1a06f4f6d6 100644 --- a/demo/openapi.yaml +++ b/demo/openapi.yaml @@ -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' From c5540a9b4d04f22983669c2ef8f42bf9f6bc06ce Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Mon, 10 Sep 2018 18:08:55 +0300 Subject: [PATCH 5/5] chore: rename dense to compact --- src/components/Fields/FieldDetails.tsx | 2 +- src/components/Markdown/AdvancedMarkdown.tsx | 2 +- src/components/Markdown/Markdown.tsx | 6 +++--- src/components/Responses/ResponseTitle.tsx | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Fields/FieldDetails.tsx b/src/components/Fields/FieldDetails.tsx index 2651d54bfa..838351ec30 100644 --- a/src/components/Fields/FieldDetails.tsx +++ b/src/components/Fields/FieldDetails.tsx @@ -52,7 +52,7 @@ export class FieldDetails extends React.PureComponent { {!renderDiscriminatorSwitch && }{' '} {showExamples && }
- +
{schema.externalDocs && ( diff --git a/src/components/Markdown/AdvancedMarkdown.tsx b/src/components/Markdown/AdvancedMarkdown.tsx index b75631d3c6..0b1bbed778 100644 --- a/src/components/Markdown/AdvancedMarkdown.tsx +++ b/src/components/Markdown/AdvancedMarkdown.tsx @@ -38,7 +38,7 @@ export class AdvancedMarkdown extends React.Component { return parts.map((part, idx) => { if (typeof part === 'string') { return React.cloneElement( - htmlWrap(), + htmlWrap(), { key: idx }, ); } diff --git a/src/components/Markdown/Markdown.tsx b/src/components/Markdown/Markdown.tsx index 0259b04ec4..544a2bc8c8 100644 --- a/src/components/Markdown/Markdown.tsx +++ b/src/components/Markdown/Markdown.tsx @@ -4,7 +4,7 @@ import { MarkdownRenderer } from '../../services'; import { SanitizedMarkdownHTML } from './SanitizedMdBlock'; export interface StylingMarkdownProps { - dense?: boolean; + compact?: boolean; inline?: boolean; } @@ -21,13 +21,13 @@ export type MarkdownProps = BaseMarkdownProps & export class Markdown extends React.Component { render() { - const { source, inline, dense, className } = this.props; + const { source, inline, compact, className } = this.props; const renderer = new MarkdownRenderer(); return ( ); diff --git a/src/components/Responses/ResponseTitle.tsx b/src/components/Responses/ResponseTitle.tsx index 00169340f9..be66fdc86d 100644 --- a/src/components/Responses/ResponseTitle.tsx +++ b/src/components/Responses/ResponseTitle.tsx @@ -27,7 +27,7 @@ export class ResponseTitle extends React.PureComponent { /> )} {code} - + ); }