From e0bbef314fc2c1df3435233767a90d7d9e9d24a2 Mon Sep 17 00:00:00 2001 From: mathew murphy Date: Fri, 10 Mar 2023 16:19:40 -0600 Subject: [PATCH] Fix a11y problems with validation error messages. --- form/bootstrap/common.go | 15 ++++++++++++++- form/bootstrap/form_for_test.go | 10 +++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/form/bootstrap/common.go b/form/bootstrap/common.go index aca962b..7c7dcaa 100644 --- a/form/bootstrap/common.go +++ b/form/bootstrap/common.go @@ -2,6 +2,7 @@ package bootstrap import ( "fmt" + "strconv" "strings" "github.com/gobuffalo/flect" @@ -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 { diff --git a/form/bootstrap/form_for_test.go b/form/bootstrap/form_for_test.go index 8a33063..abd1e2e 100644 --- a/form/bootstrap/form_for_test.go +++ b/form/bootstrap/form_for_test.go @@ -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(`
My Custom Error
`, l.String()) + r.Equal(`
My Custom Error
`, l.String()) } func Test_InputError(t *testing.T) { @@ -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(`
Name shoud be AJ.
`, l.String()) + r.Equal(`
Name shoud be AJ.
`, l.String()) } func Test_InputHidden(t *testing.T) { @@ -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(`
Name shoud be AJ.
`, l.String()) + r.Equal(`
Name shoud be AJ.
`, l.String()) } func Test_InputError_InvalidMap(t *testing.T) { @@ -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(`
Name shoud be AJ.
Name shoud start with A.
`, l.String()) + r.Equal(`
Name shoud be AJ.
Name shoud start with A.
`, l.String()) } func Test_CheckBoxError(t *testing.T) { @@ -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(`
Name shoud be AJ.
`, l.String()) + r.Equal(`
Name shoud be AJ.
`, l.String()) } type Person struct {