Skip to content

Commit a02a273

Browse files
committed
Add MarkdownHooks fallback prop
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/>`.
1 parent ad7f37f commit a02a273

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @typedef {import('./lib/index.js').AllowElement} AllowElement
33
* @typedef {import('./lib/index.js').Components} Components
44
* @typedef {import('./lib/index.js').ExtraProps} ExtraProps
5+
* @typedef {import('./lib/index.js').HooksOptions} HooksOptions
56
* @typedef {import('./lib/index.js').Options} Options
67
* @typedef {import('./lib/index.js').UrlTransform} UrlTransform
78
*/

lib/index.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @import {Element, Nodes, Parents, Root} from 'hast'
33
* @import {Root as MdastRoot} from 'mdast'
4-
* @import {ComponentType, JSX, ReactElement} from 'react'
4+
* @import {ComponentType, JSX, ReactElement, ReactNode} from 'react'
55
* @import {Options as RemarkRehypeOptions} from 'remark-rehype'
66
* @import {BuildVisitor} from 'unist-util-visit'
77
* @import {PluggableList, Processor} from 'unified'
@@ -77,6 +77,18 @@
7777
* Change URLs (default: `defaultUrlTransform`)
7878
*/
7979

80+
/**
81+
* @typedef HooksOptionsOnly
82+
* Configuration specifically for {@link MarkdownHooks}
83+
* @property {ReactNode} [fallback]
84+
* A fallback node to render while the processor isn’t done processing the markdown.
85+
*/
86+
87+
/**
88+
* @typedef {Options & HooksOptionsOnly} HooksOptions
89+
* Configuration for {@link MarkdownHooks}
90+
*/
91+
8092
/**
8193
* @callback UrlTransform
8294
* Transform all URLs.
@@ -94,7 +106,7 @@ import {unreachable} from 'devlop'
94106
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
95107
import {urlAttributes} from 'html-url-attributes'
96108
import {Fragment, jsx, jsxs} from 'react/jsx-runtime'
97-
import {createElement, useEffect, useState} from 'react'
109+
import {useEffect, useState} from 'react'
98110
import remarkParse from 'remark-parse'
99111
import remarkRehype from 'remark-rehype'
100112
import {unified} from 'unified'
@@ -193,10 +205,10 @@ export async function MarkdownAsync(options) {
193205
* For async support on the server,
194206
* see {@linkcode MarkdownAsync}.
195207
*
196-
* @param {Readonly<Options>} options
208+
* @param {Readonly<HooksOptions>} options
197209
* Props.
198-
* @returns {ReactElement}
199-
* React element.
210+
* @returns {ReactNode}
211+
* React node.
200212
*/
201213
export function MarkdownHooks(options) {
202214
const processor = createProcessor(options)
@@ -223,7 +235,7 @@ export function MarkdownHooks(options) {
223235

224236
if (error) throw error
225237

226-
return tree ? post(tree, options) : createElement(Fragment)
238+
return tree ? post(tree, options) : options.fallback
227239
}
228240

229241
/**

test.jsx

+22
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,28 @@ test('MarkdownHooks', async function (t) {
11491149
}
11501150
)
11511151

1152+
await t.test(
1153+
'should support `MarkdownHooks` loading fallback',
1154+
async function () {
1155+
const plugin = deferPlugin()
1156+
1157+
const {container} = render(
1158+
<MarkdownHooks
1159+
children={'a'}
1160+
fallback="Loading"
1161+
rehypePlugins={[plugin.plugin]}
1162+
/>
1163+
)
1164+
1165+
assert.equal(container.innerHTML, 'Loading')
1166+
plugin.resolve()
1167+
await waitFor(() => {
1168+
assert.notEqual(container.innerHTML, 'Loading')
1169+
})
1170+
assert.equal(container.innerHTML, '<p>a</p>')
1171+
}
1172+
)
1173+
11521174
await t.test('should support `MarkdownHooks` that error', async function () {
11531175
const plugin = deferPlugin()
11541176

0 commit comments

Comments
 (0)