Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/7232 multiple wrap components #7236

Merged
merged 11 commits into from
May 20, 2021
4 changes: 4 additions & 0 deletions docs/customization/plugin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ const MyWrapComponentPlugin = function(system) {
}
```

**Note:**

If you have multiple plugins wrapping the same component, you may want to change the [`pluginsOptions.pluginLoadType`](/docs/usage/configuration.md#Plugins-options) parameter to `chain`.

#### `rootInjects`

The `rootInjects` interface allows you to inject values at the top level of the system.
Expand Down
7 changes: 7 additions & 0 deletions docs/usage/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,16 @@ Read more about the plugin system in the [Customization documentation](/docs/cus
Parameter name | Docker variable | Description
--- | --- | -----
<a name="layout"></a>`layout` | _Unavailable_ | `String="BaseLayout"`. The name of a component available via the plugin system to use as the top-level layout for Swagger UI.
<a name="pluginsOptions"></a>`pluginsOptions` | _Unavailable_ | `Object`. A Javascript object to configure plugin integration and behaviors (see below).
<a name="plugins"></a>`plugins` | _Unavailable_ | `Array=[]`. An array of plugin functions to use in Swagger UI.
<a name="presets"></a>`presets` | _Unavailable_ | `Array=[SwaggerUI.presets.ApisPreset]`. An array of presets to use in Swagger UI. Usually, you'll want to include `ApisPreset` if you use this option.

##### Plugins options

Parameter name | Docker variable | Description
--- | --- | -----
<a name="pluginLoadType"></a>`pluginLoadType` | _Unavailable_ | `String=["legacy", "chain"]`. Control behavior of plugins when targeting the same component with wrapComponent.<br/>- `legacy` (default) : last plugin takes precedence over the others<br/>- `chain` : chain wrapComponents when targeting the same core component, allowing multiple plugins to wrap the same component

##### Display

Parameter name | Docker variable | Description
Expand Down
8 changes: 8 additions & 0 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ export default function SwaggerUI(opts) {
plugins: [
],

pluginsOptions: {
// Behavior during plugin registration. Can be :
// - legacy (default) : the current behavior for backward compatibility – last plugin takes precedence over the others
// - chain : chain wrapComponents when targeting the same core component
pluginLoadType: "legacy"
},

// Initial state
initialState: { },

Expand All @@ -118,6 +125,7 @@ export default function SwaggerUI(opts) {
configs: constructorConfig.configs
},
plugins: constructorConfig.presets,
pluginsOptions: constructorConfig.pluginsOptions,
state: deepExtend({
layout: {
layout: constructorConfig.layout,
Expand Down
11 changes: 7 additions & 4 deletions src/core/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default class Store {
deepExtend(this, {
state: {},
plugins: [],
pluginsOptions: {},
tim-lai marked this conversation as resolved.
Show resolved Hide resolved
system: {
configs: {},
fn: {},
Expand Down Expand Up @@ -310,19 +311,21 @@ export default class Store {

}

function combinePlugins(plugins, toolbox) {
function combinePlugins(plugins, toolbox, pluginLoadType) {
tim-lai marked this conversation as resolved.
Show resolved Hide resolved
if(isObject(plugins) && !isArray(plugins)) {
return assignDeep({}, plugins)
}

if(isFunc(plugins)) {
return combinePlugins(plugins(toolbox), toolbox)
return combinePlugins(plugins(toolbox), toolbox, pluginLoadType)
}

if(isArray(plugins)) {
const dest = pluginLoadType === "chain" ? toolbox.getComponents() : {}
tim-lai marked this conversation as resolved.
Show resolved Hide resolved

return plugins
.map(plugin => combinePlugins(plugin, toolbox))
.reduce(systemExtend, {})
.map(plugin => combinePlugins(plugin, toolbox, pluginLoadType))
.reduce(systemExtend, dest)
}

return {}
Expand Down
78 changes: 72 additions & 6 deletions test/unit/core/system/wrapComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import System from "core/system"

describe("wrapComponents", () => {
describe("should wrap a component and provide a reference to the original", () => {
it("with stateless components", function(){
it("with stateless components", function () {
// Given
const system = new System({
plugins: [
Expand Down Expand Up @@ -40,7 +40,7 @@ describe("wrapComponents", () => {
expect(children.eq(1).text()).toEqual("Wrapped component")
})

it("with React classes", function(){
it("with React classes", function () {
class MyComponent extends React.Component {
render() {
return <div>{this.props.name} component</div>
Expand Down Expand Up @@ -86,7 +86,7 @@ describe("wrapComponents", () => {
})
})

it("should provide a reference to the system to the wrapper", function(){
it("should provide a reference to the system to the wrapper", function () {

// Given

Expand Down Expand Up @@ -137,7 +137,7 @@ describe("wrapComponents", () => {
expect(children.eq(1).text()).toEqual("WOW much data")
})

it("should wrap correctly when registering more plugins", function(){
it("should wrap correctly when registering more plugins", function () {

// Given

Expand All @@ -163,7 +163,7 @@ describe("wrapComponents", () => {
})

mySystem.register([
function() {
function () {
return {
// Wrap the component and use the system
wrapComponents: {
Expand Down Expand Up @@ -191,7 +191,73 @@ describe("wrapComponents", () => {
expect(children.eq(1).text()).toEqual("WOW much data")
})

it("should wrap correctly when building a system twice", function(){
it("should wrap correctly when registering multiple plugins targeting the same component", function () {

// Given

const mySystem = new System({
pluginsOptions: {
pluginLoadType: "chain"
},
plugins: [
() => {
return {
components: {
wow: () => <div>Original component</div>
}
}
}
]
})

mySystem.register([
() => {
return {
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container1>
<OriginalComponent {...props}></OriginalComponent>
<div>Injected after</div>
</container1>
}
}
}
},
() => {
return {
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container2>
<div>Injected before</div>
<OriginalComponent {...props}></OriginalComponent>
</container2>
}
}
}
}
])

// Then
let Component = mySystem.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)

const container2 = wrapper.children().first()
expect(container2[0].name).toEqual("container2")

const children2 = container2.children()
expect(children2.length).toEqual(2)
expect(children2[0].name).toEqual("div")
expect(children2.eq(0).text()).toEqual("Injected before")
expect(children2[1].name).toEqual("container1")

const children1 = children2.children()
expect(children1.length).toEqual(2)
expect(children1.eq(0).text()).toEqual("Original component")
expect(children1[0].name).toEqual("div")
expect(children1.eq(1).text()).toEqual("Injected after")
})

it("should wrap correctly when building a system twice", function () {

// Given

Expand Down