Tremendous, beautiful plugin docs
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# Creating a custom layout
|
||||
|
||||
**Layouts** are a special type of component that Swagger-UI uses as the root component for the entire application. You can define custom layouts in order to have high-level control over what ends up on the page.
|
||||
|
||||
By default, Swagger-UI uses `BaseLayout`, which is built into the application. You can specify a different layout to be used by passing the layout's name as the `layout` parameter to Swagger-UI. Be sure to provide your custom layout as a component to Swagger-UI.
|
||||
|
||||
<br>
|
||||
|
||||
For example, if you wanted to create a custom layout that only displayed operations, you could define an `OperationsLayout`:
|
||||
|
||||
```js
|
||||
import React from "react"
|
||||
|
||||
// Create the layout component
|
||||
class OperationsLayout extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
getComponent
|
||||
} = this.props
|
||||
|
||||
const Operations = getComponent("Operations", true)
|
||||
|
||||
return {
|
||||
<div>
|
||||
<Operations />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the plugin that provides our layout component
|
||||
const OperationsLayoutPlugin = function() {
|
||||
return {
|
||||
components: {
|
||||
OperationsLayout: OperationsLayout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Provide the plugin to Swagger-UI, and select OperationsLayout
|
||||
// as the layout for Swagger-UI
|
||||
SwaggerUI({
|
||||
url: "http://petstore.swagger.io/v2/swagger.json",
|
||||
plugins: [ OperationsLayoutPlugin ],
|
||||
layout: "OperationsLayout"
|
||||
})
|
||||
```
|
||||
|
||||
### Augmenting the default layout
|
||||
|
||||
If you'd like to build around the `BaseLayout` instead of replacing it, you can pull the `BaseLayout` into your custom layout and use it:
|
||||
|
||||
```js
|
||||
import React from "react"
|
||||
|
||||
// Create the layout component
|
||||
class AugmentingLayout extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
getComponent
|
||||
} = this.props
|
||||
|
||||
const BaseLayout = getComponent("BaseLayout", true)
|
||||
|
||||
return {
|
||||
<div>
|
||||
<div className="myCustomHeader">
|
||||
<h1>I have a custom header above Swagger-UI!</h1>
|
||||
</div>
|
||||
<BaseLayout />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the plugin that provides our layout component
|
||||
const AugmentingLayoutPlugin = function() {
|
||||
return {
|
||||
components: {
|
||||
AugmentingLayout: AugmentingLayout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Provide the plugin to Swagger-UI, and select AugmentingLayout
|
||||
// as the layout for Swagger-UI
|
||||
SwaggerUI({
|
||||
url: "http://petstore.swagger.io/v2/swagger.json",
|
||||
plugins: [ AugmentingLayoutPlugin ],
|
||||
layout: "AugmentingLayout"
|
||||
})
|
||||
```
|
||||
|
||||
@@ -6,11 +6,15 @@ Swagger-UI leans heavily on concepts and patterns found in React and Redux.
|
||||
|
||||
If you aren't already familiar, here's some suggested reading:
|
||||
|
||||
- [React: Quick Start](https://reactjs.org/docs/hello-world.html)
|
||||
- [Redux README](http://redux.js.org/)
|
||||
- [React: Quick Start (reactjs.org)](https://reactjs.org/docs/hello-world.html)
|
||||
- [Redux README (redux.js.org)](http://redux.js.org/)
|
||||
|
||||
In the following documentation, we won't take the time to define the fundamentals covered in the resources above.
|
||||
|
||||
> **Note**: Some of the examples in this section contain JSX, which is a syntax extension to JavaScript that is useful for writing React components.
|
||||
>
|
||||
> If you don't want to set up a build pipeline capable of translating JSX to JavaScript, take a look at [React without JSX (reactjs.org)](https://reactjs.org/docs/react-without-jsx.html).
|
||||
|
||||
### The System
|
||||
|
||||
The _system_ is the heart of the Swagger-UI application. At runtime, it's a JavaScript object that holds many things:
|
||||
@@ -51,3 +55,17 @@ SwaggerUI({
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### getComponent
|
||||
|
||||
`getComponent` is a helper function injected into every container component, which is used to get references to components provided by the plugin system.
|
||||
|
||||
All components should be loaded through `getComponent`, since it allows other plugins to modify the component. It is preferred over a conventional `import` statement.
|
||||
|
||||
Container components in Swagger-UI can be loaded by passing `true` as the second argument to `getComponent`, like so:
|
||||
|
||||
```
|
||||
getComponent("ContainerComponentName", true)
|
||||
```
|
||||
|
||||
This will map the current system as props to the component.
|
||||
|
||||
@@ -58,7 +58,7 @@ const MyActionPlugin = () => {
|
||||
statePlugins: {
|
||||
example: {
|
||||
actions: {
|
||||
updateFavoriteColor: (str) => {
|
||||
updateFavoriteColor: (obj) => {
|
||||
return {
|
||||
type: "EXAMPLE_SET_FAV_COLOR",
|
||||
payload: str
|
||||
@@ -71,6 +71,14 @@ const MyActionPlugin = () => {
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// elsewhere
|
||||
exampleActions.updateFavoriteColor({
|
||||
name: "blue",
|
||||
hex: "#0000ff"
|
||||
})
|
||||
```
|
||||
|
||||
The Action interface enables the creation of new Redux action creators within a piece of state in the Swagger-UI system.
|
||||
|
||||
This action creator function will be bound to the `example` reducer dispatcher and exposed to container components as `exampleActions.updateFavoriteColor`.
|
||||
@@ -79,14 +87,206 @@ For more information about the concept of actions in Redux, see the [Redux Actio
|
||||
|
||||
##### Reducers
|
||||
|
||||
Reducers take a state (which is an Immutable map) and an action, and return a new state.
|
||||
|
||||
Reducers must be provided to the system under the name of the action type that they handle, in this case, `MYPLUGIN_UPDATE_SOMETHING`.
|
||||
|
||||
```js
|
||||
const MyReducerPlugin = function(system) {
|
||||
return {
|
||||
statePlugins: {
|
||||
example: {
|
||||
reducers: {
|
||||
"EXAMPLE_SET_FAV_COLOR": (state, action) => {
|
||||
//
|
||||
return state.set("favColor", fromJS(action.payload))
|
||||
// we're updating the Immutable state object...
|
||||
// we need to convert vanilla objects into an immutable type (fromJS)
|
||||
// See immutable docs about how to modify the state
|
||||
// PS: you're only working with the state under the namespace, in this case "example".
|
||||
// So you can do what you want, without worrying about /other/ namespaces
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Selectors
|
||||
|
||||
Selectors take any number of functions as arguments, and passes all results to the last function.
|
||||
|
||||
They're an easy way to keep logic for getting data out of state in one place, and is preferred over passing state data directly into components.
|
||||
|
||||
See [Reselect: `createSelector`](https://github.com/reactjs/reselect#createselectorinputselectors--inputselectors-resultfunc) for more information.
|
||||
```js
|
||||
const MySelectorPlugin = function(system) {
|
||||
return {
|
||||
statePlugins: {
|
||||
myPlugin: {
|
||||
selectors: {
|
||||
something: createSelector(
|
||||
state => state.get("something") // return the whatever "something" points to
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Components
|
||||
|
||||
You can provide a map of components to be integrated into the system.
|
||||
|
||||
Be mindful of the key names for the components you provide, as you'll need to use those names to refer to the components elsewhere.
|
||||
|
||||
```js
|
||||
const MyComponentPlugin = function(system) {
|
||||
return {
|
||||
components: {
|
||||
// components can just be functions
|
||||
HelloWorld: () => <h1>Hello World!</h1>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// elsewhere
|
||||
const HelloWorld = getComponent("HelloWorld")
|
||||
```
|
||||
|
||||
##### Wrap-Actions
|
||||
|
||||
Wrap Actions allow you to override the behavior of an action in the system.
|
||||
|
||||
This interface is very useful for building custom behavior on top of builtin actions.
|
||||
|
||||
A Wrap Action's first argument is `oriAction`, which is the action being wrapped. It is your responsibility to call the `oriAction` - if you don't, the original action will not fire!
|
||||
|
||||
```js
|
||||
const MySpecPlugin = function(system) {
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
actions: {
|
||||
updateSpec: (str) => {
|
||||
return {
|
||||
type: "SPEC_UPDATE_SPEC",
|
||||
payload: str
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this plugin allows you to watch changes to the spec that is in memory
|
||||
const MyWrapActionPlugin = function(system) {
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapActions: {
|
||||
updateSpec: function(oriAction, str) {
|
||||
doSomethingWithSpecValue(str)
|
||||
return oriAction(str) // don't forget!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Wrap-Selectors
|
||||
|
||||
Wrap Selectors allow you to override the behavior of a selector in the system.
|
||||
|
||||
They are function factories with the signature `(oriSelector, system) => (...args) => result`.
|
||||
|
||||
This interface is useful for controlling what data flows into components. We use this in the core code to disable selectors based on the API definition's version.
|
||||
|
||||
```js
|
||||
import { createSelector } from 'reselect'
|
||||
|
||||
const MySpecPlugin = function(system) {
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
selectors: {
|
||||
someData: createSelector(
|
||||
state => state.get("something")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MyWrapSelectorsPlugin = function(system) {
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapSelectors: {
|
||||
someData: (oriSelector, system) => (...args) => {
|
||||
// you can do other things here...
|
||||
// but let's just enable the default behavior
|
||||
return oriSelector(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Wrap-Components
|
||||
|
||||
Wrap Components allow you to override a component registered within the system.
|
||||
|
||||
Wrap Components are function factories with the signature `(OriginalComponent, system) => props => ReactElement`.
|
||||
|
||||
```js
|
||||
const MyNumberDisplayPlugin = function(system) {
|
||||
return {
|
||||
components: {
|
||||
NumberDisplay: ({ number }) => <span>{number}</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MyWrapComponentPlugin = function(system) {
|
||||
return {
|
||||
wrapComponents: {
|
||||
NumberDisplay: (Original, system) => (props) => {
|
||||
if(props.number > 10) {
|
||||
return <div>
|
||||
<h3>Warning! Big number ahead.</h3>
|
||||
</div>
|
||||
} else {
|
||||
return <Original {...props} />
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### fn
|
||||
|
||||
The fn interface allows you to add helper functions to the system for use elsewhere.
|
||||
|
||||
```js
|
||||
import leftPad from "left-pad"
|
||||
|
||||
const MyFnPlugin = function(system) {
|
||||
return {
|
||||
fn: {
|
||||
leftPad: leftPad
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user