diff --git a/docs/customization/plugin-api.md b/docs/customization/plugin-api.md index defba722..e5d1846c 100644 --- a/docs/customization/plugin-api.md +++ b/docs/customization/plugin-api.md @@ -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. diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index 39ee5cd2..17faa008 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -39,9 +39,16 @@ Read more about the plugin system in the [Customization documentation](/docs/cus Parameter name | Docker variable | Description --- | --- | ----- `layout` | _Unavailable_ | `String="BaseLayout"`. The name of a component available via the plugin system to use as the top-level layout for Swagger UI. +`pluginsOptions` | _Unavailable_ | `Object`. A Javascript object to configure plugin integration and behaviors (see below). `plugins` | _Unavailable_ | `Array=[]`. An array of plugin functions to use in Swagger UI. `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 +--- | --- | ----- +`pluginLoadType` | _Unavailable_ | `String=["legacy", "chain"]`. Control behavior of plugins when targeting the same component with wrapComponent.
- `legacy` (default) : last plugin takes precedence over the others
- `chain` : chain wrapComponents when targeting the same core component, allowing multiple plugins to wrap the same component + ##### Display Parameter name | Docker variable | Description diff --git a/src/core/index.js b/src/core/index.js index 000b1d3c..e5f68527 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -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: { }, @@ -118,6 +125,7 @@ export default function SwaggerUI(opts) { configs: constructorConfig.configs }, plugins: constructorConfig.presets, + pluginsOptions: constructorConfig.pluginsOptions, state: deepExtend({ layout: { layout: constructorConfig.layout, diff --git a/src/core/system.js b/src/core/system.js index 30a41ddf..a756039c 100644 --- a/src/core/system.js +++ b/src/core/system.js @@ -35,6 +35,7 @@ export default class Store { deepExtend(this, { state: {}, plugins: [], + pluginsOptions: {}, system: { configs: {}, fn: {}, @@ -63,7 +64,7 @@ export default class Store { } register(plugins, rebuild=true) { - var pluginSystem = combinePlugins(plugins, this.getSystem()) + var pluginSystem = combinePlugins(plugins, this.getSystem(), this.pluginsOptions) systemExtend(this.system, pluginSystem) if(rebuild) { this.buildSystem() @@ -310,19 +311,21 @@ export default class Store { } -function combinePlugins(plugins, toolbox) { +function combinePlugins(plugins, toolbox, pluginOptions) { if(isObject(plugins) && !isArray(plugins)) { return assignDeep({}, plugins) } if(isFunc(plugins)) { - return combinePlugins(plugins(toolbox), toolbox) + return combinePlugins(plugins(toolbox), toolbox, pluginOptions) } if(isArray(plugins)) { + const dest = pluginOptions.pluginLoadType === "chain" ? toolbox.getComponents() : {} + return plugins - .map(plugin => combinePlugins(plugin, toolbox)) - .reduce(systemExtend, {}) + .map(plugin => combinePlugins(plugin, toolbox, pluginOptions)) + .reduce(systemExtend, dest) } return {} diff --git a/test/unit/core/system/wrapComponent.jsx b/test/unit/core/system/wrapComponent.jsx index 0605b0d5..594167ce 100644 --- a/test/unit/core/system/wrapComponent.jsx +++ b/test/unit/core/system/wrapComponent.jsx @@ -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: [ @@ -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
{this.props.name} component
@@ -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 @@ -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 @@ -163,7 +163,7 @@ describe("wrapComponents", () => { }) mySystem.register([ - function() { + function () { return { // Wrap the component and use the system wrapComponents: { @@ -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: () =>
Original component
+ } + } + } + ] + }) + + mySystem.register([ + () => { + return { + wrapComponents: { + wow: (OriginalComponent, system) => (props) => { + return + +
Injected after
+
+ } + } + } + }, + () => { + return { + wrapComponents: { + wow: (OriginalComponent, system) => (props) => { + return +
Injected before
+ +
+ } + } + } + } + ]) + + // Then + let Component = mySystem.getSystem().getComponents("wow") + const wrapper = render() + + 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