[@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:
parent
ec5a39c299
commit
f7aa1ec25d
8 changed files with 83 additions and 17 deletions
6
.changeset/kind-beers-give.md
Normal file
6
.changeset/kind-beers-give.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
'@astrojs/lit': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix Lit slotted content
|
|
@ -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>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
{
|
{
|
||||||
"experimentalDecorators": true
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 () => {
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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:*",
|
||||||
|
|
|
@ -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
2
pnpm-lock.yaml
generated
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue