From 430e2a970f1509566eda4a8a3bd33ab5dd8ca764 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Wed, 13 Sep 2017 17:20:37 -0600 Subject: [PATCH 1/3] Fixes #3596 Wrap `isShownKey` values in a function that replaces spaces with underscores. When parsing the hash on route change, replace the spaces in the values with underscores again. --- src/core/components/operations.jsx | 6 +++--- src/core/plugins/deep-linking/spec-wrap-actions.js | 3 ++- src/core/utils.js | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 2aec664a..d10f8a5b 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -1,7 +1,7 @@ import React from "react" import PropTypes from "prop-types" import { helpers } from "swagger-client" - +import { replaceSpacesWithUnderscores } from "core/utils" const { opId } = helpers export default class Operations extends React.Component { @@ -69,7 +69,7 @@ export default class Operations extends React.Component { let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"]) let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) - let isShownKey = ["operations-tag", tag] + let isShownKey = ["operations-tag", replaceSpacesWithUnderscores(tag)] let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list") return ( @@ -124,7 +124,7 @@ export default class Operations extends React.Component { 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 isShownKey = ["operations", replaceSpacesWithUnderscores(tag), replaceSpacesWithUnderscores(operationId)] const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method")) const response = specSelectors.responseFor(op.get("path"), op.get("method")) diff --git a/src/core/plugins/deep-linking/spec-wrap-actions.js b/src/core/plugins/deep-linking/spec-wrap-actions.js index bb13a6ff..79665536 100644 --- a/src/core/plugins/deep-linking/spec-wrap-actions.js +++ b/src/core/plugins/deep-linking/spec-wrap-actions.js @@ -1,4 +1,5 @@ import scrollTo from "scroll-to-element" +import { replaceSpacesWithUnderscores } from "core/utils" const SCROLL_OFFSET = -5 let hasHashBeenParsed = false @@ -27,7 +28,7 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) hash = hash.slice(1) } - let [tag, operationId] = hash.split("/") + let [tag, operationId] = hash.split("/").map(v => replaceSpacesWithUnderscores(v)) if(tag && operationId) { // Pre-expand and scroll to the operation diff --git a/src/core/utils.js b/src/core/utils.js index 743c4749..75ee0f08 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -650,3 +650,5 @@ export const shallowEqualKeys = (a,b, keys) => { return eq(a[key], b[key]) }) } + +export const replaceSpacesWithUnderscores = (str) => str.replace(/\s/, "_") From a6e070ac76bb29f9f3bcb8a4fb93a07dd55c3df4 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Wed, 13 Sep 2017 21:32:46 -0600 Subject: [PATCH 2/3] Replace spaces with underscores when setting the hash value and inserting the ID into the DOM. Escape the deep link path when querying for the DOM element on hash change. --- package.json | 1 + src/core/components/operations.jsx | 6 +++--- src/core/plugins/deep-linking/layout-wrap-actions.js | 5 +++-- src/core/plugins/deep-linking/spec-wrap-actions.js | 8 ++++---- src/core/utils.js | 4 +++- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 709752ff..8d85d26f 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dependencies": { "base64-js": "^1.2.0", "brace": "0.7.0", + "css.escape": "1.5.1", "deep-extend": "0.4.1", "expect": "1.20.2", "getbase": "^2.8.2", diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index d10f8a5b..e0bf0645 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -1,7 +1,7 @@ import React from "react" import PropTypes from "prop-types" import { helpers } from "swagger-client" -import { replaceSpacesWithUnderscores } from "core/utils" +import { createDeepLinkPath } from "core/utils" const { opId } = helpers export default class Operations extends React.Component { @@ -69,7 +69,7 @@ export default class Operations extends React.Component { let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"]) let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) - let isShownKey = ["operations-tag", replaceSpacesWithUnderscores(tag)] + let isShownKey = ["operations-tag", createDeepLinkPath(tag)] let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list") return ( @@ -124,7 +124,7 @@ export default class Operations extends React.Component { const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id") - const isShownKey = ["operations", replaceSpacesWithUnderscores(tag), replaceSpacesWithUnderscores(operationId)] + const isShownKey = ["operations", createDeepLinkPath(tag), createDeepLinkPath(operationId)] const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method")) const response = specSelectors.responseFor(op.get("path"), op.get("method")) diff --git a/src/core/plugins/deep-linking/layout-wrap-actions.js b/src/core/plugins/deep-linking/layout-wrap-actions.js index 72d98948..f1e67d12 100644 --- a/src/core/plugins/deep-linking/layout-wrap-actions.js +++ b/src/core/plugins/deep-linking/layout-wrap-actions.js @@ -1,4 +1,5 @@ import { setHash } from "./helpers" +import { createDeepLinkPath } from "core/utils" export const show = (ori, { getConfigs }) => (...args) => { ori(...args) @@ -19,12 +20,12 @@ export const show = (ori, { getConfigs }) => (...args) => { if(type === "operations") { let [, tag, operationId] = thing - setHash(`/${tag}/${operationId}`) + setHash(`/${createDeepLinkPath(tag)}/${createDeepLinkPath(operationId)}`) } if(type === "operations-tag") { let [, tag] = thing - setHash(`/${tag}`) + setHash(`/${createDeepLinkPath(tag)}`) } } diff --git a/src/core/plugins/deep-linking/spec-wrap-actions.js b/src/core/plugins/deep-linking/spec-wrap-actions.js index 79665536..ebeebe5c 100644 --- a/src/core/plugins/deep-linking/spec-wrap-actions.js +++ b/src/core/plugins/deep-linking/spec-wrap-actions.js @@ -1,5 +1,5 @@ import scrollTo from "scroll-to-element" -import { replaceSpacesWithUnderscores } from "core/utils" +import { escapeDeepLinkPath } from "core/utils" const SCROLL_OFFSET = -5 let hasHashBeenParsed = false @@ -28,21 +28,21 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) hash = hash.slice(1) } - let [tag, operationId] = hash.split("/").map(v => replaceSpacesWithUnderscores(v)) + 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}`, { + scrollTo(`#operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`, { offset: SCROLL_OFFSET }) } else if(tag) { // Pre-expand and scroll to the tag layoutActions.show(["operations-tag", tag], true) - scrollTo(`#operations-tag-${tag}`, { + scrollTo(`#operations-tag-${escapeDeepLinkPath(tag)}`, { offset: SCROLL_OFFSET }) } diff --git a/src/core/utils.js b/src/core/utils.js index 75ee0f08..eda25c3f 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -8,6 +8,7 @@ import some from "lodash/some" import eq from "lodash/eq" import { memoizedSampleFromSchema, memoizedCreateXMLExample } from "core/plugins/samples/fn" import win from "./window" +import cssEscape from "css.escape" const DEFAULT_REPONSE_KEY = "default" @@ -651,4 +652,5 @@ export const shallowEqualKeys = (a,b, keys) => { }) } -export const replaceSpacesWithUnderscores = (str) => str.replace(/\s/, "_") +export const createDeepLinkPath = (str) => str.replace(/\s/g, "_") +export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) ) \ No newline at end of file From 840912cb567011d5967fe977197063aa4e7fc40b Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Wed, 13 Sep 2017 21:38:07 -0600 Subject: [PATCH 3/3] Handle null value in createDeepLinkPath --- src/core/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/utils.js b/src/core/utils.js index eda25c3f..4a2845b7 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -652,5 +652,5 @@ export const shallowEqualKeys = (a,b, keys) => { }) } -export const createDeepLinkPath = (str) => str.replace(/\s/g, "_") +export const createDeepLinkPath = (str) => str ? str.replace(/\s/g, "_") : "" export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) ) \ No newline at end of file