Skip to content

Commit 72e9af1

Browse files
committed
fix: resuming a component using styles and a text node
1 parent 3ddc6c7 commit 72e9af1

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
lines changed

.changeset/bright-cows-sell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
fix: resuming a component using styles and a text node

packages/qwik/src/core/client/vnode.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,18 +1838,18 @@ function materializeFromVNodeData(
18381838
processVNodeData(vData, (peek, consumeValue, consume, nextToConsumeIdx) => {
18391839
if (isNumber(peek())) {
18401840
// Element counts get encoded as numbers.
1841-
while (!isElement(child)) {
1841+
while (
1842+
!isElement(child) ||
1843+
// We pretend that style element's don't exist as they can get moved out.
1844+
// skip over style elements, as those need to be moved to the head
1845+
// and are not included in the counts.
1846+
isQStyleElement(child)
1847+
) {
18421848
child = fastNextSibling(child);
18431849
if (!child) {
18441850
throw qError(QError.materializeVNodeDataError, [vData, peek(), nextToConsumeIdx]);
18451851
}
18461852
}
1847-
// We pretend that style element's don't exist as they can get moved out.
1848-
while (isQStyleElement(child)) {
1849-
// skip over style elements, as those need to be moved to the head
1850-
// and are not included in the counts.
1851-
child = fastNextSibling(child);
1852-
}
18531853
combinedText = null;
18541854
previousTextNode = null;
18551855
let value = 0;
@@ -1912,6 +1912,10 @@ function materializeFromVNodeData(
19121912
} else if (peek() === VNodeDataChar.SLOT) {
19131913
vnode_setAttr(null, vParent, QSlot, consumeValue());
19141914
} else {
1915+
// skip over style elements in front of text nodes, where text node is the first child (except the style node)
1916+
while (isQStyleElement(child)) {
1917+
child = fastNextSibling(child);
1918+
}
19151919
const textNode =
19161920
child && fastNodeType(child) === /* Node.TEXT_NODE */ 3 ? (child as Text) : null;
19171921
// must be alphanumeric

packages/qwik/src/core/tests/use-styles.spec.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
Fragment as Component,
33
component$,
4+
Fragment,
45
inlinedQrl,
56
Fragment as Signal,
67
Slot,
@@ -189,6 +190,35 @@ describe.each([
189190
const qStyles = container.document.querySelectorAll(QStyleSelector);
190191
expect(qStyles).toHaveLength(2);
191192
});
193+
it('should skip style node in front of text node', async () => {
194+
const InnerCmp = component$(() => {
195+
return <div>Hello world</div>;
196+
});
197+
198+
const STYLE = `.container{color: blue;}`;
199+
const Cmp = component$(() => {
200+
useStylesQrl(inlinedQrl(STYLE, 's_styles1'));
201+
const groupSig = useSignal('1');
202+
return (
203+
<>
204+
Some text:{' '}
205+
<button onClick$={() => (groupSig.value = '2')}>click</button>
206+
{/* Enforce Cmp component materialization, because of dynamic content */}
207+
{groupSig.value === '2' && <InnerCmp />}
208+
</>
209+
);
210+
});
211+
const { vNode } = await render(<Cmp />, { debug });
212+
expect(vNode).toMatchVDOM(
213+
<Component ssr-required>
214+
<Fragment ssr-required>
215+
{'Some text:'}
216+
{' '}
217+
<button>click</button>
218+
</Fragment>
219+
</Component>
220+
);
221+
});
192222
});
193223

194224
describe('html wrapper', () => {

0 commit comments

Comments
 (0)