Repository: maragudk/gomponents
Pull request #19 (2020-09-24 19:15:22)

Add convenience helpers for form elements

codecov[bot]: # [Codecov](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=h1) Report
> Merging [#19](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=desc) into [master](https://codecov.io/gh/maragudk/gomponents/commit/4c109f9f1d4db4733d267ece171a533521029bdb?el=desc) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/maragudk/gomponents/pull/19/graphs/tree.svg?width=650&height=150&src=pr&token=JYN032TYAC)](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##            master       #19   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         5    +2     
  Lines           92       106   +14     
=========================================
+ Hits            92       106   +14     
```


| [Impacted Files](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [attr/forms.go](https://codecov.io/gh/maragudk/gomponents/pull/19/diff?src=pr&el=tree#diff-YXR0ci9mb3Jtcy5nbw==) | `100.00% <100.00%> (ø)` | |
| [el/elements.go](https://codecov.io/gh/maragudk/gomponents/pull/19/diff?src=pr&el=tree#diff-ZWwvZWxlbWVudHMuZ28=) | `100.00% <100.00%> (ø)` | |
| [el/forms.go](https://codecov.io/gh/maragudk/gomponents/pull/19/diff?src=pr&el=tree#diff-ZWwvZm9ybXMuZ28=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=footer). Last update [4c109f9...2dfcf19](https://codecov.io/gh/maragudk/gomponents/pull/19?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/attr/forms.go b/attr/forms.go
new file mode 100644
index 0000000..b3eec05
--- /dev/null
+++ b/attr/forms.go
@@ -0,0 +1,15 @@
+package attr
+
+import (
+	g "github.com/maragudk/gomponents"
+)
+
+// Placeholder returns an attribute with name "placeholder" and the given value.
+func Placeholder(v string) g.Node {
+	return g.Attr("placeholder", v)
+}
+
+// Required returns an attribute with name "required".
+func Required() g.Node {
+	return g.Attr("required")
+}
diff --git a/el/elements.go b/el/elements.go
index 6745d1c..31bba14 100644
--- a/el/elements.go
+++ b/el/elements.go
@@ -121,9 +121,7 @@ func Em(text string, children ...g.Node) g.NodeFunc {
 }
 
 func Img(src, alt string, children ...g.Node) g.NodeFunc {
-	newChildren := prepend(g.Attr("alt", alt), children)
-	newChildren = prepend(g.Attr("src", src), newChildren)
-	return g.El("img", newChildren...)
+	return g.El("img", prepend2(g.Attr("src", src), g.Attr("alt", alt), children)...)
 }
 
 func prepend(node g.Node, nodes []g.Node) []g.Node {
@@ -131,3 +129,9 @@ func prepend(node g.Node, nodes []g.Node) []g.Node {
 	newNodes = append(newNodes, nodes...)
 	return newNodes
 }
+
+func prepend2(node1, node2 g.Node, nodes []g.Node) []g.Node {
+	newNodes := []g.Node{node1, node2}
+	newNodes = append(newNodes, nodes...)
+	return newNodes
+}
diff --git a/el/forms.go b/el/forms.go
new file mode 100644
index 0000000..9ada591
--- /dev/null
+++ b/el/forms.go
@@ -0,0 +1,52 @@
+package el
+
+import (
+	"fmt"
+
+	g "github.com/maragudk/gomponents"
+)
+
+// Button returns an element with name "button" and the given children.
+func Button(children ...g.Node) g.NodeFunc {
+	return g.El("button", children...)
+}
+
+// Form returns an element with name "form", the given action and method attributes, and the given children.
+func Form(action, method string, children ...g.Node) g.NodeFunc {
+	return g.El("form", prepend2(g.Attr("action", action), g.Attr("method", method), children)...)
+}
+
+// Input returns an element with name "input", the given type and name attributes, and the given children.
+// Note that "type" is a keyword in Go, so the parameter is called typ.
+func Input(typ, name string, children ...g.Node) g.NodeFunc {
+	return g.El("input", prepend2(g.Attr("type", typ), g.Attr("name", name), children)...)
+}
+
+// Label returns an element with name "label", the given for attribute, and the given children.
+// Note that "for" is a keyword in Go, so the parameter is called forr.
+func Label(forr string, children ...g.Node) g.NodeFunc {
+	return g.El("label", prepend(g.Attr("for", forr), children)...)
+}
+
+// Option returns an element with name "option", the given text content and value attribute, and the given children.
+func Option(text, value string, children ...g.Node) g.NodeFunc {
+	return g.El("option", prepend2(g.Attr("value", value), g.Text(text), children)...)
+}
+
+// Progress returns an element with name "progress", the given value and max attributes, and the given children.
+func Progress(value, max float64, children ...g.Node) g.NodeFunc {
+	return g.El("progress", prepend2(
+		g.Attr("value", fmt.Sprintf("%v", value)),
+		g.Attr("max", fmt.Sprintf("%v", max)),
+		children)...)
+}
+
+// Select returns an element with name "select", the given name attribute, and the given children.
+func Select(name string, children ...g.Node) g.NodeFunc {
+	return g.El("select", prepend(g.Attr("name", name), children)...)
+}
+
+// Textarea returns an element with name "textarea", the given name attribute, and the given children.
+func Textarea(name string, children ...g.Node) g.NodeFunc {
+	return g.El("textarea", prepend(g.Attr("name", name), children)...)
+}


Test patch:
diff --git a/attr/forms_test.go b/attr/forms_test.go
new file mode 100644
index 0000000..3ff077b
--- /dev/null
+++ b/attr/forms_test.go
@@ -0,0 +1,16 @@
+package attr_test
+
+import (
+	"testing"
+
+	g "github.com/maragudk/gomponents"
+	"github.com/maragudk/gomponents/assert"
+	"github.com/maragudk/gomponents/attr"
+)
+
+func TestForms(t *testing.T) {
+	t.Run("adds placeholder and required attributes", func(t *testing.T) {
+		e := g.El("input", attr.Placeholder("hat"), attr.Required())
+		assert.Equal(t, `<input placeholder="hat" required/>`, e)
+	})
+}
diff --git a/el/forms_test.go b/el/forms_test.go
new file mode 100644
index 0000000..65f2fae
--- /dev/null
+++ b/el/forms_test.go
@@ -0,0 +1,58 @@
+package el_test
+
+import (
+	"testing"
+
+	g "github.com/maragudk/gomponents"
+	"github.com/maragudk/gomponents/assert"
+	"github.com/maragudk/gomponents/el"
+)
+
+func TestButton(t *testing.T) {
+	t.Run("returns a button element", func(t *testing.T) {
+		assert.Equal(t, `<button/>`, el.Button())
+	})
+}
+
+func TestForm(t *testing.T) {
+	t.Run("returns a form element with action and method attributes", func(t *testing.T) {
+		assert.Equal(t, `<form action="/" method="post"/>`, el.Form("/", "post"))
+	})
+}
+
+func TestInput(t *testing.T) {
+	t.Run("returns an input element with attributes type and name", func(t *testing.T) {
+		assert.Equal(t, `<input type="text" name="hat"/>`, el.Input("text", "hat"))
+	})
+}
+
+func TestLabel(t *testing.T) {
+	t.Run("returns a label element with attribute for", func(t *testing.T) {
+		assert.Equal(t, `<label for="hat">Hat</label>`, el.Label("hat", g.Text("Hat")))
+	})
+}
+
+func TestOption(t *testing.T) {
+	t.Run("returns an option element with attribute label and content", func(t *testing.T) {
+		assert.Equal(t, `<option value="hat">Hat</option>`, el.Option("Hat", "hat"))
+	})
+}
+
+func TestProgress(t *testing.T) {
+	t.Run("returns a progress element with attributes value and max", func(t *testing.T) {
+		assert.Equal(t, `<progress value="5.5" max="10"/>`, el.Progress(5.5, 10))
+	})
+}
+
+func TestSelect(t *testing.T) {
+	t.Run("returns a select element with attribute name", func(t *testing.T) {
+		assert.Equal(t, `<select name="hat"><option value="partyhat">Partyhat</option></select>`,
+			el.Select("hat", el.Option("Partyhat", "partyhat")))
+	})
+}
+
+func TestTextarea(t *testing.T) {
+	t.Run("returns a textarea element with attribute name", func(t *testing.T) {
+		assert.Equal(t, `<textarea name="hat"/>`, el.Textarea("hat"))
+	})
+}