feat: consolidate syntax highlighting code into standalone plugin (#9783)
This commit is contained in:
@@ -1,32 +1,30 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import { CopyToClipboard } from "react-copy-to-clipboard"
|
import { CopyToClipboard } from "react-copy-to-clipboard"
|
||||||
import {SyntaxHighlighter, getStyle} from "core/syntax-highlighting"
|
|
||||||
import get from "lodash/get"
|
import get from "lodash/get"
|
||||||
import { requestSnippetGenerator_curl_bash } from "../plugins/request-snippets/fn"
|
import { requestSnippetGenerator_curl_bash } from "../plugins/request-snippets/fn"
|
||||||
|
|
||||||
export default class Curl extends React.Component {
|
export default class Curl extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
getConfigs: PropTypes.func.isRequired,
|
getConfigs: PropTypes.func.isRequired,
|
||||||
|
getComponent: PropTypes.func.isRequired,
|
||||||
request: PropTypes.object.isRequired
|
request: PropTypes.object.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { request, getConfigs } = this.props
|
let { request, getConfigs, getComponent } = this.props
|
||||||
let curl = requestSnippetGenerator_curl_bash(request)
|
let curl = requestSnippetGenerator_curl_bash(request)
|
||||||
|
|
||||||
const config = getConfigs()
|
const config = getConfigs()
|
||||||
|
const SyntaxHighlighter = getComponent("SyntaxHighlighter", true)
|
||||||
|
|
||||||
const curlBlock = get(config, "syntaxHighlight.activated")
|
const curlBlock = get(config, "syntaxHighlight.activated") ? (
|
||||||
? <SyntaxHighlighter
|
<SyntaxHighlighter language="bash" className="curl microlight">
|
||||||
language="bash"
|
{curl}
|
||||||
className="curl microlight"
|
</SyntaxHighlighter>
|
||||||
style={getStyle(get(config, "syntaxHighlight.theme"))}
|
) : (
|
||||||
>
|
|
||||||
{curl}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
:
|
|
||||||
<textarea readOnly={true} className="curl" value={curl}></textarea>
|
<textarea readOnly={true} className="curl" value={curl}></textarea>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="curl-command">
|
<div className="curl-command">
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ export default function Example(props) {
|
|||||||
const { example, showValue, getComponent, getConfigs } = props
|
const { example, showValue, getComponent, getConfigs } = props
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown", true)
|
const Markdown = getComponent("Markdown", true)
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("HighlightCode", true)
|
||||||
|
|
||||||
if(!example) return null
|
if (!example) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="example">
|
<div className="example">
|
||||||
@@ -28,7 +28,10 @@ export default function Example(props) {
|
|||||||
{showValue && example.has("value") ? (
|
{showValue && example.has("value") ? (
|
||||||
<section className="example__section">
|
<section className="example__section">
|
||||||
<div className="example__section-header">Example Value</div>
|
<div className="example__section-header">Example Value</div>
|
||||||
<HighlightCode getConfigs={ getConfigs } value={stringify(example.get("value"))} />
|
<HighlightCode
|
||||||
|
getConfigs={getConfigs}
|
||||||
|
value={stringify(example.get("value"))}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -69,13 +69,14 @@ export default class LiveResponse extends React.Component {
|
|||||||
const hasHeaders = returnObject.length !== 0
|
const hasHeaders = returnObject.length !== 0
|
||||||
const Markdown = getComponent("Markdown", true)
|
const Markdown = getComponent("Markdown", true)
|
||||||
const RequestSnippets = getComponent("RequestSnippets", true)
|
const RequestSnippets = getComponent("RequestSnippets", true)
|
||||||
const Curl = getComponent("curl")
|
const Curl = getComponent("curl", true)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{ curlRequest && (requestSnippetsEnabled === true || requestSnippetsEnabled === "true"
|
{ curlRequest && (requestSnippetsEnabled === true || requestSnippetsEnabled === "true"
|
||||||
? <RequestSnippets request={ curlRequest }/>
|
? <RequestSnippets request={ curlRequest }/>
|
||||||
: <Curl request={ curlRequest } getConfigs={ getConfigs } />) }
|
: <Curl request={ curlRequest } />
|
||||||
|
)}
|
||||||
{ url && <div>
|
{ url && <div>
|
||||||
<div className="request-url">
|
<div className="request-url">
|
||||||
<h4>Request URL</h4>
|
<h4>Request URL</h4>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const ModelExample = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { defaultModelRendering, defaultModelExpandDepth } = getConfigs()
|
const { defaultModelRendering, defaultModelExpandDepth } = getConfigs()
|
||||||
const ModelWrapper = getComponent("ModelWrapper")
|
const ModelWrapper = getComponent("ModelWrapper")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("HighlightCode", true)
|
||||||
const exampleTabId = randomBytes(5).toString("base64")
|
const exampleTabId = randomBytes(5).toString("base64")
|
||||||
const examplePanelId = randomBytes(5).toString("base64")
|
const examplePanelId = randomBytes(5).toString("base64")
|
||||||
const modelTabId = randomBytes(5).toString("base64")
|
const modelTabId = randomBytes(5).toString("base64")
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export default class ParamBody extends PureComponent {
|
|||||||
|
|
||||||
const Button = getComponent("Button")
|
const Button = getComponent("Button")
|
||||||
const TextArea = getComponent("TextArea")
|
const TextArea = getComponent("TextArea")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("HighlightCode", true)
|
||||||
const ContentType = getComponent("contentType")
|
const ContentType = getComponent("contentType")
|
||||||
// for domains where specSelectors not passed
|
// for domains where specSelectors not passed
|
||||||
let parameter = specSelectors ? specSelectors.parameterWithMetaByIdentity(pathMethod, param) : param
|
let parameter = specSelectors ? specSelectors.parameterWithMetaByIdentity(pathMethod, param) : param
|
||||||
@@ -148,7 +148,7 @@ export default class ParamBody extends PureComponent {
|
|||||||
contentTypes={ consumes }
|
contentTypes={ consumes }
|
||||||
onChange={onChangeConsumes}
|
onChange={onChangeConsumes}
|
||||||
className="body-param-content-type"
|
className="body-param-content-type"
|
||||||
ariaLabel="Parameter content type"
|
ariaLabel="Parameter content type"
|
||||||
controlId={controlId}
|
controlId={controlId}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export default class ResponseBody extends React.PureComponent {
|
|||||||
render() {
|
render() {
|
||||||
let { content, contentType, url, headers={}, getConfigs, getComponent } = this.props
|
let { content, contentType, url, headers={}, getConfigs, getComponent } = this.props
|
||||||
const { parsedContent } = this.state
|
const { parsedContent } = this.state
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("HighlightCode", true)
|
||||||
const downloadName = "response_" + new Date().getTime()
|
const downloadName = "response_" + new Date().getTime()
|
||||||
let body, bodyEl
|
let body, bodyEl
|
||||||
url = url || ""
|
url = url || ""
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export default class Response extends React.Component {
|
|||||||
let links = response.get("links")
|
let links = response.get("links")
|
||||||
const ResponseExtension = getComponent("ResponseExtension")
|
const ResponseExtension = getComponent("ResponseExtension")
|
||||||
const Headers = getComponent("headers")
|
const Headers = getComponent("headers")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("HighlightCode", true)
|
||||||
const ModelExample = getComponent("modelExample")
|
const ModelExample = getComponent("modelExample")
|
||||||
const Markdown = getComponent("Markdown", true)
|
const Markdown = getComponent("Markdown", true)
|
||||||
const OperationLink = getComponent("operationLink")
|
const OperationLink = getComponent("operationLink")
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import UtilPlugin from "./plugins/util"
|
|||||||
import ViewPlugin from "./plugins/view"
|
import ViewPlugin from "./plugins/view"
|
||||||
import ViewLegacyPlugin from "core/plugins/view-legacy"
|
import ViewLegacyPlugin from "core/plugins/view-legacy"
|
||||||
import DownloadUrlPlugin from "./plugins/download-url"
|
import DownloadUrlPlugin from "./plugins/download-url"
|
||||||
|
import SyntaxHighlightingPlugin from "core/plugins/syntax-highlighting"
|
||||||
import SafeRenderPlugin from "./plugins/safe-render"
|
import SafeRenderPlugin from "./plugins/safe-render"
|
||||||
|
|
||||||
import { parseSearch } from "./utils"
|
import { parseSearch } from "./utils"
|
||||||
@@ -271,5 +272,6 @@ SwaggerUI.plugins = {
|
|||||||
View: ViewPlugin,
|
View: ViewPlugin,
|
||||||
ViewLegacy: ViewLegacyPlugin,
|
ViewLegacy: ViewLegacyPlugin,
|
||||||
DownloadUrl: DownloadUrlPlugin,
|
DownloadUrl: DownloadUrlPlugin,
|
||||||
|
SyntaxHighlighting: SyntaxHighlightingPlugin,
|
||||||
SafeRender: SafeRenderPlugin,
|
SafeRender: SafeRenderPlugin,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const RequestBody = ({
|
|||||||
const Markdown = getComponent("Markdown", true)
|
const Markdown = getComponent("Markdown", true)
|
||||||
const ModelExample = getComponent("modelExample")
|
const ModelExample = getComponent("modelExample")
|
||||||
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("HighlightCode", true)
|
||||||
const ExamplesSelectValueRetainer = getComponent("ExamplesSelectValueRetainer")
|
const ExamplesSelectValueRetainer = getComponent("ExamplesSelectValueRetainer")
|
||||||
const Example = getComponent("Example")
|
const Example = getComponent("Example")
|
||||||
const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty")
|
const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty")
|
||||||
@@ -152,7 +152,7 @@ const RequestBody = ({
|
|||||||
{
|
{
|
||||||
Map.isMap(bodyProperties) && bodyProperties.entrySeq().map(([key, schema]) => {
|
Map.isMap(bodyProperties) && bodyProperties.entrySeq().map(([key, schema]) => {
|
||||||
if (schema.get("readOnly")) return
|
if (schema.get("readOnly")) return
|
||||||
|
|
||||||
const oneOf = schema.get("oneOf")?.get(0)?.toJS()
|
const oneOf = schema.get("oneOf")?.get(0)?.toJS()
|
||||||
const anyOf = schema.get("anyOf")?.get(0)?.toJS()
|
const anyOf = schema.get("anyOf")?.get(0)?.toJS()
|
||||||
schema = fromJS(fn.mergeJsonSchema(schema.toJS(), oneOf ?? anyOf ?? {}))
|
schema = fromJS(fn.mergeJsonSchema(schema.toJS(), oneOf ?? anyOf ?? {}))
|
||||||
@@ -169,7 +169,7 @@ const RequestBody = ({
|
|||||||
let initialValue = fn.getSampleSchema(schema, false, {
|
let initialValue = fn.getSampleSchema(schema, false, {
|
||||||
includeWriteOnly: true
|
includeWriteOnly: true
|
||||||
})
|
})
|
||||||
|
|
||||||
if (initialValue === false) {
|
if (initialValue === false) {
|
||||||
initialValue = "false"
|
initialValue = "false"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import PropTypes from "prop-types"
|
|||||||
import get from "lodash/get"
|
import get from "lodash/get"
|
||||||
import isFunction from "lodash/isFunction"
|
import isFunction from "lodash/isFunction"
|
||||||
import { CopyToClipboard } from "react-copy-to-clipboard"
|
import { CopyToClipboard } from "react-copy-to-clipboard"
|
||||||
import { SyntaxHighlighter, getStyle } from "core/syntax-highlighting"
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
@@ -42,6 +41,7 @@ const RequestSnippets = ({ request, requestSnippetsSelectors, getConfigs, getCom
|
|||||||
|
|
||||||
const ArrowIcon = getComponent("ArrowUpIcon")
|
const ArrowIcon = getComponent("ArrowUpIcon")
|
||||||
const ArrowDownIcon = getComponent("ArrowDownIcon")
|
const ArrowDownIcon = getComponent("ArrowDownIcon")
|
||||||
|
const SyntaxHighlighter = getComponent("SyntaxHighlighter", true)
|
||||||
|
|
||||||
const [activeLanguage, setActiveLanguage] = useState(requestSnippetsSelectors.getSnippetGenerators()?.keySeq().first())
|
const [activeLanguage, setActiveLanguage] = useState(requestSnippetsSelectors.getSnippetGenerators()?.keySeq().first())
|
||||||
const [isExpanded, setIsExpanded] = useState(requestSnippetsSelectors?.getDefaultExpanded())
|
const [isExpanded, setIsExpanded] = useState(requestSnippetsSelectors?.getDefaultExpanded())
|
||||||
@@ -99,16 +99,16 @@ const RequestSnippets = ({ request, requestSnippetsSelectors, getConfigs, getCom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SnippetComponent = canSyntaxHighlight
|
const SnippetComponent = canSyntaxHighlight ? (
|
||||||
? <SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language={activeGenerator.get("syntax")}
|
language={activeGenerator.get("syntax")}
|
||||||
className="curl microlight"
|
className="curl microlight"
|
||||||
style={getStyle(get(config, "syntaxHighlight.theme"))}
|
|
||||||
>
|
>
|
||||||
{snippet}
|
{snippet}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
:
|
) : (
|
||||||
<textarea readOnly={true} className="curl" value={snippet}></textarea>
|
<textarea readOnly={true} className="curl" value={snippet}></textarea>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="request-snippets" ref={rootRef}>
|
<div className="request-snippets" ref={rootRef}>
|
||||||
@@ -147,7 +147,7 @@ const RequestSnippets = ({ request, requestSnippetsSelectors, getConfigs, getCom
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestSnippets.propTypes = {
|
RequestSnippets.propTypes = {
|
||||||
|
|||||||
25
src/core/plugins/syntax-highlighting/after-load.js
Normal file
25
src/core/plugins/syntax-highlighting/after-load.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/light"
|
||||||
|
import js from "react-syntax-highlighter/dist/esm/languages/hljs/javascript"
|
||||||
|
import json from "react-syntax-highlighter/dist/esm/languages/hljs/json"
|
||||||
|
import xml from "react-syntax-highlighter/dist/esm/languages/hljs/xml"
|
||||||
|
import bash from "react-syntax-highlighter/dist/esm/languages/hljs/bash"
|
||||||
|
import yaml from "react-syntax-highlighter/dist/esm/languages/hljs/yaml"
|
||||||
|
import http from "react-syntax-highlighter/dist/esm/languages/hljs/http"
|
||||||
|
import powershell from "react-syntax-highlighter/dist/esm/languages/hljs/powershell"
|
||||||
|
import javascript from "react-syntax-highlighter/dist/esm/languages/hljs/javascript"
|
||||||
|
|
||||||
|
const afterLoad = () => {
|
||||||
|
SyntaxHighlighter.registerLanguage("json", json)
|
||||||
|
SyntaxHighlighter.registerLanguage("js", js)
|
||||||
|
SyntaxHighlighter.registerLanguage("xml", xml)
|
||||||
|
SyntaxHighlighter.registerLanguage("yaml", yaml)
|
||||||
|
SyntaxHighlighter.registerLanguage("http", http)
|
||||||
|
SyntaxHighlighter.registerLanguage("bash", bash)
|
||||||
|
SyntaxHighlighter.registerLanguage("powershell", powershell)
|
||||||
|
SyntaxHighlighter.registerLanguage("javascript", javascript)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default afterLoad
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
import React, { useRef, useEffect } from "react"
|
import React, { useRef, useEffect } from "react"
|
||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import cx from "classnames"
|
import classNames from "classnames"
|
||||||
import {SyntaxHighlighter, getStyle} from "core/syntax-highlighting"
|
|
||||||
import get from "lodash/get"
|
import get from "lodash/get"
|
||||||
import isFunction from "lodash/isFunction"
|
|
||||||
import saveAs from "js-file-download"
|
import saveAs from "js-file-download"
|
||||||
import { CopyToClipboard } from "react-copy-to-clipboard"
|
import { CopyToClipboard } from "react-copy-to-clipboard"
|
||||||
|
|
||||||
const HighlightCode = ({value, fileName = "response.txt", className, downloadable, getConfigs, canCopy, language}) => {
|
const HighlightCode = ({
|
||||||
const config = isFunction(getConfigs) ? getConfigs() : null
|
value,
|
||||||
const canSyntaxHighlight = get(config, "syntaxHighlight") !== false && get(config, "syntaxHighlight.activated", true)
|
fileName = "response.txt",
|
||||||
|
className,
|
||||||
|
downloadable,
|
||||||
|
getConfigs,
|
||||||
|
getComponent,
|
||||||
|
canCopy,
|
||||||
|
language,
|
||||||
|
}) => {
|
||||||
|
const config = getConfigs()
|
||||||
|
const canSyntaxHighlight =
|
||||||
|
get(config, "syntaxHighlight") !== false &&
|
||||||
|
get(config, "syntaxHighlight.activated", true)
|
||||||
const rootRef = useRef(null)
|
const rootRef = useRef(null)
|
||||||
|
|
||||||
useEffect(() => {
|
const SyntaxHighlighter = getComponent("SyntaxHighlighter", true)
|
||||||
const childNodes = Array
|
|
||||||
.from(rootRef.current.childNodes)
|
|
||||||
.filter(node => !!node.nodeType && node.classList.contains("microlight"))
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
childNodes.forEach(node => node.addEventListener("mousewheel", handlePreventYScrollingBeyondElement, { passive: false }))
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
childNodes.forEach(node => node.removeEventListener("mousewheel", handlePreventYScrollingBeyondElement))
|
|
||||||
}
|
|
||||||
}, [value, className, language])
|
|
||||||
|
|
||||||
const handleDownload = () => {
|
const handleDownload = () => {
|
||||||
saveAs(value, fileName)
|
saveAs(value, fileName)
|
||||||
@@ -32,7 +32,11 @@ const HighlightCode = ({value, fileName = "response.txt", className, downloadabl
|
|||||||
|
|
||||||
const handlePreventYScrollingBeyondElement = (e) => {
|
const handlePreventYScrollingBeyondElement = (e) => {
|
||||||
const { target, deltaY } = e
|
const { target, deltaY } = e
|
||||||
const { scrollHeight: contentHeight, offsetHeight: visibleHeight, scrollTop } = target
|
const {
|
||||||
|
scrollHeight: contentHeight,
|
||||||
|
offsetHeight: visibleHeight,
|
||||||
|
scrollTop,
|
||||||
|
} = target
|
||||||
const scrollOffset = visibleHeight + scrollTop
|
const scrollOffset = visibleHeight + scrollTop
|
||||||
const isElementScrollable = contentHeight > visibleHeight
|
const isElementScrollable = contentHeight > visibleHeight
|
||||||
const isScrollingPastTop = scrollTop === 0 && deltaY < 0
|
const isScrollingPastTop = scrollTop === 0 && deltaY < 0
|
||||||
@@ -43,31 +47,57 @@ const HighlightCode = ({value, fileName = "response.txt", className, downloadabl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const childNodes = Array.from(rootRef.current.childNodes).filter(
|
||||||
|
(node) => !!node.nodeType && node.classList.contains("microlight")
|
||||||
|
)
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
childNodes.forEach((node) =>
|
||||||
|
node.addEventListener(
|
||||||
|
"mousewheel",
|
||||||
|
handlePreventYScrollingBeyondElement,
|
||||||
|
{ passive: false }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
childNodes.forEach((node) =>
|
||||||
|
node.removeEventListener(
|
||||||
|
"mousewheel",
|
||||||
|
handlePreventYScrollingBeyondElement
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [value, className, language])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="highlight-code" ref={rootRef}>
|
<div className="highlight-code" ref={rootRef}>
|
||||||
{canCopy && (
|
{canCopy && (
|
||||||
<div className="copy-to-clipboard">
|
<div className="copy-to-clipboard">
|
||||||
<CopyToClipboard text={value}><button/></CopyToClipboard>
|
<CopyToClipboard text={value}>
|
||||||
|
<button />
|
||||||
|
</CopyToClipboard>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!downloadable ? null :
|
{!downloadable ? null : (
|
||||||
<button className="download-contents" onClick={handleDownload}>
|
<button className="download-contents" onClick={handleDownload}>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{canSyntaxHighlight
|
{canSyntaxHighlight ? (
|
||||||
? <SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language={language}
|
language={language}
|
||||||
className={cx(className, "microlight")}
|
className={classNames(className, "microlight")}
|
||||||
style={getStyle(get(config, "syntaxHighlight.theme", "agate"))}
|
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
: <pre className={cx(className, "microlight")}>{value}</pre>
|
) : (
|
||||||
}
|
<pre className={classNames(className, "microlight")}>{value}</pre>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -75,11 +105,12 @@ const HighlightCode = ({value, fileName = "response.txt", className, downloadabl
|
|||||||
HighlightCode.propTypes = {
|
HighlightCode.propTypes = {
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
getConfigs: PropTypes.func.isRequired,
|
getConfigs: PropTypes.func.isRequired,
|
||||||
|
getComponent: PropTypes.func.isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
downloadable: PropTypes.bool,
|
downloadable: PropTypes.bool,
|
||||||
fileName: PropTypes.string,
|
fileName: PropTypes.string,
|
||||||
language: PropTypes.string,
|
language: PropTypes.string,
|
||||||
canCopy: PropTypes.bool
|
canCopy: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HighlightCode
|
export default HighlightCode
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import ReactSyntaxHighlighter from "react-syntax-highlighter/dist/esm/light"
|
||||||
|
import get from "lodash/get"
|
||||||
|
|
||||||
|
const SyntaxHighlighter = ({
|
||||||
|
language,
|
||||||
|
className = "",
|
||||||
|
getConfigs,
|
||||||
|
syntaxHighlighting = {},
|
||||||
|
children = null,
|
||||||
|
}) => {
|
||||||
|
const configs = getConfigs()
|
||||||
|
const theme = get(configs, "syntaxHighlight.theme")
|
||||||
|
const { styles, defaultStyle } = syntaxHighlighting
|
||||||
|
const style = styles?.[theme] ?? defaultStyle
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactSyntaxHighlighter
|
||||||
|
language={language}
|
||||||
|
className={className}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ReactSyntaxHighlighter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SyntaxHighlighter.propTypes = {
|
||||||
|
language: PropTypes.string.isRequired,
|
||||||
|
className: PropTypes.string,
|
||||||
|
getConfigs: PropTypes.func.isRequired,
|
||||||
|
syntaxHighlighting: PropTypes.shape({
|
||||||
|
styles: PropTypes.object,
|
||||||
|
defaultStyle: PropTypes.object,
|
||||||
|
}),
|
||||||
|
children: PropTypes.node,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyntaxHighlighter
|
||||||
20
src/core/plugins/syntax-highlighting/index.js
Normal file
20
src/core/plugins/syntax-highlighting/index.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import afterLoad from "./after-load"
|
||||||
|
import { styles, defaultStyle } from "./root-injects"
|
||||||
|
import SyntaxHighlighter from "./components/SyntaxHighlighter"
|
||||||
|
import HighlightCode from "./components/HighlightCode"
|
||||||
|
|
||||||
|
const SyntaxHighlightingPlugin = () => ({
|
||||||
|
afterLoad,
|
||||||
|
rootInjects: {
|
||||||
|
syntaxHighlighting: { styles, defaultStyle },
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
SyntaxHighlighter,
|
||||||
|
HighlightCode,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default SyntaxHighlightingPlugin
|
||||||
22
src/core/plugins/syntax-highlighting/root-injects.js
Normal file
22
src/core/plugins/syntax-highlighting/root-injects.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import agate from "react-syntax-highlighter/dist/esm/styles/hljs/agate"
|
||||||
|
import arta from "react-syntax-highlighter/dist/esm/styles/hljs/arta"
|
||||||
|
import monokai from "react-syntax-highlighter/dist/esm/styles/hljs/monokai"
|
||||||
|
import nord from "react-syntax-highlighter/dist/esm/styles/hljs/nord"
|
||||||
|
import obsidian from "react-syntax-highlighter/dist/esm/styles/hljs/obsidian"
|
||||||
|
import tomorrowNight from "react-syntax-highlighter/dist/esm/styles/hljs/tomorrow-night"
|
||||||
|
import idea from "react-syntax-highlighter/dist/esm/styles/hljs/idea"
|
||||||
|
|
||||||
|
export const styles = {
|
||||||
|
agate,
|
||||||
|
arta,
|
||||||
|
monokai,
|
||||||
|
nord,
|
||||||
|
obsidian,
|
||||||
|
"tomorrow-night": tomorrowNight,
|
||||||
|
idea,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultStyle = agate
|
||||||
@@ -18,6 +18,7 @@ import UtilPlugin from "core/plugins/util"
|
|||||||
import ViewPlugin from "core/plugins/view"
|
import ViewPlugin from "core/plugins/view"
|
||||||
import ViewLegacyPlugin from "core/plugins/view-legacy"
|
import ViewLegacyPlugin from "core/plugins/view-legacy"
|
||||||
import DownloadUrlPlugin from "core/plugins/download-url"
|
import DownloadUrlPlugin from "core/plugins/download-url"
|
||||||
|
import SyntaxHighlightingPlugin from "core/plugins/syntax-highlighting"
|
||||||
import SafeRenderPlugin from "core/plugins/safe-render"
|
import SafeRenderPlugin from "core/plugins/safe-render"
|
||||||
// ad-hoc plugins
|
// ad-hoc plugins
|
||||||
import CoreComponentsPlugin from "core/presets/base/plugins/core-components"
|
import CoreComponentsPlugin from "core/presets/base/plugins/core-components"
|
||||||
@@ -45,6 +46,7 @@ const BasePreset = () => [
|
|||||||
FilterPlugin,
|
FilterPlugin,
|
||||||
OnCompletePlugin,
|
OnCompletePlugin,
|
||||||
RequestSnippetsPlugin,
|
RequestSnippetsPlugin,
|
||||||
|
SyntaxHighlightingPlugin,
|
||||||
SafeRenderPlugin(),
|
SafeRenderPlugin(),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import OperationSummaryMethod from "core/components/operation-summary-method"
|
|||||||
import OperationSummaryPath from "core/components/operation-summary-path"
|
import OperationSummaryPath from "core/components/operation-summary-path"
|
||||||
import OperationExt from "core/components/operation-extensions"
|
import OperationExt from "core/components/operation-extensions"
|
||||||
import OperationExtRow from "core/components/operation-extension-row"
|
import OperationExtRow from "core/components/operation-extension-row"
|
||||||
import HighlightCode from "core/components/highlight-code"
|
|
||||||
import Responses from "core/components/responses"
|
import Responses from "core/components/responses"
|
||||||
import Response from "core/components/response"
|
import Response from "core/components/response"
|
||||||
import ResponseExtension from "core/components/response-extension"
|
import ResponseExtension from "core/components/response-extension"
|
||||||
@@ -103,7 +102,6 @@ const CoreComponentsPlugin = () => ({
|
|||||||
OperationSummary,
|
OperationSummary,
|
||||||
OperationSummaryMethod,
|
OperationSummaryMethod,
|
||||||
OperationSummaryPath,
|
OperationSummaryPath,
|
||||||
highlightCode: HighlightCode,
|
|
||||||
responses: Responses,
|
responses: Responses,
|
||||||
response: Response,
|
response: Response,
|
||||||
ResponseExtension: ResponseExtension,
|
ResponseExtension: ResponseExtension,
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/light"
|
|
||||||
import js from "react-syntax-highlighter/dist/esm/languages/hljs/javascript"
|
|
||||||
import json from "react-syntax-highlighter/dist/esm/languages/hljs/json"
|
|
||||||
import xml from "react-syntax-highlighter/dist/esm/languages/hljs/xml"
|
|
||||||
import bash from "react-syntax-highlighter/dist/esm/languages/hljs/bash"
|
|
||||||
import yaml from "react-syntax-highlighter/dist/esm/languages/hljs/yaml"
|
|
||||||
import http from "react-syntax-highlighter/dist/esm/languages/hljs/http"
|
|
||||||
import powershell from "react-syntax-highlighter/dist/esm/languages/hljs/powershell"
|
|
||||||
import javascript from "react-syntax-highlighter/dist/esm/languages/hljs/javascript"
|
|
||||||
|
|
||||||
import agate from "react-syntax-highlighter/dist/esm/styles/hljs/agate"
|
|
||||||
import arta from "react-syntax-highlighter/dist/esm/styles/hljs/arta"
|
|
||||||
import monokai from "react-syntax-highlighter/dist/esm/styles/hljs/monokai"
|
|
||||||
import nord from "react-syntax-highlighter/dist/esm/styles/hljs/nord"
|
|
||||||
import obsidian from "react-syntax-highlighter/dist/esm/styles/hljs/obsidian"
|
|
||||||
import tomorrowNight from "react-syntax-highlighter/dist/esm/styles/hljs/tomorrow-night"
|
|
||||||
import idea from "react-syntax-highlighter/dist/esm/styles/hljs/idea"
|
|
||||||
|
|
||||||
SyntaxHighlighter.registerLanguage("json", json)
|
|
||||||
SyntaxHighlighter.registerLanguage("js", js)
|
|
||||||
SyntaxHighlighter.registerLanguage("xml", xml)
|
|
||||||
SyntaxHighlighter.registerLanguage("yaml", yaml)
|
|
||||||
SyntaxHighlighter.registerLanguage("http", http)
|
|
||||||
SyntaxHighlighter.registerLanguage("bash", bash)
|
|
||||||
SyntaxHighlighter.registerLanguage("powershell", powershell)
|
|
||||||
SyntaxHighlighter.registerLanguage("javascript", javascript)
|
|
||||||
|
|
||||||
const styles = {agate, arta, monokai, nord, obsidian, "tomorrow-night": tomorrowNight, idea}
|
|
||||||
export const availableStyles = Object.keys(styles)
|
|
||||||
|
|
||||||
export const getStyle = name => {
|
|
||||||
if (!availableStyles.includes(name)) {
|
|
||||||
console.warn(`Request style '${name}' is not available, returning default instead`)
|
|
||||||
return agate
|
|
||||||
}
|
|
||||||
return styles[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
export {SyntaxHighlighter, styles}
|
|
||||||
@@ -1,26 +1,39 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import expect from "expect"
|
import expect from "expect"
|
||||||
import { shallow, mount } from "enzyme"
|
import { shallow, mount } from "enzyme"
|
||||||
import HighlightCode from "core/components/highlight-code"
|
import HighlightCode from "core/plugins/syntax-highlighting/components/HighlightCode"
|
||||||
|
import SyntaxHighlighter from "core/plugins/syntax-highlighting/components/SyntaxHighlighter"
|
||||||
|
|
||||||
const fakeGetConfigs = () => ({syntaxHighlight: {activated: true, theme: "agate"}})
|
const fakeGetConfigs = () => ({ syntaxHighlight: { activated: true, theme: "agate" }})
|
||||||
|
const fakeGetComponent = (name, isContainer) => {
|
||||||
|
const components = { HighlightCode, SyntaxHighlighter }
|
||||||
|
const Component = components[name]
|
||||||
|
|
||||||
|
if (isContainer) {
|
||||||
|
return ({ ...props }) => {
|
||||||
|
return <Component getConfigs={fakeGetConfigs} getComponent={fakeGetComponent} {...props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Component
|
||||||
|
}
|
||||||
|
|
||||||
describe("<HighlightCode />", () => {
|
describe("<HighlightCode />", () => {
|
||||||
it("should render a Download button if downloadable", () => {
|
it("should render a Download button if downloadable", () => {
|
||||||
const props = {downloadable: true, getConfigs: fakeGetConfigs }
|
const props = { downloadable: true, getConfigs: fakeGetConfigs, getComponent: fakeGetComponent }
|
||||||
const wrapper = shallow(<HighlightCode {...props} />)
|
const wrapper = shallow(<HighlightCode {...props} />)
|
||||||
expect(wrapper.find(".download-contents").length).toEqual(1)
|
expect(wrapper.find(".download-contents").length).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should render a Copy To Clipboard button if copyable", () => {
|
it("should render a Copy To Clipboard button if copyable", () => {
|
||||||
const props = {canCopy: true, getConfigs: fakeGetConfigs }
|
const props = { canCopy: true, getConfigs: fakeGetConfigs, getComponent: fakeGetComponent }
|
||||||
const wrapper = shallow(<HighlightCode {...props} />)
|
const wrapper = shallow(<HighlightCode {...props} />)
|
||||||
expect(wrapper.find("CopyToClipboard").length).toEqual(1)
|
expect(wrapper.find("CopyToClipboard").length).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should render values in a preformatted element", () => {
|
it("should render values in a preformatted element", () => {
|
||||||
const value = "test text"
|
const value = "test text"
|
||||||
const props = {value: value, getConfigs: fakeGetConfigs}
|
const props = { value, getConfigs: fakeGetConfigs, getComponent: fakeGetComponent }
|
||||||
const wrapper = mount(<HighlightCode {...props} />)
|
const wrapper = mount(<HighlightCode {...props} />)
|
||||||
const preTag = wrapper.find("pre")
|
const preTag = wrapper.find("pre")
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import { shallow } from "enzyme"
|
|||||||
import ResponseBody from "core/components/response-body"
|
import ResponseBody from "core/components/response-body"
|
||||||
|
|
||||||
describe("<ResponseBody />", function () {
|
describe("<ResponseBody />", function () {
|
||||||
const highlightCodeComponent = () => null
|
|
||||||
const components = {
|
const components = {
|
||||||
highlightCode: highlightCodeComponent
|
HighlightCode: () => null
|
||||||
}
|
}
|
||||||
const props = {
|
const props = {
|
||||||
getComponent: c => components[c],
|
getComponent: c => components[c],
|
||||||
@@ -15,27 +14,27 @@ describe("<ResponseBody />", function () {
|
|||||||
props.contentType = "application/json"
|
props.contentType = "application/json"
|
||||||
props.content = "{\"key\": \"a test value\"}"
|
props.content = "{\"key\": \"a test value\"}"
|
||||||
const wrapper = shallow(<ResponseBody {...props} />)
|
const wrapper = shallow(<ResponseBody {...props} />)
|
||||||
expect(wrapper.find("highlightCodeComponent").length).toEqual(1)
|
expect(wrapper.find("HighlightCode").length).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("renders ResponseBody as 'text/html'", function () {
|
it("renders ResponseBody as 'text/html'", function () {
|
||||||
props.contentType = "application/json"
|
props.contentType = "application/json"
|
||||||
props.content = "<b>Result</b>"
|
props.content = "<b>Result</b>"
|
||||||
const wrapper = shallow(<ResponseBody {...props} />)
|
const wrapper = shallow(<ResponseBody {...props} />)
|
||||||
expect(wrapper.find("highlightCodeComponent").length).toEqual(1)
|
expect(wrapper.find("HighlightCode").length).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("renders ResponseBody as 'image/svg'", function () {
|
it("renders ResponseBody as 'image/svg'", function () {
|
||||||
props.contentType = "image/svg"
|
props.contentType = "image/svg"
|
||||||
const wrapper = shallow(<ResponseBody {...props} />)
|
const wrapper = shallow(<ResponseBody {...props} />)
|
||||||
expect(wrapper.find("highlightCodeComponent").length).toEqual(0)
|
expect(wrapper.find("HighlightCode").length).toEqual(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should render a copyable highlightCodeComponent for text types", function () {
|
it("should render a copyable highlightCodeComponent for text types", function () {
|
||||||
props.contentType = "text/plain"
|
props.contentType = "text/plain"
|
||||||
props.content = "test text"
|
props.content = "test text"
|
||||||
const wrapper = shallow(<ResponseBody {...props} />)
|
const wrapper = shallow(<ResponseBody {...props} />)
|
||||||
expect(wrapper.find("highlightCodeComponent[canCopy]").length).toEqual(1)
|
expect(wrapper.find("HighlightCode[canCopy]").length).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should render Download file link for non-empty Blob response", function () {
|
it("should render Download file link for non-empty Blob response", function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user