Support Astro.slots.render for mdx (#4973)

* Support Astro.slots.render for mdx

* Remove extra imports
This commit is contained in:
Bjorn Lu 2022-10-04 22:18:34 +08:00 committed by GitHub
parent 5d58787f7a
commit c733d4fb81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 69 additions and 2 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Support Astro.slots.render for mdx

View file

@ -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) =>

View file

@ -29,6 +29,8 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
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):

View file

@ -0,0 +1,6 @@
---
const { id } = Astro.props;
const content = await Astro.slots.render('default');
---
<div id={id} set:html={content} />

View file

@ -0,0 +1,5 @@
---
const { id, text } = Astro.props;
---
<div id={id} set:html={Astro.slots.render('default', [text])} />

View file

@ -0,0 +1,6 @@
---
const { id } = Astro.props;
const content = await Astro.slots.render('default');
---
<div id={id} set:html={content} />

View file

@ -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 id="render">render</Render>
<RenderFn id="render-fn">{() => "render-fn"}</RenderFn>
<RenderArgs id="render-args" text="render-args">{(text) => <span>{text}</span>}</RenderArgs>

View file

@ -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');
});
});
});