docs: DX improvements to a workflow / step reference page (#10906)

This commit is contained in:
Shahed Nasser
2025-01-10 13:47:08 +02:00
committed by GitHub
parent 428fce5313
commit a126f40bbe
23 changed files with 138116 additions and 73 deletions
@@ -1,6 +1,5 @@
import { Badge, DecisionProcessIcon } from "docs-ui" import { DecisionProcessIcon, SourceCodeLink } from "docs-ui"
import { config } from "../../../../../config" import { config } from "../../../../../config"
import Link from "next/link"
export type TagsOperationDescriptionSectionWorkflowBadgeProps = { export type TagsOperationDescriptionSectionWorkflowBadgeProps = {
workflow: string workflow: string
@@ -12,21 +11,11 @@ const TagsOperationDescriptionSectionWorkflowBadge = ({
return ( return (
<p className="my-1"> <p className="my-1">
Workflow{" "} Workflow{" "}
<Link <SourceCodeLink
href={`${config.baseUrl}/resources/references/medusa-workflows/${workflow}`} link={`${config.baseUrl}/resources/references/medusa-workflows/${workflow}`}
className="align-middle" text={workflow}
target="_blank" icon={<DecisionProcessIcon />}
rel="noreferrer" />{" "}
>
<Badge
variant="neutral"
className="inline-flex hover:bg-medusa-tag-neutral-bg-hover cursor-pointer"
childrenWrapperClassName="inline-flex flex-row gap-[3px] items-center"
>
<DecisionProcessIcon />
<span>{workflow}</span>
</Badge>
</Link>{" "}
is used in this API route. is used in this API route.
</p> </p>
) )
@@ -4,6 +4,7 @@ import {
MDXComponents as UiMdxComponents, MDXComponents as UiMdxComponents,
TypeList, TypeList,
WorkflowDiagram, WorkflowDiagram,
SourceCodeLink,
} from "docs-ui" } from "docs-ui"
import { CommerceModuleSections } from "../CommerceModuleSections" import { CommerceModuleSections } from "../CommerceModuleSections"
@@ -13,6 +14,7 @@ const MDXComponents: MDXComponentsType = {
TypeList, TypeList,
WorkflowDiagram, WorkflowDiagram,
CommerceModuleSections, CommerceModuleSections,
SourceCodeLink,
} }
export default MDXComponents export default MDXComponents
@@ -0,0 +1,37 @@
import React from "react"
import { Link } from "../Link"
import { Badge } from "../Badge"
import { Github } from "@medusajs/icons"
import clsx from "clsx"
type SourceCodeLinkProps = {
link: string
text?: string
icon?: React.ReactNode
className?: string
}
export const SourceCodeLink = ({
link,
text,
icon,
className,
}: SourceCodeLinkProps) => {
return (
<Link
href={link}
target="_blank"
rel="noreferrer"
className={clsx("my-docs_0.5 align-middle inline-block", className)}
>
<Badge
variant="neutral"
className="inline-flex hover:bg-medusa-tag-neutral-bg-hover cursor-pointer"
childrenWrapperClassName="inline-flex flex-row gap-[3px] items-center"
>
{icon || <Github />}
<span>{text || "Source Code"}</span>
</Badge>
</Link>
)
}
@@ -1,6 +1,6 @@
"use client" "use client"
import React, { useId } from "react" import React, { forwardRef, useId } from "react"
import { Tooltip as ReactTooltip } from "react-tooltip" import { Tooltip as ReactTooltip } from "react-tooltip"
import type { ITooltip } from "react-tooltip" import type { ITooltip } from "react-tooltip"
import clsx from "clsx" import clsx from "clsx"
@@ -15,47 +15,52 @@ export type TooltipProps = {
} & React.HTMLAttributes<HTMLSpanElement> & } & React.HTMLAttributes<HTMLSpanElement> &
ITooltip ITooltip
export const Tooltip = ({ export const Tooltip = forwardRef<HTMLSpanElement, TooltipProps>(
text = "", function Tooltip(
tooltipClassName = "", {
children, text = "",
html = "", tooltipClassName = "",
tooltipChildren, children,
className, html = "",
innerClassName, tooltipChildren,
...tooltipProps className,
}: TooltipProps) => { innerClassName,
const elementId = useId() ...tooltipProps
},
ref
) {
const elementId = useId()
return ( return (
<span className={clsx(className, "notranslate")} translate="no"> <span className={clsx(className, "notranslate")} translate="no" ref={ref}>
<span <span
id={elementId} id={elementId}
data-tooltip-content={text} data-tooltip-content={text}
data-tooltip-html={html} data-tooltip-html={html}
data-tooltip-id={elementId} data-tooltip-id={elementId}
className={innerClassName} className={innerClassName}
> >
{children} {children}
</span>
<ReactTooltip
anchorId={elementId}
// anchorSelect={elementId ? `#${elementId}` : undefined}
className={clsx(
"!text-compact-x-small !shadow-elevation-tooltip dark:!shadow-elevation-tooltip-dark !rounded-docs_DEFAULT",
"!py-docs_0.25 !z-[399] hidden !px-docs_0.5 lg:block",
"!bg-medusa-bg-component",
"!text-medusa-fg-base text-center",
tooltipClassName
)}
wrapper="span"
noArrow={true}
positionStrategy={"fixed"}
opacity={1}
{...tooltipProps}
>
{tooltipChildren}
</ReactTooltip>
</span> </span>
<ReactTooltip )
anchorId={elementId} }
// anchorSelect={elementId ? `#${elementId}` : undefined} )
className={clsx(
"!text-compact-x-small !shadow-elevation-tooltip dark:!shadow-elevation-tooltip-dark !rounded-docs_DEFAULT",
"!py-docs_0.25 !z-[399] hidden !px-docs_0.5 lg:block",
"!bg-medusa-bg-component",
"!text-medusa-fg-base text-center",
tooltipClassName
)}
wrapper="span"
noArrow={true}
positionStrategy={"fixed"}
opacity={1}
{...tooltipProps}
>
{tooltipChildren}
</ReactTooltip>
</span>
)
}
@@ -41,6 +41,7 @@ const TypeListItem = ({
elementKey, elementKey,
sectionTitle, sectionTitle,
referenceType = "method", referenceType = "method",
openedLevel = 0,
}: TypeListItemProps) => { }: TypeListItemProps) => {
const { isBrowser } = useIsBrowser() const { isBrowser } = useIsBrowser()
const pathname = usePathname() const pathname = usePathname()
@@ -249,6 +250,7 @@ const TypeListItem = ({
className={clsx(getItemClassNames())} className={clsx(getItemClassNames())}
heightAnimation={true} heightAnimation={true}
id={typeId ? typeId : ""} id={typeId ? typeId : ""}
openInitial={openedLevel >= level}
> >
{typeItem.children && ( {typeItem.children && (
<TypeListItems <TypeListItems
@@ -5,6 +5,7 @@ import { Loading } from "@/components"
export type CommonProps = { export type CommonProps = {
expandUrl?: string expandUrl?: string
sectionTitle?: string sectionTitle?: string
openedLevel?: number
} }
export type Type = { export type Type = {
@@ -31,6 +32,7 @@ export const TypeList = ({
className, className,
sectionTitle, sectionTitle,
expandUrl, expandUrl,
openedLevel,
...props ...props
}: ParameterTypesType) => { }: ParameterTypesType) => {
return ( return (
@@ -47,6 +49,7 @@ export const TypeList = ({
types={types} types={types}
expandUrl={expandUrl} expandUrl={expandUrl}
sectionTitle={sectionTitle} sectionTitle={sectionTitle}
openedLevel={openedLevel}
/> />
</Suspense> </Suspense>
</div> </div>
@@ -0,0 +1,37 @@
import React from "react"
import { InlineCode } from "../../../InlineCode"
import { Text } from "@medusajs/ui"
import { Bolt, InformationCircle } from "@medusajs/icons"
export const WorkflowDiagramLegend = () => {
return (
<div className="flex gap-docs_0.5 mt-1">
<div className="flex items-center gap-docs_0.5">
<div className="flex size-[20px] items-center justify-center text-medusa-tag-orange-icon">
<Bolt />
</div>
<Text
size="xsmall"
leading="compact"
weight="plus"
className="select-none"
>
Workflow Hook
</Text>
</div>
<div className="flex items-center gap-docs_0.5">
<div className="flex size-[20px] items-center justify-center text-medusa-tag-green-icon">
<InformationCircle />
</div>
<Text
size="xsmall"
leading="compact"
weight="plus"
className="select-none"
>
Step conditioned by <InlineCode>when</InlineCode>
</Text>
</div>
</div>
)
}
@@ -3,7 +3,7 @@
import { Text } from "@medusajs/ui" import { Text } from "@medusajs/ui"
import clsx from "clsx" import clsx from "clsx"
import Link from "next/link" import Link from "next/link"
import React, { useMemo } from "react" import React, { useEffect, useMemo, useRef, useState } from "react"
import { WorkflowStepUi } from "types" import { WorkflowStepUi } from "types"
import { InlineCode, MarkdownContent, Tooltip } from "../../.." import { InlineCode, MarkdownContent, Tooltip } from "../../.."
import { Bolt, InformationCircle } from "@medusajs/icons" import { Bolt, InformationCircle } from "@medusajs/icons"
@@ -14,11 +14,34 @@ export type WorkflowDiagramNodeProps = {
export const WorkflowDiagramStepNode = ({ step }: WorkflowDiagramNodeProps) => { export const WorkflowDiagramStepNode = ({ step }: WorkflowDiagramNodeProps) => {
const stepId = step.name.split(".").pop() const stepId = step.name.split(".").pop()
const [offset, setOffset] = useState<number | undefined>(undefined)
const ref = useRef<HTMLSpanElement>(null)
const description = useMemo(() => { const description = useMemo(() => {
return step.description?.replaceAll(/:::[a-z]*/g, "") || "" return step.description?.replaceAll(/:::[a-z]*/g, "") || ""
}, [step.description]) }, [step.description])
useEffect(() => {
if (!ref.current) {
return
}
// find parent
const diagramParent = ref.current.closest(".workflow-list-diagram")
const nodeParent = ref.current.closest(".workflow-node-group")
if (!diagramParent || !nodeParent) {
return
}
const nodeBoundingRect = nodeParent.getBoundingClientRect()
const diagramBoundingRect = diagramParent.getBoundingClientRect()
setOffset(
Math.max(diagramBoundingRect.width - nodeBoundingRect.width + 10, 10)
)
}, [ref.current])
return ( return (
<Tooltip <Tooltip
tooltipClassName="!text-left max-w-[300px] text-pretty overflow-scroll" tooltipClassName="!text-left max-w-[300px] text-pretty overflow-scroll"
@@ -43,6 +66,8 @@ export const WorkflowDiagramStepNode = ({ step }: WorkflowDiagramNodeProps) => {
} }
clickable={true} clickable={true}
place="right" place="right"
offset={offset}
ref={ref}
> >
<Link <Link
href={step.link || `#${step.name}`} href={step.link || `#${step.name}`}
@@ -14,7 +14,7 @@ export const WorkflowDiagramListDepth = ({
cluster, cluster,
}: WorkflowDiagramListDepthProps) => { }: WorkflowDiagramListDepthProps) => {
return ( return (
<div className="flex items-start"> <div className="flex items-start workflow-node-group w-fit">
<WorkflowDiagramLine step={cluster} /> <WorkflowDiagramLine step={cluster} />
<div className="flex flex-col justify-center gap-y-docs_0.5"> <div className="flex flex-col justify-center gap-y-docs_0.5">
{cluster.map((step, index) => ( {cluster.map((step, index) => (
@@ -4,6 +4,7 @@ import React from "react"
import { createNodeClusters, getNextCluster } from "../../../utils" import { createNodeClusters, getNextCluster } from "../../../utils"
import { WorkflowDiagramCommonProps } from "../../.." import { WorkflowDiagramCommonProps } from "../../.."
import { WorkflowDiagramListDepth } from "./Depth" import { WorkflowDiagramListDepth } from "./Depth"
import { WorkflowDiagramLegend } from "../Common/Legend"
export const WorkflowDiagramList = ({ export const WorkflowDiagramList = ({
workflow, workflow,
@@ -11,7 +12,7 @@ export const WorkflowDiagramList = ({
const clusters = createNodeClusters(workflow.steps) const clusters = createNodeClusters(workflow.steps)
return ( return (
<div className="flex flex-col gap-docs_0.5 my-docs_1"> <div className="flex flex-col gap-docs_0.5 my-docs_1 workflow-list-diagram w-fit">
{Object.entries(clusters).map(([depth, cluster]) => { {Object.entries(clusters).map(([depth, cluster]) => {
const next = getNextCluster(clusters, Number(depth)) const next = getNextCluster(clusters, Number(depth))
@@ -19,6 +20,7 @@ export const WorkflowDiagramList = ({
<WorkflowDiagramListDepth cluster={cluster} next={next} key={depth} /> <WorkflowDiagramListDepth cluster={cluster} next={next} key={depth} />
) )
})} })}
<WorkflowDiagramLegend />
</div> </div>
) )
} }
@@ -66,6 +66,7 @@ export * from "./Search/Suggestions/Item"
export * from "./Select" export * from "./Select"
export * from "./Sidebar" export * from "./Sidebar"
export * from "./Sidebar/Item" export * from "./Sidebar/Item"
export * from "./SourceCodeLink"
export * from "./Table" export * from "./Table"
export * from "./Tabs" export * from "./Tabs"
export * from "./TextArea" export * from "./TextArea"
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,6 @@ export const baseOptions: Partial<TypeDocOptions> = {
excludeInternal: true, excludeInternal: true,
excludeExternals: true, excludeExternals: true,
excludeReferences: true, excludeReferences: true,
disableSources: true,
sort: ["source-order"], sort: ["source-order"],
validation: { validation: {
notExported: false, notExported: false,
@@ -77,6 +77,7 @@ import workflowHooksHelper from "./resources/helpers/workflow-hooks.js"
import ifMemberShowTitleHelper from "./resources/helpers/if-member-show-title.js" import ifMemberShowTitleHelper from "./resources/helpers/if-member-show-title.js"
import signatureCommentHelper from "./resources/helpers/signature-comment.js" import signatureCommentHelper from "./resources/helpers/signature-comment.js"
import versionHelper from "./resources/helpers/version.js" import versionHelper from "./resources/helpers/version.js"
import sourceCodeLinkHelper from "./resources/helpers/source-code-link.js"
import { MarkdownTheme } from "./theme.js" import { MarkdownTheme } from "./theme.js"
import { getDirname } from "utils" import { getDirname } from "utils"
@@ -185,4 +186,5 @@ export function registerHelpers(theme: MarkdownTheme) {
ifMemberShowTitleHelper(theme) ifMemberShowTitleHelper(theme)
signatureCommentHelper() signatureCommentHelper()
versionHelper() versionHelper()
sourceCodeLinkHelper()
} }
@@ -10,7 +10,8 @@ export default function (theme: MarkdownTheme) {
"parameterComponent", "parameterComponent",
function ( function (
this: ReflectionParameterType[], this: ReflectionParameterType[],
options: Handlebars.HelperOptions options: Handlebars.HelperOptions,
extraProps?: Record<string, unknown>
) { ) {
const { parameterComponent, maxLevel, parameterComponentExtraProps } = const { parameterComponent, maxLevel, parameterComponentExtraProps } =
theme.getFormattingOptionsForLocation() theme.getFormattingOptionsForLocation()
@@ -34,7 +35,10 @@ export default function (theme: MarkdownTheme) {
return formatParameterComponent({ return formatParameterComponent({
parameterComponent, parameterComponent,
componentItems: parameters, componentItems: parameters,
extraProps: parameterComponentExtraProps, extraProps: {
...parameterComponentExtraProps,
...extraProps,
},
sectionTitle: options.hash.sectionTitle, sectionTitle: options.hash.sectionTitle,
}) })
} }
@@ -0,0 +1,17 @@
import Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
export default function () {
Handlebars.registerHelper(
"sourceCodeLink",
function (this: SignatureReflection): string {
const source = this.parent.sources?.[0]
if (!source?.url) {
return ""
}
return `<SourceCodeLink link="${source.url}" />`
}
)
}
@@ -34,7 +34,10 @@ export default function (theme: MarkdownTheme) {
const formattedComponent = formatParameterComponent({ const formattedComponent = formatParameterComponent({
parameterComponent, parameterComponent,
componentItems: input, componentItems: input,
extraProps: parameterComponentExtraProps, extraProps: {
...parameterComponentExtraProps,
openedLevel: 1,
},
sectionTitle: options.hash.sectionTitle, sectionTitle: options.hash.sectionTitle,
}) })
@@ -34,7 +34,10 @@ export default function (theme: MarkdownTheme) {
const formattedComponent = formatParameterComponent({ const formattedComponent = formatParameterComponent({
parameterComponent, parameterComponent,
componentItems: output, componentItems: output,
extraProps: parameterComponentExtraProps, extraProps: {
...parameterComponentExtraProps,
openedLevel: 1,
},
sectionTitle: options.hash.sectionTitle, sectionTitle: options.hash.sectionTitle,
}) })
@@ -60,6 +60,9 @@ export default function (theme: MarkdownTheme) {
hash: { hash: {
sectionTitle: hook.name, sectionTitle: hook.name,
}, },
},
{
openedLevel: 1,
} }
) )
}) })
@@ -34,7 +34,10 @@ export default function (theme: MarkdownTheme) {
const formattedComponent = formatParameterComponent({ const formattedComponent = formatParameterComponent({
parameterComponent, parameterComponent,
componentItems: input, componentItems: input,
extraProps: parameterComponentExtraProps, extraProps: {
...parameterComponentExtraProps,
openedLevel: 1,
},
sectionTitle: options.hash.sectionTitle, sectionTitle: options.hash.sectionTitle,
}) })
@@ -34,7 +34,10 @@ export default function (theme: MarkdownTheme) {
const formattedComponent = formatParameterComponent({ const formattedComponent = formatParameterComponent({
parameterComponent, parameterComponent,
componentItems: output, componentItems: output,
extraProps: parameterComponentExtraProps, extraProps: {
...parameterComponentExtraProps,
openedLevel: 1,
},
sectionTitle: options.hash.sectionTitle, sectionTitle: options.hash.sectionTitle,
}) })
@@ -8,6 +8,8 @@
{{/if}} {{/if}}
{{{sourceCodeLink}}}
{{#if (sectionEnabled "member_signature_example")}} {{#if (sectionEnabled "member_signature_example")}}
{{{example this}}} {{{example this}}}
@@ -48,9 +48,11 @@ export function formatParameterComponent({
}: FormatParameterComponentProps): string { }: FormatParameterComponentProps): string {
let extraPropsArr: string[] = [] let extraPropsArr: string[] = []
if (extraProps) { if (extraProps) {
extraPropsArr = Object.entries(extraProps).map( extraPropsArr = Object.entries(extraProps).map(([key, value]) => {
([key, value]) => `${key}=${JSON.stringify(value)}` const valueJSON = JSON.stringify(value)
) const valueStr = typeof value !== "string" ? `{${valueJSON}}` : valueJSON
return `${key}=${valueStr}`
})
} }
// reorder component items to show required items first // reorder component items to show required items first
componentItems = sortComponentItems(componentItems) componentItems = sortComponentItems(componentItems)