1
0

Add generic Map implementation (#108)

Instead of waiting for a good time to switch the `Map` implementation,
I've decided to just offer two implementations: one for Go versions
before 1.18, and one for 1.18 and onwards. This is achieved using build
tags in the source files.

This is obviously a breaking change for consumers of this library that
use Go 1.18 and onwards.

See #88
This commit is contained in:
Markus Wüstenberg 2022-09-22 09:41:06 +02:00 committed by GitHub
parent 3334d6c7d0
commit f387a71230
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 37 deletions

View File

@ -1,3 +1,6 @@
//go:build go1.18
// +build go1.18
package main
import (
@ -58,8 +61,8 @@ func Navbar(currentPath string, links []PageLink) g.Node {
Ul(
NavbarLink("/", "Home", currentPath),
g.Group(g.Map(len(links), func(i int) g.Node {
return NavbarLink(links[i].Path, links[i].Name, currentPath)
g.Group(g.Map(links, func(pl PageLink) g.Node {
return NavbarLink(pl.Path, pl.Name, currentPath)
})),
),

View File

@ -1,3 +1,6 @@
//go:build go1.18
// +build go1.18
package main
import (
@ -81,8 +84,8 @@ func Navbar(currentPath string, links []PageLink) g.Node {
NavbarLink("/", "Home", currentPath == "/"),
// We can Map custom slices to Nodes
g.Group(g.Map(len(links), func(i int) g.Node {
return NavbarLink(links[i].Path, links[i].Name, currentPath == links[i].Path)
g.Group(g.Map(links, func(pl PageLink) g.Node {
return NavbarLink(pl.Path, pl.Name, currentPath == pl.Path)
})),
),
),

2
go.mod
View File

@ -1,3 +1,3 @@
module "github.com/maragudk/gomponents"
go 1.15
go 1.18

View File

@ -244,15 +244,6 @@ func Group(children []Node) Node {
return group{children: children}
}
// Map something enumerable to a list of Nodes.
func Map(length int, cb func(i int) Node) []Node {
var nodes []Node
for i := 0; i < length; i++ {
nodes = append(nodes, cb(i))
}
return nodes
}
// If condition is true, return the given Node. Otherwise, return nil.
// This helper function is good for inlining elements conditionally.
func If(condition bool, n Node) Node {

13
gomponents_generic.go Normal file
View File

@ -0,0 +1,13 @@
//go:build go1.18
// +build go1.18
package gomponents
// Map a slice of anything to a slice of Nodes.
func Map[T any](ts []T, cb func(T) Node) []Node {
var nodes []Node
for _, t := range ts {
nodes = append(nodes, cb(t))
}
return nodes
}

View File

@ -0,0 +1,34 @@
//go:build go1.18
// +build go1.18
package gomponents_test
import (
"os"
"testing"
g "github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/internal/assert"
)
func TestMap(t *testing.T) {
t.Run("maps slices to nodes", func(t *testing.T) {
items := []string{"hat", "partyhat", "turtlehat"}
lis := g.Map(items, func(i string) g.Node {
return g.El("li", g.Text(i))
})
list := g.El("ul", lis...)
assert.Equal(t, `<ul><li>hat</li><li>partyhat</li><li>turtlehat</li></ul>`, list)
})
}
func ExampleMap() {
items := []string{"party hat", "super hat"}
e := g.El("ul", g.Group(g.Map(items, func(i string) g.Node {
return g.El("li", g.Text(i))
})))
_ = e.Render(os.Stdout)
// Output: <ul><li>party hat</li><li>super hat</li></ul>
}

13
gomponents_non_generic.go Normal file
View File

@ -0,0 +1,13 @@
//go:build !go1.18
// +build !go1.18
package gomponents
// Map something enumerable to a list of Nodes.
func Map(length int, cb func(i int) Node) []Node {
var nodes []Node
for i := 0; i < length; i++ {
nodes = append(nodes, cb(i))
}
return nodes
}

View File

@ -0,0 +1,34 @@
//go:build !go1.18
// +build !go1.18
package gomponents_test
import (
"os"
"testing"
g "github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/internal/assert"
)
func TestMap(t *testing.T) {
t.Run("maps slices to nodes", func(t *testing.T) {
items := []string{"hat", "partyhat", "turtlehat"}
lis := g.Map(len(items), func(i int) g.Node {
return g.El("li", g.Text(items[i]))
})
list := g.El("ul", lis...)
assert.Equal(t, `<ul><li>hat</li><li>partyhat</li><li>turtlehat</li></ul>`, list)
})
}
func ExampleMap() {
items := []string{"party hat", "super hat"}
e := g.El("ul", g.Group(g.Map(len(items), func(i int) g.Node {
return g.El("li", g.Text(items[i]))
})))
_ = e.Render(os.Stdout)
// Output: <ul><li>party hat</li><li>super hat</li></ul>
}

View File

@ -133,7 +133,7 @@ func TestEl(t *testing.T) {
assert.Equal(t, `<div class="hat"><br></div>`, e)
})
t.Run("renders outside if node does not implement placer", func(t *testing.T) {
t.Run("renders outside if node does not implement nodeTypeDescriber", func(t *testing.T) {
e := g.El("div", outsider{})
assert.Equal(t, `<div>outsider</div>`, e)
})
@ -248,28 +248,6 @@ func TestGroup(t *testing.T) {
})
}
func TestMap(t *testing.T) {
t.Run("maps slices to nodes", func(t *testing.T) {
items := []string{"hat", "partyhat", "turtlehat"}
lis := g.Map(len(items), func(i int) g.Node {
return g.El("li", g.Text(items[i]))
})
list := g.El("ul", lis...)
assert.Equal(t, `<ul><li>hat</li><li>partyhat</li><li>turtlehat</li></ul>`, list)
})
}
func ExampleMap() {
items := []string{"party hat", "super hat"}
e := g.El("ul", g.Group(g.Map(len(items), func(i int) g.Node {
return g.El("li", g.Text(items[i]))
})))
_ = e.Render(os.Stdout)
// Output: <ul><li>party hat</li><li>super hat</li></ul>
}
func TestIf(t *testing.T) {
t.Run("returns node if condition is true", func(t *testing.T) {
n := g.El("div", g.If(true, g.El("span")))