about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2025-03-19 11:50:50 +0100
committerAlan Pearce2025-03-19 12:03:53 +0100
commit5eae1eef0f0a090ae569a23c0c52db356a109cfc (patch)
tree0e01e7dfa606f00843d02447688b280924d9f06f
parent6aea6d0965f9030d598eb87f5bfca36b638035f6 (diff)
downloadgomponents-5eae1eef0f0a090ae569a23c0c52db356a109cfc.tar.lz
gomponents-5eae1eef0f0a090ae569a23c0c52db356a109cfc.tar.zst
gomponents-5eae1eef0f0a090ae569a23c0c52db356a109cfc.zip
Add else part to If/Iff
-rw-r--r--gomponents.go25
-rw-r--r--gomponents_test.go88
2 files changed, 85 insertions, 28 deletions
diff --git a/gomponents.go b/gomponents.go
index 37267d4..4867152 100644
--- a/gomponents.go
+++ b/gomponents.go
@@ -298,20 +298,39 @@ func (g Group) Render(w io.Writer) error {
 // This helper function is good for inlining elements conditionally.
 // If it's important that the given [Node] is only evaluated if condition is true
 // (for example, when using nilable variables), use [Iff] instead.
-func If(condition bool, n Node) Node {
+func If(condition bool, n Node, otherwise ...Node) Node {
+	var o Node
+	switch len(otherwise) {
+	case 0:
+	case 1:
+		o = otherwise[0]
+	default:
+		panic("If must have just one or two nodes")
+	}
 	if condition {
 		return n
 	}
-	return nil
+	return o
 }
 
 // Iff condition is true, call the given function. Otherwise, return nil.
 // This helper function is good for inlining elements conditionally when the node depends on nilable data,
 // or some other code that could potentially panic.
 // If you just need simple conditional rendering, see [If].
-func Iff(condition bool, f func() Node) Node {
+func Iff(condition bool, f func() Node, otherwise ...func() Node) Node {
+	var o func() Node
+	switch len(otherwise) {
+	case 0:
+	case 1:
+		o = otherwise[0]
+	default:
+		panic("Iff must have just one or two nodes")
+	}
 	if condition {
 		return f()
 	}
+	if o != nil {
+		return o()
+	}
 	return nil
 }
diff --git a/gomponents_test.go b/gomponents_test.go
index eb5bed3..f3cb1b8 100644
--- a/gomponents_test.go
+++ b/gomponents_test.go
@@ -103,16 +103,19 @@ func TestEl(t *testing.T) {
 		assert.Equal(t, "<div></div>", e)
 	})
 
-	t.Run("renders an empty element without closing tag if it's a void kind element", func(t *testing.T) {
-		e := g.El("hr")
-		assert.Equal(t, "<hr>", e)
-
-		e = g.El("br")
-		assert.Equal(t, "<br>", e)
-
-		e = g.El("img")
-		assert.Equal(t, "<img>", e)
-	})
+	t.Run(
+		"renders an empty element without closing tag if it's a void kind element",
+		func(t *testing.T) {
+			e := g.El("hr")
+			assert.Equal(t, "<hr>", e)
+
+			e = g.El("br")
+			assert.Equal(t, "<br>", e)
+
+			e = g.El("img")
+			assert.Equal(t, "<img>", e)
+		},
+	)
 
 	t.Run("renders an empty element if only attributes given as children", func(t *testing.T) {
 		e := g.El("div", g.Attr("class", "hat"))
@@ -124,10 +127,13 @@ func TestEl(t *testing.T) {
 		assert.Equal(t, `<div class="hat"><br></div>`, e)
 	})
 
-	t.Run("renders attributes at the correct place regardless of placement in parameter list", func(t *testing.T) {
-		e := g.El("div", g.El("br"), g.Attr("class", "hat"))
-		assert.Equal(t, `<div class="hat"><br></div>`, e)
-	})
+	t.Run(
+		"renders attributes at the correct place regardless of placement in parameter list",
+		func(t *testing.T) {
+			e := g.El("div", g.El("br"), g.Attr("class", "hat"))
+			assert.Equal(t, `<div class="hat"><br></div>`, e)
+		},
+	)
 
 	t.Run("renders outside if node does not implement nodeTypeDescriber", func(t *testing.T) {
 		e := g.El("div", outsider{})
@@ -201,8 +207,11 @@ func TestRaw(t *testing.T) {
 }
 
 func ExampleRaw() {
-	e := g.El("span",
-		g.Raw(`<button onclick="javascript:alert('Party time!')">Party hats</button> &gt; normal hats.`),
+	e := g.El(
+		"span",
+		g.Raw(
+			`<button onclick="javascript:alert('Party time!')">Party hats</button> &gt; normal hats.`,
+		),
 	)
 	_ = e.Render(os.Stdout)
 	// Output: <span><button onclick="javascript:alert('Party time!')">Party hats</button> &gt; normal hats.</span>
@@ -216,8 +225,12 @@ func TestRawf(t *testing.T) {
 }
 
 func ExampleRawf() {
-	e := g.El("span",
-		g.Rawf(`<button onclick="javascript:alert('%v')">Party hats</button> &gt; normal hats.`, "Party time!"),
+	e := g.El(
+		"span",
+		g.Rawf(
+			`<button onclick="javascript:alert('%v')">Party hats</button> &gt; normal hats.`,
+			"Party time!",
+		),
 	)
 	_ = e.Render(os.Stdout)
 	// Output: <span><button onclick="javascript:alert('Party time!')">Party hats</button> &gt; normal hats.</span>
@@ -277,7 +290,10 @@ func TestGroup(t *testing.T) {
 	})
 
 	t.Run("does not ignore attributes at the second level and below", func(t *testing.T) {
-		children := []g.Node{g.El("div", g.Attr("class", "hat"), g.El("hr", g.Attr("id", "partyhat"))), g.El("span")}
+		children := []g.Node{
+			g.El("div", g.Attr("class", "hat"), g.El("hr", g.Attr("id", "partyhat"))),
+			g.El("span"),
+		}
 		e := g.Group(children)
 		assert.Equal(t, `<div class="hat"><hr id="partyhat"></div><span></span>`, e)
 	})
@@ -321,14 +337,21 @@ func TestIf(t *testing.T) {
 		n := g.El("div", g.If(false, g.El("span")))
 		assert.Equal(t, "<div></div>", n)
 	})
+
+	t.Run("returns second node if given and condition is false", func(t *testing.T) {
+		n := g.El("div", g.If(false, g.Text("first"), g.Text("second")))
+		assert.Equal(t, "<div>second</div>", n)
+	})
 }
 
 func ExampleIf() {
 	showMessage := true
 
 	e := g.El("div",
-		g.If(showMessage, g.El("span", g.Text("You lost your hat!"))),
-		g.If(!showMessage, g.El("span", g.Text("No messages."))),
+		g.If(showMessage,
+			g.El("span", g.Text("You lost your hat!")),
+			g.El("span", g.Text("No messages.")),
+		),
 	)
 
 	_ = e.Render(os.Stdout)
@@ -349,6 +372,15 @@ func TestIff(t *testing.T) {
 		}))
 		assert.Equal(t, "<div></div>", n)
 	})
+
+	t.Run("returns second node if given and condition is false", func(t *testing.T) {
+		n := g.El("div", g.Iff(false, func() g.Node {
+			return g.Text("first")
+		}, func() g.Node {
+			return g.Text("second")
+		}))
+		assert.Equal(t, "<div>second</div>", n)
+	})
 }
 
 func ExampleIff() {
@@ -359,11 +391,17 @@ func ExampleIff() {
 
 	e := g.El("div",
 		// This would panic using just If
-		g.Iff(user != nil, func() g.Node {
-			return g.Text(user.Name)
-		}),
+		g.Iff(
+			user != nil,
+			func() g.Node {
+				return g.Text(user.Name)
+			},
+			func() g.Node {
+				return g.Text("No user")
+			},
+		),
 	)
 
 	_ = e.Render(os.Stdout)
-	// Output: <div></div>
+	// Output: <div>No user</div>
 }