Merge pull request #3597 from owenconti/ft/3584-operation-container-component

OperationContainer component introduction
This commit is contained in:
kyle
2017-09-20 14:57:07 -07:00
committed by GitHub
9 changed files with 285 additions and 153 deletions

View File

@@ -8,7 +8,6 @@ export default class Execute extends Component {
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
operation: PropTypes.object.isRequired, operation: PropTypes.object.isRequired,
path: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
method: PropTypes.string.isRequired, method: PropTypes.string.isRequired,
onExecute: PropTypes.func onExecute: PropTypes.func
} }

View File

@@ -1,29 +1,19 @@
import React, { PureComponent } from "react" import React, { PureComponent } from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { getList } from "core/utils" import { getList } from "core/utils"
import * as CustomPropTypes from "core/proptypes" import { Iterable } from "immutable"
//import "less/opblock"
export default class Operation extends PureComponent { export default class Operation extends PureComponent {
static propTypes = { static propTypes = {
path: PropTypes.string.isRequired, operation: PropTypes.instanceOf(Iterable).isRequired,
method: PropTypes.string.isRequired,
operation: PropTypes.object.isRequired,
showSummary: PropTypes.bool,
isShownKey: CustomPropTypes.arrayOrString.isRequired, toggleShown: PropTypes.func.isRequired,
jumpToKey: CustomPropTypes.arrayOrString.isRequired, onTryoutClick: PropTypes.func.isRequired,
onCancelClick: PropTypes.func.isRequired,
allowTryItOut: PropTypes.bool, onExecute: PropTypes.func.isRequired,
displayOperationId: PropTypes.bool,
displayRequestDuration: PropTypes.bool,
response: PropTypes.object,
request: PropTypes.object,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
authActions: PropTypes.object, authActions: PropTypes.object,
authSelectors: PropTypes.object, authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
@@ -31,8 +21,7 @@ export default class Operation extends PureComponent {
oas3Actions: PropTypes.object.isRequired, oas3Actions: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired, fn: PropTypes.object.isRequired
getConfigs: PropTypes.func.isRequired
} }
static defaultProps = { static defaultProps = {
@@ -43,95 +32,59 @@ export default class Operation extends PureComponent {
displayRequestDuration: false displayRequestDuration: false
} }
constructor(props, context) { shouldComponentUpdate(nextProps) {
super(props, context) return this.props.operation !== nextProps.operation
this.state = {
tryItOutEnabled: false
}
}
componentWillReceiveProps(nextProps) {
const defaultContentType = "application/json"
let { specActions, path, method, operation } = nextProps
let producesValue = operation.get("produces_value")
let produces = operation.get("produces")
let consumes = operation.get("consumes")
let consumesValue = operation.get("consumes_value")
if(nextProps.response !== this.props.response) {
this.setState({ executeInProgress: false })
}
if (producesValue === undefined) {
producesValue = produces && produces.size ? produces.first() : defaultContentType
specActions.changeProducesValue([path, method], producesValue)
}
if (consumesValue === undefined) {
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
specActions.changeConsumesValue([path, method], consumesValue)
}
}
toggleShown =() => {
let { layoutActions, isShownKey } = this.props
layoutActions.show(isShownKey, !this.isShown())
}
isShown =() => {
let { layoutSelectors, isShownKey, getConfigs } = this.props
let { docExpansion } = getConfigs()
return layoutSelectors.isShown(isShownKey, docExpansion === "full" ) // Here is where we set the default
}
onTryoutClick =() => {
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
}
onCancelClick =() => {
let { specActions, path, method } = this.props
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
specActions.clearValidateParams([path, method])
}
onExecute = () => {
this.setState({ executeInProgress: true })
} }
render() { render() {
let { let {
isShownKey, toggleShown,
jumpToKey, onTryoutClick,
path, onCancelClick,
method, onExecute,
operation,
showSummary,
response,
request,
allowTryItOut,
displayOperationId,
displayRequestDuration,
fn, fn,
getComponent, getComponent,
getConfigs,
specActions, specActions,
specSelectors, specSelectors,
authActions, authActions,
authSelectors, authSelectors,
getConfigs,
oas3Actions oas3Actions
} = this.props } = this.props
let operationProps = this.props.operation
let summary = operation.get("summary") let {
let description = operation.get("description") isShown,
let deprecated = operation.get("deprecated") isShownKey,
let externalDocs = operation.get("externalDocs") jumpToKey,
path,
method,
op,
showSummary,
operationId,
allowTryItOut,
displayOperationId,
displayRequestDuration,
isDeepLinkingEnabled,
tryItOutEnabled,
executeInProgress
} = operationProps.toJS()
let response = operationProps.get("response")
let request = operationProps.get("request")
let {
summary,
description,
deprecated,
externalDocs,
schemes
} = op.operation
let operation = operationProps.getIn(["op", "operation"])
let responses = operation.get("responses") let responses = operation.get("responses")
let security = operation.get("security") || specSelectors.security()
let produces = operation.get("produces") let produces = operation.get("produces")
let schemes = operation.get("schemes") let security = operation.get("security") || specSelectors.security()
let parameters = getList(operation, ["parameters"]) let parameters = getList(operation, ["parameters"])
let operationId = operation.get("__originalOperationId")
let operationScheme = specSelectors.operationScheme(path, method) let operationScheme = specSelectors.operationScheme(path, method)
const Responses = getComponent("responses") const Responses = getComponent("responses")
@@ -144,23 +97,17 @@ export default class Operation extends PureComponent {
const Markdown = getComponent( "Markdown" ) const Markdown = getComponent( "Markdown" )
const Schemes = getComponent( "schemes" ) const Schemes = getComponent( "schemes" )
const { deepLinking } = getConfigs()
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
// Merge in Live Response // Merge in Live Response
if(response && response.size > 0) { if(responses && response && response.size > 0) {
let notDocumented = !responses.get(String(response.get("status"))) let notDocumented = !responses.get(String(response.get("status")))
response = response.set("notDocumented", notDocumented) response = response.set("notDocumented", notDocumented)
} }
let { tryItOutEnabled } = this.state
let shown = this.isShown()
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method ) let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
return ( return (
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} > <div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} > <div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
<span className="opblock-summary-method">{method.toUpperCase()}</span> <span className="opblock-summary-method">{method.toUpperCase()}</span>
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } > <span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<a <a
@@ -188,7 +135,7 @@ export default class Operation extends PureComponent {
} }
</div> </div>
<Collapse isOpened={shown}> <Collapse isOpened={isShown}>
<div className="opblock-body"> <div className="opblock-body">
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>} { deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
{ description && { description &&
@@ -214,8 +161,8 @@ export default class Operation extends PureComponent {
parameters={parameters} parameters={parameters}
operation={operation} operation={operation}
onChangeKey={onChangeKey} onChangeKey={onChangeKey}
onTryoutClick = { this.onTryoutClick } onTryoutClick = { onTryoutClick }
onCancelClick = { this.onCancelClick } onCancelClick = { onCancelClick }
tryItOutEnabled = { tryItOutEnabled } tryItOutEnabled = { tryItOutEnabled }
allowTryItOut={allowTryItOut} allowTryItOut={allowTryItOut}
@@ -239,25 +186,23 @@ export default class Operation extends PureComponent {
{ !tryItOutEnabled || !allowTryItOut ? null : { !tryItOutEnabled || !allowTryItOut ? null :
<Execute <Execute
getComponent={getComponent}
operation={ operation } operation={ operation }
specActions={ specActions } specActions={ specActions }
specSelectors={ specSelectors } specSelectors={ specSelectors }
path={ path } path={ path }
method={ method } method={ method }
onExecute={ this.onExecute } /> onExecute={ onExecute } />
} }
{ (!tryItOutEnabled || !response || !allowTryItOut) ? null : { (!tryItOutEnabled || !response || !allowTryItOut) ? null :
<Clear <Clear
onClick={ this.onClearClick }
specActions={ specActions } specActions={ specActions }
path={ path } path={ path }
method={ method }/> method={ method }/>
} }
</div> </div>
{this.state.executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null} {executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
{ !responses ? null : { !responses ? null :
<Responses <Responses

View File

@@ -34,14 +34,11 @@ export default class Operations extends React.Component {
let taggedOps = specSelectors.taggedOperations() let taggedOps = specSelectors.taggedOperations()
const Operation = getComponent("operation") const OperationContainer = getComponent("OperationContainer", true)
const Collapse = getComponent("Collapse") const Collapse = getComponent("Collapse")
let showSummary = layoutSelectors.showSummary()
let { let {
docExpansion, docExpansion,
displayOperationId,
displayRequestDuration,
maxDisplayedTags, maxDisplayedTags,
deepLinking deepLinking
} = getConfigs() } = getConfigs()
@@ -119,44 +116,23 @@ export default class Operations extends React.Component {
<Collapse isOpened={showTag}> <Collapse isOpened={showTag}>
{ {
operations.map( op => { operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const path = op.get("path", "") return <OperationContainer
const method = op.get("method", "") key={`${path}-${method}`}
const jumpToKey = `paths.${path}.${method}` op={op}
path={path}
const operationId = method={method}
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id") tag={tag}
const isShownKey = ["operations", createDeepLinkPath(tag), createDeepLinkPath(operationId)]
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
const request = specSelectors.requestFor(op.get("path"), op.get("method"))
return <Operation
{...op.toObject()}
isShownKey={isShownKey}
jumpToKey={jumpToKey}
showSummary={showSummary}
key={isShownKey}
response={ response }
request={ request }
allowTryItOut={allowTryItOut}
displayOperationId={displayOperationId}
displayRequestDuration={displayRequestDuration}
specActions={ specActions } specActions={ specActions }
specSelectors={ specSelectors } specSelectors={ specSelectors }
oas3Actions={oas3Actions} oas3Actions={oas3Actions}
layoutActions={ layoutActions } layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors } layoutSelectors={ layoutSelectors }
authActions={ authActions } authActions={ authActions }
authSelectors={ authSelectors } authSelectors={ authSelectors }
getComponent={ getComponent } getComponent={ getComponent }
fn={fn} fn={fn}
getConfigs={ getConfigs } getConfigs={ getConfigs }

View File

@@ -1,15 +1,15 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { fromJS } from "immutable" import { fromJS, Iterable } from "immutable"
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils" import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
export default class Responses extends React.Component { export default class Responses extends React.Component {
static propTypes = { static propTypes = {
request: PropTypes.object, request: PropTypes.instanceOf(Iterable),
tryItOutResponse: PropTypes.object, tryItOutResponse: PropTypes.instanceOf(Iterable),
responses: PropTypes.object.isRequired, responses: PropTypes.instanceOf(Iterable).isRequired,
produces: PropTypes.object, produces: PropTypes.instanceOf(Iterable),
producesValue: PropTypes.any, producesValue: PropTypes.any,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,

View File

@@ -0,0 +1,211 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { helpers } from "swagger-client"
import { Iterable, fromJS } from "immutable"
const { opId } = helpers
export default class OperationContainer extends PureComponent {
constructor(props, context) {
super(props, context)
this.state = {
tryItOutEnabled: false,
executeInProgress: false
}
}
static propTypes = {
op: PropTypes.instanceOf(Iterable).isRequired,
tag: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
operationId: PropTypes.string.isRequired,
showSummary: PropTypes.bool.isRequired,
isShown: PropTypes.bool.isRequired,
isShownKey: PropTypes.instanceOf(Iterable).isRequired,
jumpToKey: PropTypes.string.isRequired,
allowTryItOut: PropTypes.bool,
displayOperationId: PropTypes.bool,
displayRequestDuration: PropTypes.bool,
response: PropTypes.instanceOf(Iterable),
request: PropTypes.instanceOf(Iterable),
isDeepLinkingEnabled: PropTypes.bool.isRequired,
getComponent: PropTypes.func.isRequired,
authActions: PropTypes.object,
authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired
}
static defaultProps = {
showSummary: true,
response: null,
allowTryItOut: true,
displayOperationId: false,
displayRequestDuration: false
}
mapStateToProps(nextState, props) {
const { op, layoutSelectors, getConfigs } = props
const { docExpansion, deepLinking, displayOperationId, displayRequestDuration } = getConfigs()
const showSummary = layoutSelectors.showSummary()
const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
const isShownKey = fromJS(["operations", props.tag, operationId])
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
return {
operationId,
isDeepLinkingEnabled,
isShownKey,
showSummary,
displayOperationId,
displayRequestDuration,
isShown: layoutSelectors.isShown(isShownKey, docExpansion === "full" ),
jumpToKey: `paths.${props.path}.${props.method}`,
allowTryItOut: props.specSelectors.allowTryItOutFor(props.path, props.method),
response: props.specSelectors.responseFor(props.path, props.method),
request: props.specSelectors.requestFor(props.path, props.method)
}
}
componentWillReceiveProps(nextProps) {
const defaultContentType = "application/json"
let { specActions, path, method, op } = nextProps
let operation = op.get("operation")
let producesValue = operation.get("produces_value")
let produces = operation.get("produces")
let consumes = operation.get("consumes")
let consumesValue = operation.get("consumes_value")
if(nextProps.response !== this.props.response) {
this.setState({ executeInProgress: false })
}
if (producesValue === undefined) {
producesValue = produces && produces.size ? produces.first() : defaultContentType
specActions.changeProducesValue([path, method], producesValue)
}
if (consumesValue === undefined) {
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
specActions.changeConsumesValue([path, method], consumesValue)
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.tryItOutEnabled !== nextState.tryItOutEnabled
|| this.state.executeInProgress !== nextState.executeInProgress
|| this.props.op !== nextProps.op
|| this.props.tag !== nextProps.tag
|| this.props.path !== nextProps.path
|| this.props.method !== nextProps.method
|| this.props.operationId !== nextProps.operationId
|| this.props.showSummary !== nextProps.showSummary
|| this.props.isShown !== nextProps.isShown
|| this.props.isShownKey !== nextProps.isShownKey
|| this.props.jumpToKey !== nextProps.jumpToKey
|| this.props.allowTryItOut !== nextProps.allowTryItOut
|| this.props.displayOperationId !== nextProps.displayOperationId
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
|| this.props.response !== nextProps.response
|| this.props.request !== nextProps.request
|| this.props.isDeepLinkingEnabled !== nextProps.isDeepLinkingEnabled
}
toggleShown =() => {
let { layoutActions, isShownKey, isShown } = this.props
layoutActions.show(isShownKey, !isShown)
}
onTryoutClick =() => {
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
}
onCancelClick =() => {
let { specActions, path, method } = this.props
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
specActions.clearValidateParams([path, method])
}
onExecute = () => {
this.setState({ executeInProgress: true })
}
render() {
let {
op,
tag,
path,
method,
operationId,
showSummary,
isShown,
isShownKey,
jumpToKey,
allowTryItOut,
response,
request,
displayOperationId,
displayRequestDuration,
isDeepLinkingEnabled,
specSelectors,
specActions,
getComponent,
getConfigs,
layoutSelectors,
layoutActions,
authActions,
authSelectors,
fn
} = this.props
const Operation = getComponent( "operation" )
const operationProps = fromJS({
op,
tag,
path,
method,
operationId,
showSummary,
isShown,
isShownKey,
jumpToKey,
allowTryItOut,
response,
request,
displayOperationId,
displayRequestDuration,
isDeepLinkingEnabled,
executeInProgress: this.state.executeInProgress,
tryItOutEnabled: this.state.tryItOutEnabled
})
return (
<Operation
operation={operationProps}
toggleShown={this.toggleShown}
onTryoutClick={this.onTryoutClick}
onCancelClick={this.onCancelClick}
onExecute={this.onExecute}
specActions={ specActions }
specSelectors={ specSelectors }
layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors }
authActions={ authActions }
authSelectors={ authSelectors }
getComponent={ getComponent }
getConfigs={ getConfigs }
fn={fn}
/>
)
}
}

View File

@@ -7,8 +7,7 @@ import * as AllPlugins from "core/plugins/all"
import { parseSearch } from "core/utils" import { parseSearch } from "core/utils"
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== "production") {
const Perf = require("react-addons-perf") window.Perf = require("react-addons-perf")
window.Perf = Perf
} }
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef

View File

@@ -20,7 +20,7 @@ const RootWrapper = (reduxStore, ComponentToWrap) => class extends Component {
} }
const makeContainer = (getSystem, component, reduxStore) => { const makeContainer = (getSystem, component, reduxStore) => {
const mapStateToProps = component.prototype.constructor.mapStateToProps || function(state) { const mapStateToProps = component.prototype.mapStateToProps || function(state) {
return {state} return {state}
} }
let wrappedWithSystem = SystemWrapper(getSystem, component, reduxStore) let wrappedWithSystem = SystemWrapper(getSystem, component, reduxStore)

View File

@@ -12,6 +12,8 @@ import SplitPaneModePlugin from "core/plugins/split-pane-mode"
import downloadUrlPlugin from "core/plugins/download-url" import downloadUrlPlugin from "core/plugins/download-url"
import deepLinkingPlugin from "core/plugins/deep-linking" import deepLinkingPlugin from "core/plugins/deep-linking"
import OperationContainer from "core/containers/OperationContainer"
import App from "core/components/app" import App from "core/components/app"
import AuthorizationPopup from "core/components/auth/authorization-popup" import AuthorizationPopup from "core/components/auth/authorization-popup"
import AuthorizeBtn from "core/components/auth/authorize-btn" import AuthorizeBtn from "core/components/auth/authorize-btn"
@@ -107,7 +109,8 @@ export default function() {
TryItOutButton, TryItOutButton,
Markdown, Markdown,
BaseLayout, BaseLayout,
VersionStamp VersionStamp,
OperationContainer
} }
} }

View File

@@ -289,8 +289,7 @@ export default class Store {
getMapStateToProps() { getMapStateToProps() {
return () => { return () => {
let obj = Object.assign({}, this.getSystem()) return Object.assign({}, this.getSystem())
return obj
} }
} }