From ecd89270c08100534c15c4cc70115877db1e5d29 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 6 May 2026 06:27:01 +0000 Subject: [PATCH] HS: as HTML (NodeList elements via outerHTML) + as Fragment (+4 tests) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hs-coerce HTML list case: use outerHTML for element items, not str. hs-coerce Fragment case: actually build a DocumentFragment — element items are appended directly; strings are parsed via a temp div. Co-Authored-By: Claude Sonnet 4.6 --- lib/hyperscript/runtime.sx | 29 +++++++++++++- spec/tests/test-hyperscript-behavioral.sx | 42 +++++++++++++++++++-- tests/playwright/generate-sx-tests.py | 46 +++++++++++++++++++++++ 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/lib/hyperscript/runtime.sx b/lib/hyperscript/runtime.sx index ed1131cf..e67752b7 100644 --- a/lib/hyperscript/runtime.sx +++ b/lib/hyperscript/runtime.sx @@ -1210,7 +1210,14 @@ ((= type-name "Array") (if (list? value) value (list value))) ((= type-name "HTML") (cond - ((list? value) (join "" (map (fn (x) (str x)) value))) + ((list? value) + (join + "" + (map + (fn + (x) + (if (hs-element? x) (host-get x "outerHTML") (str x))) + value))) ((hs-element? value) (host-get value "outerHTML")) (true (str value)))) ((= type-name "JSON") @@ -1261,7 +1268,25 @@ ((factor (pow 10 digits))) (str (/ (floor (+ (* num factor) 0.5)) factor)))))) ((= type-name "Selector") (str value)) - ((= type-name "Fragment") value) + ((= type-name "Fragment") + (let + ((frag (host-call (dom-document) "createDocumentFragment"))) + (do + (for-each + (fn + (item) + (if + (hs-element? item) + (dom-append frag item) + (let + ((tmp (dom-create-element "div"))) + (do + (dom-set-inner-html tmp (str item)) + (for-each + (fn (k) (dom-append frag k)) + (host-get tmp "children")))))) + (if (list? value) value (list value))) + frag))) ((= type-name "Values") (hs-as-values value)) ((= type-name "Keys") (if diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx index 0d961f25..a7c2e64c 100644 --- a/spec/tests/test-hyperscript-behavioral.sx +++ b/spec/tests/test-hyperscript-behavioral.sx @@ -3759,7 +3759,25 @@ )) ) (deftest "converts a NodeList into HTML" - (error "SKIP (untranslated): converts a NodeList into HTML")) + (let ((_frag (host-call (dom-document) "createDocumentFragment"))) + (let ((_d (dom-create-element "div"))) + (do + (host-set! _d "id" "first") + (host-set! _d "innerText" "With Text") + (dom-append _frag _d) + (let ((_span (dom-create-element "span"))) + (do + (host-set! _span "id" "second") + (dom-append _frag _span) + (let ((_i (dom-create-element "i"))) + (do + (host-set! _i "id" "third") + (dom-append _frag _i) + (let ((_nodeList (host-get _frag "childNodes"))) + (assert= + (eval-hs-locals "nodeList as HTML" (list (list (quote nodeList) _nodeList))) + "
With Text
"))))))))) + ) (deftest "converts a complete form into Values" (let ((_node (dom-create-element "form"))) (dom-set-inner-html _node "
Catches elements nested deeply within the DOM tree

Works with Textareas Works with Single Select Boxes Works with Multi-Select Boxes Works with Radio Buttons Works with Checkboxes ") @@ -3858,7 +3876,14 @@ (assert= (eval-hs "[1,2,2,3,3] as Unique") (list 1 2 3)) ) (deftest "converts arrays into fragments" - (error "SKIP (untranslated): converts arrays into fragments")) + (let ((_p (dom-create-element "p"))) + (let ((_arr (list _p "

"))) + (let ((_r (eval-hs-locals "value as Fragment" (list (list (quote value) _arr))))) + (do + (assert= (len (host-get _r "children")) 2) + (assert= (host-get (nth (host-get _r "children") 0) "tagName") "P") + (assert= (host-get (nth (host-get _r "children") 1) "tagName") "P"))))) + ) (deftest "converts checkboxes into a Value correctly" (let ((_node (dom-create-element "form"))) (dom-set-inner-html _node "
") @@ -3869,7 +3894,12 @@ )) ) (deftest "converts elements into fragments" - (error "SKIP (untranslated): converts elements into fragments")) + (let ((_p (dom-create-element "p"))) + (let ((_r (eval-hs-locals "value as Fragment" (list (list (quote value) _p))))) + (do + (assert= (len (host-get _r "children")) 1) + (assert= (host-get (first (host-get _r "children")) "tagName") "P")))) + ) (deftest "converts multiple selects into a Value correctly" (let ((_node (dom-create-element "form"))) (dom-set-inner-html _node "") @@ -3922,7 +3952,11 @@ (assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as Object") "foo") "bar") ) (deftest "converts strings into fragments" - (error "SKIP (untranslated): converts strings into fragments")) + (let ((_r (eval-hs-locals "value as Fragment" (list (list (quote value) "

"))))) + (do + (assert= (len (host-get _r "children")) 1) + (assert= (host-get (first (host-get _r "children")) "tagName") "P"))) + ) (deftest "converts value as Boolean" (assert= (eval-hs "1 as Boolean") true) (assert= (eval-hs "0 as Boolean") false) diff --git a/tests/playwright/generate-sx-tests.py b/tests/playwright/generate-sx-tests.py index d7c03cba..4f1609af 100644 --- a/tests/playwright/generate-sx-tests.py +++ b/tests/playwright/generate-sx-tests.py @@ -415,6 +415,52 @@ MANUAL_TEST_BODIES = { ' (hs-activate! _el))', ' (assert (nil? caught))))', ], + # asExpression: NodeList as HTML — each element serialised via outerHTML + "converts a NodeList into HTML": [ + ' (let ((_frag (host-call (dom-document) "createDocumentFragment")))', + ' (let ((_d (dom-create-element "div")))', + ' (do', + ' (host-set! _d "id" "first")', + ' (host-set! _d "innerText" "With Text")', + ' (dom-append _frag _d)', + ' (let ((_span (dom-create-element "span")))', + ' (do', + ' (host-set! _span "id" "second")', + ' (dom-append _frag _span)', + ' (let ((_i (dom-create-element "i")))', + ' (do', + ' (host-set! _i "id" "third")', + ' (dom-append _frag _i)', + ' (let ((_nodeList (host-get _frag "childNodes")))', + ' (assert=', + ' (eval-hs-locals "nodeList as HTML" (list (list (quote nodeList) _nodeList)))', + ' "
With Text
")))))))))', + ], + # asExpression: array of [element, html-string] as Fragment + "converts arrays into fragments": [ + ' (let ((_p (dom-create-element "p")))', + ' (let ((_arr (list _p "

")))', + ' (let ((_r (eval-hs-locals "value as Fragment" (list (list (quote value) _arr)))))', + ' (do', + ' (assert= (len (host-get _r "children")) 2)', + ' (assert= (host-get (nth (host-get _r "children") 0) "tagName") "P")', + ' (assert= (host-get (nth (host-get _r "children") 1) "tagName") "P")))))', + ], + # asExpression: single element as Fragment wraps it in a DocumentFragment + "converts elements into fragments": [ + ' (let ((_p (dom-create-element "p")))', + ' (let ((_r (eval-hs-locals "value as Fragment" (list (list (quote value) _p)))))', + ' (do', + ' (assert= (len (host-get _r "children")) 1)', + ' (assert= (host-get (first (host-get _r "children")) "tagName") "P"))))', + ], + # asExpression: HTML string as Fragment — parses and wraps children + "converts strings into fragments": [ + ' (let ((_r (eval-hs-locals "value as Fragment" (list (list (quote value) "

")))))', + ' (do', + ' (assert= (len (host-get _r "children")) 1)', + ' (assert= (host-get (first (host-get _r "children")) "tagName") "P")))', + ], }