Skip to content

Commit 8853790

Browse files
johnjenkinsJohn Jenkins
andauthored
fix(ssr): correctly resolve slots during hydration (#6131)
* fix(ssr): correctly resolve slots during hydration * chore: fixup tests some more * chore: tidy --------- Co-authored-by: John Jenkins <[email protected]>
1 parent c1e6838 commit 8853790

File tree

4 files changed

+43
-9
lines changed

4 files changed

+43
-9
lines changed

src/runtime/client-hydrate.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export const initializeClientHydrate = (
109109
}
110110

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

src/runtime/vdom/vdom-render.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ function addRemoveSlotScopedClass(
913913
// let's add a scoped-slot class to this slotted node's parent
914914
newParent.classList?.add(scopeId + '-s');
915915

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

test/wdio/ssr-hydration/cmp.test.tsx

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,19 @@ describe('ssr-shadow-cmp', () => {
5050
// wait for Stencil to take over and reconcile
5151
await browser.waitUntil(async () => customElements.get('ssr-shadow-cmp'));
5252
expect(typeof customElements.get('ssr-shadow-cmp')).toBe('function');
53+
5354
await expect(getNodeNames(document.querySelector('ssr-shadow-cmp').childNodes)).toBe(
5455
`text comment div comment text`,
5556
);
5657

5758
document.querySelector('#stage')?.remove();
59+
await browser.waitUntil(async () => !document.querySelector('#stage'));
5860
});
5961

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

63-
const { html } = await renderToString(
65+
await renderToString(
6466
Array(50)
6567
.fill(0)
6668
.map((_, i) => `<ssr-shadow-cmp>Value ${i}</ssr-shadow-cmp>`)
@@ -71,15 +73,44 @@ describe('ssr-shadow-cmp', () => {
7173
constrainTimeouts: false,
7274
},
7375
);
74-
const stage = document.createElement('div');
75-
stage.setAttribute('id', 'stage');
76-
stage.setHTMLUnsafe(html);
77-
document.body.appendChild(stage);
78-
7976
performance.mark('end');
8077
const renderTime = performance.measure('render', 'start', 'end').duration;
78+
await expect(renderTime).toBeLessThan(50);
79+
});
80+
81+
it('resolves slots correctly during client-side hydration', async () => {
82+
if (!document.querySelector('#stage')) {
83+
const { html } = await renderToString(
84+
`
85+
<ssr-shadow-cmp>
86+
<p>Default slot content</p>
87+
<p slot="client-only">Client-only slot content</p>
88+
</ssr-shadow-cmp>
89+
`,
90+
{
91+
fullDocument: true,
92+
serializeShadowRoot: true,
93+
constrainTimeouts: false,
94+
},
95+
);
96+
const stage = document.createElement('div');
97+
stage.setAttribute('id', 'stage');
98+
stage.setHTMLUnsafe(html);
99+
document.body.appendChild(stage);
100+
}
81101

82-
await expect(renderTime).toBeLessThan(100);
102+
// @ts-expect-error resolved through WDIO
103+
const { defineCustomElements } = await import('/dist/loader/index.js');
104+
defineCustomElements().catch(console.error);
105+
106+
// wait for Stencil to take over and reconcile
107+
await browser.waitUntil(async () => customElements.get('ssr-shadow-cmp'));
108+
expect(typeof customElements.get('ssr-shadow-cmp')).toBe('function');
109+
110+
await browser.waitUntil(async () => document.querySelector('ssr-shadow-cmp [slot="client-only"]'));
111+
await expect(document.querySelector('ssr-shadow-cmp').textContent).toBe(
112+
' Default slot content Client-only slot content ',
113+
);
83114

84115
document.querySelector('#stage')?.remove();
85116
});

test/wdio/ssr-hydration/cmp.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, h, Prop } from '@stencil/core';
1+
import { Build, Component, h, Prop } from '@stencil/core';
22

33
@Component({
44
tag: 'ssr-shadow-cmp',
@@ -20,7 +20,9 @@ export class SsrShadowCmp {
2020
'option--novalue': !this.value,
2121
}}
2222
>
23+
<slot name="top" />
2324
<slot />
25+
{Build.isBrowser && <slot name="client-only" />}
2426
</div>
2527
);
2628
}

0 commit comments

Comments
 (0)