Refactor deep-linking, in the process extracted out OperationsTag (#4349)
* add configsActions.loaded hook * add OperationTag to hold Operations * fix test for operations * refactor deep-linking plugin
This commit is contained in:
108
src/core/components/operation-tag.jsx
Normal file
108
src/core/components/operation-tag.jsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import Im from "immutable"
|
||||
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
|
||||
|
||||
export default class OperationTag extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
tagObj: Im.fromJS({}),
|
||||
tag: "",
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
tagObj: ImPropTypes.map.isRequired,
|
||||
tag: PropTypes.string.isRequired,
|
||||
|
||||
layoutSelectors: PropTypes.object.isRequired,
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
|
||||
children: PropTypes.element,
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
tagObj,
|
||||
tag,
|
||||
children,
|
||||
|
||||
layoutSelectors,
|
||||
layoutActions,
|
||||
getConfigs,
|
||||
getComponent,
|
||||
} = this.props
|
||||
|
||||
let {
|
||||
docExpansion,
|
||||
deepLinking,
|
||||
} = getConfigs()
|
||||
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
|
||||
const Collapse = getComponent("Collapse")
|
||||
const Markdown = getComponent("Markdown")
|
||||
const DeepLink = getComponent("DeepLink")
|
||||
|
||||
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
|
||||
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
|
||||
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
|
||||
|
||||
let isShownKey = ["operations-tag", createDeepLinkPath(tag)]
|
||||
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
|
||||
|
||||
return (
|
||||
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} >
|
||||
|
||||
<h4
|
||||
onClick={() => layoutActions.show(isShownKey, !showTag)}
|
||||
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
|
||||
id={isShownKey.join("-")}>
|
||||
<DeepLink
|
||||
enabled={isDeepLinkingEnabled}
|
||||
isShown={showTag}
|
||||
path={tag}
|
||||
text={tag} />
|
||||
{ !tagDescription ? <small></small> :
|
||||
<small>
|
||||
<Markdown source={tagDescription} />
|
||||
</small>
|
||||
}
|
||||
|
||||
<div>
|
||||
{ !tagExternalDocsDescription ? null :
|
||||
<small>
|
||||
{ tagExternalDocsDescription }
|
||||
{ tagExternalDocsUrl ? ": " : null }
|
||||
{ tagExternalDocsUrl ?
|
||||
<a
|
||||
href={sanitizeUrl(tagExternalDocsUrl)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
target={"_blank"}
|
||||
>{tagExternalDocsUrl}</a> : null
|
||||
}
|
||||
</small>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="expand-operation"
|
||||
title={showTag ? "Collapse operation": "Expand operation"}
|
||||
onClick={() => layoutActions.show(isShownKey, !showTag)}>
|
||||
|
||||
<svg className="arrow" width="20" height="20">
|
||||
<use href={showTag ? "#large-arrow-down" : "#large-arrow"} xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
|
||||
</svg>
|
||||
</button>
|
||||
</h4>
|
||||
|
||||
<Collapse isOpened={showTag}>
|
||||
{children}
|
||||
</Collapse>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import Im from "immutable"
|
||||
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
|
||||
|
||||
const SWAGGER2_OPERATION_METHODS = [
|
||||
"get", "put", "post", "delete", "options", "head", "patch"
|
||||
@@ -38,18 +37,12 @@ export default class Operations extends React.Component {
|
||||
let taggedOps = specSelectors.taggedOperations()
|
||||
|
||||
const OperationContainer = getComponent("OperationContainer", true)
|
||||
const Collapse = getComponent("Collapse")
|
||||
const Markdown = getComponent("Markdown")
|
||||
const DeepLink = getComponent("DeepLink")
|
||||
const OperationTag = getComponent("OperationTag")
|
||||
|
||||
let {
|
||||
docExpansion,
|
||||
maxDisplayedTags,
|
||||
deepLinking
|
||||
} = getConfigs()
|
||||
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
|
||||
let filter = layoutSelectors.currentFilter()
|
||||
|
||||
if (filter) {
|
||||
@@ -66,56 +59,16 @@ export default class Operations extends React.Component {
|
||||
<div>
|
||||
{
|
||||
taggedOps.map( (tagObj, tag) => {
|
||||
let operations = tagObj.get("operations")
|
||||
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
|
||||
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
|
||||
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
|
||||
|
||||
let isShownKey = ["operations-tag", createDeepLinkPath(tag)]
|
||||
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
|
||||
|
||||
const operations = tagObj.get("operations")
|
||||
return (
|
||||
<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" }
|
||||
id={isShownKey.join("-")}>
|
||||
<DeepLink
|
||||
enabled={isDeepLinkingEnabled}
|
||||
isShown={showTag}
|
||||
path={tag}
|
||||
text={tag} />
|
||||
{ !tagDescription ? <small></small> :
|
||||
<small>
|
||||
<Markdown source={tagDescription} />
|
||||
</small>
|
||||
}
|
||||
|
||||
<div>
|
||||
{ !tagExternalDocsDescription ? null :
|
||||
<small>
|
||||
{ tagExternalDocsDescription }
|
||||
{ tagExternalDocsUrl ? ": " : null }
|
||||
{ tagExternalDocsUrl ?
|
||||
<a
|
||||
href={sanitizeUrl(tagExternalDocsUrl)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
target={"_blank"}
|
||||
>{tagExternalDocsUrl}</a> : null
|
||||
}
|
||||
</small>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button className="expand-operation" title={showTag ? "Collapse operation": "Expand operation"} onClick={() => layoutActions.show(isShownKey, !showTag)}>
|
||||
<svg className="arrow" width="20" height="20">
|
||||
<use href={showTag ? "#large-arrow-down" : "#large-arrow"} xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
|
||||
</svg>
|
||||
</button>
|
||||
</h4>
|
||||
|
||||
<Collapse isOpened={showTag}>
|
||||
<OperationTag
|
||||
key={"operation-" + tag}
|
||||
tagObj={tagObj}
|
||||
tag={tag}
|
||||
layoutSelectors={layoutSelectors}
|
||||
layoutActions={layoutActions}
|
||||
getConfigs={getConfigs}
|
||||
getComponent={getComponent}>
|
||||
{
|
||||
operations.map( op => {
|
||||
const path = op.get("path")
|
||||
@@ -145,8 +98,9 @@ export default class Operations extends React.Component {
|
||||
/>
|
||||
}).toArray()
|
||||
}
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
|
||||
</OperationTag>
|
||||
)
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ module.exports = function SwaggerUI(opts) {
|
||||
|
||||
const defaults = {
|
||||
// Some general settings, that we floated to the top
|
||||
dom_id: null,
|
||||
dom_id: null, // eslint-disable-line camelcase
|
||||
domNode: null,
|
||||
spec: {},
|
||||
url: "",
|
||||
@@ -131,10 +131,6 @@ module.exports = function SwaggerUI(opts) {
|
||||
var system = store.getSystem()
|
||||
|
||||
const downloadSpec = (fetchedConfig) => {
|
||||
if(typeof constructorConfig !== "object") {
|
||||
return system
|
||||
}
|
||||
|
||||
let localConfig = system.specSelectors.getLocalConfig ? system.specSelectors.getLocalConfig() : {}
|
||||
let mergedConfig = deepExtend({}, localConfig, constructorConfig, fetchedConfig || {}, queryConfig)
|
||||
|
||||
@@ -144,6 +140,7 @@ module.exports = function SwaggerUI(opts) {
|
||||
}
|
||||
|
||||
store.setConfigs(mergedConfig)
|
||||
system.configsActions.loaded()
|
||||
|
||||
if (fetchedConfig !== null) {
|
||||
if (!queryConfig.url && typeof mergedConfig.spec === "object" && Object.keys(mergedConfig.spec).length) {
|
||||
@@ -171,15 +168,17 @@ module.exports = function SwaggerUI(opts) {
|
||||
return system
|
||||
}
|
||||
|
||||
let configUrl = queryConfig.config || constructorConfig.configUrl
|
||||
const configUrl = queryConfig.config || constructorConfig.configUrl
|
||||
|
||||
if (!configUrl || !system.specActions.getConfigByUrl || system.specActions.getConfigByUrl && !system.specActions.getConfigByUrl({
|
||||
if (!configUrl || !system.specActions || !system.specActions.getConfigByUrl || system.specActions.getConfigByUrl && !system.specActions.getConfigByUrl({
|
||||
url: configUrl,
|
||||
loadRemoteConfig: true,
|
||||
requestInterceptor: constructorConfig.requestInterceptor,
|
||||
responseInterceptor: constructorConfig.responseInterceptor,
|
||||
}, downloadSpec)) {
|
||||
return downloadSpec()
|
||||
} else {
|
||||
system.specActions.getConfigByUrl(configUrl, downloadSpec)
|
||||
}
|
||||
|
||||
return system
|
||||
|
||||
@@ -18,3 +18,7 @@ export function toggle(configName) {
|
||||
payload: configName,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hook
|
||||
export const loaded = () => () => {}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
// 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"
|
||||
import layout from "./layout"
|
||||
import OperationWrapper from "./operation-wrapper"
|
||||
import OperationTagWrapper from "./operation-tag-wrapper"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
return [layout, {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapActions: specWrapActions
|
||||
configs: {
|
||||
wrapActions: {
|
||||
loaded: (ori, system) => (...args) => {
|
||||
ori(...args)
|
||||
const hash = window.location.hash
|
||||
system.layoutActions.parseDeepLinkHash(hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
wrapActions: layoutWrapActions
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapComponents: {
|
||||
operation: OperationWrapper,
|
||||
OperationTag: OperationTagWrapper,
|
||||
},
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { setHash } from "./helpers"
|
||||
import { createDeepLinkPath } from "core/utils"
|
||||
|
||||
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(`/${createDeepLinkPath(tag)}/${createDeepLinkPath(operationId)}`)
|
||||
}
|
||||
|
||||
if(type === "operations-tag") {
|
||||
let [, tag] = thing
|
||||
setHash(`/${createDeepLinkPath(tag)}`)
|
||||
}
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
// This functionality is not mission critical, so if something goes wrong
|
||||
// we'll just move on
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
181
src/core/plugins/deep-linking/layout.js
Normal file
181
src/core/plugins/deep-linking/layout.js
Normal file
@@ -0,0 +1,181 @@
|
||||
import { setHash } from "./helpers"
|
||||
import zenscroll from "zenscroll"
|
||||
import Im, { fromJS } from "immutable"
|
||||
|
||||
const SCROLL_TO = "layout_scroll_to"
|
||||
const CLEAR_SCROLL_TO = "layout_clear_scroll"
|
||||
|
||||
export const show = (ori, { getConfigs, layoutSelectors }) => (...args) => {
|
||||
ori(...args)
|
||||
|
||||
if(!getConfigs().deepLinking) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let [tokenArray, shown] = args
|
||||
//Coerce in to array
|
||||
tokenArray = Array.isArray(tokenArray) ? tokenArray : [tokenArray]
|
||||
// Convert into something we can put in the URL hash
|
||||
// Or return empty, if we cannot
|
||||
const urlHashArray = layoutSelectors.urlHashArrayFromIsShownKey(tokenArray) // Will convert
|
||||
|
||||
// No hash friendly list?
|
||||
if(!urlHashArray.length)
|
||||
return
|
||||
|
||||
const [type, assetName] = urlHashArray
|
||||
|
||||
if (!shown) {
|
||||
return setHash("/")
|
||||
}
|
||||
|
||||
if (urlHashArray.length === 2) {
|
||||
setHash(`/${type}/${assetName}`)
|
||||
} else if (urlHashArray.length === 1) {
|
||||
setHash(`/${type}`)
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// This functionality is not mission critical, so if something goes wrong
|
||||
// we'll just move on
|
||||
console.error(e) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export const scrollTo = (path) => {
|
||||
return {
|
||||
type: SCROLL_TO,
|
||||
payload: Array.isArray(path) ? path : [path]
|
||||
}
|
||||
}
|
||||
|
||||
export const parseDeepLinkHash = (rawHash) => ({ layoutActions, layoutSelectors, getConfigs }) => {
|
||||
|
||||
if(!getConfigs().deepLinking) {
|
||||
return
|
||||
}
|
||||
|
||||
if(rawHash) {
|
||||
let hash = rawHash.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)
|
||||
}
|
||||
|
||||
const isShownKey = layoutSelectors.isShownKeyFromUrlHashArray(hash.split("/"))
|
||||
|
||||
layoutActions.show(isShownKey, true) // TODO: 'show' operation tag
|
||||
layoutActions.scrollTo(isShownKey)
|
||||
}
|
||||
}
|
||||
|
||||
export const readyToScroll = (isShownKey, ref) => (system) => {
|
||||
const scrollToKey = system.layoutSelectors.getScrollToKey()
|
||||
|
||||
if(Im.is(scrollToKey, fromJS(isShownKey))) {
|
||||
system.layoutActions.scrollToElement(ref)
|
||||
system.layoutActions.clearScrollTo()
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll to "ref" (dom node) with the scrollbar on "container" or the nearest parent
|
||||
export const scrollToElement = (ref, container) => (system) => {
|
||||
try {
|
||||
container = container || system.fn.getScrollParent(ref)
|
||||
let myScroller = zenscroll.createScroller(container)
|
||||
myScroller.to(ref)
|
||||
} catch(e) {
|
||||
console.error(e) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export const clearScrollTo = () => {
|
||||
return {
|
||||
type: CLEAR_SCROLL_TO,
|
||||
}
|
||||
}
|
||||
|
||||
// From: https://stackoverflow.com/a/42543908/3933724
|
||||
// Modified to return html instead of body element as last resort
|
||||
function getScrollParent(element, includeHidden) {
|
||||
const LAST_RESORT = document.documentElement
|
||||
let style = getComputedStyle(element)
|
||||
const excludeStaticParent = style.position === "absolute"
|
||||
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/
|
||||
|
||||
if (style.position === "fixed")
|
||||
return LAST_RESORT
|
||||
for (let parent = element; (parent = parent.parentElement);) {
|
||||
style = getComputedStyle(parent)
|
||||
if (excludeStaticParent && style.position === "static") {
|
||||
continue
|
||||
}
|
||||
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX))
|
||||
return parent
|
||||
}
|
||||
|
||||
return LAST_RESORT
|
||||
}
|
||||
|
||||
export default {
|
||||
fn: {
|
||||
getScrollParent,
|
||||
},
|
||||
statePlugins: {
|
||||
layout: {
|
||||
actions: {
|
||||
scrollToElement,
|
||||
scrollTo,
|
||||
clearScrollTo,
|
||||
readyToScroll,
|
||||
parseDeepLinkHash
|
||||
},
|
||||
selectors: {
|
||||
getScrollToKey(state) {
|
||||
return state.get("scrollToKey")
|
||||
},
|
||||
isShownKeyFromUrlHashArray(state, urlHashArray) {
|
||||
const [tag, operationId] = urlHashArray
|
||||
// We only put operations in the URL
|
||||
if(operationId) {
|
||||
return ["operations", tag, operationId]
|
||||
} else if (tag) {
|
||||
return ["operations-tag", tag]
|
||||
}
|
||||
return []
|
||||
},
|
||||
urlHashArrayFromIsShownKey(state, isShownKey) {
|
||||
let [type, tag, operationId] = isShownKey
|
||||
// We only put operations in the URL
|
||||
if(type == "operations") {
|
||||
return [tag, operationId]
|
||||
} else if (type == "operations-tag") {
|
||||
return [tag]
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
reducers: {
|
||||
[SCROLL_TO](state, action) {
|
||||
return state.set("scrollToKey", Im.fromJS(action.payload))
|
||||
},
|
||||
[CLEAR_SCROLL_TO](state) {
|
||||
return state.delete("scrollToKey")
|
||||
}
|
||||
},
|
||||
wrapActions: {
|
||||
show
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/core/plugins/deep-linking/operation-tag-wrapper.jsx
Normal file
25
src/core/plugins/deep-linking/operation-tag-wrapper.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from "react"
|
||||
import { PropTypes } from "prop-types"
|
||||
|
||||
const Wrapper = (Ori, system) => class OperationTagWrapper extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
tag: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
onLoad = (ref) => {
|
||||
const { tag } = this.props
|
||||
const isShownKey = ["operations-tag", tag]
|
||||
system.layoutActions.readyToScroll(isShownKey, ref)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span ref={this.onLoad}>
|
||||
<Ori {...this.props} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Wrapper
|
||||
26
src/core/plugins/deep-linking/operation-wrapper.jsx
Normal file
26
src/core/plugins/deep-linking/operation-wrapper.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from "react"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
const Wrapper = (Ori, system) => class OperationWrapper extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
operation: ImPropTypes.map.isRequired,
|
||||
}
|
||||
|
||||
onLoad = (ref) => {
|
||||
const { operation } = this.props
|
||||
const { tag, operationId } = operation.toObject()
|
||||
const isShownKey = ["operations", tag, operationId]
|
||||
system.layoutActions.readyToScroll(isShownKey, ref)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span ref={this.onLoad}>
|
||||
<Ori {...this.props} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Wrapper
|
||||
@@ -1,64 +0,0 @@
|
||||
import zenscroll from "zenscroll"
|
||||
import { escapeDeepLinkPath } from "core/utils"
|
||||
|
||||
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
|
||||
|
||||
|
||||
export const updateJsonSpec = (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("/")
|
||||
|
||||
let swaggerUI = document.querySelector(".swagger-ui")
|
||||
let myScroller = zenscroll.createScroller(swaggerUI)
|
||||
|
||||
let target
|
||||
|
||||
if(tag && operationId) {
|
||||
// Pre-expand and scroll to the operation
|
||||
layoutActions.show(["operations-tag", tag], true)
|
||||
layoutActions.show(["operations", tag, operationId], true)
|
||||
|
||||
target = document
|
||||
.getElementById(`operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`)
|
||||
} else if(tag) {
|
||||
// Pre-expand and scroll to the tag
|
||||
layoutActions.show(["operations-tag", tag], true)
|
||||
|
||||
target = document.getElementById(`operations-tag-${escapeDeepLinkPath(tag)}`)
|
||||
}
|
||||
|
||||
|
||||
if(target) {
|
||||
myScroller.to(target)
|
||||
setTimeout(() => {
|
||||
// Backup functionality: if we're still at the top of the document,
|
||||
// scroll on the entire page (not within the Swagger-UI container)
|
||||
if(zenscroll.getY() === 0) {
|
||||
zenscroll.to(target)
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
|
||||
hasHashBeenParsed = true
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import Clear from "core/components/clear"
|
||||
import LiveResponse from "core/components/live-response"
|
||||
import OnlineValidatorBadge from "core/components/online-validator-badge"
|
||||
import Operations from "core/components/operations"
|
||||
import OperationTag from "core/components/operation-tag"
|
||||
import Operation from "core/components/operation"
|
||||
import OperationExt from "core/components/operation-extensions"
|
||||
import OperationExtRow from "core/components/operation-extension-row"
|
||||
@@ -128,6 +129,7 @@ export default function() {
|
||||
OperationExt,
|
||||
OperationExtRow,
|
||||
ParameterExt,
|
||||
OperationTag,
|
||||
OperationContainer,
|
||||
DeepLink,
|
||||
InfoUrl,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env mocha */
|
||||
import React from "react"
|
||||
import expect, { createSpy } from "expect"
|
||||
import expect from "expect"
|
||||
import { render } from "enzyme"
|
||||
import { fromJS } from "immutable"
|
||||
import DeepLink from "components/deep-link"
|
||||
@@ -10,7 +10,8 @@ import {Collapse} from "components/layout-utils"
|
||||
const components = {
|
||||
Collapse,
|
||||
DeepLink,
|
||||
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />
|
||||
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />,
|
||||
OperationTag: "div",
|
||||
}
|
||||
|
||||
describe("<Operations/>", function(){
|
||||
|
||||
Reference in New Issue
Block a user