Merge branch 'master' into master
This commit is contained in:
@@ -67,7 +67,6 @@ To help with the migration, here are the currently known issues with 3.X. This l
|
|||||||
|
|
||||||
- Only part of the [parameters](#parameters) previously supported are available.
|
- Only part of the [parameters](#parameters) previously supported are available.
|
||||||
- The JSON Form Editor is not implemented.
|
- The JSON Form Editor is not implemented.
|
||||||
- Shebang URL support for operations is missing.
|
|
||||||
- Support for `collectionFormat` is partial.
|
- Support for `collectionFormat` is partial.
|
||||||
- l10n (translations) is not implemented.
|
- l10n (translations) is not implemented.
|
||||||
- Relative path support for external files is not implemented.
|
- Relative path support for external files is not implemented.
|
||||||
|
|||||||
36
docs/deep-linking.md
Normal file
36
docs/deep-linking.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Deep linking
|
||||||
|
|
||||||
|
Swagger-UI allows you to deeply link into tags and operations within a spec. When Swagger-UI is provided a URL fragment at runtime, it will automatically expand and scroll to a specified tag or operation.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
👉🏼 Add `deepLinking: true` to your Swagger-UI configuration to enable this functionality.
|
||||||
|
|
||||||
|
When you expand a tag or operation, Swagger-UI will automatically update its URL fragment with a deep link to the item.
|
||||||
|
Conversely, when you collapse a tag or operation, Swagger-UI will clear the URL fragment.
|
||||||
|
|
||||||
|
You can also right-click a tag name or operation path in order to copy a link to that tag or operation.
|
||||||
|
|
||||||
|
#### Fragment format
|
||||||
|
|
||||||
|
The fragment is formatted in one of two ways:
|
||||||
|
|
||||||
|
- `#/{tagName}`, to trigger the focus of a specific tag
|
||||||
|
- `#/{tagName}/{operationId}`, to trigger the focus of a specific operation within a tag
|
||||||
|
|
||||||
|
`operationId` is the explicit operationId provided in the spec, if one exists.
|
||||||
|
Otherwise, Swagger-UI generates an implicit operationId by combining the operation's path and method, and escaping non-alphanumeric characters.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
> I'm using Swagger-UI in an application that needs control of the URL fragment. How do I disable deep-linking?
|
||||||
|
|
||||||
|
This functionality is disabled by default, but you can pass `deepLinking: false` into Swagger-UI as a configuration item to be sure.
|
||||||
|
|
||||||
|
> Can I link to multiple tags or operations?
|
||||||
|
|
||||||
|
No, this is not supported.
|
||||||
|
|
||||||
|
> Can I collapse everything except the operation or tag I'm linking to?
|
||||||
|
|
||||||
|
Sure - use `docExpansion: none` to collapse all tags and operations. Your deep link will take precedence over the setting, so only the tag or operation you've specified will be expanded.
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
"redux-logger": "*",
|
"redux-logger": "*",
|
||||||
"reselect": "2.5.3",
|
"reselect": "2.5.3",
|
||||||
"sanitize-html": "^1.14.1",
|
"sanitize-html": "^1.14.1",
|
||||||
|
"scroll-to-element": "^2.0.0",
|
||||||
"serialize-error": "2.0.0",
|
"serialize-error": "2.0.0",
|
||||||
"shallowequal": "0.2.2",
|
"shallowequal": "0.2.2",
|
||||||
"swagger-client": "3.0.17",
|
"swagger-client": "3.0.17",
|
||||||
|
|||||||
@@ -17,16 +17,19 @@ export default class ArrayModel extends Component {
|
|||||||
render(){
|
render(){
|
||||||
let { getComponent, required, schema, depth, expandDepth } = this.props
|
let { getComponent, required, schema, depth, expandDepth } = this.props
|
||||||
let items = schema.get("items")
|
let items = schema.get("items")
|
||||||
|
let title = schema.get("title") || name
|
||||||
let properties = schema.filter( ( v, key) => ["type", "items", "$$ref"].indexOf(key) === -1 )
|
let properties = schema.filter( ( v, key) => ["type", "items", "$$ref"].indexOf(key) === -1 )
|
||||||
|
|
||||||
const ModelCollapse = getComponent("ModelCollapse")
|
const ModelCollapse = getComponent("ModelCollapse")
|
||||||
const Model = getComponent("Model")
|
const Model = getComponent("Model")
|
||||||
|
|
||||||
return <span className="model">
|
const titleEl = title &&
|
||||||
<span className="model-title">
|
<span className="model-title">
|
||||||
<span className="model-title__text">{ schema.get("title") }</span>
|
<span className="model-title__text">{ title }</span>
|
||||||
</span>
|
</span>
|
||||||
<ModelCollapse collapsed={ depth > expandDepth } collapsedContent="[...]">
|
|
||||||
|
return <span className="model">
|
||||||
|
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
|
||||||
[
|
[
|
||||||
<span><Model { ...this.props } schema={ items } required={ false }/></span>
|
<span><Model { ...this.props } schema={ items } required={ false }/></span>
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ export default class ModelCollapse extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
collapsedContent: PropTypes.any,
|
collapsedContent: PropTypes.any,
|
||||||
collapsed: PropTypes.bool,
|
collapsed: PropTypes.bool,
|
||||||
children: PropTypes.any
|
children: PropTypes.any,
|
||||||
|
title: PropTypes.element
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
collapsedContent: "{...}",
|
collapsedContent: "{...}",
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
|
title: null
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
@@ -31,11 +33,15 @@ export default class ModelCollapse extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (<span>
|
const {title} = this.props
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
|
||||||
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
||||||
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
|
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
|
||||||
</span>
|
</span>
|
||||||
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
|
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
|
||||||
</span>)
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,15 +38,13 @@ export default class ObjectModel extends Component {
|
|||||||
}
|
}
|
||||||
</span>)
|
</span>)
|
||||||
|
|
||||||
|
const titleEl = title && <span className="model-title">
|
||||||
return <span className="model">
|
|
||||||
{
|
|
||||||
title && <span className="model-title">
|
|
||||||
{ isRef && schema.get("$$ref") && <span className="model-hint">{ schema.get("$$ref") }</span> }
|
{ isRef && schema.get("$$ref") && <span className="model-hint">{ schema.get("$$ref") }</span> }
|
||||||
<span className="model-title__text">{ title }</span>
|
<span className="model-title__text">{ title }</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
|
||||||
<ModelCollapse collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
|
return <span className="model">
|
||||||
|
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
|
||||||
<span className="brace-open object">{ braceOpen }</span>
|
<span className="brace-open object">{ braceOpen }</span>
|
||||||
{
|
{
|
||||||
!isRef ? null : <JumpToPathSection name={ name }/>
|
!isRef ? null : <JumpToPathSection name={ name }/>
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ export default class Operation extends PureComponent {
|
|||||||
specActions,
|
specActions,
|
||||||
specSelectors,
|
specSelectors,
|
||||||
authActions,
|
authActions,
|
||||||
authSelectors
|
authSelectors,
|
||||||
|
getConfigs
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
let summary = operation.get("summary")
|
let summary = operation.get("summary")
|
||||||
@@ -141,6 +142,10 @@ export default class Operation extends PureComponent {
|
|||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent( "Markdown" )
|
||||||
const Schemes = getComponent( "schemes" )
|
const Schemes = getComponent( "schemes" )
|
||||||
|
|
||||||
|
const { deepLinking } = getConfigs()
|
||||||
|
|
||||||
|
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||||
|
|
||||||
// Merge in Live Response
|
// Merge in Live Response
|
||||||
if(response && response.size > 0) {
|
if(response && response.size > 0) {
|
||||||
let notDocumented = !responses.get(String(response.get("status")))
|
let notDocumented = !responses.get(String(response.get("status")))
|
||||||
@@ -152,11 +157,16 @@ export default class Operation extends PureComponent {
|
|||||||
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
|
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey} >
|
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
|
||||||
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
|
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
|
||||||
<span className="opblock-summary-method">{method.toUpperCase()}</span>
|
<span className="opblock-summary-method">{method.toUpperCase()}</span>
|
||||||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
||||||
|
<a
|
||||||
|
className="nostyle"
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
href={ isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : ""} >
|
||||||
<span>{path}</span>
|
<span>{path}</span>
|
||||||
|
</a>
|
||||||
<JumpToPath path={jumpToKey} />
|
<JumpToPath path={jumpToKey} />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -191,7 +201,9 @@ export default class Operation extends PureComponent {
|
|||||||
<div className="opblock-external-docs-wrapper">
|
<div className="opblock-external-docs-wrapper">
|
||||||
<h4 className="opblock-title_normal">Find more details</h4>
|
<h4 className="opblock-title_normal">Find more details</h4>
|
||||||
<div className="opblock-external-docs">
|
<div className="opblock-external-docs">
|
||||||
<span className="opblock-external-docs__description">{ externalDocs.get("description") }</span>
|
<span className="opblock-external-docs__description">
|
||||||
|
<Markdown source={ externalDocs.get("description") } />
|
||||||
|
</span>
|
||||||
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
|
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
|
||||||
</div>
|
</div>
|
||||||
</div> : null
|
</div> : null
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
|
import { helpers } from "swagger-client"
|
||||||
|
|
||||||
|
const { opId } = helpers
|
||||||
|
|
||||||
export default class Operations extends React.Component {
|
export default class Operations extends React.Component {
|
||||||
|
|
||||||
@@ -33,7 +36,15 @@ export default class Operations extends React.Component {
|
|||||||
const Collapse = getComponent("Collapse")
|
const Collapse = getComponent("Collapse")
|
||||||
|
|
||||||
let showSummary = layoutSelectors.showSummary()
|
let showSummary = layoutSelectors.showSummary()
|
||||||
let { docExpansion, displayOperationId, displayRequestDuration, maxDisplayedTags } = getConfigs()
|
let {
|
||||||
|
docExpansion,
|
||||||
|
displayOperationId,
|
||||||
|
displayRequestDuration,
|
||||||
|
maxDisplayedTags,
|
||||||
|
deepLinking
|
||||||
|
} = getConfigs()
|
||||||
|
|
||||||
|
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||||
|
|
||||||
let filter = layoutSelectors.currentFilter()
|
let filter = layoutSelectors.currentFilter()
|
||||||
|
|
||||||
@@ -62,8 +73,16 @@ export default class Operations extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}>
|
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}>
|
||||||
|
|
||||||
<h4 onClick={() => layoutActions.show(isShownKey, !showTag)} className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }>
|
<h4
|
||||||
|
onClick={() => layoutActions.show(isShownKey, !showTag)}
|
||||||
|
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
|
||||||
|
id={isShownKey.join("-")}>
|
||||||
|
<a
|
||||||
|
className="nostyle"
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
href={ isDeepLinkingEnabled ? `#/${tag}` : ""}>
|
||||||
<span>{tag}</span>
|
<span>{tag}</span>
|
||||||
|
</a>
|
||||||
{ !tagDescription ? null :
|
{ !tagDescription ? null :
|
||||||
<small>
|
<small>
|
||||||
{ tagDescription }
|
{ tagDescription }
|
||||||
@@ -81,11 +100,14 @@ export default class Operations extends React.Component {
|
|||||||
{
|
{
|
||||||
operations.map( op => {
|
operations.map( op => {
|
||||||
|
|
||||||
const isShownKey = ["operations", op.get("id"), tag]
|
|
||||||
const path = op.get("path", "")
|
const path = op.get("path", "")
|
||||||
const method = op.get("method", "")
|
const method = op.get("method", "")
|
||||||
const jumpToKey = `paths.${path}.${method}`
|
const jumpToKey = `paths.${path}.${method}`
|
||||||
|
|
||||||
|
const operationId =
|
||||||
|
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id")
|
||||||
|
const isShownKey = ["operations", tag, operationId]
|
||||||
|
|
||||||
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
|
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
|
||||||
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
|
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
|
||||||
const request = specSelectors.requestFor(op.get("path"), op.get("method"))
|
const request = specSelectors.requestFor(op.get("path"), op.get("method"))
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ function Markdown({ source }) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Remarkable
|
return <div className="markdown">
|
||||||
options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
|
<Remarkable
|
||||||
|
options={{html: true, typographer: true, breaks: true, linkify: true, linkTarget: "_blank"}}
|
||||||
source={sanitized}
|
source={sanitized}
|
||||||
></Remarkable>
|
></Remarkable>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
Markdown.propTypes = {
|
Markdown.propTypes = {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const CONFIGS = [
|
|||||||
"parameterMacro",
|
"parameterMacro",
|
||||||
"displayOperationId",
|
"displayOperationId",
|
||||||
"displayRequestDuration",
|
"displayRequestDuration",
|
||||||
|
"deepLinking",
|
||||||
]
|
]
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
@@ -61,6 +62,7 @@ module.exports = function SwaggerUI(opts) {
|
|||||||
custom: {},
|
custom: {},
|
||||||
displayOperationId: false,
|
displayOperationId: false,
|
||||||
displayRequestDuration: false,
|
displayRequestDuration: false,
|
||||||
|
deepLinking: false,
|
||||||
|
|
||||||
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
|
// 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.
|
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
|
||||||
|
|||||||
1
src/core/plugins/deep-linking/README.md
Normal file
1
src/core/plugins/deep-linking/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
See `docs/deep-linking.md`.
|
||||||
7
src/core/plugins/deep-linking/helpers.js
Normal file
7
src/core/plugins/deep-linking/helpers.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export const setHash = (value) => {
|
||||||
|
if(value) {
|
||||||
|
return history.pushState(null, null, `#${value}`)
|
||||||
|
} else {
|
||||||
|
return window.location.hash = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/core/plugins/deep-linking/index.js
Normal file
18
src/core/plugins/deep-linking/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// import reducers from "./reducers"
|
||||||
|
// import * as actions from "./actions"
|
||||||
|
// import * as selectors from "./selectors"
|
||||||
|
import * as specWrapActions from "./spec-wrap-actions"
|
||||||
|
import * as layoutWrapActions from "./layout-wrap-actions"
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
return {
|
||||||
|
statePlugins: {
|
||||||
|
spec: {
|
||||||
|
wrapActions: specWrapActions
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
wrapActions: layoutWrapActions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/core/plugins/deep-linking/layout-wrap-actions.js
Normal file
36
src/core/plugins/deep-linking/layout-wrap-actions.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { setHash } from "./helpers"
|
||||||
|
|
||||||
|
export const show = (ori, { getConfigs }) => (...args) => {
|
||||||
|
ori(...args)
|
||||||
|
|
||||||
|
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||||
|
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let [thing, shown] = args
|
||||||
|
let [type] = thing
|
||||||
|
|
||||||
|
if(type === "operations-tag" || type === "operations") {
|
||||||
|
if(!shown) {
|
||||||
|
return setHash("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type === "operations") {
|
||||||
|
let [, tag, operationId] = thing
|
||||||
|
setHash(`/${tag}/${operationId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type === "operations-tag") {
|
||||||
|
let [, tag] = thing
|
||||||
|
setHash(`/${tag}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(e) {
|
||||||
|
// This functionality is not mission critical, so if something goes wrong
|
||||||
|
// we'll just move on
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/core/plugins/deep-linking/spec-wrap-actions.js
Normal file
51
src/core/plugins/deep-linking/spec-wrap-actions.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import scrollTo from "scroll-to-element"
|
||||||
|
|
||||||
|
const SCROLL_OFFSET = -5
|
||||||
|
let hasHashBeenParsed = false
|
||||||
|
|
||||||
|
|
||||||
|
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||||
|
ori(...args)
|
||||||
|
|
||||||
|
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||||
|
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(window.location.hash && !hasHashBeenParsed ) {
|
||||||
|
let hash = window.location.hash.slice(1) // # is first character
|
||||||
|
|
||||||
|
if(hash[0] === "!") {
|
||||||
|
// Parse UI 2.x shebangs
|
||||||
|
hash = hash.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hash[0] === "/") {
|
||||||
|
// "/pet/addPet" => "pet/addPet"
|
||||||
|
// makes the split result cleaner
|
||||||
|
// also handles forgotten leading slash
|
||||||
|
hash = hash.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let [tag, operationId] = hash.split("/")
|
||||||
|
|
||||||
|
if(tag && operationId) {
|
||||||
|
// Pre-expand and scroll to the operation
|
||||||
|
layoutActions.show(["operations-tag", tag], true)
|
||||||
|
layoutActions.show(["operations", tag, operationId], true)
|
||||||
|
|
||||||
|
scrollTo(`#operations-${tag}-${operationId}`, {
|
||||||
|
offset: SCROLL_OFFSET
|
||||||
|
})
|
||||||
|
} else if(tag) {
|
||||||
|
// Pre-expand and scroll to the tag
|
||||||
|
layoutActions.show(["operations-tag", tag], true)
|
||||||
|
|
||||||
|
scrollTo(`#operations-tag-${tag}`, {
|
||||||
|
offset: SCROLL_OFFSET
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasHashBeenParsed = true
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import auth from "core/plugins/auth"
|
|||||||
import util from "core/plugins/util"
|
import util from "core/plugins/util"
|
||||||
import SplitPaneModePlugin from "core/plugins/split-pane-mode"
|
import SplitPaneModePlugin from "core/plugins/split-pane-mode"
|
||||||
import downloadUrlPlugin from "core/plugins/download-url"
|
import downloadUrlPlugin from "core/plugins/download-url"
|
||||||
|
import deepLinkingPlugin from "core/plugins/deep-linking"
|
||||||
|
|
||||||
import App from "core/components/app"
|
import App from "core/components/app"
|
||||||
import AuthorizationPopup from "core/components/auth/authorization-popup"
|
import AuthorizationPopup from "core/components/auth/authorization-popup"
|
||||||
@@ -131,6 +132,7 @@ export default function() {
|
|||||||
auth,
|
auth,
|
||||||
ast,
|
ast,
|
||||||
SplitPaneModePlugin,
|
SplitPaneModePlugin,
|
||||||
downloadUrlPlugin
|
downloadUrlPlugin,
|
||||||
|
deepLinkingPlugin
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -451,13 +451,13 @@ export const propChecker = (props, nextProps, objectList=[], ignoreList=[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const validateNumber = ( val ) => {
|
export const validateNumber = ( val ) => {
|
||||||
if ( !/^-?\d+(\.?\d+)?$/.test(val)) {
|
if (!/^-?\d+(\.?\d+)?$/.test(val)) {
|
||||||
return "Value must be a number"
|
return "Value must be a number"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const validateInteger = ( val ) => {
|
export const validateInteger = ( val ) => {
|
||||||
if ( !/^-?\d+$/.test(val)) {
|
if (!/^-?\d+$/.test(val)) {
|
||||||
return "Value must be an integer"
|
return "Value must be an integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,6 +485,10 @@ export const validateParam = (param, isXml) => {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( value === null || value === undefined ) {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
if ( type === "number" ) {
|
if ( type === "number" ) {
|
||||||
let err = validateNumber(value)
|
let err = validateNumber(value)
|
||||||
if (!err) return errors
|
if (!err) return errors
|
||||||
|
|||||||
@@ -390,6 +390,7 @@ body
|
|||||||
}
|
}
|
||||||
|
|
||||||
.opblock-description-wrapper,
|
.opblock-description-wrapper,
|
||||||
|
.opblock-external-docs-wrapper,
|
||||||
.opblock-title_normal
|
.opblock-title_normal
|
||||||
{
|
{
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -418,6 +419,12 @@ body
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opblock-external-docs-wrapper {
|
||||||
|
h4 {
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.execute-wrapper
|
.execute-wrapper
|
||||||
{
|
{
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -644,3 +651,16 @@ section
|
|||||||
@include text_headline();
|
@include text_headline();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.nostyle {
|
||||||
|
text-decoration: inherit;
|
||||||
|
color: inherit;
|
||||||
|
cursor: auto;
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
text-decoration: inherit;
|
||||||
|
color: inherit;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,6 +79,10 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: rgba(#000,.7);
|
background: rgba(#000,.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ describe("utils", function(){
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("validates numbers", function() {
|
it("validates numbers", function() {
|
||||||
|
// string instead of a number
|
||||||
param = fromJS({
|
param = fromJS({
|
||||||
required: false,
|
required: false,
|
||||||
type: "number",
|
type: "number",
|
||||||
@@ -221,9 +222,28 @@ describe("utils", function(){
|
|||||||
})
|
})
|
||||||
result = validateParam( param, false )
|
result = validateParam( param, false )
|
||||||
expect( result ).toEqual( ["Value must be a number"] )
|
expect( result ).toEqual( ["Value must be a number"] )
|
||||||
|
|
||||||
|
// undefined value
|
||||||
|
param = fromJS({
|
||||||
|
required: false,
|
||||||
|
type: "number",
|
||||||
|
value: undefined
|
||||||
|
})
|
||||||
|
result = validateParam( param, false )
|
||||||
|
expect( result ).toEqual( [] )
|
||||||
|
|
||||||
|
// null value
|
||||||
|
param = fromJS({
|
||||||
|
required: false,
|
||||||
|
type: "number",
|
||||||
|
value: null
|
||||||
|
})
|
||||||
|
result = validateParam( param, false )
|
||||||
|
expect( result ).toEqual( [] )
|
||||||
})
|
})
|
||||||
|
|
||||||
it("validates integers", function() {
|
it("validates integers", function() {
|
||||||
|
// string instead of integer
|
||||||
param = fromJS({
|
param = fromJS({
|
||||||
required: false,
|
required: false,
|
||||||
type: "integer",
|
type: "integer",
|
||||||
@@ -231,6 +251,24 @@ describe("utils", function(){
|
|||||||
})
|
})
|
||||||
result = validateParam( param, false )
|
result = validateParam( param, false )
|
||||||
expect( result ).toEqual( ["Value must be an integer"] )
|
expect( result ).toEqual( ["Value must be an integer"] )
|
||||||
|
|
||||||
|
// undefined value
|
||||||
|
param = fromJS({
|
||||||
|
required: false,
|
||||||
|
type: "integer",
|
||||||
|
value: undefined
|
||||||
|
})
|
||||||
|
result = validateParam( param, false )
|
||||||
|
expect( result ).toEqual( [] )
|
||||||
|
|
||||||
|
// null value
|
||||||
|
param = fromJS({
|
||||||
|
required: false,
|
||||||
|
type: "integer",
|
||||||
|
value: null
|
||||||
|
})
|
||||||
|
result = validateParam( param, false )
|
||||||
|
expect( result ).toEqual( [] )
|
||||||
})
|
})
|
||||||
|
|
||||||
it("validates arrays", function() {
|
it("validates arrays", function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user