diff options
-rw-r--r-- | attr/attributes.go | 4 | ||||
-rw-r--r-- | attr/attributes_test.go | 6 | ||||
-rw-r--r-- | gomponents.go | 44 | ||||
-rw-r--r-- | gomponents_test.go | 11 |
4 files changed, 57 insertions, 8 deletions
diff --git a/attr/attributes.go b/attr/attributes.go index 56fcd74..415e6f1 100644 --- a/attr/attributes.go +++ b/attr/attributes.go @@ -35,6 +35,10 @@ func (c Classes) Render() string { return g.Attr("class", strings.Join(included, " ")).Render() } +func (c Classes) Place() g.Placement { + return g.Inside +} + // String satisfies fmt.Stringer. func (c Classes) String() string { return c.Render() diff --git a/attr/attributes_test.go b/attr/attributes_test.go index 9c7ece7..4da28e3 100644 --- a/attr/attributes_test.go +++ b/attr/attributes_test.go @@ -3,6 +3,7 @@ package attr_test import ( "testing" + g "github.com/maragudk/gomponents" "github.com/maragudk/gomponents/assert" "github.com/maragudk/gomponents/attr" ) @@ -29,6 +30,11 @@ func TestClasses(t *testing.T) { }) }) + t.Run("renders as attribute in an element", func(t *testing.T) { + e := g.El("div", attr.Classes{"hat": true}) + assert.Equal(t, `<div class="hat"/>`, e) + }) + t.Run("also works with fmt", func(t *testing.T) { a := attr.Classes{"hat": true} if a.String() != ` class="hat"` { diff --git a/gomponents.go b/gomponents.go index 21d3e75..73d0545 100644 --- a/gomponents.go +++ b/gomponents.go @@ -19,6 +19,20 @@ type Node interface { Render() string } +// Placer can be implemented to tell Render functions where to place the string representation of a Node +// in the parent element. +type Placer interface { + Place() Placement +} + +// Placement is used with the Placer interface. +type Placement int + +const ( + Outside = Placement(iota) + Inside +) + // NodeFunc is render function that is also a Node. type NodeFunc func() string @@ -26,6 +40,10 @@ func (n NodeFunc) Render() string { return n() } +func (n NodeFunc) Place() Placement { + return Outside +} + // String satisfies fmt.Stringer. func (n NodeFunc) String() string { return n.Render() @@ -35,7 +53,7 @@ func (n NodeFunc) String() string { // Use this if no convenience creator exists. func El(name string, children ...Node) NodeFunc { return func() string { - var b, attrString, childrenString strings.Builder + var b, inside, outside strings.Builder b.WriteString("<") b.WriteString(name) @@ -46,22 +64,28 @@ func El(name string, children ...Node) NodeFunc { } for _, c := range children { - if _, ok := c.(attr); ok { - attrString.WriteString(c.Render()) - } else { - childrenString.WriteString(c.Render()) + if p, ok := c.(Placer); ok { + switch p.Place() { + case Inside: + inside.WriteString(c.Render()) + case Outside: + outside.WriteString(c.Render()) + } + continue } + // If c doesn't implement Placer, default to outside + outside.WriteString(c.Render()) } - b.WriteString(attrString.String()) + b.WriteString(inside.String()) - if childrenString.Len() == 0 { + if outside.Len() == 0 { b.WriteString("/>") return b.String() } b.WriteString(">") - b.WriteString(childrenString.String()) + b.WriteString(outside.String()) b.WriteString("</") b.WriteString(name) b.WriteString(">") @@ -97,6 +121,10 @@ func (a attr) Render() string { return fmt.Sprintf(` %v="%v"`, a.name, *a.value) } +func (a attr) Place() Placement { + return Inside +} + // String satisfies fmt.Stringer. func (a attr) String() string { return a.Render() diff --git a/gomponents_test.go b/gomponents_test.go index ee0b91d..65b82b5 100644 --- a/gomponents_test.go +++ b/gomponents_test.go @@ -52,6 +52,12 @@ func TestAttr(t *testing.T) { }) } +type outsider struct{} + +func (o outsider) Render() string { + return "outsider" +} + func TestEl(t *testing.T) { t.Run("renders an empty element if no children given", func(t *testing.T) { e := g.El("div") @@ -72,6 +78,11 @@ func TestEl(t *testing.T) { e := g.El("div", g.El("span"), g.Attr("class", "hat")) assert.Equal(t, `<div class="hat"><span/></div>`, e) }) + + t.Run("renders outside if node does not implement placer", func(t *testing.T) { + e := g.El("div", outsider{}) + assert.Equal(t, `<div>outsider</div>`, e) + }) } func TestText(t *testing.T) { |