Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated dependencies and improved bootstrap 5 support #139

Merged
merged 3 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ jobs:
- name: Test
run: |
go mod tidy -v
go test -tags "sqlite" -cover ./...
go test -tags "sqlite" -cover ./...
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,3 @@ Documentation is currently in [this repository Wiki](https://github.com/gobuffal
If you want to contribute, please read this article first: [Contributing to Open Source Git Repositories in Go](https://splice.com/blog/contributing-open-source-git-repositories-go/). It shows how to configure your git environment to avoid common pitfalls. This article is recommended to all those who are looking to contribute to any Go projects.

^ Taken from [gobuffalo.io](https://https://gobuffalo.io/docs/contributing)

### ⚠️ Send PRs to development branch

The way we release in Tags is:

1. We use `development` branch to accumulate changes to be released
2. Once we decide to make a release we send a PR from `development` to `master` with those changes to be added.
3. Once that PR gets merged we tag a new release with the vX.X.X scheme.
63 changes: 53 additions & 10 deletions form/bootstrap/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,37 @@ func buildOptions(opts tags.Options, err bool) {
}

if opts["tag_only"] != true {
opts["class"] = strings.Join([]string{fmt.Sprint(opts["class"]), "form-control"}, " ")
if opts["type"] == "checkbox" {
opts["class"] = strings.Join([]string{fmt.Sprint(opts["class"]), "form-check-input"}, " ")
} else {
opts["class"] = strings.Join([]string{fmt.Sprint(opts["class"]), "form-control"}, " ")
}
}

if err {
opts["class"] = strings.Join([]string{fmt.Sprint(opts["class"]), "is-invalid"}, " ")
}

opts["class"] = strings.TrimSpace(opts["class"].(string))
delete(opts, "hide_label")
}

func divWrapper(opts tags.Options, fn func(opts tags.Options) tags.Body) *tags.Tag {
divClass := "form-group"
divClass := "form-group" // btw, form-group was deprecated in Bootstrap 5
labelClass := "form-label"
hasErrors := false
errors := []string{}
hasHelp := false
helpMessage := ""

if opts["errors"] != nil && len(opts["errors"].([]string)) > 0 {
divClass = "form-group has-error"
hasErrors = true
errors = opts["errors"].([]string)
delete(opts, "errors")
if opts["div_class"] != nil {
divClass = opts["div_class"].(string)
delete(opts, "div_class")
}

if opts["label_class"] != nil {
labelClass = opts["label_class"].(string)
delete(opts, "label_class")
}

if opts["bootstrap"] != nil {
Expand All @@ -45,6 +56,19 @@ func divWrapper(opts tags.Options, fn func(opts tags.Options) tags.Body) *tags.T
delete(opts, "bootstrap")
}

if opts["help"] != nil {
hasHelp = true
helpMessage = opts["help"].(string)
delete(opts, "help")
}

if opts["errors"] != nil && len(opts["errors"].([]string)) > 0 {
divClass += " has-error"
hasErrors = true
errors = opts["errors"].([]string)
delete(opts, "errors")
}

div := tags.New("div", tags.Options{
"class": divClass,
})
Expand All @@ -55,13 +79,14 @@ func divWrapper(opts tags.Options, fn func(opts tags.Options) tags.Body) *tags.T
opts["label"] = flect.Titleize(tf)
}
}

delete(opts, "tags-field")

useLabel := opts["hide_label"] == nil
if useLabel && opts["label"] != nil {
div.Prepend(tags.New("label", tags.Options{
"body": opts["label"],
"for": opts["id"],
"body": opts["label"],
"class": labelClass,
}))
delete(opts, "label")
}
Expand All @@ -72,7 +97,16 @@ func divWrapper(opts tags.Options, fn func(opts tags.Options) tags.Body) *tags.T
return fn(opts).(*tags.Tag)
}

div.Append(fn(opts))
isFloatingLabel := strings.Contains(divClass, "form-floating")
if opts["type"] == "checkbox" || isFloatingLabel {
if isFloatingLabel && opts["placeholder"] == nil {
// bootstrap 5 floating label requires this
opts["placeholder"] = opts["name"]
}
div.Prepend(fn(opts))
} else {
div.Append(fn(opts))
}

if hasErrors {
for _, err := range errors {
Expand All @@ -82,5 +116,14 @@ func divWrapper(opts tags.Options, fn func(opts tags.Options) tags.Body) *tags.T
}))
}
}

if hasHelp {
div.Append(tags.New("div", tags.Options{
"id": fmt.Sprintf("%v-help", opts["name"]),
"class": "form-text",
"body": helpMessage,
}))
}

return div
}
4 changes: 2 additions & 2 deletions form/bootstrap/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func Test_BootstrapFormGroupClass(t *testing.T) {
expected string
}{
{
expected: `<div class="form-group row"><label>Name</label><input class=" form-control" id="-Name" name="Name" type="text" value="" /></div>`,
expected: `<div class="form-group row"><label class="form-label" for="-Name">Name</label><input class="form-control" id="-Name" name="Name" type="text" value="" /></div>`,
options: tags.Options{
"bootstrap": map[string]interface{}{
"form-group-class": "form-group row",
Expand All @@ -25,7 +25,7 @@ func Test_BootstrapFormGroupClass(t *testing.T) {
},

{
expected: `<div class="form-group"><label>Name</label><input class=" form-control" id="-Name" name="Name" type="text" value="" /></div>`,
expected: `<div class="form-group"><label class="form-label" for="-Name">Name</label><input class="form-control" id="-Name" name="Name" type="text" value="" /></div>`,
options: tags.Options{},
},
}
Expand Down
61 changes: 33 additions & 28 deletions form/bootstrap/form_for.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package bootstrap

import (
"fmt"
"strings"
"html/template"

"github.com/gobuffalo/tags/v3"
"github.com/gobuffalo/tags/v3/form"
Expand All @@ -16,37 +15,43 @@ type FormFor struct {

//CheckboxTag adds a checkbox to a form wrapped with a form-control and a label
func (f FormFor) CheckboxTag(field string, opts tags.Options) *tags.Tag {
opts["type"] = "checkbox"
opts = f.buildOptions(field, opts)

label := field
if opts["label"] != nil {
label = fmt.Sprint(opts["label"])
}
hl := opts["hide_label"]
delete(opts, "label")

fieldKey := validators.GenerateKey(field)
if err := f.Errors.Get(fieldKey); err != nil {
opts["errors"] = err
}
opts["div_class"] = "form-check"
opts["label_class"] = "form-check-label"

return divWrapper(opts, func(o tags.Options) tags.Body {
if o["class"] != nil {
cls := strings.Split(o["class"].(string), " ")
ncls := make([]string, 0, len(cls))
for _, c := range cls {
if c != "form-control" {
ncls = append(ncls, c)
}
}
o["class"] = strings.Join(ncls, " ")
value := opts["value"]
delete(opts, "value")

checked := opts["checked"]
delete(opts, "checked")
if checked == nil {
checked = "true"
}
if label != "" {
o["label"] = label
opts["value"] = checked

unchecked := opts["unchecked"]
delete(opts, "unchecked")

if opts["tag_only"] == true {
tag := f.FormFor.InputTag(field, opts)
tag.Checked = template.HTMLEscaper(value) == template.HTMLEscaper(checked)
return tag
}
if hl != nil {
o["hide_label"] = hl

tag := f.FormFor.InputTag(field, opts)
tag.Checked = template.HTMLEscaper(value) == template.HTMLEscaper(checked)

if opts["name"] != nil && unchecked != nil {
tag.AfterTag = append(tag.AfterTag, tags.New("input", tags.Options{
"type": "hidden",
"name": opts["name"],
"value": unchecked,
}))
}
return f.FormFor.CheckboxTag(field, o)
return tag
})
}

Expand Down Expand Up @@ -126,6 +131,6 @@ func (f FormFor) buildOptions(field string, opts tags.Options) tags.Options {
if err := f.Errors.Get(fieldName); err != nil {
opts["errors"] = err
}

f.FormFor.BuildOptions(field, opts)
return opts
}
Loading