fix(html): ignore empty helper tokens
Drop empty strings from join-based accessibility helpers and class names so generated attributes stay clean. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
511a10f54b
commit
2ce8876cb5
2 changed files with 74 additions and 4 deletions
38
node.go
38
node.go
|
|
@ -187,21 +187,30 @@ func AriaLabel(n Node, label string) Node {
|
|||
// Example: AriaDescribedBy(El("input"), "help-text", "error-text").
|
||||
// Multiple IDs are joined with spaces, matching the HTML attribute format.
|
||||
func AriaDescribedBy(n Node, ids ...string) Node {
|
||||
return Attr(n, "aria-describedby", strings.Join(ids, " "))
|
||||
if value := joinNonEmpty(ids...); value != "" {
|
||||
return Attr(n, "aria-describedby", value)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// node.go: AriaLabelledBy sets the aria-labelledby attribute on an element node.
|
||||
// Example: AriaLabelledBy(El("section"), "section-title").
|
||||
// Multiple IDs are joined with spaces, matching the HTML attribute format.
|
||||
func AriaLabelledBy(n Node, ids ...string) Node {
|
||||
return Attr(n, "aria-labelledby", strings.Join(ids, " "))
|
||||
if value := joinNonEmpty(ids...); value != "" {
|
||||
return Attr(n, "aria-labelledby", value)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// node.go: AriaControls sets the aria-controls attribute on an element node.
|
||||
// Example: AriaControls(El("button"), "menu-panel").
|
||||
// Multiple IDs are joined with spaces, matching the HTML attribute format.
|
||||
func AriaControls(n Node, ids ...string) Node {
|
||||
return Attr(n, "aria-controls", strings.Join(ids, " "))
|
||||
if value := joinNonEmpty(ids...); value != "" {
|
||||
return Attr(n, "aria-controls", value)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// node.go: AriaCurrent sets the aria-current attribute on an element node.
|
||||
|
|
@ -278,7 +287,10 @@ func AltText(n Node, text string) Node {
|
|||
// Example: Class(El("div"), "card", "card--primary").
|
||||
// Multiple class tokens are joined with spaces.
|
||||
func Class(n Node, classes ...string) Node {
|
||||
return Attr(n, "class", strings.Join(classes, " "))
|
||||
if value := joinNonEmpty(classes...); value != "" {
|
||||
return Attr(n, "class", value)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// node.go: AriaHidden sets the aria-hidden attribute on an element node.
|
||||
|
|
@ -338,6 +350,24 @@ func AutoFocus(n Node) Node {
|
|||
return Attr(n, "autofocus", "autofocus")
|
||||
}
|
||||
|
||||
func joinNonEmpty(parts ...string) string {
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var filtered []string
|
||||
for i := range parts {
|
||||
if parts[i] == "" {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, parts[i])
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(filtered, " ")
|
||||
}
|
||||
|
||||
func (n *elNode) Render(ctx *Context) string {
|
||||
if n == nil {
|
||||
return ""
|
||||
|
|
|
|||
40
node_test.go
40
node_test.go
|
|
@ -265,6 +265,16 @@ func TestAriaDescribedByHelper(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAriaDescribedByHelper_IgnoresEmptyIDs(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := AriaDescribedBy(El("input"), "", "hint-1", "", "hint-2")
|
||||
got := node.Render(ctx)
|
||||
want := `<input aria-describedby="hint-1 hint-2">`
|
||||
if got != want {
|
||||
t.Errorf("AriaDescribedBy() with empty IDs = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAriaLabelledByHelper(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := AriaLabelledBy(El("input"), "label-1", "label-2")
|
||||
|
|
@ -275,6 +285,16 @@ func TestAriaLabelledByHelper(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAriaLabelledByHelper_IgnoresEmptyIDs(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := AriaLabelledBy(El("input"), "", "label-1", "", "label-2")
|
||||
got := node.Render(ctx)
|
||||
want := `<input aria-labelledby="label-1 label-2">`
|
||||
if got != want {
|
||||
t.Errorf("AriaLabelledBy() with empty IDs = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAriaControlsHelper(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := AriaControls(El("button", Raw("menu")), "menu-panel", "shortcut-hints")
|
||||
|
|
@ -285,6 +305,16 @@ func TestAriaControlsHelper(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAriaControlsHelper_IgnoresEmptyIDs(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := AriaControls(El("button", Raw("menu")), "", "menu-panel", "")
|
||||
got := node.Render(ctx)
|
||||
want := `<button aria-controls="menu-panel">menu</button>`
|
||||
if got != want {
|
||||
t.Errorf("AriaControls() with empty IDs = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAriaCurrentHelper(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := AriaCurrent(El("a", Raw("Home")), "page")
|
||||
|
|
@ -412,6 +442,16 @@ func TestClassHelper(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestClassHelper_IgnoresEmptyClasses(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := Class(El("div", Raw("content")), "", "card", "", "card--primary")
|
||||
got := node.Render(ctx)
|
||||
want := `<div class="card card--primary">content</div>`
|
||||
if got != want {
|
||||
t.Errorf("Class() with empty classes = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAriaHiddenHelper(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue