diff --git a/index.js b/index.js index 629aec0..d0fc80e 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ * @typedef {import('./lib/index.js').AllowElement} AllowElement * @typedef {import('./lib/index.js').Components} Components * @typedef {import('./lib/index.js').ExtraProps} ExtraProps + * @typedef {import('./lib/index.js').HooksOptions} HooksOptions * @typedef {import('./lib/index.js').Options} Options * @typedef {import('./lib/index.js').UrlTransform} UrlTransform */ diff --git a/lib/index.js b/lib/index.js index c606747..ae2287e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,7 +1,7 @@ /** * @import {Element, Nodes, Parents, Root} from 'hast' * @import {Root as MdastRoot} from 'mdast' - * @import {ComponentType, JSX, ReactElement} from 'react' + * @import {ComponentType, JSX, ReactElement, ReactNode} from 'react' * @import {Options as RemarkRehypeOptions} from 'remark-rehype' * @import {BuildVisitor} from 'unist-util-visit' * @import {PluggableList, Processor} from 'unified' @@ -77,6 +77,18 @@ * Change URLs (default: `defaultUrlTransform`) */ +/** + * @typedef HooksOptionsOnly + * Configuration specifically for {@link MarkdownHooks} + * @property {ReactNode} [fallback] + * A fallback node to render while the processor isn’t done processing the markdown. + */ + +/** + * @typedef {Options & HooksOptionsOnly} HooksOptions + * Configuration for {@link MarkdownHooks} + */ + /** * @callback UrlTransform * Transform all URLs. @@ -94,7 +106,7 @@ import {unreachable} from 'devlop' import {toJsxRuntime} from 'hast-util-to-jsx-runtime' import {urlAttributes} from 'html-url-attributes' import {Fragment, jsx, jsxs} from 'react/jsx-runtime' -import {createElement, useEffect, useState} from 'react' +import {useEffect, useState} from 'react' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import {unified} from 'unified' @@ -193,10 +205,10 @@ export async function MarkdownAsync(options) { * For async support on the server, * see {@linkcode MarkdownAsync}. * - * @param {Readonly} options + * @param {Readonly} options * Props. - * @returns {ReactElement} - * React element. + * @returns {ReactNode} + * React node. */ export function MarkdownHooks(options) { const processor = createProcessor(options) @@ -223,7 +235,7 @@ export function MarkdownHooks(options) { if (error) throw error - return tree ? post(tree, options) : createElement(Fragment) + return tree ? post(tree, options) : options.fallback } /** diff --git a/test.jsx b/test.jsx index 8bbf4f7..f951186 100644 --- a/test.jsx +++ b/test.jsx @@ -1149,6 +1149,28 @@ test('MarkdownHooks', async function (t) { } ) + await t.test( + 'should support `MarkdownHooks` loading fallback', + async function () { + const plugin = deferPlugin() + + const {container} = render( + + ) + + assert.equal(container.innerHTML, 'Loading') + plugin.resolve() + await waitFor(() => { + assert.notEqual(container.innerHTML, 'Loading') + }) + assert.equal(container.innerHTML, '

a

') + } + ) + await t.test('should support `MarkdownHooks` that error', async function () { const plugin = deferPlugin()