Skip to content
New issue

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

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

Already on GitHub? # to your account

feat: adding ability to override default classes #1

Merged
merged 1 commit into from
Feb 3, 2023
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
78 changes: 69 additions & 9 deletions form.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ type form struct {
labelClass string
inputClass string
selectClass string
checkboxClass string
radioClass string
fileClass string
textAreaClass string
dateInputClass string
errorClass string
}

// SetAuthenticityToken allows to set the authenticity token
Expand Down Expand Up @@ -63,7 +69,13 @@ func (f form) InputTag(opts tags.Options) *tags.Tag {
opts["type"] = "text"
}

opts["class"] = f.inputClass
// Set the class for the input
if cl := opts["class"]; cl == nil {
opts["class"] = f.inputClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.inputClass, cl)
}

delete(opts, "tag_only")

return tags.New("input", opts)
Expand All @@ -74,6 +86,13 @@ func (f form) InputTag(opts tags.Options) *tags.Tag {
func (f form) CheckboxTag(opts tags.Options) *tags.Tag {
opts["type"] = "checkbox"

// Set the class for the checkbox
if cl := opts["class"]; cl == nil {
opts["class"] = f.checkboxClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.checkboxClass, cl)
}

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

Expand Down Expand Up @@ -125,13 +144,27 @@ func (f form) FileTag(opts tags.Options) *tags.Tag {
f.Options["enctype"] = "multipart/form-data"
opts["type"] = "file"

// Set the class for the file input
if cl := opts["class"]; cl == nil {
opts["class"] = f.fileClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.fileClass, cl)
}

return tags.New("input", opts)
}

// RadioButton builds a tailwindcss input[type=radio] with passed options
func (f form) RadioButtonTag(opts tags.Options) *tags.Tag {
opts["type"] = "radio"

// Set the class for the radio
if cl := opts["class"]; cl == nil {
opts["class"] = f.radioClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.radioClass, cl)
}

var label string
if opts["label"] != nil {
label = fmt.Sprint(opts["label"])
Expand All @@ -155,22 +188,25 @@ func (f form) RadioButtonTag(opts tags.Options) *tags.Tag {

ct := tags.New("input", opts)
ct.Checked = template.HTMLEscaper(value) == template.HTMLEscaper(checked)
labelOptions := tags.Options{
"body": strings.Join([]string{ct.String(), label}, " "),
}

// If the ID is provided, give it to the label's for attribute
var labelOptions = tags.Options{}
if ID != "" {
labelOptions["for"] = ID
}

tag := tags.New("label", labelOptions)
tag := f.Label(strings.Join([]string{ct.String(), label}, " "), labelOptions)
return tag
}

// SelectTag constructs a new `<select>` tag from a form.
func (f form) SelectTag(opts tags.Options) *tags.Tag {
opts["class"] = f.selectClass
// Set the class for the select
if cl := opts["class"]; cl == nil {
opts["class"] = f.selectClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.selectClass, cl)
}

return NewSelectTag(opts)
}
Expand All @@ -188,6 +224,13 @@ func (f form) TextAreaTag(opts tags.Options) *tags.Tag {
delete(opts, "value")
}

// Set the class for the textarea
if cl := opts["class"]; cl == nil {
opts["class"] = f.textAreaClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.textAreaClass, cl)
}

delete(opts, "tag_only")

return tags.New("textarea", opts)
Expand All @@ -204,6 +247,13 @@ func (f form) DateTimeTag(opts tags.Options) *tags.Tag {
opts["format"] = "2006-01-02T03:04"
}

// Set the class for the date input
if cl := opts["class"]; cl == nil {
opts["class"] = f.dateInputClass
} else {
opts["class"] = fmt.Sprintf("%v %v", f.dateInputClass, cl)
}

delete(opts, "tag_only")
return tags.New("input", opts)
}
Expand All @@ -224,6 +274,8 @@ func (f form) SubmitTag(value string, opts tags.Options) *tags.Tag {
return tags.New("input", opts)
}

const defaultBoxStyle = "border border-gray-300 rounded-md py-1.5 px-3 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 "

// NewForm creates a new form from passed options, it sets POST as the
// default method and also handles other methods as PUT by adding
// a `_method` hidden input.
Expand All @@ -239,9 +291,17 @@ func NewForm(opts tags.Options, help hctx.Context) *form {
}

form := &form{
labelClass: "block text-sm font-medium text-gray-700",
selectClass: "mt-1 block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm",
inputClass: "border border-gray-300 rounded-md py-1.5 px-3 w-full",
// TODO: define default classes for the missing components.
fieldContainerClass: "",
dateInputClass: defaultBoxStyle + "text-sm font-medium text-gray-700",
labelClass: "block text-sm font-medium text-gray-700",
inputClass: defaultBoxStyle + "w-full",
selectClass: defaultBoxStyle + "w-full py-2 mt-1 text-base sm:text-sm",
checkboxClass: "",
radioClass: "",
fileClass: "",
textAreaClass: defaultBoxStyle + "w-full",
errorClass: "",

Tag: tags.New("form", opts),
}
Expand Down
166 changes: 165 additions & 1 deletion form_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package tailush_test

import (
"fmt"
"net/http"
"strings"
"tailush"
"testing"

"github.com/wawandco/tailush"

"github.com/gobuffalo/plush/v4"
"github.com/gobuffalo/tags/v3"
)
Expand Down Expand Up @@ -110,6 +112,28 @@ func TestLabel(t *testing.T) {
t.Fatalf("form should contain mixed classes")
}
})

t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseLabelClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

l := f.Label("any", tags.Options{})
if !strings.Contains(string(l.HTML()), `<label class="my-custom-class">any</label>`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseLabelClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

l := f.Label("any", tags.Options{"class": "my-other-class"})
if !strings.Contains(string(l.HTML()), `<label class="my-custom-class my-other-class">any</label>`) {
t.Fatalf("form should contain mixed classes")
}
})
}

func TestFileTag(t *testing.T) {
Expand All @@ -124,7 +148,28 @@ func TestFileTag(t *testing.T) {
if !strings.Contains(string(f.HTML()), `enctype="multipart/form-data"`) {
t.Fatalf("enctype should be multipart/form-data")
}
})

t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseFileClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ft := f.FileTag(tags.Options{})
if !strings.Contains(string(ft.HTML()), `<input class="my-custom-class" type="file" />`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseFileClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ft := f.FileTag(tags.Options{"class": "my-other-class"})
if !strings.Contains(string(ft.HTML()), `<input class="my-custom-class my-other-class" type="file" />`) {
t.Fatalf("form should contain mixed classes")
}
})
}

Expand Down Expand Up @@ -174,6 +219,28 @@ func TestTextArea(t *testing.T) {
t.Fatalf("textarea should not contain tag_only")
}
})

t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseTextAreaClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ta := f.TextAreaTag(tags.Options{})
if !strings.Contains(string(ta.HTML()), `<textarea class="my-custom-class"></textarea>`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseTextAreaClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ta := f.TextAreaTag(tags.Options{"class": "my-other-class"})
if !strings.Contains(string(ta.HTML()), `<textarea class="my-custom-class my-other-class"></textarea>`) {
t.Fatalf("form should contain mixed classes")
}
})
}

func TestHiddenTag(t *testing.T) {
Expand All @@ -186,3 +253,100 @@ func TestHiddenTag(t *testing.T) {
}
})
}

func TestInputTag(t *testing.T) {
t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseInputClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

it := f.InputTag(tags.Options{})
if !strings.Contains(string(it.HTML()), `<input class="my-custom-class" type="text" />`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseInputClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

it := f.InputTag(tags.Options{"class": "my-other-class"})
if !strings.Contains(string(it.HTML()), `<input class="my-custom-class my-other-class" type="text" />`) {
t.Fatalf("form should contain mixed classes")
}
})
}

func TestDateTimeTag(t *testing.T) {
t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseDateInputClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

dt := f.DateTimeTag(tags.Options{})
if !strings.Contains(string(dt.HTML()), `<input class="my-custom-class" format="2006-01-02T03:04" type="datetime-local" />`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseDateInputClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

dt := f.DateTimeTag(tags.Options{"class": "my-other-class"})
if !strings.Contains(string(dt.HTML()), `<input class="my-custom-class my-other-class" format="2006-01-02T03:04" type="datetime-local" />`) {
t.Fatalf("form should contain mixed classes")
}
})
}

func TestCheckboxTag(t *testing.T) {
t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseCheckboxClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ct := f.CheckboxTag(tags.Options{})
if !strings.Contains(string(ct.HTML()), `<input class="my-custom-class" type="checkbox" value="true" />`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseCheckboxClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ct := f.CheckboxTag(tags.Options{"class": "my-other-class"})
if !strings.Contains(string(ct.HTML()), `<input class="my-custom-class my-other-class" type="checkbox" value="true" />`) {
t.Fatalf("form should contain mixed classes")
}
})
}

func TestRadioButtonTag(t *testing.T) {
t.Run("overriding default classes", func(t *testing.T) {
opt := tailush.UseRadioClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ct := f.RadioButtonTag(tags.Options{})
fmt.Println(string(ct.HTML()))
if !strings.Contains(string(ct.HTML()), `<input class="my-custom-class" type="radio" checked />`) {
t.Fatalf("form shouldn't contain mixed classes")
}
})

t.Run("overriding default and passing classes", func(t *testing.T) {
opt := tailush.UseRadioClass("my-custom-class")
f := tailush.NewForm(tags.Options{}, newHctx())
opt(f)

ct := f.RadioButtonTag(tags.Options{"class": "my-other-class"})
if !strings.Contains(string(ct.HTML()), `<input class="my-custom-class my-other-class" type="radio" checked />`) {
t.Fatalf("form should contain mixed classes")
}
})
}
4 changes: 2 additions & 2 deletions formfn.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ func FormFn(formOptions ...FormHelperOption) any {
form := NewForm(opts, help)

// Run the options on the form
for _, v := range formOptions {
v(form)
for _, optionFn := range formOptions {
optionFn(form)
}

help.Set(hn, form)
Expand Down
4 changes: 2 additions & 2 deletions formforfn.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func FormForFn(formOptions ...FormHelperOption) any {

// Run the options on the form
formf := NewFormFor(model, opts, help)
for _, v := range formOptions {
v(formf.form)
for _, optionFn := range formOptions {
optionFn(formf.form)
}

help.Set(hn, formf)
Expand Down
Loading