diff --git a/src/components/ApiInfo/ApiDescription.tsx b/src/components/ApiInfo/ApiDescription.tsx new file mode 100644 index 00000000..7f5d2dc2 --- /dev/null +++ b/src/components/ApiInfo/ApiDescription.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; + +import { MiddlePanel, Row } from '../../common-elements/'; + +import { AppStore } from '../../services/AppStore'; +import { Markdown } from '../Markdown/Markdown'; +import { SecurityDefs } from '../SecuritySchemes/SecuritySchemes'; + +export interface ApiDescriptionProps { + store: AppStore; +} + +const ALLOWED_COMPONENTS = { + 'security-definitions': { + component: SecurityDefs, + propsSelector: _store => ({ + securitySchemes: _store!.spec.securitySchemes, + }), + }, +}; + +export class ApiDescription extends React.PureComponent { + render() { + const { store } = this.props; + const description = store.spec.info.description; + return ( + + + + + + ); + } +} diff --git a/src/components/ApiInfo/ApiInfo.tsx b/src/components/ApiInfo/ApiInfo.tsx index be9128fc..683dd528 100644 --- a/src/components/ApiInfo/ApiInfo.tsx +++ b/src/components/ApiInfo/ApiInfo.tsx @@ -93,22 +93,6 @@ export class ApiInfo extends React.Component {

)) || null} - -
- ({ - securitySchemes: _store!.spec.securitySchemes, - }), - }, - }} - store={store} - /> -
); diff --git a/src/components/ApiInfo/index.ts b/src/components/ApiInfo/index.ts new file mode 100644 index 00000000..c82a5768 --- /dev/null +++ b/src/components/ApiInfo/index.ts @@ -0,0 +1,2 @@ +export { ApiDescription } from './ApiDescription'; +export { ApiInfo } from './ApiInfo'; diff --git a/src/components/Markdown/Markdown.tsx b/src/components/Markdown/Markdown.tsx index a64db627..1b652b19 100644 --- a/src/components/Markdown/Markdown.tsx +++ b/src/components/Markdown/Markdown.tsx @@ -2,97 +2,107 @@ import * as React from 'react'; import styled, { ResolvedThemeInterface, StyledComponentClass } from '../../styled-components'; import * as DOMPurify from 'dompurify'; -import { AppStore, MarkdownRenderer } from '../../services'; +import { AppStore, MarkdownRenderer, MDXComponentMeta } from '../../services'; import { OptionsContext } from '../OptionsProvider'; -import { markdownCss } from './styles'; +import { StyledMarkdownBlock } from './styled.elements'; -export interface MDComponent { - component: React.ComponentClass; - propsSelector: (store?: AppStore) => any; - attrs?: object; -} +const StyledMarkdownSpan = StyledMarkdownBlock.withComponent('span'); -export interface MarkdownProps { - source: string; +export interface StylingMarkdownProps { dense?: boolean; inline?: boolean; - className?: string; +} + +export interface BaseMarkdownProps extends StylingMarkdownProps { raw?: boolean; - components?: Dict; sanitize?: boolean; store?: AppStore; } -class InternalMarkdown extends React.Component { +const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html); + +function SanitizedMarkdownHTML(props: StylingMarkdownProps & { html: string }) { + const Wrap = props.inline ? StyledMarkdownSpan : StyledMarkdownBlock; + + return ( + + {options => ( + + )} + + ); +} + +export interface MarkdownProps extends BaseMarkdownProps { + allowedComponents?: Dict; + source: string; +} + +export class Markdown extends React.Component { constructor(props: MarkdownProps) { super(props); - if (props.components && props.inline) { + if (props.allowedComponents && props.inline) { throw new Error('Markdown Component: "inline" mode doesn\'t support "components"'); } } render() { - const { source, raw, className, components, inline, dense, store } = this.props; + const { source, raw, allowedComponents, store, inline, dense } = this.props; - if (components && !store) { - throw new Error('When using components with Markdwon in ReDoc, store prop must be provided'); + if (allowedComponents && !store) { + throw new Error('When using componentes in markdown, store prop must be provided'); } - const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html); - const renderer = new MarkdownRenderer(); - const parts = components - ? renderer.renderMdWithComponents(source, components, raw) - : [renderer.renderMd(source, raw)]; + if (allowedComponents) { + return ( + + ); + } else { + return ( + + ); + } + } +} + +export interface AdvancedMarkdownProps extends BaseMarkdownProps { + parts: Array; +} + +export class AdvancedMarkdown extends React.Component { + render() { + const { raw, inline, dense, store, parts } = this.props; if (!parts.length) { return null; } - let appendClass = ' redoc-markdown'; - if (dense) { - appendClass += ' -dense'; - } - if (inline) { - appendClass += ' -inline'; - } - return ( - - {options => - inline ? ( - - ) : ( -
- {parts.map( - (part, idx) => - typeof part === 'string' ? ( -
- ) : ( - - ), - )} -
- ) - } - + <> + {parts.map( + (part, idx) => + typeof part === 'string' ? ( + + ) : ( + + ), + )} + ); } } - -export const Markdown = styled(InternalMarkdown)` - ${markdownCss}; -`; diff --git a/src/components/Markdown/styles.ts b/src/components/Markdown/styled.elements.ts similarity index 86% rename from src/components/Markdown/styles.ts rename to src/components/Markdown/styled.elements.ts index d86e0585..2e94b65d 100644 --- a/src/components/Markdown/styles.ts +++ b/src/components/Markdown/styled.elements.ts @@ -1,9 +1,15 @@ +import * as React from 'react'; import { InterpolationFunction, Styles, ThemeProps } from 'styled-components'; import { headerCommonMixin, linkifyMixin } from '../../common-elements'; -import { css, ResolvedThemeInterface, StyledComponentClass } from '../../styled-components'; +import styled, { + css, + ResolvedThemeInterface, + StyledComponentClass, + withProps, +} from '../../styled-components'; -export const markdownCss = css` +export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean }>(styled.div)` font-family: ${props => props.theme.baseFont.family}; font-weight: ${props => props.theme.baseFont.weight}; @@ -15,13 +21,17 @@ export const markdownCss = css` } } - &.-dense p { + ${({ dense }) => + dense && + ` p { margin: 0; - } + }`} - &.-inline p { + ${({ inline }) => + inline && + ` p { display: inline-block; - } + }`} h1 { ${headerCommonMixin(1)}; diff --git a/src/components/Redoc/Redoc.tsx b/src/components/Redoc/Redoc.tsx index c3622054..9fd74360 100644 --- a/src/components/Redoc/Redoc.tsx +++ b/src/components/Redoc/Redoc.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { ThemeProvider } from '../../styled-components'; import { AppStore } from '../../services'; -import { ApiInfo } from '../ApiInfo/ApiInfo'; +import { ApiDescription, ApiInfo } from '../ApiInfo/'; import { ApiLogo } from '../ApiLogo/ApiLogo'; import { ContentItems } from '../ContentItems/ContentItems'; import { OptionsProvider } from '../OptionsProvider'; @@ -52,6 +52,7 @@ export class Redoc extends React.Component { + diff --git a/src/components/ResponseSamples/ResponseSamples.tsx b/src/components/ResponseSamples/ResponseSamples.tsx index d8fda924..54f04baa 100644 --- a/src/components/ResponseSamples/ResponseSamples.tsx +++ b/src/components/ResponseSamples/ResponseSamples.tsx @@ -6,16 +6,6 @@ import { MediaContentModel, OperationModel } from '../../services/models'; import { Tab, TabList, TabPanel, Tabs } from '../../common-elements'; import { PayloadSamples } from '../PayloadSamples/PayloadSamples'; -export interface ResponseSampleProps { - content: MediaContentModel; -} - -class ResponseSample extends React.Component { - render() { - return ; - } -} - export interface ResponseSamplesProps { operation: OperationModel; } @@ -45,7 +35,7 @@ export class ResponseSamples extends React.Component { {responses.map(response => ( - + ; ))} diff --git a/src/components/Responses/Response.tsx b/src/components/Responses/Response.tsx index 2404dce5..7c46a659 100644 --- a/src/components/Responses/Response.tsx +++ b/src/components/Responses/Response.tsx @@ -2,14 +2,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ResponseModel } from '../../services/models'; - -import { UnderlinedHeader } from '../../common-elements'; -import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel'; -import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch'; -import { Schema } from '../Schema'; - -import { Markdown } from '../Markdown/Markdown'; -import { ResponseHeaders } from './ResponseHeaders'; +import { ResponseDetails } from './ResponseDetails'; import { ResponseDetailsWrap, StyledResponseTitle } from './styled.elements'; @observer @@ -38,24 +31,10 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> { {expanded && !empty && ( - {description && } - - - {({ schema }) => { - return ; - }} - + )}
); } - - private renderDropdown = props => { - return ( - - Response Schema: - - ); - }; } diff --git a/src/components/Responses/ResponseDetails.tsx b/src/components/Responses/ResponseDetails.tsx new file mode 100644 index 00000000..821fc2da --- /dev/null +++ b/src/components/Responses/ResponseDetails.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; + +import { ResponseModel } from '../../services/models'; + +import { UnderlinedHeader } from '../../common-elements'; +import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel'; +import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch'; +import { Schema } from '../Schema'; + +import { Markdown } from '../Markdown/Markdown'; +import { ResponseHeaders } from './ResponseHeaders'; + +export class ResponseDetails extends React.PureComponent<{ response: ResponseModel }> { + render() { + const { description, headers, content } = this.props.response; + return ( + <> + {description && } + + + {({ schema }) => { + return ; + }} + + + ); + } + + private renderDropdown = props => { + return ( + + Response Schema: + + ); + }; +} diff --git a/src/components/SecuritySchemes/SecuritySchemes.tsx b/src/components/SecuritySchemes/SecuritySchemes.tsx index 49b3db60..6641158d 100644 --- a/src/components/SecuritySchemes/SecuritySchemes.tsx +++ b/src/components/SecuritySchemes/SecuritySchemes.tsx @@ -6,6 +6,7 @@ import { H2, ShareLink } from '../../common-elements'; import styled from '../../styled-components'; import { OpenAPISecurityScheme } from '../../types'; import { Markdown } from '../Markdown/Markdown'; +import { StyledMarkdownBlock } from '../Markdown/styled.elements'; const AUTH_TYPES = { oauth2: 'OAuth2', @@ -89,47 +90,49 @@ export class SecurityDefs extends React.PureComponent { {scheme.id} - - - - Security scheme type: - {AUTH_TYPES[scheme.type] || scheme.type} - - {scheme.apiKey ? ( + + + - - + + - ) : scheme.http ? ( - [ - - - - , - scheme.http.scheme === 'bearer' && - scheme.http.bearerFormat && ( - - - - - ), - ] - ) : scheme.openId ? ( - - - - - ) : scheme.flows ? ( - Object.keys(scheme.flows).map(type => ( - - )) - ) : null} - - + {scheme.apiKey ? ( + + + + + ) : scheme.http ? ( + [ + + + + , + scheme.http.scheme === 'bearer' && + scheme.http.bearerFormat && ( + + + + + ), + ] + ) : scheme.openId ? ( + + + + + ) : scheme.flows ? ( + Object.keys(scheme.flows).map(type => ( + + )) + ) : null} + +
{scheme.apiKey.in} parameter name: {scheme.apiKey.name} Security scheme type: {AUTH_TYPES[scheme.type] || scheme.type}
HTTP Authorization Scheme {scheme.http.scheme}
Bearer format "{scheme.http.bearerFormat}"
Connect URL - - {scheme.openId.connectUrl} - -
{scheme.apiKey.in} parameter name: {scheme.apiKey.name}
HTTP Authorization Scheme {scheme.http.scheme}
Bearer format "{scheme.http.bearerFormat}"
Connect URL + + {scheme.openId.connectUrl} + +
+
))} diff --git a/src/components/index.ts b/src/components/index.ts index 09dac433..c380a474 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,6 +1,7 @@ export * from './RedocStandalone'; export * from './Redoc/Redoc'; export * from './ApiInfo/ApiInfo'; +export * from './ApiInfo/ApiDescription'; export * from './ApiLogo/ApiLogo'; export * from './ContentItems/ContentItems'; export { ApiContentWrap, BackgroundStub, RedocWrap } from './Redoc/styled.elements'; @@ -10,6 +11,18 @@ export * from './Operation/Operation'; export * from './Loading/Loading'; export * from './RedocStandalone'; export * from './JsonViewer'; +export * from './Markdown/Markdown'; +export { StyledMarkdownBlock } from './Markdown/styled.elements'; +export * from './SecuritySchemes/SecuritySchemes'; + +export * from './Responses/Response'; +export * from './Responses/ResponseDetails'; +export * from './Responses/ResponseHeaders'; +export * from './Responses/ResponsesList'; +export * from './Responses/ResponseTitle'; +export * from './ResponseSamples/ResponseSamples'; +export * from './PayloadSamples/PayloadSamples'; +export * from './MediaTypeSwitch/MediaTypesSwitch'; export * from './ErrorBoundary'; export * from './StoreProvider'; diff --git a/src/index.ts b/src/index.ts index 5dc04e42..98f6c473 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export * from './components'; +export { MiddlePanel, Row, RightPanel } from './common-elements/'; export * from './services'; export * from './utils'; diff --git a/yarn.lock b/yarn.lock index d1969f5c..1e39dae5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,7 +8,7 @@ dependencies: "@babel/highlight" "7.0.0-beta.47" -"@babel/code-frame@^7.0.0-beta.35": +"@babel/code-frame@^7.0.0-beta.35", "@babel/code-frame@^7.0.0-beta.39": version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.51.tgz#bd71d9b192af978df915829d39d4094456439a0c" dependencies: @@ -568,7 +568,7 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.0.1, ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: +ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0, ajv@^5.5.2: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -1512,6 +1512,16 @@ beautify-benchmark@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/beautify-benchmark/-/beautify-benchmark-0.2.4.tgz#3151def14c1a2e0d07ff2e476861c7ed0e1ae39b" +better-ajv-errors@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/better-ajv-errors/-/better-ajv-errors-0.5.1.tgz#59dcd5e84b582ba2b5ddbb960bcfcbf439b369af" + dependencies: + "@babel/code-frame" "^7.0.0-beta.39" + chalk "^2.3.1" + json-to-ast "^2.0.2" + jsonpointer "^4.0.1" + leven "^2.1.0" + big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" @@ -1913,7 +1923,7 @@ chalk@2.1.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -chalk@2.4.1, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1: +chalk@2.4.1, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -5473,6 +5483,10 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +json-to-ast@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/json-to-ast/-/json-to-ast-2.0.3.tgz#ef76a29207e7ab93cc058d862e6511a0e8e4792d" + json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -5501,6 +5515,10 @@ jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" +jsonpointer@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -6253,7 +6271,7 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.0.0: +node-fetch@^2.0.0, node-fetch@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" @@ -6436,6 +6454,45 @@ nwsapi@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.4.tgz#dc79040a5f77b97716dc79565fc7fc3ef7d50570" +oas-kit-common@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/oas-kit-common/-/oas-kit-common-1.0.3.tgz#f9957227bd001d346b42f4907a3df917c535f77c" + +oas-linter@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/oas-linter/-/oas-linter-1.0.3.tgz#a8bce67fe6b29c87a87b5cb39919cd660f4dbe77" + dependencies: + js-yaml "^3.11.0" + should "^13.2.1" + +oas-resolver@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/oas-resolver/-/oas-resolver-1.0.6.tgz#359e86ee8312effc1211f805891a9d8cc8c92f03" + dependencies: + js-yaml "^3.11.0" + node-fetch "^2.1.1" + oas-kit-common "^1.0.3" + reftools "^1.0.0" + yargs "^11.0.0" + +oas-schema-walker@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/oas-schema-walker/-/oas-schema-walker-1.0.3.tgz#f1ae73a671bef5fdcc71335d65e623fa4abb46ff" + +oas-validator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/oas-validator/-/oas-validator-1.1.3.tgz#49820f4aafd2bbea5d02f28bcea785de369af3b8" + dependencies: + ajv "^5.5.2" + better-ajv-errors "^0.5.1" + js-yaml "^3.11.0" + oas-kit-common "^1.0.3" + oas-linter "^1.0.3" + oas-resolver "^1.0.6" + oas-schema-walker "^1.0.3" + reftools "^1.0.0" + should "^13.2.1" + oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -7610,9 +7667,9 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" -reftools@0.0.20: - version "0.0.20" - resolved "https://registry.yarnpkg.com/reftools/-/reftools-0.0.20.tgz#011e00736e51c631149a3a22b4c05b7383bdee8c" +reftools@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.0.0.tgz#3a5f979205c21ed702eae9f6ddcb4d88b4379a4a" regenerate@^1.2.1: version "1.4.0" @@ -8127,7 +8184,7 @@ should-util@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063" -should@^13.0.1: +should@^13.2.1: version "13.2.1" resolved "https://registry.yarnpkg.com/should/-/should-13.2.1.tgz#84e6ebfbb145c79e0ae42307b25b3f62dcaf574e" dependencies: @@ -8623,18 +8680,19 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -swagger2openapi@^2.11.0: - version "2.11.16" - resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-2.11.16.tgz#7614ed2ebe0617656f8777bf67a33e8b0fba4e9b" +swagger2openapi@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-3.2.3.tgz#89976ad3d3fda6a00b45cc9f7e3917b597eaa736" dependencies: - ajv "^5.0.1" call-me-maybe "^1.0.1" - co "^4.6.0" js-yaml "^3.6.1" node-fetch "^2.0.0" node-readfiles "^0.2.0" - reftools "0.0.20" - should "^13.0.1" + oas-kit-common "^1.0.3" + oas-resolver "^1.0.6" + oas-schema-walker "^1.0.3" + oas-validator "^1.1.3" + reftools "^1.0.0" yargs "^11.0.0" symbol-observable@1.0.1: