diff --git a/__tests__/browser/__image_snapshots__/markdown-test-js-visual-regression-tests-rdmd-syntax-renders-child-tests-without-surprises-1-snap.png b/__tests__/browser/__image_snapshots__/markdown-test-js-visual-regression-tests-rdmd-syntax-renders-child-tests-without-surprises-1-snap.png new file mode 100644 index 000000000..ceddb0867 Binary files /dev/null and b/__tests__/browser/__image_snapshots__/markdown-test-js-visual-regression-tests-rdmd-syntax-renders-child-tests-without-surprises-1-snap.png differ diff --git a/__tests__/browser/markdown.test.js b/__tests__/browser/markdown.test.js index 5ee938bc4..b350b3a1f 100644 --- a/__tests__/browser/markdown.test.js +++ b/__tests__/browser/markdown.test.js @@ -13,6 +13,7 @@ describe('visual regression tests', () => { const docs = [ 'callouts', 'calloutTests', + 'childTests', 'codeBlocks', // skipping this because they sporadically failure with network timing // issues diff --git a/__tests__/custom-components/index.test.tsx b/__tests__/custom-components/index.test.tsx index 743158e49..c8926b5f5 100644 --- a/__tests__/custom-components/index.test.tsx +++ b/__tests__/custom-components/index.test.tsx @@ -3,13 +3,13 @@ import type { RMDXModule } from '../../types'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { execute } from '../helpers'; import { compile, run } from '../../lib'; -import { RMDXModule } from '../../types'; +import { execute } from '../helpers'; describe('Custom Components', () => { let Example; let Multiple; + let Nesting; beforeEach(async () => { Example = await execute('It works!', {}, {}, { getDefault: false }); @@ -22,6 +22,16 @@ export const Second = () =>
Second
; {}, { getDefault: false }, ); + Nesting = await execute( + ` +export const WithChildren = ({ children }) =>
{children}
; + +
{props.children}
+`, + {}, + {}, + { getDefault: false }, + ); }); it('renders custom components', async () => { @@ -47,9 +57,17 @@ export const Second = () =>
Second
; expect(screen.getByText('Second')).toBeVisible(); }); + it('renders a nested exported custom component', async () => { + const doc = 'Hello, Test User!'; + const Page = (await execute(doc, undefined, { components: { Nesting } })) as RMDXModule['default']; + render(); + + expect(screen.getByText('Hello, Test User!')).toBeVisible(); + }); + it('renders the default export of a custom component and passes through props', async () => { - const Test = (await run(await compile(`{props.attr}`))) as RMDXModule; - const doc = ``; + const Test = (await run(await compile('{props.attr}'))) as RMDXModule; + const doc = ''; const Page = await run(await compile(doc), { components: { Test } }); render(); diff --git a/__tests__/fixtures/child-tests.mdx b/__tests__/fixtures/child-tests.mdx new file mode 100644 index 000000000..818dee83d --- /dev/null +++ b/__tests__/fixtures/child-tests.mdx @@ -0,0 +1,4 @@ + + Step One + Step Two + diff --git a/example/components.ts b/example/components.ts index 15d3ec068..547619e5f 100644 --- a/example/components.ts +++ b/example/components.ts @@ -25,6 +25,21 @@ export const StyledComponent = () => { ; } `, + Steps: ` +export const Step = ({ children }) => { + return ( +
+
+ {children} +
+
+ ); +}; + +
+ {props.children} +
+ `, }; export default components; diff --git a/example/docs.ts b/example/docs.ts index be2b01b35..2aa9abbae 100644 --- a/example/docs.ts +++ b/example/docs.ts @@ -1,4 +1,5 @@ import calloutTests from '../__tests__/fixtures/callout-tests.md'; +import childTests from '../__tests__/fixtures/child-tests.mdx'; import codeBlockTests from '../__tests__/fixtures/code-block-tests.md'; import exportTests from '../__tests__/fixtures/export-tests.mdx'; import imageTests from '../__tests__/fixtures/image-tests.mdx'; @@ -25,6 +26,7 @@ const lowerCase = (str: string) => const fixtures = Object.entries({ calloutTests, callouts, + childTests, codeBlockTests, codeBlocks, embeds, diff --git a/lib/run.tsx b/lib/run.tsx index c53a8d895..8c363e2a8 100644 --- a/lib/run.tsx +++ b/lib/run.tsx @@ -12,7 +12,7 @@ import * as runtime from 'react/jsx-runtime'; import * as Components from '../components'; import Contexts from '../contexts'; -import { tocToMdx } from '../processor/plugin/toc'; +import { tocHastToMdx } from '../processor/plugin/toc'; import User from '../utils/user'; import compile from './compile'; @@ -54,10 +54,13 @@ const makeUseMDXComponents = (more: ReturnType = {}): UseMdxCo const run = async (string: string, _opts: RunOpts = {}) => { const { Fragment } = runtime; const { components = {}, terms, variables, baseUrl, imports = {}, ...opts } = _opts; + + const tocsByTag: Record = {}; const exportedComponents = Object.entries(components).reduce((memo, [tag, mod]) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { default: Content, toc, Toc, stylesheets, ...rest } = mod; memo[tag] = Content; + tocsByTag[tag] = toc; if (rest) { Object.entries(rest).forEach(([subTag, component]) => { @@ -83,7 +86,7 @@ const run = async (string: string, _opts: RunOpts = {}) => { const { Toc: _Toc, toc, default: Content, stylesheet, ...exports } = await exec(string); let Toc: React.FC | undefined; - const tocMdx = tocToMdx(toc, components); + const tocMdx = tocHastToMdx(toc, tocsByTag); if (tocMdx) { const compiledToc = await compile(tocMdx); const tocModule = await exec(compiledToc, { useMDXComponents: () => ({ p: Fragment }) }); diff --git a/processor/plugin/toc.ts b/processor/plugin/toc.ts index 1e1b95643..3eb85d0b4 100644 --- a/processor/plugin/toc.ts +++ b/processor/plugin/toc.ts @@ -1,4 +1,4 @@ -import type { CustomComponents, HastHeading, IndexableElements, TocList, TocListItem } from '../../types'; +import type { CustomComponents, HastHeading, IndexableElements, RMDXModule, TocList, TocListItem } from '../../types'; import type { Root } from 'hast'; import type { MdxjsEsm } from 'mdast-util-mdxjs-esm'; import type { Transformer } from 'unified'; @@ -89,11 +89,11 @@ const tocToHast = (headings: HastHeading[] = []): TocList => { return ast; }; -export const tocToMdx = (toc: IndexableElements[], components: CustomComponents) => { +export const tocHastToMdx = (toc: IndexableElements[], components: Record) => { const tree: Root = { type: 'root', children: toc }; visit(tree, 'mdxJsxFlowElement', (node, index, parent) => { - const subToc = components[node.name].toc || []; + const subToc = components[node.name] || []; parent.children.splice(index, 1, ...subToc); });