From c3f9c094d1b6761c2a847f5c807aac86120402b6 Mon Sep 17 00:00:00 2001 From: Gwyn Judd Date: Wed, 7 Jun 2017 16:29:46 +1200 Subject: [PATCH] Added maxRows and filter support Useful for the display of very large swagger specs. Can limit the number of operations to a smaller value and search --- README.md | 2 ++ src/core/components/layouts/base.jsx | 7 ++++--- src/core/components/operations.jsx | 18 ++++++++++++++++-- src/core/index.js | 4 +++- src/plugins/topbar/topbar.jsx | 15 +++++++++++++++ src/standalone/layout.jsx | 24 +++++++++++++++++++++--- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c872e4c5..28943fab 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,8 @@ modelPropertyMacro | MUST be a function. Function to set default values to each docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'. displayOperationId | Controls the display of operationId in operations list. The default is `false`. displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`. +maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations. +filter | If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be true/false to enable or disable, or an explicit filter string in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag. ### Plugins diff --git a/src/core/components/layouts/base.jsx b/src/core/components/layouts/base.jsx index 9da4b347..7e927c0b 100644 --- a/src/core/components/layouts/base.jsx +++ b/src/core/components/layouts/base.jsx @@ -10,11 +10,12 @@ export default class BaseLayout extends React.Component { specSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + filter: PropTypes.string.isRequired } render() { - let { specSelectors, specActions, getComponent } = this.props + let { specSelectors, specActions, getComponent, filter } = this.props let info = specSelectors.info() let url = specSelectors.url() @@ -66,7 +67,7 @@ export default class BaseLayout extends React.Component { - + diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 6c20d963..fff1b226 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -11,7 +11,8 @@ export default class Operations extends React.Component { layoutActions: PropTypes.object.isRequired, authActions: PropTypes.object.isRequired, authSelectors: PropTypes.object.isRequired, - getConfigs: PropTypes.func.isRequired + getConfigs: PropTypes.func.isRequired, + filter: PropTypes.string.isRequired }; render() { @@ -24,6 +25,7 @@ export default class Operations extends React.Component { authActions, authSelectors, getConfigs, + filter, fn } = this.props @@ -33,7 +35,19 @@ export default class Operations extends React.Component { const Collapse = getComponent("Collapse") let showSummary = layoutSelectors.showSummary() - let { docExpansion, displayOperationId, displayRequestDuration } = getConfigs() + let { docExpansion, displayOperationId, displayRequestDuration, maxDisplayedTags } = getConfigs() + + if (filter) { + if (filter !== true) { + taggedOps = taggedOps.filter((tagObj, tag) => { + return tag.indexOf(filter) !== -1 + }) + } + } + + if (maxDisplayedTags && !isNaN(maxDisplayedTags) && maxDisplayedTags >= 0) { + taggedOps = taggedOps.slice(0, maxDisplayedTags) + } return (
diff --git a/src/core/index.js b/src/core/index.js index 80335da5..381748d4 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -6,7 +6,7 @@ import ApisPreset from "core/presets/apis" import * as AllPlugins from "core/plugins/all" import { parseSeach, filterConfigs } from "core/utils" -const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", +const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", "maxDisplayedTags", "filter", "apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl", "showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" , "displayRequestDuration"] @@ -26,6 +26,8 @@ module.exports = function SwaggerUI(opts) { urls: null, layout: "BaseLayout", docExpansion: "list", + maxDisplayedTags: null, + filter: null, validatorUrl: "https://online.swagger.io/validator", configs: {}, custom: {}, diff --git a/src/plugins/topbar/topbar.jsx b/src/plugins/topbar/topbar.jsx index 11b5c7eb..087b3c93 100644 --- a/src/plugins/topbar/topbar.jsx +++ b/src/plugins/topbar/topbar.jsx @@ -6,6 +6,11 @@ import Logo from "./logo_small.png" export default class Topbar extends React.Component { + static propTypes = { + onFilterChange: PropTypes.func.isRequired, + filter: PropTypes.string.isRequired + } + constructor(props, context) { super(props, context) this.state = { url: props.specSelectors.url(), selectedIndex: 0 } @@ -80,6 +85,11 @@ export default class Topbar extends React.Component { } } + onFilterChange =(e) => { + let {target: {value}} = e + this.props.onFilterChange(value) + } + render() { let { getComponent, specSelectors, getConfigs } = this.props const Button = getComponent("Button") @@ -87,6 +97,7 @@ export default class Topbar extends React.Component { let isLoading = specSelectors.loadingStatus() === "loading" let isFailed = specSelectors.loadingStatus() === "failed" + let filter = this.props.filter let inputStyle = {} if(isFailed) inputStyle.color = "red" @@ -124,6 +135,10 @@ export default class Topbar extends React.Component { Swagger UX swagger + { + filter === null || filter === false ? null : + + }
{control}
diff --git a/src/standalone/layout.jsx b/src/standalone/layout.jsx index 20ca50d1..783b9a1c 100644 --- a/src/standalone/layout.jsx +++ b/src/standalone/layout.jsx @@ -10,7 +10,23 @@ export default class StandaloneLayout extends React.Component { specSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + getConfigs: PropTypes.func.isRequired + } + + constructor(props) { + super(props) + + this.handleFilterChange = this.handleFilterChange.bind(this) + + let { getConfigs } = this.props + let { filter } = getConfigs() + + this.state = { filter: filter } + } + + handleFilterChange(filter) { + this.setState({ filter: filter }) } render() { @@ -24,12 +40,14 @@ export default class StandaloneLayout extends React.Component { const BaseLayout = getComponent("BaseLayout", true) const OnlineValidatorBadge = getComponent("onlineValidatorBadge", true) + const filter = this.state.filter + const loadingStatus = specSelectors.loadingStatus() return ( - { Topbar ? : null } + { Topbar ? : null } { loadingStatus === "loading" &&

Loading...

@@ -45,7 +63,7 @@ export default class StandaloneLayout extends React.Component {

Failed to load config.

} - { !loadingStatus || loadingStatus === "success" && } + { !loadingStatus || loadingStatus === "success" && }