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

Improve checkbox layout #686

Merged
merged 1 commit into from
Aug 17, 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
94 changes: 57 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ This generates:

```html
<div class="mb-3">
<label class="form-label visually-hidden" for="user_comment">Comment</label>
<label class="visually-hidden" for="user_comment">Comment</label>
<textarea class="form-control" id="user_comment" name="user[comment]" placeholder="Leave a comment...">
</textarea>
</div>
Expand All @@ -275,7 +275,7 @@ This generates:

```html
<div class="mb-3">
<label class="form-label custom-class required" for="user_email">Email</label>
<label class="custom-class required" for="user_email">Email</label>
<input aria-required="true" class="form-control" id="user_email" name="user[email]" required="required" type="text" value="[email protected]">
</div>
```
Expand All @@ -291,7 +291,7 @@ This generates:

```html
<div class="mb-3">
<label class="form-label visually-hidden required" for="user_email">Email</label>
<label class="visually-hidden required" for="user_email">Email</label>
<input aria-required="true" class="form-control" id="user_email" name="user[email]" placeholder="Email" required="required" type="text" value="">
</div>
```
Expand Down Expand Up @@ -787,7 +787,7 @@ This generates:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" class="form-control-plaintext" id="user_email" name="user[email]" readonly required="required" type="text" value="[email protected]">
</div>
Expand Down Expand Up @@ -1049,17 +1049,19 @@ This generates:
```html
<form accept-charset="UTF-8" action="/users" class="new_user row row-cols-auto g-3 align-items-center" id="new_user" method="post">
<div class="col">
<label class="form-label visually-hidden me-sm-2 required" for="user_email">Email</label>
<label class="visually-hidden required" for="user_email">Email</label>
<input aria-required="true" class="form-control" id="user_email" name="user[email]" required="required" type="email" value="[email protected]">
</div>
<div class="col">
<label class="form-label visually-hidden me-sm-2" for="user_password">Password</label>
<label class="visually-hidden" for="user_password">Password</label>
<input class="form-control" id="user_password" name="user[password]" type="password">
</div>
<div class="form-check form-check-inline mb-3">
<input autocomplete="off" name="user[remember_me]" type="hidden" value="0">
<input class="form-check-input" id="user_remember_me" name="user[remember_me]" type="checkbox" value="1">
<label class="form-check-label" for="user_remember_me">Remember me</label>
<div class="col">
<div class="form-check form-check-inline">
<input autocomplete="off" name="user[remember_me]" type="hidden" value="0">
<input class="form-check-input" id="user_remember_me" name="user[remember_me]" type="checkbox" value="1">
<label class="form-check-label" for="user_remember_me">Remember me</label>
</div>
</div>
<div class="col">
<input class="btn btn-secondary" data-disable-with="Create User" name="commit" type="submit" value="Create User">
Expand Down Expand Up @@ -1088,17 +1090,15 @@ To use a horizontal-layout form with labels to the left of the control, use the
`layout: :horizontal` option. You should specify both `label_col` and
`control_col` css classes as well (they default to `col-sm-2` and `col-sm-10`).

In the example below, the checkbox and submit button have been wrapped in a
`form_group` to keep them properly aligned.
In the example below, the submit button has been wrapped in a `form_group` to
keep it properly aligned.

![Example 38](demo/doc/screenshots/bootstrap/readme/38_example.png "Example 38")
```erb
<%= bootstrap_form_for(@user, layout: :horizontal, label_col: "col-sm-2", control_col: "col-sm-10") do |f| %>
<%= f.email_field :email %>
<%= f.password_field :password %>
<%= f.form_group do %>
<%= f.check_box :remember_me %>
<% end %>
<%= f.check_box :remember_me %>
lcreid marked this conversation as resolved.
Show resolved Hide resolved
<%= f.form_group do %>
<%= f.submit %>
<% end %>
Expand All @@ -1110,13 +1110,13 @@ This generates:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" class="form-control" id="user_email" name="user[email]" required="required" type="email" value="[email protected]">
</div>
</div>
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2" for="user_password">Password</label>
<label class="col-form-label col-sm-2" for="user_password">Password</label>
<div class="col-sm-10">
<input class="form-control" id="user_password" name="user[password]" type="password">
</div>
Expand Down Expand Up @@ -1144,7 +1144,8 @@ The `label_col` and `control_col` css classes can also be changed per control:
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
<%= f.text_field :age, control_col: "col-sm-3" %>
<%= f.text_field :age, label_col: "col-sm-3", control_col: "col-sm-3" %>
<%= f.check_box :terms, label_col: "", control_col: "col-sm-11" %>
<%= f.form_group do %>
<%= f.submit %>
<% end %>
Expand All @@ -1156,17 +1157,26 @@ This generates:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" class="form-control" id="user_email" name="user[email]" required="required" type="email" value="[email protected]">
</div>
</div>
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2" for="user_age">Age</label>
<label class="col-form-label col-sm-3" for="user_age">Age</label>
<div class="col-sm-3">
<input class="form-control" id="user_age" name="user[age]" type="text" value="42">
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-10">
<div class="form-check">
<input autocomplete="off" name="user[terms]" type="hidden" value="0">
<input class="form-check-input" control_col="col-sm-11" id="user_terms" label_col="" name="user[terms]" type="checkbox" value="1">
<label class="form-check-label" for="user_terms">Terms</label>
</div>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-10 offset-sm-2">
<input class="btn btn-secondary" data-disable-with="Create User" name="commit" type="submit" value="Create User">
Expand Down Expand Up @@ -1213,13 +1223,13 @@ This generates:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" class="form-control" id="user_email" name="user[email]" required="required" type="email" value="[email protected]">
</div>
</div>
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2" for="user_age">Age</label>
<label class="col-form-label col-sm-2" for="user_age">Age</label>
<div class="col-sm-10 additional-control-col-class">
<input class="form-control" id="user_age" name="user[age]" type="text" value="42">
</div>
Expand All @@ -1240,11 +1250,12 @@ The form-level `layout` can be overridden per field, unless the form-level layou
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
<%= f.text_field :feet, layout: :default %>
<%= f.text_field :inches, layout: :default %>
<%= f.form_group do %>
<%= f.submit %>
<% end %>
<div class="row">
<div class="col"><%= f.text_field :feet, layout: :default %></div>
<div class="col"><%= f.text_field :inches, layout: :default %></div>
</div>
<%= f.check_box :terms, layout: :default %>
<%= f.submit %>
<% end %>
```

Expand All @@ -1253,24 +1264,33 @@ This generates:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" class="form-control" id="user_email" name="user[email]" required="required" type="email" value="[email protected]">
</div>
</div>
<div class="mb-3">
<label class="form-label" for="user_feet">Feet</label>
<input class="form-control" id="user_feet" name="user[feet]" type="text" value="5">
<div class="row">
<div class="col">
<div class="mb-3">
<label class="form-label" for="user_feet">Feet</label>
<input class="form-control" id="user_feet" name="user[feet]" type="text" value="5">
</div>
</div>
<div class="col">
<div class="mb-3">
<label class="form-label" for="user_inches">Inches</label>
<input class="form-control" id="user_inches" name="user[inches]" type="text" value="7">
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="user_inches">Inches</label>
<input class="form-control" id="user_inches" name="user[inches]" type="text" value="7">
</div>
<div class="mb-3 row">
<div class="col-sm-10 offset-sm-2">
<input class="btn btn-secondary" data-disable-with="Create User" name="commit" type="submit" value="Create User">
<div class="form-check">
<input autocomplete="off" name="user[terms]" type="hidden" value="0">
<input class="form-check-input" id="user_terms" layout="default" name="user[terms]" type="checkbox" value="1">
<label class="form-check-label" for="user_terms">Terms</label>
</div>
</div>
<input class="btn btn-secondary" data-disable-with="Create User" name="commit" type="submit" value="Create User">
</form>
```

Expand Down
Binary file modified demo/doc/screenshots/bootstrap/index/00_horizontal_form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/index/02_inline_form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/05_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/36_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/39_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/41_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions lib/bootstrap_form/components/labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ def generate_label(id, name, options, custom_label_col, group_layout)
end

def label_classes(name, options, custom_label_col, group_layout)
classes = ["form-label", options[:class], label_layout_classes(custom_label_col, group_layout)]
classes = [options[:class] || label_layout_classes(custom_label_col, group_layout)]
add_class = options.delete(:add_class)
classes.prepend(add_class) if add_class
classes << "required" if required_field_options(options, name)[:required]
options.delete(:required)
classes << "text-danger" if label_errors && error?(name)
Expand All @@ -34,7 +36,9 @@ def label_layout_classes(custom_label_col, group_layout)
if layout_horizontal?(group_layout)
["col-form-label", (custom_label_col || label_col)]
elsif layout_inline?(group_layout)
["me-sm-2"]
%w[form-label me-sm-2]
else
"form-label"
end
end

Expand Down
6 changes: 2 additions & 4 deletions lib/bootstrap_form/form_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ def form_group(*args, &block)
tag.div(**options.except(:append, :id, :label, :help, :icon,
:input_group_class, :label_col, :control_col,
:add_control_col_class, :layout, :prepend, :floating)) do
form_group_content(
generate_label(options[:id], name, options[:label], options[:label_col], options[:layout]),
generate_help(name, options[:help]), options, &block
)
label = generate_label(options[:id], name, options[:label], options[:label_col], options[:layout])
form_group_content(label, generate_help(name, options[:help]), options, &block)
end
end

Expand Down
15 changes: 13 additions & 2 deletions lib/bootstrap_form/inputs/check_box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,30 @@ module CheckBox
def check_box_with_bootstrap(name, options={}, checked_value="1", unchecked_value="0", &block)
options = options.symbolize_keys!

tag.div(class: check_box_wrapper_class(options), **options[:wrapper].to_h.except(:class)) do
content = tag.div(class: check_box_wrapper_class(options), **options[:wrapper].to_h.except(:class)) do
html = check_box_without_bootstrap(name, check_box_options(name, options), checked_value, unchecked_value)
html << check_box_label(name, options, checked_value, &block) unless options[:skip_label]
html << generate_error(name) if options[:error_message]
html
end
wrapper(content, options)
end

bootstrap_alias :check_box
end

private

def wrapper(content, options)
if layout == :inline && !options[:multiple]
tag.div(class: "col") { content }
elsif layout == :horizontal && !options[:multiple]
form_group(layout: layout_in_effect(options[:layout]), label_col: options[:label_col]) { content }
else
content
end
end

def check_box_options(name, options)
check_box_options = options.except(:class, :label, :label_class, :error_message, :help,
:inline, :hide_label, :skip_label, :wrapper, :wrapper_class, :switch)
Expand Down Expand Up @@ -71,7 +82,7 @@ def check_box_label_class(options)
def check_box_wrapper_class(options)
classes = ["form-check"]
classes << "form-check-inline" if layout_inline?(options[:inline])
classes << "mb-3" unless options[:multiple] || layout == :horizontal
classes << "mb-3" unless options[:multiple] || %i[horizontal inline].include?(layout)
classes << "form-switch" if options[:switch]
classes << options.dig(:wrapper, :class).presence
classes << options[:wrapper_class].presence
Expand Down
10 changes: 10 additions & 0 deletions lib/bootstrap_form/inputs/inputs_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module InputsCollection
private

def inputs_collection(name, collection, value, text, options={})
options[:label] ||= { class: group_label_class(options[:layout]) }
options[:inline] ||= layout_inline?(options[:layout])

form_group_builder(name, options) do
Expand All @@ -21,6 +22,15 @@ def inputs_collection(name, collection, value, text, options={})
end
end

def group_label_class(field_layout)
if layout_horizontal?(field_layout)
group_label_class = "col-form-label #{label_col} pt-0"
elsif layout_inline?(field_layout)
group_label_class = "form-check form-check-inline ps-0"
end
group_label_class
end

# FIXME: Find a way to reduce the parameter list size
# rubocop:disable Metrics/ParameterLists
def form_group_collection_input_options(options, text, obj, index, input_value, collection)
Expand Down
20 changes: 8 additions & 12 deletions test/bootstrap_checkbox_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ class BootstrapCheckboxTest < ActionView::TestCase
expected = <<~HTML
<form accept-charset="UTF-8" action="/users" class="new_user row row-cols-auto g-3 align-items-center" id="new_user" method="post">
#{'<input name="utf8" type="hidden" value="&#x2713;"/>' unless ::Rails::VERSION::STRING >= '6'}
<div class="form-check form-check-inline mb-3">
<input #{autocomplete_attr} name="user[terms]" type="hidden" value="0" />
<input class="form-check-input" id="user_terms" name="user[terms]" type="checkbox" value="1" />
<label class="form-check-label" for="user_terms">
I agree to the terms
</label>
<div class="col">
<div class="form-check form-check-inline">
<input #{autocomplete_attr} name="user[terms]" type="hidden" value="0" />
<input class="form-check-input" id="user_terms" name="user[terms]" type="checkbox" value="1" />
<label class="form-check-label" for="user_terms">I agree to the terms</label>
</div>
</div>
</form>
HTML
Expand Down Expand Up @@ -218,15 +218,11 @@ class BootstrapCheckboxTest < ActionView::TestCase
<label class="form-label" for="user_misc">Misc</label>
<div class="form-check">
<input class="form-check-input" id="user_misc_1" name="user[misc][]" type="checkbox" value="1" />
<label class="form-check-label" for="user_misc_1">
Foo
</label>
<label class="form-check-label" for="user_misc_1">Foo</label>
</div>
<div class="form-check">
<input class="form-check-input" id="user_misc_二" name="user[misc][]" type="checkbox" value="二" />
<label class="form-check-label" for="user_misc_二">
Bar
</label>
<label class="form-check-label" for="user_misc_二">Bar</label>
</div>
</div>
HTML
Expand Down
10 changes: 5 additions & 5 deletions test/bootstrap_fields_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ class BootstrapFieldsTest < ActionView::TestCase
test "text fields are wrapped correctly when horizontal and gutter classes are given" do
expected = <<~HTML
<div class="mb-3 g-3">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
Expand All @@ -231,7 +231,7 @@ class BootstrapFieldsTest < ActionView::TestCase
test "text fields are wrapped correctly when horizontal and multiple wrapper classes specified" do
expected = <<~HTML
<div class="bogus-2 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
Expand All @@ -244,7 +244,7 @@ class BootstrapFieldsTest < ActionView::TestCase
test "text fields are wrapped correctly when horizontal and wrapper class specified" do
expected = <<~HTML
<div class="bogus-1 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
Expand All @@ -256,7 +256,7 @@ class BootstrapFieldsTest < ActionView::TestCase
test "text fields are wrapped correctly when horizontal and multiple wrapper classes specified (reverse order)" do
expected = <<~HTML
<div class="bogus-2 row">
<label class="form-label col-form-label col-sm-2 required" for="user_email">Email</label>
<label class="col-form-label col-sm-2 required" for="user_email">Email</label>
<div class="col-sm-10">
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
Expand Down Expand Up @@ -383,7 +383,7 @@ class BootstrapFieldsTest < ActionView::TestCase
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
#{'<input name="utf8" type="hidden" value="&#x2713;"/>' unless ::Rails::VERSION::STRING >= '6'}
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2" for="user_address_attributes_street">Street</label>
<label class="col-form-label col-sm-2" for="user_address_attributes_street">Street</label>
<div class="col-sm-10">
<input class="form-control" id="user_address_attributes_street" name="user[address_attributes][street]" type="text" value="123 Main Street" />
</div>
Expand Down
Loading