diff --git a/.changeset/friendly-wolves-juggle.md b/.changeset/friendly-wolves-juggle.md new file mode 100644 index 000000000..a8a3d3bed --- /dev/null +++ b/.changeset/friendly-wolves-juggle.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Support Astro.slots.render for mdx diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts index f6bb57ed7..f0cd02c54 100644 --- a/packages/astro/src/core/render/result.ts +++ b/packages/astro/src/core/render/result.ts @@ -11,6 +11,7 @@ import type { SSRResult, } from '../../@types/astro'; import { renderSlot } from '../../runtime/server/index.js'; +import { renderJSX } from '../../runtime/server/jsx.js'; import { AstroCookies } from '../cookies/index.js'; import { LogOptions, warn } from '../logger/core.js'; import { isScriptRequest } from './script.js'; @@ -94,8 +95,6 @@ class Slots { if (!this.has(name)) return undefined; if (!cacheable) { const component = await this.#slots[name](); - const expression = getFunctionExpression(component); - if (!Array.isArray(args)) { warn( this.#loggingOpts, @@ -103,12 +102,20 @@ class Slots { `Expected second parameter to be an array, received a ${typeof args}. If you're trying to pass an array as a single argument and getting unexpected results, make sure you're passing your array as a item of an array. Ex: Astro.slots.render('default', [["Hello", "World"]])` ); } else { + // Astro + const expression = getFunctionExpression(component); if (expression) { const slot = expression(...args); return await renderSlot(this.#result, slot).then((res) => res != null ? String(res) : res ); } + // JSX + if (typeof component === 'function') { + return await renderJSX(this.#result, component(...args)).then((res) => + res != null ? String(res) : res + ); + } } } const content = await renderSlot(this.#result, this.#slots[name]).then((res) => diff --git a/packages/astro/src/runtime/server/jsx.ts b/packages/astro/src/runtime/server/jsx.ts index 6f0cfb336..3e50f33f6 100644 --- a/packages/astro/src/runtime/server/jsx.ts +++ b/packages/astro/src/runtime/server/jsx.ts @@ -29,6 +29,8 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise { return vnode; case typeof vnode === 'string': return markHTMLString(escapeHTML(vnode)); + case typeof vnode === 'function': + return vnode; case !vnode && vnode !== 0: return ''; case Array.isArray(vnode): diff --git a/packages/astro/test/fixtures/slots-react/src/components/Render.astro b/packages/astro/test/fixtures/slots-react/src/components/Render.astro new file mode 100644 index 000000000..c106c4d06 --- /dev/null +++ b/packages/astro/test/fixtures/slots-react/src/components/Render.astro @@ -0,0 +1,6 @@ +--- +const { id } = Astro.props; +const content = await Astro.slots.render('default'); +--- + +
diff --git a/packages/astro/test/fixtures/slots-react/src/components/RenderArgs.astro b/packages/astro/test/fixtures/slots-react/src/components/RenderArgs.astro new file mode 100644 index 000000000..d017f2629 --- /dev/null +++ b/packages/astro/test/fixtures/slots-react/src/components/RenderArgs.astro @@ -0,0 +1,5 @@ +--- +const { id, text } = Astro.props; +--- + +
diff --git a/packages/astro/test/fixtures/slots-react/src/components/RenderFn.astro b/packages/astro/test/fixtures/slots-react/src/components/RenderFn.astro new file mode 100644 index 000000000..c106c4d06 --- /dev/null +++ b/packages/astro/test/fixtures/slots-react/src/components/RenderFn.astro @@ -0,0 +1,6 @@ +--- +const { id } = Astro.props; +const content = await Astro.slots.render('default'); +--- + +
diff --git a/packages/astro/test/fixtures/slots-react/src/pages/slottedapi-render.mdx b/packages/astro/test/fixtures/slots-react/src/pages/slottedapi-render.mdx new file mode 100644 index 000000000..af9c06612 --- /dev/null +++ b/packages/astro/test/fixtures/slots-react/src/pages/slottedapi-render.mdx @@ -0,0 +1,9 @@ +import Render from '../components/Render.astro'; +import RenderFn from '../components/RenderFn.astro'; +import RenderArgs from '../components/RenderArgs.astro'; + +# Slots: MDX + +render +{() => "render-fn"} +{(text) => {text}} diff --git a/packages/astro/test/slots-react.test.js b/packages/astro/test/slots-react.test.js index 8e61d41ec..a5ac46ab3 100644 --- a/packages/astro/test/slots-react.test.js +++ b/packages/astro/test/slots-react.test.js @@ -73,4 +73,31 @@ describe('Slots: React', () => { expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); }); }); + + describe('Slots.render() API', async () => { + it('Simple imperative slot render', async () => { + const html = await fixture.readFile('/slottedapi-render/index.html'); + const $ = cheerio.load(html); + + expect($('#render')).to.have.lengthOf(1); + expect($('#render').text()).to.equal('render'); + }); + + it('Child function render without args', async () => { + const html = await fixture.readFile('/slottedapi-render/index.html'); + const $ = cheerio.load(html); + + expect($('#render-fn')).to.have.lengthOf(1); + expect($('#render-fn').text()).to.equal('render-fn'); + }); + + it('Child function render with args', async () => { + const html = await fixture.readFile('/slottedapi-render/index.html'); + const $ = cheerio.load(html); + + expect($('#render-args')).to.have.lengthOf(1); + expect($('#render-args span')).to.have.lengthOf(1); + expect($('#render-args').text()).to.equal('render-args'); + }); + }); });