Skip to content

Commit

Permalink
feat: add support for React@18 in backward compatible way (#9435)
Browse files Browse the repository at this point in the history
Any React version matching this semver is supported: >= 16.8 < 19

Refs #8126
Refs #8414
  • Loading branch information
char0n committed Dec 20, 2023
1 parent 08fe66b commit 98b5309
Show file tree
Hide file tree
Showing 11 changed files with 2,178 additions and 24,303 deletions.
26,138 changes: 1,998 additions & 24,140 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@
"prop-types": "^15.8.1",
"randexp": "^0.5.3",
"randombytes": "^2.1.0",
"react": "=17.0.2",
"react": ">=16.8.0 <19",
"react-copy-to-clipboard": "5.1.0",
"react-debounce-input": "=3.3.0",
"react-dom": "=17.0.2",
"react-dom": ">=16.8.0 <19",
"react-immutable-proptypes": "2.2.0",
"react-immutable-pure-component": "^2.2.0",
"react-inspector": "^6.0.1",
Expand Down Expand Up @@ -125,7 +125,7 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@release-it/conventional-changelog": "=5.1.0",
"@svgr/webpack": "=8.1.0",
"@wojtekmaj/enzyme-adapter-react-17": "=0.8.0",
"@cfaester/enzyme-adapter-react-18": "=0.7.1",
"autoprefixer": "^10.4.16",
"babel-loader": "^9.1.3",
"babel-plugin-lodash": "=3.3.4",
Expand Down Expand Up @@ -177,7 +177,7 @@
"prettier": "^3.1.0",
"process": "^0.11.10",
"react-refresh": "^0.14.0",
"react-test-renderer": "=17.0.2",
"react-test-renderer": "^18.2.0",
"release-it": "=15.4.2",
"rimraf": "^5.0.5",
"sass": "^1.69.5",
Expand Down
2 changes: 2 additions & 0 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import SpecPlugin from "./plugins/spec"
import SwaggerClientPlugin from "./plugins/swagger-client"
import UtilPlugin from "./plugins/util"
import ViewPlugin from "./plugins/view"
import ViewLegacyPlugin from "core/plugins/view-legacy"
import DownloadUrlPlugin from "./plugins/download-url"
import SafeRenderPlugin from "./plugins/safe-render"

Expand Down Expand Up @@ -268,6 +269,7 @@ SwaggerUI.plugins = {
SwaggerClient: SwaggerClientPlugin,
Util: UtilPlugin,
View: ViewPlugin,
ViewLegacy: ViewLegacyPlugin,
DownloadUrl: DownloadUrlPlugin,
SafeRender: SafeRenderPlugin,
}
2 changes: 1 addition & 1 deletion src/core/plugins/oas3/components/servers-container.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ export default class ServersContainer extends React.Component {
/>
</div> ) : null
}
}
}
281 changes: 127 additions & 154 deletions src/core/plugins/oas3/components/servers.jsx
Original file line number Diff line number Diff line change
@@ -1,176 +1,149 @@
import React from "react"
/**
* @prettier
*/
import React, { useCallback, useEffect } from "react"
import { OrderedMap } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"

export default class Servers extends React.Component {
const Servers = ({
servers,
currentServer,
setSelectedServer,
setServerVariableValue,
getServerVariable,
getEffectiveServerValue,
}) => {
const currentServerDefinition =
servers.find((s) => s.get("url") === currentServer) || OrderedMap()
const currentServerVariableDefs =
currentServerDefinition.get("variables") || OrderedMap()
const shouldShowVariableUI = currentServerVariableDefs.size !== 0

useEffect(() => {
if (currentServer) return

static propTypes = {
servers: ImPropTypes.list.isRequired,
currentServer: PropTypes.string.isRequired,
setSelectedServer: PropTypes.func.isRequired,
setServerVariableValue: PropTypes.func.isRequired,
getServerVariable: PropTypes.func.isRequired,
getEffectiveServerValue: PropTypes.func.isRequired
}

componentDidMount() {
let { servers, currentServer } = this.props
// fire 'change' event to set default 'value' of select
setSelectedServer(servers.first()?.get("url"))
}, [])

if(currentServer) {
useEffect(() => {
// server has changed, we may need to set default values
const currentServerDefinition = servers.find(
(server) => server.get("url") === currentServer
)
if (!currentServerDefinition) {
setSelectedServer(servers.first().get("url"))
return
}

// fire 'change' event to set default 'value' of select
this.setServer(servers.first()?.get("url"))
}

UNSAFE_componentWillReceiveProps(nextProps) {
let {
servers,
setServerVariableValue,
getServerVariable
} = nextProps
if (this.props.currentServer !== nextProps.currentServer || this.props.servers !== nextProps.servers) {
// Server has changed, we may need to set default values
let currentServerDefinition = servers
.find(v => v.get("url") === nextProps.currentServer)
let prevServerDefinition = this.props.servers
.find(v => v.get("url") === this.props.currentServer) || OrderedMap()

if(!currentServerDefinition) {
return this.setServer(servers.first().get("url"))
}

let prevServerVariableDefs = prevServerDefinition.get("variables") || OrderedMap()
let prevServerVariableDefaultKey = prevServerVariableDefs.find(v => v.get("default")) || OrderedMap()
let prevServerVariableDefaultValue = prevServerVariableDefaultKey.get("default")

let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
let currentServerVariableDefaultKey = currentServerVariableDefs.find(v => v.get("default")) || OrderedMap()
let currentServerVariableDefaultValue = currentServerVariableDefaultKey.get("default")

currentServerVariableDefs.map((val, key) => {
let currentValue = getServerVariable(nextProps.currentServer, key)

// note: it is possible for both key/val to be the same across definitions,
// but we will try to detect a change in default values between definitions
// only set the default value if the user hasn't set one yet
// or if the definition appears to have changed
if (!currentValue || prevServerVariableDefaultValue !== currentServerVariableDefaultValue) {
setServerVariableValue({
server: nextProps.currentServer,
key,
val: val.get("default") || ""
})
}
const currentServerVariableDefs =
currentServerDefinition.get("variables") || OrderedMap()
currentServerVariableDefs.map((val, key) => {
setServerVariableValue({
server: currentServer,
key,
val: val.get("default") || "",
})
}
}

onServerChange =( e ) => {
this.setServer( e.target.value )
})
}, [currentServer, servers])

// set default variable values
}
const handleServerChange = useCallback(
(e) => {
setSelectedServer(e.target.value)
},
[setSelectedServer]
)

onServerVariableValueChange = ( e ) => {
let {
setServerVariableValue,
currentServer
} = this.props
const handleServerVariableChange = useCallback(
(e) => {
const variableName = e.target.getAttribute("data-variable")
const newVariableValue = e.target.value

let variableName = e.target.getAttribute("data-variable")
let newVariableValue = e.target.value

if(typeof setServerVariableValue === "function") {
setServerVariableValue({
server: currentServer,
key: variableName,
val: newVariableValue
val: newVariableValue,
})
}
}

setServer = ( value ) => {
let { setSelectedServer } = this.props

setSelectedServer(value)
}

render() {
let { servers,
currentServer,
getServerVariable,
getEffectiveServerValue
} = this.props


let currentServerDefinition = servers.find(s => s.get("url") === currentServer) || OrderedMap()

let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()

let shouldShowVariableUI = currentServerVariableDefs.size !== 0

return (
<div className="servers">
<label htmlFor="servers">
<select onChange={ this.onServerChange } value={currentServer}>
{ servers.valueSeq().map(
( server ) =>
<option
value={ server.get("url") }
key={ server.get("url") }>
{ server.get("url") }
{ server.get("description") && ` - ${server.get("description")}` }
},
[setServerVariableValue, currentServer]
)

return (
<div className="servers">
<label htmlFor="servers">
<select onChange={handleServerChange} value={currentServer}>
{servers
.valueSeq()
.map((server) => (
<option value={server.get("url")} key={server.get("url")}>
{server.get("url")}
{server.get("description") && ` - ${server.get("description")}`}
</option>
).toArray()}
</select>
</label>
{ shouldShowVariableUI ?
<div>

<div className={"computed-url"}>
Computed URL:
<code>
{getEffectiveServerValue(currentServer)}
</code>
</div>
<h4>Server variables</h4>
<table>
<tbody>
{
currentServerVariableDefs.entrySeq().map(([name, val]) => {
return <tr key={name}>
<td>{name}</td>
<td>
{ val.get("enum") ?
<select data-variable={name} onChange={this.onServerVariableValueChange}>
{val.get("enum").map(enumValue => {
return <option
selected={enumValue === getServerVariable(currentServer, name)}
))
.toArray()}
</select>
</label>
{shouldShowVariableUI && (
<div>
<div className={"computed-url"}>
Computed URL:
<code>{getEffectiveServerValue(currentServer)}</code>
</div>
<h4>Server variables</h4>
<table>
<tbody>
{currentServerVariableDefs.entrySeq().map(([name, val]) => {
return (
<tr key={name}>
<td>{name}</td>
<td>
{val.get("enum") ? (
<select
data-variable={name}
onChange={handleServerVariableChange}
>
{val.get("enum").map((enumValue) => {
return (
<option
selected={
enumValue ===
getServerVariable(currentServer, name)
}
key={enumValue}
value={enumValue}>
value={enumValue}
>
{enumValue}
</option>
})}
</select> :
<input
type={"text"}
value={getServerVariable(currentServer, name) || ""}
onChange={this.onServerVariableValueChange}
data-variable={name}
></input>
}
</td>
</tr>
})
}
</tbody>
</table>
</div>: null
}
</div>
)
}
)
})}
</select>
) : (
<input
type={"text"}
value={getServerVariable(currentServer, name) || ""}
onChange={handleServerVariableChange}
data-variable={name}
></input>
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)}
</div>
)
}
Servers.propTypes = {
servers: ImPropTypes.list.isRequired,
currentServer: PropTypes.string.isRequired,
setSelectedServer: PropTypes.func.isRequired,
setServerVariableValue: PropTypes.func.isRequired,
getServerVariable: PropTypes.func.isRequired,
getEffectiveServerValue: PropTypes.func.isRequired,
}

export default Servers
25 changes: 25 additions & 0 deletions src/core/plugins/view-legacy/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @prettier
*/
import { getComponent } from "core/plugins/view/root-injects"
import { render } from "./root-injects"

const ViewLegacyPlugin = ({ React, getSystem, getStore, getComponents }) => {
const rootInjects = {}
const reactMajorVersion = parseInt(React?.version, 10)

if (reactMajorVersion >= 16 && reactMajorVersion < 18) {
rootInjects.render = render(
getSystem,
getStore,
getComponent,
getComponents
)
}

return {
rootInjects,
}
}

export default ViewLegacyPlugin
12 changes: 12 additions & 0 deletions src/core/plugins/view-legacy/root-injects.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @prettier
*/
import React from "react"
import ReactDOM from "react-dom"

export const render =
(getSystem, getStore, getComponent, getComponents) => (domNode) => {
const App = getComponent(getSystem, getStore, getComponents)("App", "root")

ReactDOM.render(<App />, domNode)
}

0 comments on commit 98b5309

Please sign in to comment.