Skip to content

Commit

Permalink
Merge pull request #147 from lpar/main
Browse files Browse the repository at this point in the history
Fix a11y problems with validation error messages generated by Bootstrap form_for
  • Loading branch information
sio4 authored Mar 11, 2023
2 parents 8984d91 + e0bbef3 commit b8072db
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 6 deletions.
15 changes: 14 additions & 1 deletion form/bootstrap/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bootstrap

import (
"fmt"
"strconv"
"strings"

"github.com/gobuffalo/flect"
Expand Down Expand Up @@ -109,12 +110,24 @@ func divWrapper(opts tags.Options, fn func(opts tags.Options) tags.Body) *tags.T
}

if hasErrors {
for _, err := range errors {
fieldID, _ := opts["id"].(string)
errorIDs := make([]string, 0, len(errors))
for i, err := range errors {
errID := "error-" + fieldID + "-" + strconv.Itoa(i)
div.Append(tags.New("div", tags.Options{
"class": "invalid-feedback help-block",
"body": err,
"id": errID,
}))
errorIDs = append(errorIDs, errID)
}
descBy, _ := opts["aria-describedby"].(string)
if descBy != "" {
descBy += " "
}
descBy += strings.Join(errorIDs, " ")
opts["aria-describedby"] = descBy
opts["aria-invalid"] = "true"
}

if hasHelp {
Expand Down
10 changes: 5 additions & 5 deletions form/bootstrap/form_for_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func Test_InputError_CustomError(t *testing.T) {

f := NewFormFor(struct{ Name string }{}, tags.Options{"errors": errors})
l := f.InputTag("Name", tags.Options{"label": "Custom"})
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block">My Custom Error</div></div>`, l.String())
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input aria-describedby="error--Name-0" aria-invalid="true" class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block" id="error--Name-0">My Custom Error</div></div>`, l.String())
}

func Test_InputError(t *testing.T) {
Expand All @@ -220,7 +220,7 @@ func Test_InputError(t *testing.T) {

f := NewFormFor(struct{ Name string }{}, tags.Options{"errors": errors})
l := f.InputTag("Name", tags.Options{"label": "Custom"})
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block">Name shoud be AJ.</div></div>`, l.String())
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input aria-describedby="error--Name-0" aria-invalid="true" class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block" id="error--Name-0">Name shoud be AJ.</div></div>`, l.String())
}

func Test_InputHidden(t *testing.T) {
Expand All @@ -246,7 +246,7 @@ func Test_InputError_Map(t *testing.T) {

f := NewFormFor(struct{ Name string }{}, tags.Options{"errors": errors})
l := f.InputTag("Name", tags.Options{"label": "Custom"})
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block">Name shoud be AJ.</div></div>`, l.String())
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input aria-describedby="error--Name-0" aria-invalid="true" class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block" id="error--Name-0">Name shoud be AJ.</div></div>`, l.String())
}

func Test_InputError_InvalidMap(t *testing.T) {
Expand All @@ -270,7 +270,7 @@ func Test_InputMultipleError(t *testing.T) {

f := NewFormFor(struct{ Name string }{}, tags.Options{"errors": errors})
l := f.InputTag("Name", tags.Options{"label": "Custom"})
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block">Name shoud be AJ.</div><div class="invalid-feedback help-block">Name shoud start with A.</div></div>`, l.String())
r.Equal(`<div class="form-group has-error"><label class="form-label" for="-Name">Custom</label><input aria-describedby="error--Name-0 error--Name-1" aria-invalid="true" class="form-control is-invalid" id="-Name" name="Name" type="text" value="" /><div class="invalid-feedback help-block" id="error--Name-0">Name shoud be AJ.</div><div class="invalid-feedback help-block" id="error--Name-1">Name shoud start with A.</div></div>`, l.String())
}

func Test_CheckBoxError(t *testing.T) {
Expand All @@ -281,7 +281,7 @@ func Test_CheckBoxError(t *testing.T) {

f := NewFormFor(struct{ Name string }{}, tags.Options{"errors": errors})
l := f.CheckboxTag("Name", tags.Options{"label": "Custom"})
r.Equal(`<div class="form-check has-error"><input class="form-check-input is-invalid" id="-Name" name="Name" type="checkbox" value="true" /><label class="form-check-label" for="-Name">Custom</label><div class="invalid-feedback help-block">Name shoud be AJ.</div></div>`, l.String())
r.Equal(`<div class="form-check has-error"><input aria-describedby="error--Name-0" aria-invalid="true" class="form-check-input is-invalid" id="-Name" name="Name" type="checkbox" value="true" /><label class="form-check-label" for="-Name">Custom</label><div class="invalid-feedback help-block" id="error--Name-0">Name shoud be AJ.</div></div>`, l.String())
}

type Person struct {
Expand Down

0 comments on commit b8072db

Please sign in to comment.