Skip to content

Commit 31b5e84

Browse files
authored
Add classes to the div.form-check that wraps check boxes and radio buttons. (bootstrap-ruby#479)
* Rename div_class to make refactoring more obvious. * Fix issue bootstrap-ruby#476 for check_boxes. * Fix issue bootstrap-ruby#476 for radio buttons. * Test for `:custom` option just once. * Add wrapper_class for check boxes and radio buttons.
1 parent 83b01ab commit 31b5e84

File tree

5 files changed

+142
-75
lines changed

5 files changed

+142
-75
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
### New features
88

9+
* [#476] Give a way to pass classes to the `div.form-check` wrapper for check boxes and radio buttons - [@lcreid](https://github.com/lcreid).
910
* [461](https://github.com/bootstrap-ruby/bootstrap_form/pull/461): default form-inline class applied to parent content div on date select helpers. Can override with a :skip_inline option on the field helper - [@lancecarlson](https://github.com/lancecarlson).
1011
* Your contribution here!
1112
* The `button`, `submit`, and `primary` helpers can now receive an additional option, `extra_class`. This option allows us to specify additional CSS classes to be added to the corresponding button/input, _while_ maintaining the original default ones. E.g., a primary button with an `extra_class` 'test-button' will have its final CSS classes declaration as 'btn btn-primary test-button'.

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,15 @@ To display checkboxes and radios inline, pass the `inline: true` option:
348348
<% end %>
349349
```
350350

351+
Check boxes and radio buttons are wrapped in a `div.form-check`. You can add classes to this `div` with the `:wrapper_class` option:
352+
353+
```erb
354+
<%= f.radio_button :skill_level, 0, label: "Novice", inline: true, wrapper_class: "w-auto" %>
355+
```
356+
351357
#### Collections
352358

353-
`bootstrap_form` also provides helpers that automatically creates the
359+
`bootstrap_form` also provides helpers that automatically create the
354360
`form_group` and the `radio_button`s or `check_box`es for you:
355361

356362
```erb

lib/bootstrap_form/form_builder.rb

+42-74
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,24 @@ def time_zone_select_with_bootstrap(method, priority_zones = nil, options = {},
130130

131131
def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_value = "0", &block)
132132
options = options.symbolize_keys!
133-
check_box_options = options.except(:label, :label_class, :error_message, :help, :inline, :custom, :hide_label, :skip_label)
133+
check_box_options = options.except(:label, :label_class, :error_message, :help, :inline, :custom, :hide_label, :skip_label, :wrapper_class)
134134
check_box_classes = [check_box_options[:class]]
135135
check_box_classes << "position-static" if options[:skip_label] || options[:hide_label]
136136
check_box_classes << "is-invalid" if has_error?(name)
137+
138+
label_classes = [options[:label_class]]
139+
label_classes << hide_class if options[:hide_label]
140+
137141
if options[:custom]
138142
check_box_options[:class] = (["custom-control-input"] + check_box_classes).compact.join(' ')
143+
wrapper_class = ["custom-control", "custom-checkbox"]
144+
wrapper_class.append("custom-control-inline") if layout_inline?(options[:inline])
145+
label_class = label_classes.prepend("custom-control-label").compact.join(" ")
139146
else
140147
check_box_options[:class] = (["form-check-input"] + check_box_classes).compact.join(' ')
148+
wrapper_class = ["form-check"]
149+
wrapper_class.append("form-check-inline") if layout_inline?(options[:inline])
150+
label_class = label_classes.prepend("form-check-label").compact.join(" ")
141151
end
142152

143153
checkbox_html = check_box_without_bootstrap(name, check_box_options, checked_value, unchecked_value)
@@ -153,103 +163,61 @@ def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_
153163
"#{name}_#{checked_value.to_s.gsub(/\s/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s}"
154164
end
155165

156-
label_classes = [options[:label_class]]
157-
label_classes << hide_class if options[:hide_label]
158-
159-
if options[:custom]
160-
div_class = ["custom-control", "custom-checkbox"]
161-
div_class.append("custom-control-inline") if layout_inline?(options[:inline])
162-
label_class = label_classes.prepend("custom-control-label").compact.join(" ")
166+
label_options = { class: label_class }
167+
label_options[:for] = options[:id] if options[:id].present?
163168

164-
label_options = { class: label_class }
165-
label_options[:for] = options[:id] if options[:id].present?
169+
wrapper_class.append(options[:wrapper_class]) if options[:wrapper_class]
166170

167-
content_tag(:div, class: div_class.compact.join(" ")) do
168-
html = if options[:skip_label]
169-
checkbox_html
170-
else
171-
checkbox_html
172-
.concat(label(label_name, label_description, label_options))
173-
end
174-
html.concat(generate_error(name)) if options[:error_message]
175-
html
176-
end
177-
else
178-
wrapper_class = "form-check"
179-
wrapper_class += " form-check-inline" if layout_inline?(options[:inline])
180-
label_class = label_classes.prepend("form-check-label").compact.join(" ")
181-
182-
label_options = { class: label_class }
183-
label_options[:for] = options[:id] if options[:id].present?
184-
185-
content_tag(:div, class: wrapper_class) do
186-
html = if options[:skip_label]
187-
checkbox_html
188-
else
189-
checkbox_html
190-
.concat(label(label_name, label_description, label_options))
191-
end
192-
html.concat(generate_error(name)) if options[:error_message]
193-
html
171+
content_tag(:div, class: wrapper_class.compact.join(" ")) do
172+
html = if options[:skip_label]
173+
checkbox_html
174+
else
175+
checkbox_html.concat(label(label_name, label_description, label_options))
194176
end
177+
html.concat(generate_error(name)) if options[:error_message]
178+
html
195179
end
196180
end
197181

198182
bootstrap_method_alias :check_box
199183

200184
def radio_button_with_bootstrap(name, value, *args)
201185
options = args.extract_options!.symbolize_keys!
202-
radio_options = options.except(:label, :label_class, :error_message, :help, :inline, :custom, :hide_label, :skip_label)
186+
radio_options = options.except(:label, :label_class, :error_message, :help, :inline, :custom, :hide_label, :skip_label, :wrapper_class)
203187
radio_classes = [options[:class]]
204188
radio_classes << "position-static" if options[:skip_label] || options[:hide_label]
205189
radio_classes << "is-invalid" if has_error?(name)
206-
if options[:custom]
207-
radio_options[:class] = radio_classes.prepend("custom-control-input").compact.join(' ')
208-
else
209-
radio_options[:class] = radio_classes.prepend("form-check-input").compact.join(' ')
210-
end
211-
radio_html = radio_button_without_bootstrap(name, value, radio_options)
212190

213-
disabled_class = " disabled" if options[:disabled]
214191
label_classes = [options[:label_class]]
215192
label_classes << hide_class if options[:hide_label]
216193

217194
if options[:custom]
218-
div_class = ["custom-control", "custom-radio"]
219-
div_class.append("custom-control-inline") if layout_inline?(options[:inline])
195+
radio_options[:class] = radio_classes.prepend("custom-control-input").compact.join(' ')
196+
wrapper_class = ["custom-control", "custom-radio"]
197+
wrapper_class.append("custom-control-inline") if layout_inline?(options[:inline])
220198
label_class = label_classes.prepend("custom-control-label").compact.join(" ")
221-
222-
label_options = { value: value, class: label_class }
223-
label_options[:for] = options[:id] if options[:id].present?
224-
225-
content_tag(:div, class: div_class.compact.join(" ")) do
226-
html = if options[:skip_label]
227-
radio_html
228-
else
229-
radio_html
230-
.concat(label(name, options[:label], label_options))
231-
end
232-
html.concat(generate_error(name)) if options[:error_message]
233-
html
234-
end
235199
else
236-
wrapper_class = "form-check"
237-
wrapper_class += " form-check-inline" if layout_inline?(options[:inline])
200+
radio_options[:class] = radio_classes.prepend("form-check-input").compact.join(' ')
201+
wrapper_class = ["form-check"]
202+
wrapper_class.append("form-check-inline") if layout_inline?(options[:inline])
203+
wrapper_class.append("disabled") if options[:disabled]
238204
label_class = label_classes.prepend("form-check-label").compact.join(" ")
205+
end
206+
radio_html = radio_button_without_bootstrap(name, value, radio_options)
239207

240-
label_options = { value: value, class: label_class }
241-
label_options[:for] = options[:id] if options[:id].present?
208+
label_options = { value: value, class: label_class }
209+
label_options[:for] = options[:id] if options[:id].present?
242210

243-
content_tag(:div, class: "#{wrapper_class}#{disabled_class}") do
244-
html = if options[:skip_label]
245-
radio_html
246-
else
247-
radio_html
248-
.concat(label(name, options[:label], label_options))
249-
end
250-
html.concat(generate_error(name)) if options[:error_message]
251-
html
211+
wrapper_class.append(options[:wrapper_class]) if options[:wrapper_class]
212+
213+
content_tag(:div, class: wrapper_class.compact.join(" ")) do
214+
html = if options[:skip_label]
215+
radio_html
216+
else
217+
radio_html.concat(label(name, options[:label], label_options))
252218
end
219+
html.concat(generate_error(name)) if options[:error_message]
220+
html
253221
end
254222
end
255223

test/bootstrap_checkbox_test.rb

+48
Original file line numberDiff line numberDiff line change
@@ -658,4 +658,52 @@ class BootstrapCheckboxTest < ActionView::TestCase
658658
end
659659
assert_equivalent_xml expected, actual
660660
end
661+
662+
test "check box with custom wrapper class" do
663+
expected = <<-HTML.strip_heredoc
664+
<div class="form-check custom-class">
665+
<input name="user[terms]" type="hidden" value="0" />
666+
<input class="form-check-input" id="user_terms" name="user[terms]" type="checkbox" value="1" />
667+
<label class="form-check-label" for="user_terms">
668+
I agree to the terms
669+
</label>
670+
</div>
671+
HTML
672+
assert_equivalent_xml expected, @builder.check_box(:terms, label: 'I agree to the terms', wrapper_class: "custom-class")
673+
end
674+
675+
test "inline check box with custom wrapper class" do
676+
expected = <<-HTML.strip_heredoc
677+
<div class="form-check form-check-inline custom-class">
678+
<input name="user[terms]" type="hidden" value="0" />
679+
<input class="form-check-input" id="user_terms" name="user[terms]" type="checkbox" value="1" />
680+
<label class="form-check-label" for="user_terms">
681+
I agree to the terms
682+
</label>
683+
</div>
684+
HTML
685+
assert_equivalent_xml expected, @builder.check_box(:terms, label: 'I agree to the terms', inline: true, wrapper_class: "custom-class")
686+
end
687+
688+
test "custom check box with custom wrapper class" do
689+
expected = <<-HTML.strip_heredoc
690+
<div class="custom-control custom-checkbox custom-class">
691+
<input name="user[terms]" type="hidden" value="0" />
692+
<input class="custom-control-input" id="user_terms" name="user[terms]" type="checkbox" value="1" />
693+
<label class="custom-control-label" for="user_terms">I agree to the terms</label>
694+
</div>
695+
HTML
696+
assert_equivalent_xml expected, @builder.check_box(:terms, {label: 'I agree to the terms', custom: true, wrapper_class: "custom-class"})
697+
end
698+
699+
test "custom inline check box with custom wrapper class" do
700+
expected = <<-HTML.strip_heredoc
701+
<div class="custom-control custom-checkbox custom-control-inline custom-class">
702+
<input name="user[terms]" type="hidden" value="0" />
703+
<input class="custom-control-input" id="user_terms" name="user[terms]" type="checkbox" value="1" />
704+
<label class="custom-control-label" for="user_terms">I agree to the terms</label>
705+
</div>
706+
HTML
707+
assert_equivalent_xml expected, @builder.check_box(:terms, {label: 'I agree to the terms', inline: true, custom: true, wrapper_class: "custom-class"})
708+
end
661709
end

test/bootstrap_radio_button_test.rb

+44
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,48 @@ class BootstrapRadioButtonTest < ActionView::TestCase
484484
HTML
485485
assert_equivalent_xml expected, @builder.radio_button(:misc, '1', {label: 'This is a radio button', custom: true, hide_label: true})
486486
end
487+
488+
test "radio button with custom wrapper class" do
489+
expected = <<-HTML.strip_heredoc
490+
<div class="form-check custom-class">
491+
<input class="form-check-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
492+
<label class="form-check-label" for="user_misc_1">
493+
This is a radio button
494+
</label>
495+
</div>
496+
HTML
497+
assert_equivalent_xml expected, @builder.radio_button(:misc, '1', label: 'This is a radio button', wrapper_class: "custom-class")
498+
end
499+
500+
test "inline radio button with custom wrapper class" do
501+
expected = <<-HTML.strip_heredoc
502+
<div class="form-check form-check-inline custom-class">
503+
<input class="form-check-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
504+
<label class="form-check-label" for="user_misc_1">
505+
This is a radio button
506+
</label>
507+
</div>
508+
HTML
509+
assert_equivalent_xml expected, @builder.radio_button(:misc, '1', label: 'This is a radio button', inline: true, wrapper_class: "custom-class")
510+
end
511+
512+
test "custom radio button with custom wrapper class" do
513+
expected = <<-HTML.strip_heredoc
514+
<div class="custom-control custom-radio custom-class">
515+
<input class="custom-control-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
516+
<label class="custom-control-label" for="user_misc_1">This is a radio button</label>
517+
</div>
518+
HTML
519+
assert_equivalent_xml expected, @builder.radio_button(:misc, '1', {label: 'This is a radio button', custom: true, wrapper_class: "custom-class"})
520+
end
521+
522+
test "custom inline radio button with custom wrapper class" do
523+
expected = <<-HTML.strip_heredoc
524+
<div class="custom-control custom-radio custom-control-inline custom-class">
525+
<input class="custom-control-input" id="user_misc_1" name="user[misc]" type="radio" value="1" />
526+
<label class="custom-control-label" for="user_misc_1">This is a radio button</label>
527+
</div>
528+
HTML
529+
assert_equivalent_xml expected, @builder.radio_button(:misc, '1', {label: 'This is a radio button', inline: true, custom: true, wrapper_class: "custom-class"})
530+
end
487531
end

0 commit comments

Comments
 (0)