Repository: maragudk/gomponents
Pull request #55 (2020-12-10 12:46:57)

Simplify available elements
`a`, `form`, `img`, `input`, `label`, `option`, `progress`, `select`, and `textarea` are now just regular elements (without helper parameters), because:
- Sometimes the use case doesn't fit (`a` as anchor without href, for example)
- There's no reason these are special among the others, so streamlining them makes sense

Also added new attributes `action`, `alt`, `for`, `method` that I had somehow missed.

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

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

```diff
@@            Coverage Diff            @@
##            master       #55   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            5         5           
  Lines          238       239    +1     
=========================================
+ Hits           238       239    +1     
```


| [Impacted Files](https://codecov.io/gh/maragudk/gomponents/pull/55?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [html/attributes.go](https://codecov.io/gh/maragudk/gomponents/pull/55/diff?src=pr&el=tree#diff-aHRtbC9hdHRyaWJ1dGVzLmdv) | `100.00% <100.00%> (ø)` | |
| [html/elements.go](https://codecov.io/gh/maragudk/gomponents/pull/55/diff?src=pr&el=tree#diff-aHRtbC9lbGVtZW50cy5nbw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/maragudk/gomponents/pull/55?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/55?src=pr&el=footer). Last update [100ae9e...bce55f1](https://codecov.io/gh/maragudk/gomponents/pull/55?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/README.md b/README.md
index 5a8a954..31f0101 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@ func Navbar(currentPath string) g.Node {
 }
 
 func NavbarLink(href, name, currentPath string) g.Node {
-	return A(href, c.Classes{"is-active": currentPath == href}, g.Text(name))
+	return A(Href(href), c.Classes{"is-active": currentPath == href}, g.Text(name))
 }
 ```
 
@@ -129,7 +129,7 @@ func Navbar(currentPath string) g.Node {
 }
 
 func NavbarLink(href, name, currentPath string) g.Node {
-	return A(href, c.Classes{"is-active": currentPath == href}, g.Text(name))
+	return A(Href(href), c.Classes{"is-active": currentPath == href}, g.Text(name))
 }
 ```
 
diff --git a/examples/dot-import/dot-import.go b/examples/dot-import/dot-import.go
index 1d595b8..d735785 100644
--- a/examples/dot-import/dot-import.go
+++ b/examples/dot-import/dot-import.go
@@ -40,5 +40,5 @@ func Navbar(currentPath string) g.Node {
 }
 
 func NavbarLink(href, name, currentPath string) g.Node {
-	return A(href, Classes{"is-active": currentPath == href}, g.Text(name))
+	return A(Href(href), Classes{"is-active": currentPath == href}, g.Text(name))
 }
diff --git a/examples/simple/simple.go b/examples/simple/simple.go
index 529cf18..c872512 100644
--- a/examples/simple/simple.go
+++ b/examples/simple/simple.go
@@ -64,7 +64,7 @@ func navbar(props navbarProps) g.Node {
 	lis := g.Map(len(items), func(i int) g.Node {
 		item := items[i]
 		return h.Li(
-			h.A(item.path, c.Classes(map[string]bool{"is-active": props.path == item.path}), g.Text(item.text)),
+			h.A(h.Href(item.path), c.Classes(map[string]bool{"is-active": props.path == item.path}), g.Text(item.text)),
 		)
 	})
 	return h.Ul(h.Class("nav"), g.Group(lis))
diff --git a/html/attributes.go b/html/attributes.go
index 3db8584..e67bc1f 100644
--- a/html/attributes.go
+++ b/html/attributes.go
@@ -48,6 +48,14 @@ func Accept(v string) g.Node {
 	return g.Attr("accept", v)
 }
 
+func Action(v string) g.Node {
+	return g.Attr("action", v)
+}
+
+func Alt(v string) g.Node {
+	return g.Attr("alt", v)
+}
+
 func AutoComplete(v string) g.Node {
 	return g.Attr("autocomplete", v)
 }
@@ -68,6 +76,10 @@ func Content(v string) g.Node {
 	return g.Attr("content", v)
 }
 
+func For(v string) g.Node {
+	return g.Attr("for", v)
+}
+
 func FormAttr(v string) g.Node {
 	return g.Attr("form", v)
 }
@@ -96,6 +108,10 @@ func MaxLength(v string) g.Node {
 	return g.Attr("maxlength", v)
 }
 
+func Method(v string) g.Node {
+	return g.Attr("method", v)
+}
+
 func Min(v string) g.Node {
 	return g.Attr("min", v)
 }
diff --git a/html/elements.go b/html/elements.go
index 4f1a0ec..c3fa396 100644
--- a/html/elements.go
+++ b/html/elements.go
@@ -4,16 +4,11 @@
 package html
 
 import (
-	"fmt"
 	"io"
 
 	g "github.com/maragudk/gomponents"
 )
 
-func A(href string, children ...g.Node) g.NodeFunc {
-	return g.El("a", g.Attr("href", href), g.Group(children))
-}
-
 // Doctype returns a special kind of Node that prefixes its sibling with the string "<!doctype html>".
 func Doctype(sibling g.Node) g.NodeFunc {
 	return func(w io.Writer) error {
@@ -24,48 +19,8 @@ func Doctype(sibling g.Node) g.NodeFunc {
 	}
 }
 
-// FormEl returns an element with name "form", the given action and method attributes, and the given children.
-func FormEl(action, method string, children ...g.Node) g.NodeFunc {
-	return g.El("form", g.Attr("action", action), g.Attr("method", method), g.Group(children))
-}
-
-func Img(src, alt string, children ...g.Node) g.NodeFunc {
-	return g.El("img", g.Attr("src", src), g.Attr("alt", alt), g.Group(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", g.Attr("type", typ), g.Attr("name", name), g.Group(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", g.Attr("for", forr), g.Group(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", g.Attr("value", value), g.Text(text), g.Group(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",
-		g.Attr("value", fmt.Sprintf("%v", value)),
-		g.Attr("max", fmt.Sprintf("%v", max)),
-		g.Group(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", g.Attr("name", name), g.Group(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", g.Attr("name", name), g.Group(children))
+func A(children ...g.Node) g.NodeFunc {
+	return g.El("a", children...)
 }
 
 func Address(children ...g.Node) g.NodeFunc {
@@ -156,6 +111,10 @@ func Embed(children ...g.Node) g.NodeFunc {
 	return g.El("embed", children...)
 }
 
+func FormEl(children ...g.Node) g.NodeFunc {
+	return g.El("form", children...)
+}
+
 func FieldSet(children ...g.Node) g.NodeFunc {
 	return g.El("fieldset", children...)
 }
@@ -192,6 +151,18 @@ func IFrame(children ...g.Node) g.NodeFunc {
 	return g.El("iframe", children...)
 }
 
+func Img(children ...g.Node) g.NodeFunc {
+	return g.El("img", children...)
+}
+
+func Input(children ...g.Node) g.NodeFunc {
+	return g.El("input", children...)
+}
+
+func Label(children ...g.Node) g.NodeFunc {
+	return g.El("label", children...)
+}
+
 func Legend(children ...g.Node) g.NodeFunc {
 	return g.El("legend", children...)
 }
@@ -240,6 +211,10 @@ func OptGroup(children ...g.Node) g.NodeFunc {
 	return g.El("optgroup", children...)
 }
 
+func Option(children ...g.Node) g.NodeFunc {
+	return g.El("option", children...)
+}
+
 func P(children ...g.Node) g.NodeFunc {
 	return g.El("p", children...)
 }
@@ -256,6 +231,10 @@ func Pre(children ...g.Node) g.NodeFunc {
 	return g.El("pre", children...)
 }
 
+func Progress(children ...g.Node) g.NodeFunc {
+	return g.El("progress", children...)
+}
+
 func Script(children ...g.Node) g.NodeFunc {
 	return g.El("script", children...)
 }
@@ -264,6 +243,10 @@ func Section(children ...g.Node) g.NodeFunc {
 	return g.El("section", children...)
 }
 
+func Select(children ...g.Node) g.NodeFunc {
+	return g.El("select", children...)
+}
+
 func Source(children ...g.Node) g.NodeFunc {
 	return g.El("source", children...)
 }
@@ -296,6 +279,10 @@ func Td(children ...g.Node) g.NodeFunc {
 	return g.El("td", children...)
 }
 
+func Textarea(children ...g.Node) g.NodeFunc {
+	return g.El("textarea", children...)
+}
+
 func TFoot(children ...g.Node) g.NodeFunc {
 	return g.El("tfoot", children...)
 }
@@ -356,32 +343,26 @@ func FigCaption(text string, children ...g.Node) g.NodeFunc {
 	return g.El("figcaption", g.Text(text), g.Group(children))
 }
 
-// H1 returns an element with name "h1", the given text content, and the given children.
 func H1(text string, children ...g.Node) g.NodeFunc {
 	return g.El("h1", g.Text(text), g.Group(children))
 }
 
-// H2 returns an element with name "h2", the given text content, and the given children.
 func H2(text string, children ...g.Node) g.NodeFunc {
 	return g.El("h2", g.Text(text), g.Group(children))
 }
 
-// H3 returns an element with name "h3", the given text content, and the given children.
 func H3(text string, children ...g.Node) g.NodeFunc {
 	return g.El("h3", g.Text(text), g.Group(children))
 }
 
-// H4 returns an element with name "h4", the given text content, and the given children.
 func H4(text string, children ...g.Node) g.NodeFunc {
 	return g.El("h4", g.Text(text), g.Group(children))
 }
 
-// H5 returns an element with name "h5", the given text content, and the given children.
 func H5(text string, children ...g.Node) g.NodeFunc {
 	return g.El("h5", g.Text(text), g.Group(children))
 }
 
-// H6 returns an element with name "h6", the given text content, and the given children.
 func H6(text string, children ...g.Node) g.NodeFunc {
 	return g.El("h6", g.Text(text), g.Group(children))
 }


Test patch:
diff --git a/html/attributes_test.go b/html/attributes_test.go
index 7bf45bb..ba8cbb4 100644
--- a/html/attributes_test.go
+++ b/html/attributes_test.go
@@ -34,11 +34,14 @@ func TestBooleanAttributes(t *testing.T) {
 func TestSimpleAttributes(t *testing.T) {
 	cases := map[string]func(string) g.Node{
 		"accept":       Accept,
+		"action":       Action,
+		"alt":          Alt,
 		"autocomplete": AutoComplete,
 		"charset":      Charset,
 		"class":        Class,
 		"cols":         Cols,
 		"content":      Content,
+		"for":          For,
 		"form":         FormAttr,
 		"height":       Height,
 		"href":         Href,
@@ -46,6 +49,7 @@ func TestSimpleAttributes(t *testing.T) {
 		"lang":         Lang,
 		"max":          Max,
 		"maxlength":    MaxLength,
+		"method":       Method,
 		"min":          Min,
 		"minlength":    MinLength,
 		"name":         Name,
diff --git a/html/elements_test.go b/html/elements_test.go
index f89919d..e9220e5 100644
--- a/html/elements_test.go
+++ b/html/elements_test.go
@@ -27,63 +27,9 @@ func TestDoctype(t *testing.T) {
 	})
 }
 
-func TestFormEl(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"></form>`, FormEl("/", "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">`, 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>`, 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>`, 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"></progress>`, 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>`,
-			Select("hat", 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"></textarea>`, Textarea("hat"))
-	})
-}
-
-func TestA(t *testing.T) {
-	t.Run("returns an a element with a href attribute", func(t *testing.T) {
-		assert.Equal(t, `<a href="#">hat</a>`, A("#", g.Text("hat")))
-	})
-}
-
-func TestImg(t *testing.T) {
-	t.Run("returns an img element with href and alt attributes", func(t *testing.T) {
-		assert.Equal(t, `<img src="hat.png" alt="hat" id="image">`, Img("hat.png", "hat", g.Attr("id", "image")))
-	})
-}
-
 func TestSimpleElements(t *testing.T) {
 	cases := map[string]func(...g.Node) g.NodeFunc{
+		"a":          A,
 		"address":    Address,
 		"article":    Article,
 		"aside":      Aside,
@@ -104,11 +50,13 @@ func TestSimpleElements(t *testing.T) {
 		"fieldset":   FieldSet,
 		"figure":     Figure,
 		"footer":     Footer,
+		"form":       FormEl,
 		"head":       Head,
 		"header":     Header,
 		"hgroup":     HGroup,
 		"html":       HTML,
 		"iframe":     IFrame,
+		"label":      Label,
 		"legend":     Legend,
 		"li":         Li,
 		"main":       Main,
@@ -119,11 +67,14 @@ func TestSimpleElements(t *testing.T) {
 		"object":     Object,
 		"ol":         Ol,
 		"optgroup":   OptGroup,
+		"option":     Option,
 		"p":          P,
 		"picture":    Picture,
 		"pre":        Pre,
+		"progress":   Progress,
 		"script":     Script,
 		"section":    Section,
+		"select":     Select,
 		"span":       Span,
 		"style":      StyleEl,
 		"summary":    Summary,
@@ -131,6 +82,7 @@ func TestSimpleElements(t *testing.T) {
 		"table":      Table,
 		"tbody":      TBody,
 		"td":         Td,
+		"textarea":   Textarea,
 		"tfoot":      TFoot,
 		"th":         Th,
 		"thead":      THead,
@@ -154,6 +106,8 @@ func TestSimpleVoidKindElements(t *testing.T) {
 		"col":    Col,
 		"embed":  Embed,
 		"hr":     Hr,
+		"img":    Img,
+		"input":  Input,
 		"link":   Link,
 		"meta":   Meta,
 		"param":  Param,