diff --git a/package.json b/package.json index 9c757875..dffef0e0 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,8 @@ "prettier-eslint": "^8.8.1", "puppeteer": "^1.2.0", "raf": "^3.4.0", - "react": "^16.2.0", - "react-dom": "^16.2.0", + "react": "^16.3.0-alpha.2", + "react-dom": "^16.3.0-alpha.2", "rimraf": "^2.6.2", "shelljs": "^0.8.1", "source-map-loader": "^0.2.1", @@ -158,8 +158,14 @@ "collectCoverageFrom": [ "src/**/*.{ts,tsx}" ], - "coverageReporters": ["json", "lcov", "text-summary"], - "coveragePathIgnorePatterns": ["\\.d\\.ts$"] + "coverageReporters": [ + "json", + "lcov", + "text-summary" + ], + "coveragePathIgnorePatterns": [ + "\\.d\\.ts$" + ] }, "prettier": { "singleQuote": true, diff --git a/src/components/Endpoint/Endpoint.tsx b/src/components/Endpoint/Endpoint.tsx index 01d455e3..734fa32b 100644 --- a/src/components/Endpoint/Endpoint.tsx +++ b/src/components/Endpoint/Endpoint.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ShelfIcon } from '../../common-elements'; import { OperationModel } from '../../services'; -import { ComponentWithOptions } from '../OptionsProvider'; +import { OptionsContext } from '../OptionsProvider'; import { SelectOnClick } from '../SelectOnClick/SelectOnClick'; import { @@ -25,7 +25,7 @@ export interface EndpointState { expanded: boolean; } -export class Endpoint extends ComponentWithOptions { +export class Endpoint extends React.Component { constructor(props) { super(props); this.state = { @@ -38,39 +38,41 @@ export class Endpoint extends ComponentWithOptions }; render() { - const { operation, inverted } = this.props; + const { operation, inverted, hideHostname } = this.props; const { expanded } = this.state; - const hideHostname = this.props.hideHostname || this.options.hideHostname; - // TODO: highlight server variables, e.g. https://{user}.test.com return ( - - - {operation.httpVerb}{' '} - {operation.path} - - - - {operation.servers.map(server => ( - -
{server.description}
- - - {!hideHostname && {server.url}} - {operation.path} - - -
- ))} -
-
+ + {options => ( + + + {operation.httpVerb}{' '} + {operation.path} + + + + {operation.servers.map(server => ( + +
{server.description}
+ + + {!(hideHostname || options.hideHostname) && {server.url}} + {operation.path} + + +
+ ))} +
+
+ )} +
); } } diff --git a/src/components/Markdown/Markdown.tsx b/src/components/Markdown/Markdown.tsx index c53454e5..1cc58e67 100644 --- a/src/components/Markdown/Markdown.tsx +++ b/src/components/Markdown/Markdown.tsx @@ -3,7 +3,7 @@ import styled from '../../styled-components'; import * as DOMPurify from 'dompurify'; import { AppStore, MarkdownRenderer } from '../../services'; -import { ComponentWithOptions } from '../OptionsProvider'; +import { OptionsContext } from '../OptionsProvider'; import { markdownCss } from './styles'; @@ -24,7 +24,7 @@ export interface MarkdownProps { store?: AppStore; } -class InternalMarkdown extends ComponentWithOptions { +class InternalMarkdown extends React.Component { constructor(props: MarkdownProps) { super(props); @@ -40,10 +40,7 @@ class InternalMarkdown extends ComponentWithOptions { throw new Error('When using components with Markdwon in ReDoc, store prop must be provided'); } - const sanitize = - this.props.sanitize || this.options.untrustedSpec - ? (html: string) => DOMPurify.sanitize(html) - : (html: string) => html; + const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html); const renderer = new MarkdownRenderer(); const parts = components @@ -62,26 +59,36 @@ class InternalMarkdown extends ComponentWithOptions { appendClass += ' -inline'; } - if (inline) { - return ( - - ); - } - return ( -
- {parts.map( - (part, idx) => - typeof part === 'string' ? ( -
- ) : ( - - ), - )} -
+ + {options => + inline ? ( + + ) : ( +
+ {parts.map( + (part, idx) => + typeof part === 'string' ? ( +
+ ) : ( + + ), + )} +
+ ) + } + ); } } diff --git a/src/components/Operation/Operation.tsx b/src/components/Operation/Operation.tsx index 818b9034..7deef2ee 100644 --- a/src/components/Operation/Operation.tsx +++ b/src/components/Operation/Operation.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements'; -import { ComponentWithOptions } from '../OptionsProvider'; +import { OptionsContext } from '../OptionsProvider'; import { ShareLink } from '../../common-elements/linkify'; import { Endpoint } from '../Endpoint/Endpoint'; @@ -40,31 +40,34 @@ interface OperationProps { } @observer -export class Operation extends ComponentWithOptions { +export class Operation extends React.Component { render() { const { operation } = this.props; const { name: summary, description, deprecated } = operation; - const pathInMiddle = this.options.pathInMiddlePanel; return ( - - -

- - {summary} {deprecated && Deprecated } -

- {pathInMiddle && } - {description !== undefined && } - - - -
- - {!pathInMiddle && } - - - -
+ + {options => ( + + +

+ + {summary} {deprecated && Deprecated } +

+ {options.pathInMiddlePanel && } + {description !== undefined && } + + + +
+ + {!options.pathInMiddlePanel && } + + + +
+ )} +
); } } diff --git a/src/components/OptionsProvider.ts b/src/components/OptionsProvider.ts index 41b4e4ad..abc5b892 100644 --- a/src/components/OptionsProvider.ts +++ b/src/components/OptionsProvider.ts @@ -3,32 +3,19 @@ import * as React from 'react'; import { RedocNormalizedOptions } from '../services/RedocNormalizedOptions'; -export interface OptionsProviderProps { - options: RedocNormalizedOptions; +// TODO: contribute declarations to @types/react once 16.3 is released +type ReactProviderComponent = React.ComponentType<{ value: T }>; +type ReactConsumerComponent = React.ComponentType<{ children: ((value: T) => React.ReactNode) }>; + +interface ReactContext { + Provider: ReactProviderComponent; + Consumer: ReactConsumerComponent; } -export class OptionsProvider extends React.Component { - static childContextTypes = { - redocOptions: PropTypes.object.isRequired, - }; - - getChildContext() { - return { - redocOptions: this.props.options, - }; - } - - render() { - return React.Children.only(this.props.children); - } +declare module 'react' { + function createContext(defatulValue: T): ReactContext; } -export class ComponentWithOptions

extends React.Component { - static contextTypes = { - redocOptions: PropTypes.object, - }; - - get options(): RedocNormalizedOptions { - return this.context.redocOptions || {}; - } -} +export const OptionsContext = React.createContext(new RedocNormalizedOptions({})); +export const OptionsProvider = OptionsContext.Provider; +export const OptionsConsumer = OptionsContext.Consumer; diff --git a/src/components/Redoc/Redoc.tsx b/src/components/Redoc/Redoc.tsx index c5feae26..b63642df 100644 --- a/src/components/Redoc/Redoc.tsx +++ b/src/components/Redoc/Redoc.tsx @@ -36,7 +36,7 @@ export class Redoc extends React.Component { const store = this.props.store; return ( - + diff --git a/src/components/SideMenu/SideMenu.tsx b/src/components/SideMenu/SideMenu.tsx index d4800c4a..1957484e 100644 --- a/src/components/SideMenu/SideMenu.tsx +++ b/src/components/SideMenu/SideMenu.tsx @@ -1,6 +1,6 @@ import { observer } from 'mobx-react'; import * as React from 'react'; -import { ComponentWithOptions } from '../OptionsProvider'; +import { OptionsContext } from '../OptionsProvider'; import { IMenuItem, MenuStore } from '../../services/MenuStore'; import { MenuItems } from './MenuItems'; @@ -8,25 +8,30 @@ import { MenuItems } from './MenuItems'; import { PerfectScrollbar } from '../../common-elements/perfect-scrollbar'; @observer -export class SideMenu extends ComponentWithOptions<{ menu: MenuStore }> { +export class SideMenu extends React.Component<{ menu: MenuStore }> { private _updateScroll?: () => void; render() { const store = this.props.menu; - const nativeScrollbars = this.options.nativeScrollbars; - return nativeScrollbars ? ( - - ) : ( - - - + return ( + + {options => + options.nativeScrollbars ? ( + + ) : ( + + + + ) + } + ); } diff --git a/src/components/StickySidebar/StickyResponsiveSidebar.tsx b/src/components/StickySidebar/StickyResponsiveSidebar.tsx index fca03115..1d4a037a 100644 --- a/src/components/StickySidebar/StickyResponsiveSidebar.tsx +++ b/src/components/StickySidebar/StickyResponsiveSidebar.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { MenuStore } from '../../services/MenuStore'; import { RedocNormalizedOptions, RedocRawOptions } from '../../services/RedocNormalizedOptions'; import styled, { media, withProps } from '../../styled-components'; -import { ComponentWithOptions } from '../OptionsProvider'; +import { OptionsContext } from '../OptionsProvider'; import { AnimatedChevronButton } from './ChevronSvg'; let Stickyfill; @@ -67,7 +67,7 @@ const FloatingButton = styled.div` `; @observer -export class StickyResponsiveSidebar extends ComponentWithOptions { +export class StickyResponsiveSidebar extends React.Component { stickyElement: Element; componentDidMount() { @@ -82,39 +82,42 @@ export class StickyResponsiveSidebar extends ComponentWithOptions - { - this.stickyElement = el; - }} - > - {this.props.children} - - - - - + + {options => ( + <> + { + this.stickyElement = el; + }} + > + {this.props.children} + + + + + + )} + ); } diff --git a/yarn.lock b/yarn.lock index ad8d5e41..ded9d5ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7547,9 +7547,9 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044" +react-dom@^16.3.0-alpha.2: + version "16.3.0-alpha.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.0-alpha.2.tgz#a970b6185684941e89a568c09321d22643457cb6" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -7596,9 +7596,9 @@ react-test-renderer@^16.0.0-0: object-assign "^4.1.1" prop-types "^15.6.0" -react@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" +react@^16.3.0-alpha.2: + version "16.3.0-alpha.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.3.0-alpha.2.tgz#91e2b82bb985b23e7b6555f810e1fd94894afce2" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0"