Skip to content

Commit bd3ec46

Browse files
committed
Refactor Solid emitter to align textarea handling with other runtimes
- Updated SOLID_ATTR_TYPES to correct attribute names for dialog and details elements. - Modified textarea handling in SolidEmitter to use a controlled value prop instead of children, ensuring consistency with other runtimes. - Enhanced normalization logic to accommodate textarea value attributes, improving SSR compatibility. These changes aim to improve the SolidJS emitter's behavior and maintain parity with other rendering runtimes.
1 parent 5c88a7d commit bd3ec46

3 files changed

Lines changed: 19 additions & 10 deletions

File tree

src/emitters/solid.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ const SOLID_ATTR_TYPES: Record<string, [string, string]> = {
5555
img: ["ImgHTMLAttributes", "HTMLImageElement"],
5656
source: ["SourceHTMLAttributes", "HTMLSourceElement"],
5757
picture: ["HTMLAttributes", "HTMLElement"],
58-
dialog: ["DialogHTMLAttributes", "HTMLDialogElement"],
59-
details: ["DetailsHTMLAttributes", "HTMLDetailsElement"],
58+
dialog: ["DialogHtmlAttributes", "HTMLDialogElement"],
59+
details: ["DetailsHtmlAttributes", "HTMLDetailsElement"],
6060
summary: ["HTMLAttributes", "HTMLElement"],
61-
table: ["TableHTMLAttributes", "HTMLTableElement"],
61+
table: ["HTMLAttributes", "HTMLTableElement"],
6262
caption: ["HTMLAttributes", "HTMLTableCaptionElement"],
6363
thead: ["HTMLAttributes", "HTMLTableSectionElement"],
6464
tbody: ["HTMLAttributes", "HTMLTableSectionElement"],
@@ -480,16 +480,17 @@ export class SolidEmitter implements Emitter {
480480
});
481481
let open = attrs.length > 0 ? `<${tagCode} ${attrs}` : `<${tagCode}`;
482482

483-
// Match other runtimes' SSR DOM: textarea text is children, not a
484-
// `value=""` attribute (Solid controlled `value` would add that attr).
483+
// Controlled `value` for client updates; empty string is stripped in
484+
// parity normalizeHtml so SSR DOM matches runtimes that use children.
485485
if (
486486
!isDynamic &&
487487
tagCode === "textarea" &&
488488
node.children.length === 1 &&
489489
node.children[0]!.kind === "text"
490490
) {
491491
const value = printTsExpr((node.children[0] as { expr: Expr }).expr, ctx);
492-
return `${open}>{(${value}) ?? ""}\n</textarea>`;
492+
open += ` value={(${value}) ?? ""}`;
493+
return `${open} />`;
493494
}
494495
if (node.void || node.children.length === 0) return `${open} />`;
495496
const children = node.children

tests/emitters.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ describe("solid emitter", () => {
112112
expect(file("ui/stack/stack.solid.tsx")).toContain("<Dynamic component={resolvedTagValue()}");
113113
});
114114

115-
it("maps the textarea content contract to children (not value attr)", () => {
116-
expect(file("ui/textarea/textarea.solid.tsx")).toContain(">{(");
117-
expect(file("ui/textarea/textarea.solid.tsx")).not.toContain("value={(");
115+
it("maps the textarea content contract to a controlled value prop", () => {
116+
expect(file("ui/textarea/textarea.solid.tsx")).toContain("value={(");
118117
});
119118
});
120119

tests/support/normalize.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function normalizeNode(node: P5Node): NormalizedNode | string | null {
3737
if (!node.tagName) return null;
3838

3939
const attrs: Record<string, string> = {};
40+
let textareaValue: string | undefined;
4041
for (const attr of node.attrs ?? []) {
4142
// Svelte SSR adds event-replay shims on media elements; not part of the
4243
// DOM contract.
@@ -51,13 +52,21 @@ function normalizeNode(node: P5Node): NormalizedNode | string | null {
5152
}
5253
// Solid SSR (`Dynamic` / hydratable markers) — not part of the DOM contract.
5354
if (attr.name === "data-hk") continue;
55+
// Solid controlled textarea uses value=; other runtimes put text as children.
56+
if (attr.name === "value" && node.tagName === "textarea") {
57+
textareaValue = attr.value;
58+
continue;
59+
}
5460
attrs[attr.name] = attr.value;
5561
}
56-
const children = mergeText(
62+
let children = mergeText(
5763
(node.childNodes ?? [])
5864
.map(normalizeNode)
5965
.filter((c): c is NormalizedNode | string => c !== null)
6066
);
67+
if (textareaValue !== undefined && children.length === 0 && textareaValue !== "") {
68+
children = [textareaValue];
69+
}
6170
return { tag: node.tagName, attrs, children };
6271
}
6372

0 commit comments

Comments
 (0)