feat: expose plugins and presets on SwaggerUI global symbol (#9189)
Part of this commit is also: - complete plugins consolidation - complete presets consolidation - build system consolidation Refs #9188
This commit is contained in:
@@ -17,6 +17,8 @@ Welcome to the Swagger UI documentation!
|
||||
- [Overview](customization/overview.md)
|
||||
- [Plugin API](customization/plugin-api.md)
|
||||
- [Custom layout](customization/custom-layout.md)
|
||||
- [Adding plugin](customization/add-plugin.md)
|
||||
- [Plug-Points](customization/plug-points.md)
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
127
docs/customization/add-plugin.md
Normal file
127
docs/customization/add-plugin.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Add a plugin
|
||||
|
||||
### Swagger-UI relies on plugins for all the good stuff.
|
||||
|
||||
Plugins allow you to add
|
||||
- `statePlugins`
|
||||
- `selectors` - query the state
|
||||
- `reducers` - modify the state
|
||||
- `actions` - fire and forget, that will eventually be handled by a reducer. You *can* rely on the result of async actions. But in general it's not recommended
|
||||
- `wrapActions` - replace an action with a wrapped action (useful for hooking into existing `actions`)
|
||||
- `components` - React components
|
||||
- `fn` - commons functions
|
||||
|
||||
To add a plugin we include it in the configs...
|
||||
|
||||
```js
|
||||
SwaggerUI({
|
||||
url: 'some url',
|
||||
plugins: [ ... ]
|
||||
})
|
||||
```
|
||||
|
||||
Or if you're updating the core plugins.. you'll add it to the base preset: [src/core/presets/base.js](https://github.com/swagger-api/swagger-ui/blob/master/src/core/presets/base.js)
|
||||
|
||||
Each Plugin is a function that returns an object. That object will get merged with the `system` and later bound to the state.
|
||||
Here is an example of each `type`
|
||||
|
||||
```js
|
||||
// A contrived, but quite full example....
|
||||
|
||||
export function SomePlugin(toolbox) {
|
||||
|
||||
const UPDATE_SOMETHING = "some_namespace_update_something" // strings just need to be uniuqe... see below
|
||||
|
||||
// Tools
|
||||
const fromJS = toolbox.Im.fromJS // needed below
|
||||
const createSelector = toolbox.createSelector // same, needed below
|
||||
|
||||
return {
|
||||
statePlugins: {
|
||||
|
||||
someNamespace: {
|
||||
actions: {
|
||||
actionName: (args)=> ({type: UPDATE_SOMETHING, payload: args}), // Synchronous action must return an object for the reducer to handle
|
||||
anotherAction: (a,b,c) => (system) => system.someNamespaceActions.actionName(a || b) // Asynchronous actions must return a function. The function gets the whole system, and can call other actions (based on state if needed)
|
||||
},
|
||||
wrapActions: {
|
||||
anotherAction: (oriAction, system) => (...args) => {
|
||||
oriAction(...args) // Usually we at least call the original action
|
||||
system.someNamespace.actionName(...args) // why not call this?
|
||||
console.log("args", args) // Log the args
|
||||
// anotherAction in the someNamespace has now been replaced with the this function
|
||||
}
|
||||
},
|
||||
reducers: {
|
||||
[UPDATE_SOMETHING]: (state, action) => { // Take a state (which is immutable) and an action (see synchronous actions) and return a new state
|
||||
return state.set("something", fromJS(action.payload)) // we're updating the Immutable state object... we need to convert vanilla objects into an immutable type (fromJS)
|
||||
// See immutable about how to modify the state
|
||||
// PS: you're only working with the state under the namespace, in this case "someNamespace". So you can do what you want, without worrying about /other/ namespaces
|
||||
}
|
||||
},
|
||||
selectors: {
|
||||
// creatSelector takes a list of fn's and passes all the results to the last fn.
|
||||
// eg: createSelector(a => a, a => a+1, (a,a2) => a + a2)(1) // = 3
|
||||
something: createSelector( // see [reselect#createSelector](https://github.com/reactjs/reselect#createselectorinputselectors--inputselectors-resultfunc)
|
||||
getState => getState(), // This is a requirement... because we `bind` selectors, we don't want to bind to any particular state (which is an immutable value) so we bind to a function, which returns the current state
|
||||
state => state.get("something") // return the whatever "something" points to
|
||||
),
|
||||
foo: getState => "bar" // In the end selectors are just functions that we pass getState to
|
||||
}
|
||||
}
|
||||
|
||||
... // you can include as many namespaces as you want. They just get merged into the 'system'
|
||||
|
||||
},
|
||||
|
||||
components: {
|
||||
foo: ()=> <h1> Hello </h1> // just a map of names to react components, naturally you'd want to import a fuller react component
|
||||
},
|
||||
|
||||
fn: {
|
||||
addOne: (a) => a + 1 // just any extra functions you want to include
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
>The plugin factory gets one argument, which I like to call `toolbox`.
|
||||
This argument is the entire plugin system (at the point the plugin factory is called). It also includes a reference to the `Immutable` lib, so that plugin authors don't need to include it.
|
||||
|
||||
|
||||
### The Plugin system
|
||||
|
||||
Each plugin you include will end up getting merged into the `system`, which is just an object.
|
||||
|
||||
Then we bind the `system` to our state. And flatten it, so that we don't need to reach into deep objects
|
||||
|
||||
> ie: spec.actions becomes specActions, spec.selectors becomes specSelectors
|
||||
|
||||
You can reach this bound system by calling `getSystem` on the store.
|
||||
|
||||
`getSystem` is the heart of this whole project. Each container component will receive a spread of props from `getSystem`
|
||||
|
||||
here is an example....
|
||||
```js
|
||||
class Bobby extends React.Component {
|
||||
|
||||
handleClick(e) {
|
||||
this.props.someNamespaceActions.actionName() // fires an action... which the reducer will *eventually* see
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let { someNamespaceSelectors, someNamespaceActions } = this.props // this.props has the whole state spread
|
||||
let something = someNamespaceSelectors.something() // calls our selector, which returns some state (either an immutable object or value)
|
||||
|
||||
return (
|
||||
<h1 onClick={this.handleClick.bind(this)}> Hello {something} </h1> // render the contents
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
TODO: a lot more elaboration
|
||||
`
|
||||
Reference in New Issue
Block a user