From f7aa1ec25d1584f7abd421903fbef66b1c050e2a Mon Sep 17 00:00:00 2001 From: Sebastiaan Date: Sat, 7 Jan 2023 14:29:16 +0100 Subject: [PATCH] [@astrojs/lit] Fix slotted content into main (#5791) * Fix add the missing slot attribute to child nodes * Add lit slot tests * Add changeset --- .changeset/kind-beers-give.md | 6 ++++ .../lit-element/src/pages/slots.astro | 30 +++++++++++++++-- .../test/fixtures/lit-element/tsconfig.json | 4 ++- packages/astro/test/lit-element.test.js | 32 +++++++++++++++---- packages/astro/test/ssr-lit.test.js | 2 +- packages/integrations/lit/package.json | 3 +- packages/integrations/lit/server.js | 21 +++++++++--- pnpm-lock.yaml | 2 ++ 8 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 .changeset/kind-beers-give.md diff --git a/.changeset/kind-beers-give.md b/.changeset/kind-beers-give.md new file mode 100644 index 000000000000..2cda00bcfd51 --- /dev/null +++ b/.changeset/kind-beers-give.md @@ -0,0 +1,6 @@ +--- +'astro': patch +'@astrojs/lit': patch +--- + +Fix Lit slotted content diff --git a/packages/astro/test/fixtures/lit-element/src/pages/slots.astro b/packages/astro/test/fixtures/lit-element/src/pages/slots.astro index b8fc4963c6f0..6ef0cd1832a7 100644 --- a/packages/astro/test/fixtures/lit-element/src/pages/slots.astro +++ b/packages/astro/test/fixtures/lit-element/src/pages/slots.astro @@ -7,9 +7,33 @@ import {MyElement} from '../components/my-element.js'; LitElement | Slot - -
default
-
named
+ +
my-element default 1
+
my-element default 2
+ +

my-element named 1

+

my-element named 2

+ +
    +
  • Custom elements
  • +
  • Shadow DOM
  • +
  • HTML templates
  • +
+ + +

slotted my-element default

+ +
slotted my-element named 1
+
slotted my-element named 2
+ + +

slotted slotted my-element default 1

+
slotted slotted my-element default 2
+ +
slotted slotted my-element named 1
+
slotted slotted my-element named 2
+
+
diff --git a/packages/astro/test/fixtures/lit-element/tsconfig.json b/packages/astro/test/fixtures/lit-element/tsconfig.json index f79d7bdca151..504cd646e149 100644 --- a/packages/astro/test/fixtures/lit-element/tsconfig.json +++ b/packages/astro/test/fixtures/lit-element/tsconfig.json @@ -1,3 +1,5 @@ { - "experimentalDecorators": true + "compilerOptions": { + "experimentalDecorators": true + } } diff --git a/packages/astro/test/lit-element.test.js b/packages/astro/test/lit-element.test.js index 3ee0a00a52d1..c14f5af55388 100644 --- a/packages/astro/test/lit-element.test.js +++ b/packages/astro/test/lit-element.test.js @@ -70,15 +70,35 @@ describe('LitElement test', function () { const html = await fixture.readFile('/slots/index.html'); const $ = cheerio.load(html); - expect($('my-element').length).to.equal(1); + const $rootMyElement = $('#root'); + const $slottedMyElement = $('#slotted'); + const $slottedSlottedMyElement = $('#slotted-slotted'); - const [defaultSlot, namedSlot] = $('template').siblings().toArray(); + expect($('my-element').length).to.equal(3); - // has default slot content in lightdom - expect($(defaultSlot).text()).to.equal('default'); + // Root my-element + expect($rootMyElement.children('.default').length).to.equal(2); + expect($rootMyElement.children('.default').eq(1).text()).to.equal('my-element default 2'); - // has named slot content in lightdom - expect($(namedSlot).text()).to.equal('named'); + expect($rootMyElement.children('[slot="named"]').length).to.equal(4); + expect($rootMyElement.children('[slot="named"]').eq(1).text()).to.equal('my-element named 2'); + expect($rootMyElement.children('[slot="named"]').eq(2).attr('id')).to.equal('list'); + expect($rootMyElement.children('[slot="named"]').eq(3).attr('id')).to.equal('slotted'); + + // Slotted my-element first level + expect($slottedMyElement.children('.default').length).to.equal(1); + expect($slottedMyElement.children('.default').eq(0).text()).to.equal('slotted my-element default'); + + expect($slottedMyElement.children('[slot="named"]').length).to.equal(3); + expect($slottedMyElement.children('[slot="named"]').eq(1).text()).to.equal('slotted my-element named 2'); + expect($slottedMyElement.children('[slot="named"]').eq(2).attr('id')).to.equal('slotted-slotted'); + + // Slotted my-element second level + expect($slottedSlottedMyElement.children('.default').length).to.equal(2); + expect($slottedSlottedMyElement.children('.default').eq(1).text()).to.equal('slotted slotted my-element default 2'); + + expect($slottedSlottedMyElement.children('[slot="named"]').length).to.equal(2); + expect($slottedSlottedMyElement.children('[slot="named"]').eq(1).text()).to.equal('slotted slotted my-element named 2'); }); it('Is able to build when behind getStaticPaths', async () => { diff --git a/packages/astro/test/ssr-lit.test.js b/packages/astro/test/ssr-lit.test.js index 53555b9838ac..98d58b395acd 100644 --- a/packages/astro/test/ssr-lit.test.js +++ b/packages/astro/test/ssr-lit.test.js @@ -25,7 +25,7 @@ describe('Lit integration in SSR', () => { } it('Is able to load', async () => { - delete globalThis.window; + delete globalThis.window; // On Windows this results in `ReferenceError: window is not defined` const html = await fetchHTML('/'); const $ = cheerioLoad(html); expect($('#win').text()).to.equal('function'); diff --git a/packages/integrations/lit/package.json b/packages/integrations/lit/package.json index 9781094c7003..24410f7e8fb5 100644 --- a/packages/integrations/lit/package.json +++ b/packages/integrations/lit/package.json @@ -33,7 +33,8 @@ "test": "mocha" }, "dependencies": { - "@lit-labs/ssr": "^2.2.0" + "@lit-labs/ssr": "^2.2.0", + "parse5": "^7.1.2" }, "devDependencies": { "astro": "workspace:*", diff --git a/packages/integrations/lit/server.js b/packages/integrations/lit/server.js index 2f90766729b9..59c79f55befd 100644 --- a/packages/integrations/lit/server.js +++ b/packages/integrations/lit/server.js @@ -1,6 +1,7 @@ import './server-shim.js'; import '@lit-labs/ssr/lib/render-lit-html.js'; import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js'; +import * as parse5 from 'parse5'; function isCustomElementTag(name) { return typeof name === 'string' && /-/.test(name); @@ -58,12 +59,22 @@ function* render(Component, attrs, slots) { yield ''; } if (slots) { - for (const [slot, value] of Object.entries(slots)) { - if (slot === 'default') { - yield `${value || ''}`; - } else { - yield `${value || ''}`; + for (let [slot, value = ''] of Object.entries(slots)) { + if (slot !== 'default' && value) { + // Parse the value as a concatenated string + const fragment = parse5.parseFragment(`${value}`); + + // Add the missing slot attribute to child Element nodes + for (const node of fragment.childNodes) { + if (node.tagName && !node.attrs.some(({ name }) => name === 'slot')) { + node.attrs.push({ name: 'slot', value: slot}); + } + } + + value = parse5.serialize(fragment); } + + yield value; } } yield ``; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f26b44e6742..f751364faa9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2838,9 +2838,11 @@ importers: cheerio: ^1.0.0-rc.11 lit: ^2.2.5 mocha: ^9.2.2 + parse5: ^7.1.2 sass: ^1.52.2 dependencies: '@lit-labs/ssr': 2.3.0 + parse5: 7.1.2 devDependencies: astro: link:../../astro astro-scripts: link:../../../scripts