Skip to content

Commit

Permalink
Improve checkbox layout
Browse files Browse the repository at this point in the history
Indent checkbox into second column for horizontal layout
Align label with the top of the first check box in a collection in horizontal layout
Vertically align check boxes and labels in inline layout.
Allow overriding the label class, not just add more classes.
  • Loading branch information
donv committed Aug 14, 2023
1 parent 5ba0e87 commit 6863c9c
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 53 deletions.
24 changes: 12 additions & 12 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 @@ -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 me-sm-2 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 me-sm-2" 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 @@ -1096,9 +1098,7 @@ In the example below, the checkbox and submit button have been wrapped in a
<%= 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 %>
<%= f.form_group do %>
<%= f.submit %>
<% end %>
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.
2 changes: 1 addition & 1 deletion lib/bootstrap_form/components/labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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] || "form-label", label_layout_classes(custom_label_col, group_layout)]
classes << "required" if required_field_options(options, name)[:required]
options.delete(:required)
classes << "text-danger" if label_errors && error?(name)
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 { 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[: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
if layout_horizontal?
group_label_class = "col-form-label pt-0"
elsif layout_inline?
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
12 changes: 6 additions & 6 deletions test/bootstrap_form_group_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class BootstrapFormGroupTest < ActionView::TestCase
test "hiding a label" do
expected = <<~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" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
HTML
Expand All @@ -38,7 +38,7 @@ class BootstrapFormGroupTest < ActionView::TestCase
test "adding a custom label class via the label_class parameter" do
expected = <<~HTML
<div class="mb-3">
<label class="form-label btn required" for="user_email">Email</label>
<label class="btn required" for="user_email">Email</label>
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
HTML
Expand All @@ -48,7 +48,7 @@ class BootstrapFormGroupTest < ActionView::TestCase
test "adding a custom label class via the html_options label hash" do
expected = <<~HTML
<div class="mb-3">
<label class="form-label btn required" for="user_email">Email</label>
<label class="btn required" for="user_email">Email</label>
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
HTML
Expand All @@ -58,7 +58,7 @@ class BootstrapFormGroupTest < ActionView::TestCase
test "adding a custom label and changing the label text via the html_options label hash" do
expected = <<~HTML
<div class="mb-3">
<label class="form-label btn required" for="user_email">Email Address</label>
<label class="btn required" for="user_email">Email Address</label>
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="text" value="[email protected]" />
</div>
HTML
Expand Down Expand Up @@ -109,7 +109,7 @@ class BootstrapFormGroupTest < ActionView::TestCase
test "label as placeholder" do
expected = <<~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" required="required" class="form-control" id="user_email" placeholder="Email" name="user[email]" type="text" value="[email protected]" />
</div>
HTML
Expand Down Expand Up @@ -379,7 +379,7 @@ class BootstrapFormGroupTest < ActionView::TestCase

expected = <<~HTML
<div class="mb-3 row">
<label class="form-label foo col-form-label col-sm-2" for="bar">Custom Control</label>
<label class="foo col-form-label col-sm-2" for="bar">Custom Control</label>
<div class="col-sm-10">
<input class="form-control-plaintext" value="Bar">
</div>
Expand Down
54 changes: 34 additions & 20 deletions test/bootstrap_form_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,15 @@ class BootstrapFormTest < ActionView::TestCase
<label class="form-label me-sm-2 required" for="user_email">Email</label>
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="email" value="[email protected]" />
</div>
<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>
<div class="col">
<label class="form-label me-sm-2" for="user_misc">Misc</label>
<label class="form-check form-check-inline ps-0 me-sm-2" for="user_misc">Misc</label>
<div class="form-check form-check-inline">
<input class="form-check-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
<label class="form-check-label" for="user_misc_1">Foo</label>
Expand Down Expand Up @@ -179,13 +181,17 @@ class BootstrapFormTest < ActionView::TestCase
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="email" value="[email protected]" />
</div>
</div>
<div class="form-check">
<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="mb-3 row">
<div class="col-sm-10 offset-sm-2">
<div class="form-check">
<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>
</div>
<div class="mb-3 row">
<label class="form-label col-form-label col-sm-2" for="user_misc">Misc</label>
<label class="col-form-label pt-0 col-form-label col-sm-2" for="user_misc">Misc</label>
<div class="col-sm-10">
<div class="form-check">
<input class="form-check-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
Expand Down Expand Up @@ -228,13 +234,17 @@ class BootstrapFormTest < ActionView::TestCase
<label class="form-label required" for="user_email">Email</label>
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="email" value="[email protected]" />
</div>
<div class="form-check">
<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="mb-3 row">
<div class="col-sm-10 offset-sm-2">
<div class="form-check">
<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>
</div>
<div class="mb-3">
<label class="form-label" for="user_misc">Misc</label>
<label class="col-form-label pt-0" for="user_misc">Misc</label>
<div class="form-check">
<input class="form-check-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
<label class="form-check-label" for="user_misc_1">Foo</label>
Expand Down Expand Up @@ -274,13 +284,17 @@ class BootstrapFormTest < ActionView::TestCase
<label class="form-label me-sm-2 required" for="user_email">Email</label>
<input aria-required="true" required="required" class="form-control" id="user_email" name="user[email]" type="email" value="[email protected]" />
</div>
<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 class="mb-3 row">
<div class="col-sm-10 offset-sm-2">
<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>
</div>
<div class="mb-3 col-auto g-3">
<label class="form-label me-sm-2" for="user_misc">Misc</label>
<label class="col-form-label pt-0 me-sm-2" for="user_misc">Misc</label>
<div class="form-check form-check-inline">
<input class="form-check-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
<label class="form-check-label" for="user_misc_1">Foo</label>
Expand Down

0 comments on commit 6863c9c

Please sign in to comment.