diff --git a/README.md b/README.md index 2c9e884d..b231ed54 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ domNode | The HTML DOM element inside which SwaggerUi will put the user interfac oauth2RedirectUrl | OAuth redirect URL tagsSorter | Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI. operationsSorter | Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged. +defaultModelRendering | Controls how models are shown when the API is first rendered. (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.) It can be set to 'model' or 'example', and the default is 'example'. +defaultModelExpandDepth | The default expansion depth for models. The default value is 1. configUrl | Configs URL parameterMacro | MUST be a function. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable modelPropertyMacro | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable diff --git a/src/core/components/model-example.jsx b/src/core/components/model-example.jsx index 768ee04b..23d7c802 100644 --- a/src/core/components/model-example.jsx +++ b/src/core/components/model-example.jsx @@ -7,14 +7,19 @@ export default class ModelExample extends React.Component { specSelectors: PropTypes.object.isRequired, schema: PropTypes.object.isRequired, example: PropTypes.any.isRequired, - isExecute: PropTypes.bool + isExecute: PropTypes.bool, + getConfigs: PropTypes.func.isRequired } constructor(props, context) { super(props, context) - + let { getConfigs } = this.props + let { defaultModelRendering } = getConfigs() + if (defaultModelRendering !== "example" && defaultModelRendering !== "model") { + defaultModelRendering = "example" + } this.state = { - activeTab: "example" + activeTab: defaultModelRendering } } @@ -27,7 +32,8 @@ export default class ModelExample extends React.Component { } render() { - let { getComponent, specSelectors, schema, example, isExecute } = this.props + let { getComponent, specSelectors, schema, example, isExecute, getConfigs } = this.props + let { defaultModelExpandDepth } = getConfigs() const ModelWrapper = getComponent("ModelWrapper") return
@@ -47,7 +53,7 @@ export default class ModelExample extends React.Component { !isExecute && this.state.activeTab === "model" && + expandDepth={ defaultModelExpandDepth } /> } diff --git a/src/core/components/models.jsx b/src/core/components/models.jsx index 1af412ab..e02e5c11 100644 --- a/src/core/components/models.jsx +++ b/src/core/components/models.jsx @@ -13,7 +13,7 @@ export default class Models extends Component { render(){ let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props let definitions = specSelectors.definitions() - let { docExpansion } = getConfigs() + let { docExpansion, defaultModelExpandDepth } = getConfigs() let showModels = layoutSelectors.isShown("models", docExpansion === "full" || docExpansion === "list" ) const ModelWrapper = getComponent("ModelWrapper") @@ -33,6 +33,7 @@ export default class Models extends Component { definitions.entrySeq().map( ( [ name, model ])=>{ return
{!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ?
diff --git a/src/core/components/parameter-row.jsx b/src/core/components/parameter-row.jsx index 04466a38..82dc064a 100644 --- a/src/core/components/parameter-row.jsx +++ b/src/core/components/parameter-row.jsx @@ -11,7 +11,8 @@ export default class ParameterRow extends Component { isExecute: PropTypes.bool, onChangeConsumes: PropTypes.func.isRequired, specSelectors: PropTypes.object.isRequired, - pathMethod: PropTypes.array.isRequired + pathMethod: PropTypes.array.isRequired, + getConfigs: PropTypes.func.isRequired } constructor(props, context) { @@ -56,7 +57,7 @@ export default class ParameterRow extends Component { } render() { - let {param, onChange, getComponent, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props + let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props let { isOAS3 } = specSelectors @@ -121,6 +122,7 @@ export default class ParameterRow extends Component { { bodyParam && schema ? ( diff --git a/src/core/components/responses.jsx b/src/core/components/responses.jsx index e365a44b..e9e29fec 100644 --- a/src/core/components/responses.jsx +++ b/src/core/components/responses.jsx @@ -12,12 +12,12 @@ export default class Responses extends React.Component { produces: PropTypes.object, producesValue: PropTypes.any, getComponent: PropTypes.func.isRequired, + getConfigs: PropTypes.func.isRequired, specSelectors: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired, pathMethod: PropTypes.array.isRequired, displayRequestDuration: PropTypes.bool.isRequired, - fn: PropTypes.object.isRequired, - getConfigs: PropTypes.func.isRequired + fn: PropTypes.object.isRequired } static defaultProps = { @@ -89,6 +89,7 @@ export default class Responses extends React.Component { response={ response } specSelectors={ specSelectors } contentType={ producesValue } + getConfigs={ getConfigs } getComponent={ getComponent }/> ) }).toArray() diff --git a/src/core/index.js b/src/core/index.js index ddf162ab..abc0ba36 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -45,6 +45,8 @@ module.exports = function SwaggerUI(opts) { requestInterceptor: (a => a), responseInterceptor: (a => a), showMutatedRequest: true, + defaultModelRendering: "example", + defaultModelExpandDepth: 1, // Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance. // Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest. diff --git a/test/components/model-example.js b/test/components/model-example.js new file mode 100644 index 00000000..f4225c30 --- /dev/null +++ b/test/components/model-example.js @@ -0,0 +1,116 @@ +/* eslint-env mocha */ +import React from "react" +import expect, { createSpy } from "expect" +import { shallow } from "enzyme" +import ModelExample from "components/model-example" +import ModelComponent from "components/model-wrapper" + +describe("", function(){ + // Given + let components = { + ModelWrapper: ModelComponent + } + let props = { + getComponent: (c) => { + return components[c] + }, + specSelectors: {}, + schema: {}, + example: "{\"example\": \"value\"}", + isExecute: false, + getConfigs: () => ({ + defaultModelRendering: "model", + defaultModelExpandDepth: 1 + }) + } + let exampleSelectedTestInputs = [ + { defaultModelRendering: "model", isExecute: true }, + { defaultModelRendering: "example", isExecute: true }, + { defaultModelRendering: "example", isExecute: false }, + { defaultModelRendering: "othervalue", isExecute: true }, + { defaultModelRendering: "othervalue", isExecute: false } + ] + let modelSelectedTestInputs = [ + { defaultModelRendering: "model", isExecute: false } + ] + + + it("renders model and example tabs", function(){ + // When + let wrapper = shallow() + + // Then should render tabs + expect(wrapper.find("div > ul.tab").length).toEqual(1) + + let tabs = wrapper.find("div > ul.tab").children() + expect(tabs.length).toEqual(2) + tabs.forEach((node) => { + expect(node.length).toEqual(1) + expect(node.name()).toEqual("li") + expect(node.hasClass("tabitem")).toEqual(true) + }) + expect(tabs.at(0).text()).toEqual("Example Value") + expect(tabs.at(1).text()).toEqual("Model") + }) + + exampleSelectedTestInputs.forEach(function(testInputs) { + it("example tab is selected if isExecute = " + testInputs.isExecute + " and defaultModelRendering = " + testInputs.defaultModelRendering, function(){ + // When + props.isExecute = testInputs.isExecute + props.getConfigs = () => ({ + defaultModelRendering: testInputs.defaultModelRendering, + defaultModelExpandDepth: 1 + }) + let wrapper = shallow() + + // Then + let tabs = wrapper.find("div > ul.tab").children() + + let exampleTab = tabs.at(0) + expect(exampleTab.hasClass("active")).toEqual(true) + let modelTab = tabs.at(1) + expect(modelTab.hasClass("active")).toEqual(false) + + expect(wrapper.find("div > div").length).toEqual(1) + expect(wrapper.find("div > div").text()).toEqual(props.example) + }) + }) + + modelSelectedTestInputs.forEach(function(testInputs) { + it("model tab is selected if isExecute = " + testInputs.isExecute + " and defaultModelRendering = " + testInputs.defaultModelRendering, function(){ + // When + props.isExecute = testInputs.isExecute + props.getConfigs = () => ({ + defaultModelRendering: testInputs.defaultModelRendering, + defaultModelExpandDepth: 1 + }) + let wrapper = shallow() + + // Then + let tabs = wrapper.find("div > ul.tab").children() + + let exampleTab = tabs.at(0) + expect(exampleTab.hasClass("active")).toEqual(false) + let modelTab = tabs.at(1) + expect(modelTab.hasClass("active")).toEqual(true) + + expect(wrapper.find("div > div").length).toEqual(1) + expect(wrapper.find("div > div").find(ModelComponent).props().expandDepth).toBe(1) + }) + }) + + it("passes defaultModelExpandDepth to ModelComponent", function(){ + // When + let expandDepth = 0 + props.isExecute = false + props.getConfigs = () => ({ + defaultModelRendering: "model", + defaultModelExpandDepth: expandDepth + }) + let wrapper = shallow() + + // Then + expect(wrapper.find("div > div").find(ModelComponent).props().expandDepth).toBe(expandDepth) + }) + +}) diff --git a/test/components/models.js b/test/components/models.js new file mode 100644 index 00000000..750a8cf1 --- /dev/null +++ b/test/components/models.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +import React from "react" +import expect, { createSpy } from "expect" +import { shallow } from "enzyme" +import { fromJS } from "immutable" +import Models from "components/models" +import ModelCollpase from "components/model-collapse" +import ModelComponent from "components/model-wrapper" + +describe("", function(){ + // Given + let components = { + Collapse: ModelCollpase, + ModelWrapper: ModelComponent + } + let props = { + getComponent: (c) => { + return components[c] + }, + specSelectors: { + definitions: function() { + return fromJS({ + def1: {}, + def2: {} + }) + } + }, + layoutSelectors: { + isShown: createSpy() + }, + layoutActions: {}, + getConfigs: () => ({ + docExpansion: "list", + defaultModelExpandDepth: 0 + }) + } + + + it("passes defaultModelExpandDepth to ModelWrapper", function(){ + // When + let wrapper = shallow() + + // Then should render tabs + expect(wrapper.find("ModelCollapse").length).toEqual(1) + expect(wrapper.find("ModelComponent").length).toBeGreaterThan(0) + wrapper.find("ModelComponent").forEach((modelWrapper) => { + expect(modelWrapper.props().expandDepth).toBe(0) + }) + }) + +})