Skip to content

Commit

Permalink
Add MarkdownHooks fallback prop
Browse files Browse the repository at this point in the history
The `<MarkdownHooks/>` component now supports a new prop named
`fallback`. This fallback is displayed while the initial content hasn’t
loaded yet. The name `fallback` was chosen to match the same prop from
`<Suspense/>`.
  • Loading branch information
remcohaszing committed Mar 5, 2025
1 parent ad7f37f commit a02a273
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 6 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
24 changes: 18 additions & 6 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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.
Expand All @@ -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'
Expand Down Expand Up @@ -193,10 +205,10 @@ export async function MarkdownAsync(options) {
* For async support on the server,
* see {@linkcode MarkdownAsync}.
*
* @param {Readonly<Options>} options
* @param {Readonly<HooksOptions>} options
* Props.
* @returns {ReactElement}
* React element.
* @returns {ReactNode}
* React node.
*/
export function MarkdownHooks(options) {
const processor = createProcessor(options)
Expand All @@ -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
}

/**
Expand Down
22 changes: 22 additions & 0 deletions test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<MarkdownHooks
children={'a'}
fallback="Loading"
rehypePlugins={[plugin.plugin]}
/>
)

assert.equal(container.innerHTML, 'Loading')
plugin.resolve()
await waitFor(() => {
assert.notEqual(container.innerHTML, 'Loading')
})
assert.equal(container.innerHTML, '<p>a</p>')
}
)

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

Expand Down

0 comments on commit a02a273

Please sign in to comment.