Expose slots to components (#1368)
* Expose slots to components via Astro.slots * test: Add Astro.slots API tests * docs: Document Astro.slots API * docs: Duplicate Astro.slots documentation to other api-reference markdown * Update proposal to use booleans, based upon RFC feedback * update implementation & tests based on request * changeset
This commit is contained in:
parent
8f727647af
commit
98d785af1d
10 changed files with 169 additions and 0 deletions
5
.changeset/pink-toes-shake.md
Normal file
5
.changeset/pink-toes-shake.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Expose slots to components
|
|
@ -49,6 +49,17 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
|
||||||
}[]
|
}[]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `Astro.slots`
|
||||||
|
|
||||||
|
`Astro.slots` returns an object with any slotted regions passed into the current Astro file.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const {
|
||||||
|
heading as headingSlot, // true or undefined, based on whether `<* slot="heading">` was used.
|
||||||
|
default as defaultSlot, // true or undefined, based on whether `<* slot>` or `<* default>` was used.
|
||||||
|
} = Astro.slots;
|
||||||
|
```
|
||||||
|
|
||||||
### `Astro.request`
|
### `Astro.request`
|
||||||
|
|
||||||
`Astro.request` returns an object with the following properties:
|
`Astro.request` returns an object with the following properties:
|
||||||
|
|
|
@ -86,6 +86,17 @@ const path = Astro.site.pathname;
|
||||||
<h1>Welcome to {path}</h1>
|
<h1>Welcome to {path}</h1>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `Astro.slots`
|
||||||
|
|
||||||
|
`Astro.slots` returns an object with any slotted regions passed into the current Astro file.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const {
|
||||||
|
heading as headingSlot, // true or undefined, based on whether `<* slot="heading">` was used.
|
||||||
|
default as defaultSlot, // true or undefined, based on whether `<* slot>` or `<* default>` was used.
|
||||||
|
} = Astro.slots;
|
||||||
|
```
|
||||||
|
|
||||||
## `getStaticPaths()`
|
## `getStaticPaths()`
|
||||||
|
|
||||||
If a page uses dynamic params in the filename, that component will need to export a `getStaticPaths()` function.
|
If a page uses dynamic params in the filename, that component will need to export a `getStaticPaths()` function.
|
||||||
|
|
|
@ -158,12 +158,25 @@ import { __astro_hoisted_scripts } from 'astro/dist/internal/__astro_hoisted_scr
|
||||||
const __astroScripts = __astro_hoisted_scripts([${result.components.map((n) => `typeof ${n} !== 'undefined' && ${n}`)}], ${JSON.stringify(result.hoistedScripts)});
|
const __astroScripts = __astro_hoisted_scripts([${result.components.map((n) => `typeof ${n} !== 'undefined' && ${n}`)}], ${JSON.stringify(result.hoistedScripts)});
|
||||||
const __astroInternal = Symbol('astro.internal');
|
const __astroInternal = Symbol('astro.internal');
|
||||||
const __astroContext = Symbol.for('astro.context');
|
const __astroContext = Symbol.for('astro.context');
|
||||||
|
const __astroSlotted = Symbol.for('astro.slotted');
|
||||||
async function __render(props, ...children) {
|
async function __render(props, ...children) {
|
||||||
const Astro = Object.create(__TopLevelAstro, {
|
const Astro = Object.create(__TopLevelAstro, {
|
||||||
props: {
|
props: {
|
||||||
value: props,
|
value: props,
|
||||||
enumerable: true
|
enumerable: true
|
||||||
},
|
},
|
||||||
|
slots: {
|
||||||
|
value: children.reduce(
|
||||||
|
(slots, child) => {
|
||||||
|
for (let name in child.$slots) {
|
||||||
|
slots[name] = Boolean(child.$slots[name])
|
||||||
|
}
|
||||||
|
return slots
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
enumerable: true
|
||||||
|
},
|
||||||
pageCSS: {
|
pageCSS: {
|
||||||
value: (props[__astroContext] && props[__astroContext].pageCSS) || [],
|
value: (props[__astroContext] && props[__astroContext].pageCSS) || [],
|
||||||
enumerable: true
|
enumerable: true
|
||||||
|
|
|
@ -74,4 +74,60 @@ Slots('Slots work on Components', async ({ runtime }) => {
|
||||||
assert.equal($('#default').children('astro-component').length, 1, 'Slotted component into default slot');
|
assert.equal($('#default').children('astro-component').length, 1, 'Slotted component into default slot');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Slots('Slots API work on Components', async ({ runtime }) => {
|
||||||
|
// IDs will exist whether the slots are filled or not
|
||||||
|
{
|
||||||
|
const result = await runtime.load('/slottedapi-default');
|
||||||
|
assert.ok(!result.error, `build error: ${result.error}`);
|
||||||
|
|
||||||
|
const $ = doc(result.contents);
|
||||||
|
|
||||||
|
assert.equal($('#a').length, 1);
|
||||||
|
assert.equal($('#b').length, 1);
|
||||||
|
assert.equal($('#c').length, 1);
|
||||||
|
assert.equal($('#default').length, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs will not exist because the slots are not filled
|
||||||
|
{
|
||||||
|
const result = await runtime.load('/slottedapi-empty');
|
||||||
|
assert.ok(!result.error, `build error: ${result.error}`);
|
||||||
|
|
||||||
|
const $ = doc(result.contents);
|
||||||
|
|
||||||
|
assert.equal($('#a').length, 0);
|
||||||
|
assert.equal($('#b').length, 0);
|
||||||
|
assert.equal($('#c').length, 0);
|
||||||
|
assert.equal($('#default').length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs will exist because the slots are filled
|
||||||
|
{
|
||||||
|
const result = await runtime.load('/slottedapi-filled');
|
||||||
|
assert.ok(!result.error, `build error: ${result.error}`);
|
||||||
|
|
||||||
|
const $ = doc(result.contents);
|
||||||
|
|
||||||
|
assert.equal($('#a').length, 1);
|
||||||
|
assert.equal($('#b').length, 1);
|
||||||
|
assert.equal($('#c').length, 1);
|
||||||
|
|
||||||
|
assert.equal($('#default').length, 0); // the default slot is not filled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default ID will exist because the default slot is filled
|
||||||
|
{
|
||||||
|
const result = await runtime.load('/slottedapi-default-filled');
|
||||||
|
assert.ok(!result.error, `build error: ${result.error}`);
|
||||||
|
|
||||||
|
const $ = doc(result.contents);
|
||||||
|
|
||||||
|
assert.equal($('#a').length, 0);
|
||||||
|
assert.equal($('#b').length, 0);
|
||||||
|
assert.equal($('#c').length, 0);
|
||||||
|
|
||||||
|
assert.equal($('#default').length, 1); // the default slot is filled
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Slots.run();
|
Slots.run();
|
||||||
|
|
15
packages/astro/test/fixtures/astro-slots/src/components/SlottedAPI.astro
vendored
Normal file
15
packages/astro/test/fixtures/astro-slots/src/components/SlottedAPI.astro
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{Astro.slots.a && <div id="a">
|
||||||
|
<slot name="a" />
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
{Astro.slots.b && <div id="b">
|
||||||
|
<slot name="b" />
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
{Astro.slots.c && <div id="c">
|
||||||
|
<slot name="c" />
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
{Astro.slots.default && <div id="default">
|
||||||
|
<slot />
|
||||||
|
</div>}
|
15
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default-filled.astro
vendored
Normal file
15
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default-filled.astro
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
import Slotted from '../components/SlottedAPI.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Test Astro.slots behavior. -->
|
||||||
|
<!-- IDs will exist because the slots are filled -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Slotted>
|
||||||
|
Default
|
||||||
|
</Slotted>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default.astro
vendored
Normal file
13
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default.astro
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
import Slotted from '../components/Slotted.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Test default slots behavior. -->
|
||||||
|
<!-- IDs will exist whether the slots are filled or not -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Slotted />
|
||||||
|
</body>
|
||||||
|
</html>
|
13
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-empty.astro
vendored
Normal file
13
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-empty.astro
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
import Slotted from '../components/SlottedAPI.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Test Astro.slots behavior. -->
|
||||||
|
<!-- IDs will not exist because the slots are not filled -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Slotted />
|
||||||
|
</body>
|
||||||
|
</html>
|
17
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-filled.astro
vendored
Normal file
17
packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-filled.astro
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
import Slotted from '../components/SlottedAPI.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Test Astro.slots behavior. -->
|
||||||
|
<!-- IDs will exist because the slots are filled -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Slotted>
|
||||||
|
<span slot="a">A</span>
|
||||||
|
<span slot="b">B</span>
|
||||||
|
<span slot="c">C</span>
|
||||||
|
</Slotted>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue