feat(html): recurse attrs through iterator wrappers
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
264ecc3f84
commit
a86c8ef770
3 changed files with 67 additions and 2 deletions
|
|
@ -22,7 +22,7 @@ All concrete node types are unexported structs with exported constructor functio
|
|||
| Constructor | Behaviour |
|
||||
|-------------|-----------|
|
||||
| `El(tag, ...Node)` | HTML element with children. Void elements (`br`, `img`, `input`, etc.) never emit a closing tag. |
|
||||
| `Attr(Node, key, value)` | Sets an attribute on an `El` node. Traverses through `If`, `Unless`, and `Entitled` wrappers. Returns the node for chaining. |
|
||||
| `Attr(Node, key, value)` | Sets an attribute on an `El` node. Traverses through `If`, `Unless`, `Entitled`, `Switch`, and iterator wrappers. Returns the node for chaining. |
|
||||
| `Text(key, ...any)` | Translated text via `go-i18n`. Output is always HTML-escaped. |
|
||||
| `Raw(content)` | Unescaped trusted content. Explicit escape hatch. |
|
||||
| `If(cond, Node)` | Renders the child only when the condition function returns true. |
|
||||
|
|
|
|||
20
node.go
20
node.go
|
|
@ -149,7 +149,8 @@ func El(tag string, children ...Node) Node {
|
|||
}
|
||||
|
||||
// Attr sets an attribute on an El node. Returns the node for chaining.
|
||||
// It recursively traverses through wrappers like If, Unless, and Entitled.
|
||||
// It recursively traverses through wrappers like If, Unless, Entitled,
|
||||
// Switch, and Each/EachSeq so the attribute lands on the rendered element.
|
||||
func Attr(n Node, key, value string) Node {
|
||||
switch t := n.(type) {
|
||||
case *elNode:
|
||||
|
|
@ -160,6 +161,12 @@ func Attr(n Node, key, value string) Node {
|
|||
Attr(t.node, key, value)
|
||||
case *entitledNode:
|
||||
Attr(t.node, key, value)
|
||||
case *switchNode:
|
||||
for caseKey, caseNode := range t.cases {
|
||||
t.cases[caseKey] = Attr(caseNode, key, value)
|
||||
}
|
||||
case interface{ setAttr(string, string) }:
|
||||
t.setAttr(key, value)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
|
@ -415,3 +422,14 @@ func (n *eachNode[T]) renderWithPath(ctx *Context, path string) string {
|
|||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (n *eachNode[T]) setAttr(key, value string) {
|
||||
if n == nil || n.fn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
prev := n.fn
|
||||
n.fn = func(item T) Node {
|
||||
return Attr(prev(item), key, value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
node_test.go
47
node_test.go
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
i18n "dappco.re/go/core/i18n"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func TestRawNode_Render(t *testing.T) {
|
||||
|
|
@ -378,6 +379,52 @@ func TestAttr_ThroughEntitledNode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAttr_ThroughSwitchNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := Switch(func(*Context) string { return "dark" }, map[string]Node{
|
||||
"dark": El("div", Raw("content")),
|
||||
"light": El("div", Raw("other")),
|
||||
})
|
||||
|
||||
Attr(node, "class", "theme")
|
||||
|
||||
got := node.Render(ctx)
|
||||
want := `<div class="theme">content</div>`
|
||||
if got != want {
|
||||
t.Errorf("Attr through Switch = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttr_ThroughEachNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := Each([]string{"a", "b"}, func(item string) Node {
|
||||
return El("span", Raw(item))
|
||||
})
|
||||
|
||||
Attr(node, "class", "item")
|
||||
|
||||
got := node.Render(ctx)
|
||||
want := `<span class="item">a</span><span class="item">b</span>`
|
||||
if got != want {
|
||||
t.Errorf("Attr through Each = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttr_ThroughEachSeqNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := EachSeq(slices.Values([]string{"a", "b"}), func(item string) Node {
|
||||
return El("span", Raw(item))
|
||||
})
|
||||
|
||||
Attr(node, "class", "item")
|
||||
|
||||
got := node.Render(ctx)
|
||||
want := `<span class="item">a</span><span class="item">b</span>`
|
||||
if got != want {
|
||||
t.Errorf("Attr through EachSeq = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextNode_WithService(t *testing.T) {
|
||||
svc, _ := i18n.New()
|
||||
ctx := NewContextWithService(svc)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue