docs: prep for v2 documentation (#6710)
This PR includes documentation that preps for v2 docs (but doesn't introduce new docs). _Note: The number of file changes in the PR is due to find-and-replace within the `references` which is unavoidable. Let me know if I should move it to another PR._ ## Changes - Change Medusa version in base OAS used for v2. - Fix to docblock generator related to not catching all path parameters. - Added typedoc plugin that generates ER Diagrams, which will be used specifically for data model references in commerce modules. - Changed OAS tool to output references in `www/apps/api-reference/specs-v2` directory when the `--v2` option is used. - Added a version switcher to the API reference to switch between V1 and V2. This switcher is enabled by an environment variable, so it won't be visible/usable at the moment. - Upgraded docusaurus to v3.0.1 - Added new Vale rules to ensure correct spelling of Medusa Admin and module names. - Added new components to the `docs-ui` package that will be used in future documentation changes.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useRequestRunner } from "../../../hooks"
|
||||
import { CodeBlock } from ".."
|
||||
import { Card } from "../../Card"
|
||||
import { Button, InputText } from "../../.."
|
||||
import { ApiMethod, ApiDataOptions, ApiTestingOptions } from "types"
|
||||
|
||||
type ApiRunnerProps = {
|
||||
apiMethod: ApiMethod
|
||||
apiUrl: string
|
||||
pathData?: Record<string, unknown>
|
||||
queryData?: Record<string, unknown>
|
||||
bodyData?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const ApiRunner = ({
|
||||
apiMethod,
|
||||
apiUrl,
|
||||
pathData,
|
||||
bodyData,
|
||||
queryData,
|
||||
}: ApiRunnerProps) => {
|
||||
// assemble api testing options
|
||||
const [apiTestingOptions, setApiTestingOptions] = useState<ApiTestingOptions>(
|
||||
{
|
||||
method: apiMethod,
|
||||
url: apiUrl,
|
||||
pathData,
|
||||
bodyData,
|
||||
queryData,
|
||||
}
|
||||
)
|
||||
const [isRunning, setIsRunning] = useState(false)
|
||||
const [ran, setRan] = useState(false)
|
||||
const hasData = (data?: Record<string, unknown>): boolean =>
|
||||
data !== undefined && Object.keys(data).length > 0
|
||||
// TODO change to be based on whether auth/data needed
|
||||
const manualTestTrigger = useMemo(
|
||||
() =>
|
||||
hasData(apiTestingOptions.pathData) ||
|
||||
hasData(apiTestingOptions.queryData) ||
|
||||
hasData(apiTestingOptions.bodyData),
|
||||
[apiTestingOptions]
|
||||
)
|
||||
const [responseLogs, setResponseLogs] = useState<string[]>([])
|
||||
const pushMessage = (...message: string[]) =>
|
||||
setResponseLogs((prev) => [...prev, ...message])
|
||||
const { runRequest } = useRequestRunner({
|
||||
pushLog: pushMessage,
|
||||
onFinish: () => setIsRunning(false),
|
||||
replaceLog: (message) => setResponseLogs([message]),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!isRunning && !manualTestTrigger && !ran) {
|
||||
setIsRunning(true)
|
||||
}
|
||||
}, [apiTestingOptions, manualTestTrigger, isRunning, ran])
|
||||
|
||||
useEffect(() => {
|
||||
if (isRunning && !ran) {
|
||||
setRan(true)
|
||||
setResponseLogs(["Sending request..."])
|
||||
runRequest(apiTestingOptions)
|
||||
}
|
||||
}, [isRunning, ran])
|
||||
|
||||
const getParamsElms = ({
|
||||
data,
|
||||
title,
|
||||
nameInApiOptions,
|
||||
}: {
|
||||
data: ApiDataOptions
|
||||
title: string
|
||||
nameInApiOptions: "pathData" | "bodyData" | "queryData"
|
||||
}) => (
|
||||
<div className="flex flex-col gap-docs_0.5">
|
||||
<span className="text-compact-medium-plus text-medusa-fg-base">
|
||||
{title}
|
||||
</span>
|
||||
<div className="flex gap-docs_0.5">
|
||||
{Object.keys(data).map((pathParam, index) => (
|
||||
<InputText
|
||||
name={pathParam}
|
||||
onChange={(e) =>
|
||||
setApiTestingOptions((prev) => ({
|
||||
...prev,
|
||||
[nameInApiOptions]: {
|
||||
...prev[nameInApiOptions],
|
||||
[pathParam]: e.target.value,
|
||||
},
|
||||
}))
|
||||
}
|
||||
key={index}
|
||||
placeholder={pathParam}
|
||||
value={
|
||||
typeof data[pathParam] === "string"
|
||||
? (data[pathParam] as string)
|
||||
: typeof data[pathParam] === "number"
|
||||
? (data[pathParam] as number)
|
||||
: `${data[pathParam]}`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{manualTestTrigger && (
|
||||
<Card className="font-base mb-docs_1" contentClassName="gap-docs_0.5">
|
||||
{apiTestingOptions.pathData &&
|
||||
getParamsElms({
|
||||
data: apiTestingOptions.pathData,
|
||||
title: "Path Parameters",
|
||||
nameInApiOptions: "pathData",
|
||||
})}
|
||||
{apiTestingOptions.bodyData &&
|
||||
getParamsElms({
|
||||
data: apiTestingOptions.bodyData,
|
||||
title: "Request Body Parameters",
|
||||
nameInApiOptions: "bodyData",
|
||||
})}
|
||||
{apiTestingOptions.queryData &&
|
||||
getParamsElms({
|
||||
data: apiTestingOptions.queryData,
|
||||
title: "Request Query Parameters",
|
||||
nameInApiOptions: "queryData",
|
||||
})}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsRunning(true)
|
||||
setRan(false)
|
||||
}}
|
||||
>
|
||||
Send Request
|
||||
</Button>
|
||||
</Card>
|
||||
)}
|
||||
{(isRunning || ran) && (
|
||||
<CodeBlock
|
||||
source={responseLogs.join("\n")}
|
||||
lang="json"
|
||||
title="Testing Result"
|
||||
collapsed={true}
|
||||
blockStyle="subtle"
|
||||
noReport={true}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
"use client"
|
||||
|
||||
import React, { useMemo } from "react"
|
||||
import clsx from "clsx"
|
||||
import { CodeBlockStyle } from ".."
|
||||
import { useColorMode } from "@/providers"
|
||||
import { Badge, BadgeVariant } from "@/components"
|
||||
|
||||
export type CodeBlockHeaderMeta = {
|
||||
badgeLabel?: string
|
||||
badgeColor?: BadgeVariant
|
||||
}
|
||||
|
||||
type CodeBlockHeaderProps = {
|
||||
children?: React.ReactNode
|
||||
title?: string
|
||||
blockStyle?: CodeBlockStyle
|
||||
} & CodeBlockHeaderMeta
|
||||
|
||||
export const CodeBlockHeader = ({
|
||||
children,
|
||||
title,
|
||||
blockStyle = "loud",
|
||||
badgeLabel,
|
||||
badgeColor,
|
||||
}: CodeBlockHeaderProps) => {
|
||||
const { colorMode } = useColorMode()
|
||||
|
||||
const borderColor = useMemo(
|
||||
() =>
|
||||
clsx(
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "border-medusa-code-border",
|
||||
colorMode === "dark" && "border-medusa-border-base",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "border-medusa-border-base",
|
||||
colorMode === "dark" && "border-medusa-code-border",
|
||||
]
|
||||
),
|
||||
[blockStyle, colorMode]
|
||||
)
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"py-docs_0.75 rounded-t-docs_DEFAULT px-docs_1 mb-0",
|
||||
"flex gap-docs_2 items-start justify-between",
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "bg-medusa-code-bg-header",
|
||||
colorMode === "dark" && "bg-medusa-bg-base",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "bg-medusa-bg-component",
|
||||
colorMode === "dark" && "bg-medusa-code-bg-header",
|
||||
],
|
||||
borderColor && `border border-b-0 ${borderColor}`
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{title && (
|
||||
<div
|
||||
className={clsx(
|
||||
"txt-compact-small-plus",
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "text-medusa-code-text-subtle",
|
||||
colorMode === "dark" && "text-medusa-fg-muted",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "text-medusa-fg-subtle",
|
||||
colorMode === "dark" && "text-medusa-code-text-subtle",
|
||||
]
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
{badgeLabel && (
|
||||
<Badge variant={badgeColor || "orange"} className="font-base">
|
||||
{badgeLabel}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
241
www/packages/docs-ui/src/components/CodeBlock/Line/index.tsx
Normal file
241
www/packages/docs-ui/src/components/CodeBlock/Line/index.tsx
Normal file
@@ -0,0 +1,241 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { Highlight } from ".."
|
||||
import { RenderProps, Token } from "prism-react-renderer"
|
||||
import clsx from "clsx"
|
||||
import { MarkdownContent, Tooltip } from "@/components"
|
||||
|
||||
type CodeBlockLineProps = {
|
||||
line: Token[]
|
||||
highlights?: Highlight[]
|
||||
lineNumber: number
|
||||
showLineNumber: boolean
|
||||
bgColorClassName: string
|
||||
lineNumberColorClassName: string
|
||||
noLineNumbers?: boolean
|
||||
} & Pick<RenderProps, "getLineProps" | "getTokenProps">
|
||||
|
||||
export const CodeBlockLine = ({
|
||||
line,
|
||||
highlights = [],
|
||||
lineNumber,
|
||||
getLineProps,
|
||||
getTokenProps,
|
||||
showLineNumber,
|
||||
bgColorClassName,
|
||||
lineNumberColorClassName,
|
||||
}: CodeBlockLineProps) => {
|
||||
const lineProps = getLineProps({ line, key: lineNumber })
|
||||
// collect highlighted tokens, if there are any
|
||||
const highlightedTokens: {
|
||||
start: number
|
||||
end: number
|
||||
highlight: Highlight
|
||||
}[] = []
|
||||
|
||||
highlights.forEach((highlight) => {
|
||||
if (!highlight.text) {
|
||||
return
|
||||
}
|
||||
let startIndex: number | undefined = undefined
|
||||
let currentPositionInHighlightedText = 0
|
||||
let endIndex = 0
|
||||
const found = line.some((token, tokenIndex) => {
|
||||
if (token.empty || !token.content.length) {
|
||||
startIndex = undefined
|
||||
currentPositionInHighlightedText = 0
|
||||
return false
|
||||
}
|
||||
const comparisonLength = Math.min(
|
||||
token.content.length,
|
||||
highlight.text!.substring(currentPositionInHighlightedText).length
|
||||
)
|
||||
const nextPositionInHighlightedText =
|
||||
currentPositionInHighlightedText + comparisonLength
|
||||
|
||||
const canHighlight =
|
||||
!highlightedTokens.length ||
|
||||
!highlightedTokens.some(
|
||||
(token) => tokenIndex >= token.start && tokenIndex <= token.end
|
||||
)
|
||||
|
||||
if (
|
||||
token.content.substring(0, comparisonLength) ===
|
||||
highlight.text?.substring(
|
||||
currentPositionInHighlightedText,
|
||||
nextPositionInHighlightedText
|
||||
) &&
|
||||
canHighlight
|
||||
) {
|
||||
if (startIndex === undefined) {
|
||||
startIndex = tokenIndex
|
||||
}
|
||||
currentPositionInHighlightedText = nextPositionInHighlightedText
|
||||
}
|
||||
|
||||
if (currentPositionInHighlightedText === highlight.text!.length) {
|
||||
// matching text was found, break loop
|
||||
endIndex = tokenIndex
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
if (found && startIndex !== undefined) {
|
||||
highlightedTokens.push({
|
||||
start: startIndex,
|
||||
end: endIndex,
|
||||
highlight,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// sort highlighted tokens by their start position
|
||||
highlightedTokens.sort((tokensA, tokensB) => {
|
||||
if (tokensA.start < tokensB.start) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return tokensA.start > tokensB.start ? 1 : 0
|
||||
})
|
||||
|
||||
// if there are highlighted tokens, split tokens in the
|
||||
// line by segments of not highlighted and highlighted token
|
||||
// if there are no highlighted tokens, the line is used as-is.
|
||||
const transformedLine: {
|
||||
tokens: Token[]
|
||||
type: "default" | "highlighted"
|
||||
highlight?: Highlight
|
||||
}[] = highlightedTokens.length
|
||||
? []
|
||||
: [
|
||||
{
|
||||
tokens: line,
|
||||
type: "default",
|
||||
},
|
||||
]
|
||||
|
||||
let lastIndex = 0
|
||||
// go through highlighted tokens to add the segments before/after to the
|
||||
// transformedLines array
|
||||
highlightedTokens.forEach((highlightedTokensItem, index) => {
|
||||
if (lastIndex < highlightedTokensItem.start) {
|
||||
transformedLine.push({
|
||||
tokens: line.slice(lastIndex, highlightedTokensItem.start),
|
||||
type: "default",
|
||||
})
|
||||
}
|
||||
transformedLine.push({
|
||||
tokens: line.slice(
|
||||
highlightedTokensItem.start,
|
||||
highlightedTokensItem.end + 1
|
||||
),
|
||||
type: "highlighted",
|
||||
highlight: highlightedTokensItem.highlight,
|
||||
})
|
||||
lastIndex = highlightedTokensItem.end + 1
|
||||
|
||||
// if this is the last item in `highlightedTokens` and
|
||||
// its end index is less than the line's length, that means
|
||||
// there are tokens at the end of the line that aren't highlighted
|
||||
// and should be pushed as-is to the `transformedLines` array.
|
||||
if (index === highlightedTokens.length - 1 && lastIndex < line.length - 1) {
|
||||
transformedLine.push({
|
||||
tokens: line.slice(lastIndex),
|
||||
type: "default",
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const getTokensElm = ({
|
||||
tokens,
|
||||
isHighlighted,
|
||||
offset,
|
||||
}: {
|
||||
tokens: Token[]
|
||||
isHighlighted: boolean
|
||||
offset: number
|
||||
}) => (
|
||||
<span
|
||||
className={clsx(
|
||||
// TODO change code colors and class names based on figma colors
|
||||
isHighlighted && [
|
||||
"lg:py-px lg:px-[6px] lg:border-medusa-code-icon lg:rounded-docs_sm",
|
||||
"lg:bg-medusa-code-border lg:cursor-pointer",
|
||||
]
|
||||
)}
|
||||
>
|
||||
{tokens.map((token, key) => {
|
||||
const tokenKey = offset + key
|
||||
const { className: tokenClassName, ...rest } = getTokenProps({
|
||||
token,
|
||||
key: tokenKey,
|
||||
})
|
||||
return (
|
||||
<span key={tokenKey} className={clsx(tokenClassName)} {...rest} />
|
||||
)
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
|
||||
const isHighlightedLine = useMemo(
|
||||
() => highlights.length && !highlightedTokens.length,
|
||||
[highlights, highlightedTokens]
|
||||
)
|
||||
|
||||
return (
|
||||
<span
|
||||
key={lineNumber}
|
||||
{...lineProps}
|
||||
className={clsx(
|
||||
"table-row",
|
||||
isHighlightedLine && "bg-medusa-code-bg-header",
|
||||
lineProps.className
|
||||
)}
|
||||
>
|
||||
{showLineNumber && (
|
||||
<span
|
||||
className={clsx(
|
||||
"mr-docs_1 table-cell select-none",
|
||||
"sticky left-0 w-[1%] px-docs_1 text-right",
|
||||
bgColorClassName,
|
||||
lineNumberColorClassName
|
||||
)}
|
||||
>
|
||||
{lineNumber + 1}
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
{transformedLine.map(({ tokens, type, highlight }, index) => {
|
||||
const offset =
|
||||
index === 0 ? 0 : transformedLine[index - 1].tokens.length
|
||||
const tooltipText =
|
||||
highlight?.tooltipText ||
|
||||
(isHighlightedLine
|
||||
? highlights.find((h) => h.tooltipText !== undefined)?.tooltipText
|
||||
: undefined)
|
||||
const isHighlighted = type === "highlighted"
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{tooltipText && (
|
||||
<Tooltip
|
||||
text={tooltipText}
|
||||
tooltipClassName="font-base"
|
||||
render={({ content }) => (
|
||||
<MarkdownContent
|
||||
allowedElements={["a", "strong", "code"]}
|
||||
unwrapDisallowed={true}
|
||||
>
|
||||
{content || ""}
|
||||
</MarkdownContent>
|
||||
)}
|
||||
>
|
||||
{getTokensElm({ tokens, isHighlighted, offset })}
|
||||
</Tooltip>
|
||||
)}
|
||||
{!tooltipText && getTokensElm({ tokens, isHighlighted, offset })}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -1,124 +1,329 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import React, { useMemo, useState } from "react"
|
||||
import clsx from "clsx"
|
||||
import { HighlightProps, Highlight, themes } from "prism-react-renderer"
|
||||
import { CopyButton, useColorMode } from "docs-ui"
|
||||
import { SquareTwoStackSolid } from "@medusajs/icons"
|
||||
import { CopyButton, Tooltip, LegacyLink } from "@/components"
|
||||
import { useColorMode } from "@/providers"
|
||||
import { ExclamationCircle, PlaySolid, SquareTwoStack } from "@medusajs/icons"
|
||||
import { CodeBlockHeader, CodeBlockHeaderMeta } from "./Header"
|
||||
import { CodeBlockLine } from "./Line"
|
||||
import { ApiAuthType, ApiDataOptions, ApiMethod } from "types"
|
||||
import { CSSTransition } from "react-transition-group"
|
||||
import { ApiRunner } from "./ApiRunner"
|
||||
import { GITHUB_ISSUES_PREFIX } from "../.."
|
||||
|
||||
export type Highlight = {
|
||||
line: number
|
||||
text?: string
|
||||
tooltipText?: string
|
||||
}
|
||||
|
||||
export type CodeBlockMetaFields = {
|
||||
title?: string
|
||||
npm2yarn?: boolean
|
||||
highlights?: string[][]
|
||||
apiTesting?: boolean
|
||||
testApiMethod?: ApiMethod
|
||||
testApiUrl?: string
|
||||
testAuthType?: ApiAuthType
|
||||
testPathParams?: ApiDataOptions
|
||||
testQueryParams?: ApiDataOptions
|
||||
testBodyParams?: ApiDataOptions
|
||||
noCopy?: boolean
|
||||
noReport?: boolean
|
||||
noLineNumbers?: boolean
|
||||
} & CodeBlockHeaderMeta
|
||||
|
||||
export type CodeBlockStyle = "loud" | "subtle"
|
||||
|
||||
export type CodeBlockProps = {
|
||||
source: string
|
||||
lang?: string
|
||||
className?: string
|
||||
collapsed?: boolean
|
||||
} & Omit<HighlightProps, "code" | "language" | "children">
|
||||
blockStyle?: CodeBlockStyle
|
||||
children?: React.ReactNode
|
||||
} & CodeBlockMetaFields &
|
||||
Omit<HighlightProps, "code" | "language" | "children">
|
||||
|
||||
export const CodeBlock = ({
|
||||
source,
|
||||
lang = "",
|
||||
className,
|
||||
collapsed = false,
|
||||
title = "",
|
||||
highlights = [],
|
||||
apiTesting = false,
|
||||
blockStyle = "loud",
|
||||
noCopy = false,
|
||||
noReport = false,
|
||||
noLineNumbers = false,
|
||||
children,
|
||||
...rest
|
||||
}: CodeBlockProps) => {
|
||||
if (!source && typeof children === "string") {
|
||||
source = children
|
||||
}
|
||||
|
||||
const { colorMode } = useColorMode()
|
||||
const [showTesting, setShowTesting] = useState(false)
|
||||
const canShowApiTesting = useMemo(
|
||||
() => apiTesting && rest.testApiMethod && rest.testApiUrl,
|
||||
[apiTesting, rest]
|
||||
)
|
||||
|
||||
const bgColor = useMemo(
|
||||
() =>
|
||||
clsx(
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "bg-medusa-code-bg-base",
|
||||
colorMode === "dark" && "bg-medusa-bg-component",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "bg-medusa-bg-subtle",
|
||||
colorMode === "dark" && "bg-medusa-code-bg-base",
|
||||
]
|
||||
),
|
||||
[blockStyle, colorMode]
|
||||
)
|
||||
|
||||
const lineNumbersColor = useMemo(
|
||||
() =>
|
||||
clsx(
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "text-medusa-code-text-subtle",
|
||||
colorMode === "dark" && "text-medusa-fg-muted",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "text-medusa-fg-muted",
|
||||
colorMode === "dark" && "text-medusa-code-text-subtle",
|
||||
]
|
||||
),
|
||||
[blockStyle, colorMode]
|
||||
)
|
||||
|
||||
const borderColor = useMemo(
|
||||
() =>
|
||||
clsx(
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "border-medusa-code-border",
|
||||
colorMode === "dark" && "border-medusa-border-base",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "border-medusa-border-base",
|
||||
colorMode === "dark" && "border-medusa-code-border",
|
||||
]
|
||||
),
|
||||
[blockStyle, colorMode]
|
||||
)
|
||||
|
||||
const iconColor = useMemo(
|
||||
() =>
|
||||
clsx(
|
||||
blockStyle === "loud" && [
|
||||
colorMode === "light" && "text-medusa-code-icon",
|
||||
colorMode === "dark" && "text-medusa-fg-muted",
|
||||
],
|
||||
blockStyle === "subtle" && [
|
||||
colorMode === "light" && "text-medusa-fg-muted",
|
||||
colorMode === "dark" && "text-medusa-code-icon",
|
||||
]
|
||||
),
|
||||
[blockStyle, colorMode]
|
||||
)
|
||||
|
||||
if (!source.length) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const transformedHighlights: Highlight[] = highlights
|
||||
.filter((highlight) => highlight.length !== 0)
|
||||
.map((highlight) => ({
|
||||
line: parseInt(highlight[0]),
|
||||
text: highlight.length >= 2 ? highlight[1] : undefined,
|
||||
tooltipText: highlight.length >= 3 ? highlight[2] : undefined,
|
||||
}))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"bg-medusa-code-bg-base relative mb-docs_1 rounded-docs_DEFAULT",
|
||||
"border-medusa-code-border w-full max-w-full border",
|
||||
collapsed && "max-h-[400px] overflow-auto",
|
||||
className
|
||||
<>
|
||||
{title && (
|
||||
<CodeBlockHeader
|
||||
title={title}
|
||||
blockStyle={blockStyle}
|
||||
badgeLabel={rest.badgeLabel}
|
||||
badgeColor={rest.badgeColor}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<Highlight
|
||||
theme={{
|
||||
...themes.vsDark,
|
||||
plain: {
|
||||
...themes.vsDark.plain,
|
||||
backgroundColor: colorMode === "light" ? "#111827" : "#1B1B1F",
|
||||
},
|
||||
}}
|
||||
code={source.trim()}
|
||||
language={lang.toLowerCase()}
|
||||
{...rest}
|
||||
>
|
||||
{({
|
||||
className: preClassName,
|
||||
style,
|
||||
tokens,
|
||||
getLineProps,
|
||||
getTokenProps,
|
||||
}) => (
|
||||
<>
|
||||
<pre
|
||||
style={{ ...style, fontStretch: "100%" }}
|
||||
className={clsx(
|
||||
"xs:max-w-[90%] relative !my-0 break-words bg-transparent !outline-none",
|
||||
"overflow-auto break-words rounded-docs_DEFAULT p-0",
|
||||
preClassName
|
||||
)}
|
||||
>
|
||||
<code
|
||||
className={clsx(
|
||||
"text-code-body font-monospace table min-w-full pb-docs_1.5 print:whitespace-pre-wrap",
|
||||
tokens.length > 1 && "pt-docs_1 pr-docs_1",
|
||||
tokens.length <= 1 && "!py-docs_0.5 px-docs_1"
|
||||
)}
|
||||
>
|
||||
{tokens.map((line, i) => {
|
||||
const lineProps = getLineProps({ line, key: i })
|
||||
return (
|
||||
<span
|
||||
key={i}
|
||||
{...lineProps}
|
||||
className={clsx("table-row", lineProps.className)}
|
||||
>
|
||||
{tokens.length > 1 && (
|
||||
<span
|
||||
className={clsx(
|
||||
"text-medusa-code-text-subtle mr-docs_1 table-cell select-none",
|
||||
"bg-medusa-code-bg-base sticky left-0 w-[1%] px-docs_1 text-right"
|
||||
)}
|
||||
>
|
||||
{i + 1}
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
{line.map((token, key) => (
|
||||
<span key={key} {...getTokenProps({ token, key })} />
|
||||
))}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</code>
|
||||
</pre>
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute hidden gap-docs_1 md:flex",
|
||||
"xs:rounded xs:absolute xs:right-0 xs:top-0 xs:w-[calc(10%+24px)] xs:h-full xs:bg-code-fade"
|
||||
)}
|
||||
>
|
||||
<CopyButton
|
||||
text={source}
|
||||
tooltipClassName="font-base"
|
||||
className={clsx(
|
||||
"absolute",
|
||||
tokens.length === 1 && "right-docs_0.75 top-[10px]",
|
||||
tokens.length > 1 && "right-docs_1 top-docs_1"
|
||||
)}
|
||||
>
|
||||
<SquareTwoStackSolid className="text-medusa-code-icon" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
</>
|
||||
<div
|
||||
className={clsx(
|
||||
"relative mb-docs_1 rounded-b-docs_DEFAULT",
|
||||
"w-full max-w-full border",
|
||||
bgColor,
|
||||
borderColor,
|
||||
collapsed && "max-h-[400px] overflow-auto",
|
||||
!title && "rounded-t-docs_DEFAULT",
|
||||
(blockStyle === "loud" || colorMode !== "light") &&
|
||||
"code-block-highlight-dark",
|
||||
blockStyle === "subtle" &&
|
||||
colorMode === "light" &&
|
||||
"code-block-highlight-light",
|
||||
className
|
||||
)}
|
||||
</Highlight>
|
||||
</div>
|
||||
>
|
||||
<Highlight
|
||||
theme={
|
||||
blockStyle === "loud" || colorMode === "dark"
|
||||
? {
|
||||
...themes.vsDark,
|
||||
plain: {
|
||||
...themes.vsDark.plain,
|
||||
backgroundColor:
|
||||
blockStyle === "loud"
|
||||
? colorMode === "light"
|
||||
? "#111827"
|
||||
: "#27282D"
|
||||
: "#1B1B1F",
|
||||
},
|
||||
}
|
||||
: {
|
||||
...themes.vsLight,
|
||||
plain: {
|
||||
...themes.vsLight.plain,
|
||||
backgroundColor: "#F9FAFB",
|
||||
},
|
||||
}
|
||||
}
|
||||
code={source.trim()}
|
||||
language={lang.toLowerCase()}
|
||||
{...rest}
|
||||
>
|
||||
{({ className: preClassName, style, tokens, ...rest }) => (
|
||||
<>
|
||||
<pre
|
||||
style={{ ...style, fontStretch: "100%" }}
|
||||
className={clsx(
|
||||
"relative !my-0 break-words bg-transparent !outline-none",
|
||||
"overflow-auto break-words rounded-docs_DEFAULT p-0 xs:max-w-[83%]",
|
||||
preClassName
|
||||
)}
|
||||
>
|
||||
<code
|
||||
className={clsx(
|
||||
"text-code-body font-monospace table min-w-full pb-docs_1.5 print:whitespace-pre-wrap",
|
||||
tokens.length > 1 && "pt-docs_1 pr-docs_1",
|
||||
tokens.length <= 1 && "!py-docs_0.25 px-[6px]"
|
||||
)}
|
||||
>
|
||||
{tokens.map((line, i) => {
|
||||
const highlightedLines = transformedHighlights.filter(
|
||||
(highlight) => highlight.line - 1 === i
|
||||
)
|
||||
|
||||
return (
|
||||
<CodeBlockLine
|
||||
line={line}
|
||||
lineNumber={i}
|
||||
highlights={highlightedLines}
|
||||
showLineNumber={!noLineNumbers && tokens.length > 1}
|
||||
key={i}
|
||||
bgColorClassName={bgColor}
|
||||
lineNumberColorClassName={lineNumbersColor}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</code>
|
||||
</pre>
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute hidden md:flex md:justify-end",
|
||||
"xs:rounded xs:absolute xs:right-0 xs:top-0 xs:w-[calc(10%+24px)] xs:h-full xs:bg-transparent",
|
||||
tokens.length === 1 && "md:right-[6px] md:top-0",
|
||||
tokens.length > 1 && "md:right-docs_1 md:top-docs_1"
|
||||
)}
|
||||
>
|
||||
{canShowApiTesting && (
|
||||
<Tooltip
|
||||
text="Test API"
|
||||
tooltipClassName="font-base"
|
||||
className={clsx(
|
||||
"h-fit",
|
||||
tokens.length === 1 && "p-[6px]",
|
||||
tokens.length > 1 && "px-[6px] pb-[6px]"
|
||||
)}
|
||||
>
|
||||
<PlaySolid
|
||||
className={clsx("cursor-pointer", iconColor)}
|
||||
onClick={() => setShowTesting(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!noReport && (
|
||||
<Tooltip
|
||||
text="Report Issue"
|
||||
tooltipClassName="font-base"
|
||||
className={clsx(
|
||||
"h-fit",
|
||||
tokens.length === 1 && "p-[6px]",
|
||||
tokens.length > 1 && "px-[6px] pb-[6px]"
|
||||
)}
|
||||
>
|
||||
{/* TODO replace with Link once we move away from Docusaurus */}
|
||||
<LegacyLink
|
||||
href={`${GITHUB_ISSUES_PREFIX}&title=${encodeURIComponent(
|
||||
`Docs(Code Issue): `
|
||||
)}`}
|
||||
target="_blank"
|
||||
className={clsx(
|
||||
blockStyle === "loud" && "hover:bg-medusa-code-bg-base",
|
||||
"bg-transparent border-none cursor-pointer rounded",
|
||||
"[&:not(:first-child)]:ml-docs_0.5",
|
||||
"inline-flex justify-center items-center invisible xs:visible"
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ExclamationCircle className={clsx(iconColor)} />
|
||||
</LegacyLink>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!noCopy && (
|
||||
<CopyButton
|
||||
text={source}
|
||||
tooltipClassName="font-base"
|
||||
className={clsx(
|
||||
"h-fit",
|
||||
tokens.length === 1 && "p-[6px]",
|
||||
tokens.length > 1 && "px-[6px] pb-[6px]"
|
||||
)}
|
||||
>
|
||||
<SquareTwoStack className={clsx(iconColor)} />
|
||||
</CopyButton>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Highlight>
|
||||
</div>
|
||||
{canShowApiTesting && (
|
||||
<CSSTransition
|
||||
unmountOnExit
|
||||
in={showTesting}
|
||||
timeout={150}
|
||||
classNames={{
|
||||
enter: "animate-fadeIn animate-fastest",
|
||||
exit: "animate-fadeOut animate-fastest",
|
||||
}}
|
||||
>
|
||||
<ApiRunner
|
||||
apiMethod={rest.testApiMethod!}
|
||||
apiUrl={rest.testApiUrl!}
|
||||
pathData={rest.testPathParams}
|
||||
bodyData={rest.testBodyParams}
|
||||
queryData={rest.testQueryParams}
|
||||
/>
|
||||
</CSSTransition>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user