From 98d785af1d8f1f1044db5e8b3364eec0e4d580be Mon Sep 17 00:00:00 2001 From: Jonathan Neal Date: Wed, 15 Sep 2021 11:27:59 -0400 Subject: [PATCH] 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 --- .changeset/pink-toes-shake.md | 5 ++ docs/reference/api-reference.md | 11 ++++ docs/src/pages/reference/api-reference.md | 11 ++++ packages/astro/src/compiler/index.ts | 13 +++++ packages/astro/test/astro-slots.test.js | 56 +++++++++++++++++++ .../src/components/SlottedAPI.astro | 15 +++++ .../src/pages/slottedapi-default-filled.astro | 15 +++++ .../src/pages/slottedapi-default.astro | 13 +++++ .../src/pages/slottedapi-empty.astro | 13 +++++ .../src/pages/slottedapi-filled.astro | 17 ++++++ 10 files changed, 169 insertions(+) create mode 100644 .changeset/pink-toes-shake.md create mode 100644 packages/astro/test/fixtures/astro-slots/src/components/SlottedAPI.astro create mode 100644 packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default-filled.astro create mode 100644 packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default.astro create mode 100644 packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-empty.astro create mode 100644 packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-filled.astro diff --git a/.changeset/pink-toes-shake.md b/.changeset/pink-toes-shake.md new file mode 100644 index 0000000000..16ab79a5e1 --- /dev/null +++ b/.changeset/pink-toes-shake.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Expose slots to components diff --git a/docs/reference/api-reference.md b/docs/reference/api-reference.md index 3f3d8526b3..e997a18591 100644 --- a/docs/reference/api-reference.md +++ b/docs/reference/api-reference.md @@ -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` returns an object with the following properties: diff --git a/docs/src/pages/reference/api-reference.md b/docs/src/pages/reference/api-reference.md index 624ed29868..08178b4b53 100644 --- a/docs/src/pages/reference/api-reference.md +++ b/docs/src/pages/reference/api-reference.md @@ -86,6 +86,17 @@ const path = Astro.site.pathname;

Welcome to {path}

``` +### `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()` If a page uses dynamic params in the filename, that component will need to export a `getStaticPaths()` function. diff --git a/packages/astro/src/compiler/index.ts b/packages/astro/src/compiler/index.ts index 7f416a30a7..62809b285e 100644 --- a/packages/astro/src/compiler/index.ts +++ b/packages/astro/src/compiler/index.ts @@ -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 __astroInternal = Symbol('astro.internal'); const __astroContext = Symbol.for('astro.context'); +const __astroSlotted = Symbol.for('astro.slotted'); async function __render(props, ...children) { const Astro = Object.create(__TopLevelAstro, { props: { value: props, 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: { value: (props[__astroContext] && props[__astroContext].pageCSS) || [], enumerable: true diff --git a/packages/astro/test/astro-slots.test.js b/packages/astro/test/astro-slots.test.js index 72b53c3e64..3540368734 100644 --- a/packages/astro/test/astro-slots.test.js +++ b/packages/astro/test/astro-slots.test.js @@ -74,4 +74,60 @@ Slots('Slots work on Components', async ({ runtime }) => { 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(); diff --git a/packages/astro/test/fixtures/astro-slots/src/components/SlottedAPI.astro b/packages/astro/test/fixtures/astro-slots/src/components/SlottedAPI.astro new file mode 100644 index 0000000000..e1b00fff89 --- /dev/null +++ b/packages/astro/test/fixtures/astro-slots/src/components/SlottedAPI.astro @@ -0,0 +1,15 @@ +{Astro.slots.a &&
+ +
} + +{Astro.slots.b &&
+ +
} + +{Astro.slots.c &&
+ +
} + +{Astro.slots.default &&
+ +
} diff --git a/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default-filled.astro b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default-filled.astro new file mode 100644 index 0000000000..2800cb94d4 --- /dev/null +++ b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default-filled.astro @@ -0,0 +1,15 @@ +--- +import Slotted from '../components/SlottedAPI.astro'; +--- + + + + + + + + + Default + + + diff --git a/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default.astro b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default.astro new file mode 100644 index 0000000000..349d636839 --- /dev/null +++ b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-default.astro @@ -0,0 +1,13 @@ +--- +import Slotted from '../components/Slotted.astro'; +--- + + + + + + + + + + diff --git a/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-empty.astro b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-empty.astro new file mode 100644 index 0000000000..2f2ad04e9e --- /dev/null +++ b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-empty.astro @@ -0,0 +1,13 @@ +--- +import Slotted from '../components/SlottedAPI.astro'; +--- + + + + + + + + + + diff --git a/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-filled.astro b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-filled.astro new file mode 100644 index 0000000000..01714e5d1e --- /dev/null +++ b/packages/astro/test/fixtures/astro-slots/src/pages/slottedapi-filled.astro @@ -0,0 +1,17 @@ +--- +import Slotted from '../components/SlottedAPI.astro'; +--- + + + + + + + + + A + B + C + + +