Skip to content

Commit

Permalink
fix(ssr): correctly resolve slots during hydration (#6131)
Browse files Browse the repository at this point in the history
* fix(ssr): correctly resolve slots during hydration

* chore: fixup tests some more

* chore: tidy

---------

Co-authored-by: John Jenkins <[email protected]>
  • Loading branch information
johnjenkins and John Jenkins authored Jan 30, 2025
1 parent c1e6838 commit 8853790
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/runtime/client-hydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const initializeClientHydrate = (
}

if (childRenderNode.$tag$ === 'slot') {
childRenderNode.$name$ = childRenderNode.$elm$['s-sn'] || (childRenderNode.$elm$ as any)['name'] || null;
if (childRenderNode.$children$) {
childRenderNode.$flags$ |= VNODE_FLAGS.isSlotFallback;

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/vdom/vdom-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ function addRemoveSlotScopedClass(
// let's add a scoped-slot class to this slotted node's parent
newParent.classList?.add(scopeId + '-s');

if (oldParent && oldParent.classList.contains(scopeId + '-s')) {
if (oldParent && oldParent.classList?.contains(scopeId + '-s')) {
let child = ((oldParent as d.RenderNode).__childNodes || oldParent.childNodes)[0] as d.RenderNode;
let found = false;

Expand Down
45 changes: 38 additions & 7 deletions test/wdio/ssr-hydration/cmp.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,19 @@ describe('ssr-shadow-cmp', () => {
// wait for Stencil to take over and reconcile
await browser.waitUntil(async () => customElements.get('ssr-shadow-cmp'));
expect(typeof customElements.get('ssr-shadow-cmp')).toBe('function');

await expect(getNodeNames(document.querySelector('ssr-shadow-cmp').childNodes)).toBe(
`text comment div comment text`,
);

document.querySelector('#stage')?.remove();
await browser.waitUntil(async () => !document.querySelector('#stage'));
});

it('checks perf when loading lots of the same component', async () => {
performance.mark('start');

const { html } = await renderToString(
await renderToString(
Array(50)
.fill(0)
.map((_, i) => `<ssr-shadow-cmp>Value ${i}</ssr-shadow-cmp>`)
Expand All @@ -71,15 +73,44 @@ describe('ssr-shadow-cmp', () => {
constrainTimeouts: false,
},
);
const stage = document.createElement('div');
stage.setAttribute('id', 'stage');
stage.setHTMLUnsafe(html);
document.body.appendChild(stage);

performance.mark('end');
const renderTime = performance.measure('render', 'start', 'end').duration;
await expect(renderTime).toBeLessThan(50);
});

it('resolves slots correctly during client-side hydration', async () => {
if (!document.querySelector('#stage')) {
const { html } = await renderToString(
`
<ssr-shadow-cmp>
<p>Default slot content</p>
<p slot="client-only">Client-only slot content</p>
</ssr-shadow-cmp>
`,
{
fullDocument: true,
serializeShadowRoot: true,
constrainTimeouts: false,
},
);
const stage = document.createElement('div');
stage.setAttribute('id', 'stage');
stage.setHTMLUnsafe(html);
document.body.appendChild(stage);
}

await expect(renderTime).toBeLessThan(100);
// @ts-expect-error resolved through WDIO
const { defineCustomElements } = await import('/dist/loader/index.js');
defineCustomElements().catch(console.error);

// wait for Stencil to take over and reconcile
await browser.waitUntil(async () => customElements.get('ssr-shadow-cmp'));
expect(typeof customElements.get('ssr-shadow-cmp')).toBe('function');

await browser.waitUntil(async () => document.querySelector('ssr-shadow-cmp [slot="client-only"]'));
await expect(document.querySelector('ssr-shadow-cmp').textContent).toBe(
' Default slot content Client-only slot content ',
);

document.querySelector('#stage')?.remove();
});
Expand Down
4 changes: 3 additions & 1 deletion test/wdio/ssr-hydration/cmp.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, h, Prop } from '@stencil/core';
import { Build, Component, h, Prop } from '@stencil/core';

@Component({
tag: 'ssr-shadow-cmp',
Expand All @@ -20,7 +20,9 @@ export class SsrShadowCmp {
'option--novalue': !this.value,
}}
>
<slot name="top" />
<slot />
{Build.isBrowser && <slot name="client-only" />}
</div>
);
}
Expand Down

0 comments on commit 8853790

Please sign in to comment.