[@astrojs/lit] Fix slotted content into main (#5791)

* Fix  add the missing slot attribute to child nodes

* Add lit slot tests

* Add changeset
This commit is contained in:
Sebastiaan 2023-01-07 14:29:16 +01:00 committed by GitHub
parent ec5a39c299
commit f7aa1ec25d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 17 deletions

View file

@ -0,0 +1,6 @@
---
'astro': patch
'@astrojs/lit': patch
---
Fix Lit slotted content

View file

@ -7,9 +7,33 @@ import {MyElement} from '../components/my-element.js';
<title>LitElement | Slot</title> <title>LitElement | Slot</title>
</head> </head>
<body> <body>
<MyElement> <MyElement id="root">
<div>default</div> <div class="default">my-element default 1</div>
<div slot="named">named</div> <div class="default">my-element default 2</div>
<h1 slot="named">my-element named 1</h1>
<h2 slot="named">my-element named 2</h2>
<ul slot="named" id="list">
<li>Custom elements</li>
<li>Shadow DOM</li>
<li>HTML templates</li>
</ul>
<my-element id="slotted" slot="named">
<h3 class="default">slotted my-element default</h3>
<div slot="named">slotted my-element named 1</div>
<div slot="named">slotted my-element named 2</div>
<MyElement id="slotted-slotted" slot="named">
<h4 class="default">slotted slotted my-element default 1</h4>
<h5 class="default">slotted slotted my-element default 2</h5>
<div slot="named">slotted slotted my-element named 1</div>
<div slot="named">slotted slotted my-element named 2</div>
</MyElement>
</my-element>
</MyElement> </MyElement>
</body> </body>
</html> </html>

View file

@ -1,3 +1,5 @@
{ {
"experimentalDecorators": true "compilerOptions": {
"experimentalDecorators": true
}
} }

View file

@ -70,15 +70,35 @@ describe('LitElement test', function () {
const html = await fixture.readFile('/slots/index.html'); const html = await fixture.readFile('/slots/index.html');
const $ = cheerio.load(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 // Root my-element
expect($(defaultSlot).text()).to.equal('default'); 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($rootMyElement.children('[slot="named"]').length).to.equal(4);
expect($(namedSlot).text()).to.equal('named'); 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 () => { it('Is able to build when behind getStaticPaths', async () => {

View file

@ -25,7 +25,7 @@ describe('Lit integration in SSR', () => {
} }
it('Is able to load', async () => { 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 html = await fetchHTML('/');
const $ = cheerioLoad(html); const $ = cheerioLoad(html);
expect($('#win').text()).to.equal('function'); expect($('#win').text()).to.equal('function');

View file

@ -33,7 +33,8 @@
"test": "mocha" "test": "mocha"
}, },
"dependencies": { "dependencies": {
"@lit-labs/ssr": "^2.2.0" "@lit-labs/ssr": "^2.2.0",
"parse5": "^7.1.2"
}, },
"devDependencies": { "devDependencies": {
"astro": "workspace:*", "astro": "workspace:*",

View file

@ -1,6 +1,7 @@
import './server-shim.js'; import './server-shim.js';
import '@lit-labs/ssr/lib/render-lit-html.js'; import '@lit-labs/ssr/lib/render-lit-html.js';
import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js'; import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js';
import * as parse5 from 'parse5';
function isCustomElementTag(name) { function isCustomElementTag(name) {
return typeof name === 'string' && /-/.test(name); return typeof name === 'string' && /-/.test(name);
@ -58,12 +59,22 @@ function* render(Component, attrs, slots) {
yield '</template>'; yield '</template>';
} }
if (slots) { if (slots) {
for (const [slot, value] of Object.entries(slots)) { for (let [slot, value = ''] of Object.entries(slots)) {
if (slot === 'default') { if (slot !== 'default' && value) {
yield `<astro-slot>${value || ''}</astro-slot>`; // Parse the value as a concatenated string
} else { const fragment = parse5.parseFragment(`${value}`);
yield `<astro-slot slot="${slot}">${value || ''}</astro-slot>`;
// 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 `</${tagName}>`; yield `</${tagName}>`;

2
pnpm-lock.yaml generated
View file

@ -2838,9 +2838,11 @@ importers:
cheerio: ^1.0.0-rc.11 cheerio: ^1.0.0-rc.11
lit: ^2.2.5 lit: ^2.2.5
mocha: ^9.2.2 mocha: ^9.2.2
parse5: ^7.1.2
sass: ^1.52.2 sass: ^1.52.2
dependencies: dependencies:
'@lit-labs/ssr': 2.3.0 '@lit-labs/ssr': 2.3.0
parse5: 7.1.2
devDependencies: devDependencies:
astro: link:../../astro astro: link:../../astro
astro-scripts: link:../../../scripts astro-scripts: link:../../../scripts