diff --git a/demo/test/system/bootstrap_test.rb b/demo/test/system/bootstrap_test.rb
index b55681ff..cb81ecc7 100644
--- a/demo/test/system/bootstrap_test.rb
+++ b/demo/test/system/bootstrap_test.rb
@@ -4,7 +4,14 @@
require "capybara_screenshot_diff/minitest"
class BootstrapTest < ApplicationSystemTestCase
- setup { screenshot_section :bootstrap }
+ setup do
+ screenshot_section :bootstrap
+ Rails.application.config.bootstrap_form.group_around_collections = true
+ end
+
+ teardown do
+ Rails.application.config.bootstrap_form.group_around_collections = false
+ end
test "visiting the index" do
screenshot_group :index
diff --git a/lib/bootstrap_form/components/labels.rb b/lib/bootstrap_form/components/labels.rb
index 579957ed..d8f03655 100644
--- a/lib/bootstrap_form/components/labels.rb
+++ b/lib/bootstrap_form/components/labels.rb
@@ -10,16 +10,8 @@ module Labels
def generate_label(id, name, options, custom_label_col, group_layout)
return if options.blank?
- # id is the caller's options[:id] at the only place this method is called.
- # The options argument is a small subset of the options that might have
- # been passed to generate_label's caller, and definitely doesn't include
- # :id.
- options[:for] = id if acts_like_form_tag
-
- options[:class] = label_classes(name, options, custom_label_col, group_layout)
- options.delete(:class) if options[:class].none?
-
- label(name, label_text(name, options), options.except(:text))
+ prepare_label_options(id, name, options, custom_label_col, group_layout)
+ label(name, label_text(name, options[:text]), options.except(:text))
end
def label_classes(name, options, custom_label_col, group_layout)
@@ -42,14 +34,25 @@ def label_layout_classes(custom_label_col, group_layout)
end
end
- def label_text(name, options)
- label = options[:text] || object&.class&.try(:human_attribute_name, name)&.html_safe # rubocop:disable Rails/OutputSafety, Style/SafeNavigationChainLength
+ def label_text(name, text)
+ label = text || object&.class&.try(:human_attribute_name, name)&.html_safe # rubocop:disable Rails/OutputSafety, Style/SafeNavigationChainLength
if label_errors && error?(name)
(" ".html_safe + get_error_messages(name)).prepend(label)
else
label
end
end
+
+ def prepare_label_options(id, name, options, custom_label_col, group_layout)
+ # id is the caller's options[:id] at the only place this method is called.
+ # The options argument is a small subset of the options that might have
+ # been passed to generate_label's caller, and definitely doesn't include
+ # :id.
+ options[:for] = id if acts_like_form_tag
+
+ options[:class] = label_classes(name, options, custom_label_col, group_layout)
+ options.delete(:class) if options[:class].none?
+ end
end
end
end
diff --git a/lib/bootstrap_form/engine.rb b/lib/bootstrap_form/engine.rb
index 8f7c7151..33784878 100644
--- a/lib/bootstrap_form/engine.rb
+++ b/lib/bootstrap_form/engine.rb
@@ -9,6 +9,7 @@ class Engine < Rails::Engine
config.bootstrap_form = BootstrapForm.config
config.bootstrap_form.default_form_attributes ||= {}
+ config.bootstrap_form.group_around_collections = Rails.env.development? if config.bootstrap_form.group_around_collections.nil?
initializer "bootstrap_form.configure" do |app|
BootstrapForm.config = app.config.bootstrap_form
diff --git a/lib/bootstrap_form/form_group_builder.rb b/lib/bootstrap_form/form_group_builder.rb
index 79eff7e5..36e7155a 100644
--- a/lib/bootstrap_form/form_group_builder.rb
+++ b/lib/bootstrap_form/form_group_builder.rb
@@ -7,6 +7,16 @@ module FormGroupBuilder
private
def form_group_builder(method, options, html_options=nil, &)
+ form_group_builder_wrapper(method, options, html_options) do |form_group_options, no_wrapper|
+ if no_wrapper
+ yield
+ else
+ form_group(method, form_group_options, &)
+ end
+ end
+ end
+
+ def form_group_builder_wrapper(method, options, html_options=nil)
no_wrapper = options[:wrapper] == false
options = form_group_builder_options(options, method)
@@ -18,11 +28,7 @@ def form_group_builder(method, options, html_options=nil, &)
:hide_label, :skip_required, :label_as_placeholder, :wrapper_class, :wrapper
)
- if no_wrapper
- yield
- else
- form_group(method, form_group_options, &)
- end
+ yield(form_group_options, no_wrapper)
end
def form_group_builder_options(options, method)
diff --git a/lib/bootstrap_form/inputs/inputs_collection.rb b/lib/bootstrap_form/inputs/inputs_collection.rb
index 0da18b60..2b5dc808 100644
--- a/lib/bootstrap_form/inputs/inputs_collection.rb
+++ b/lib/bootstrap_form/inputs/inputs_collection.rb
@@ -7,24 +7,20 @@ module InputsCollection
private
- def inputs_collection(name, collection, value, text, options={})
+ def inputs_collection(name, collection, value, text, options={}, &)
options[:label] ||= { class: group_label_class(field_layout(options)) }
options[:inline] ||= layout_inline?(options[:layout])
- form_group_builder(name, options) do
- inputs = ActiveSupport::SafeBuffer.new
-
- collection.each_with_index do |obj, i|
- input_value = value.respond_to?(:call) ? value.call(obj) : obj.send(value)
- input_options = form_group_collection_input_options(options, text, obj, i, input_value, collection)
- inputs << yield(name, input_value, input_options)
- end
+ return group_inputs_collection(name, collection, value, text, options, &) if BootstrapForm.config.group_around_collections
- inputs
+ form_group_builder(name, options) do
+ render_collection(name, collection, value, text, options, &)
end
end
- def field_layout(options) = options[:layout] || (:inline if options[:inline] == true)
+ def field_layout(options)
+ (:inline if options[:inline] == true) || options[:layout]
+ end
def group_label_class(field_layout)
if layout_horizontal?(field_layout)
@@ -56,6 +52,63 @@ def form_group_collection_input_checked?(checked, obj, input_value)
checked == input_value || Array(checked).try(:include?, input_value) ||
checked == obj || Array(checked).try(:include?, obj)
end
+
+ def group_inputs_collection(name, collection, value, text, options={}, &)
+ group_builder(name, options) do
+ render_collection(name, collection, value, text, options, &)
+ end
+ end
+
+ def render_collection(name, collection, value, text, options={}, &)
+ inputs = ActiveSupport::SafeBuffer.new
+
+ collection.each_with_index do |obj, i|
+ input_value = value.respond_to?(:call) ? value.call(obj) : obj.send(value)
+ input_options = form_group_collection_input_options(options, text, obj, i, input_value, collection)
+ inputs << yield(name, input_value, input_options)
+ end
+
+ inputs
+ end
+
+ def group_builder(method, options, html_options=nil, &)
+ form_group_builder_wrapper(method, options, html_options) do |form_group_options, no_wrapper|
+ if no_wrapper
+ yield
+ else
+ field_group(method, form_group_options, &)
+ end
+ end
+ end
+
+ def field_group(name, options, &)
+ options[:class] = form_group_classes(options)
+
+ tag.div(
+ **options.except(
+ :add_control_col_class, :append, :control_col, :floating, :help, :icon, :id,
+ :input_group_class, :label, :label_col, :layout, :prepend
+ ),
+ aria: { labelledby: options[:id] || default_id(name) },
+ role: :group
+ ) do
+ group_label_div = generate_group_label_div(name, options)
+ prepare_label_options(options[:id], name, options[:label], options[:label_col], options[:layout])
+ form_group_content(group_label_div, generate_help(name, options[:help]), options, &)
+ end
+ end
+
+ def generate_group_label_div(name, options)
+ group_label_div_class = options.dig(:label, :class) || "form-label"
+ id = options[:id] || default_id(name)
+
+ tag.div(
+ **{ class: group_label_div_class }.compact,
+ id:
+ ) { label_text(name, options.dig(:label, :text)) }
+ end
+
+ def default_id(name) = raw("#{object_name}_#{name}") # rubocop:disable Rails/OutputSafety
end
end
end
diff --git a/test/bootstrap_checkbox_test.rb b/test/bootstrap_checkbox_test.rb
index 48a8bb94..121ba276 100644
--- a/test/bootstrap_checkbox_test.rb
+++ b/test/bootstrap_checkbox_test.rb
@@ -168,350 +168,6 @@ class BootstrapCheckboxTest < ActionView::TestCase
assert_equivalent_html expected, @builder.check_box(:terms, inline: true, label_class: "btn")
end
- test "collection_check_boxes renders the form_group correctly" do
- collection = [Address.new(id: 1, street: "Foobar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
- label: "This is a checkbox collection", help: "With a help!")
- end
-
- if Rails::VERSION::MAJOR >= 8
- test "collection_checkboxes renders the form_group correctly" do
- collection = [Address.new(id: 1, street: "Foobar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_checkboxes(:misc, collection, :id, :street,
- label: "This is a checkbox collection", help: "With a help!")
- end
- end
-
- test "collection_check_boxes renders multiple checkboxes correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street)
- end
-
- test "collection_check_boxes renders multiple checkboxes contains unicode characters in IDs correctly" do
- struct = Struct.new(:id, :name)
- collection = [struct.new(1, "Foo"), struct.new("二", "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :name)
- end
-
- test "collection_check_boxes renders inline checkboxes correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street, inline: true)
- end
-
- test "collection_check_boxes renders with checked option correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
- checked: 1)
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
- checked: collection.first)
- end
-
- test "collection_check_boxes renders with multiple checked options correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
- checked: [1, 2])
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
- checked: collection)
- end
-
- test "collection_check_boxes sanitizes values when generating label `for`" do
- collection = [Address.new(id: 1, street: "Foo St")]
- expected = <<~HTML
-
- HTML
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :street, :street)
- end
-
- test "collection_check_boxes renders multiple checkboxes with labels defined by Proc :text_method correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, proc { |a| a.street.reverse })
- end
-
- test "collection_check_boxes renders multiple checkboxes with values defined by Proc :value_method correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
- :street)
- end
-
- test "collection_check_boxes renders multiple checkboxes with labels defined by lambda :text_method correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, ->(a) { a.street.reverse })
- end
-
- test "collection_check_boxes renders multiple checkboxes with values defined by lambda :value_method correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
- :street)
- end
-
- test "collection_check_boxes renders with checked option correctly with Proc :value_method" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
- :street, checked: "address_1")
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
- :street, checked: collection.first)
- end
-
- test "collection_check_boxes renders with multiple checked options correctly with lambda :value_method" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
- :street, checked: %w[address_1 address_2])
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
- :street, checked: collection)
- end
-
- test "collection_check_boxes renders with include_hidden options correctly" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street, include_hidden: false)
- end
-
test "check_box skip label" do
expected = <<~HTML
@@ -533,84 +189,6 @@ class BootstrapCheckboxTest < ActionView::TestCase
assert_equivalent_html expected, @builder.check_box(:terms, label: "I agree to the terms", hide_label: true)
end
- test "collection_check_boxes renders error after last check box" do
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- @user.errors.add(:misc, "a box must be checked")
-
- expected = <<~HTML
-
- HTML
-
- actual = bootstrap_form_for(@user) do |f|
- f.collection_check_boxes(:misc, collection, :id, :street)
- end
-
- assert_equivalent_html expected, actual
- end
-
- test "collection_check_boxes renders data attributes" do
- collection = [
- ["1", "Foo", { "data-city": "east" }],
- ["2", "Bar", { "data-city": "west" }]
- ]
- expected = <<~HTML
-
-
Misc
-
-
- Foo
-
-
-
- Bar
-
-
- HTML
-
- assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :first, :second, include_hidden: false)
- end
-
- test "collection_check_boxes renders multiple check boxes with error correctly" do
- @user.errors.add(:misc, "error for test")
- collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
- expected = <<~HTML
-
- HTML
-
- actual = bootstrap_form_for(@user) do |f|
- f.collection_check_boxes(:misc, collection, :id, :street, checked: collection)
- end
- assert_equivalent_html expected, actual
- end
-
test "check_box renders error when asked" do
@user.errors.add(:terms, "You must accept the terms.")
expected = <<~HTML
diff --git a/test/bootstrap_collection_checkboxes_test.rb b/test/bootstrap_collection_checkboxes_test.rb
new file mode 100644
index 00000000..667b3008
--- /dev/null
+++ b/test/bootstrap_collection_checkboxes_test.rb
@@ -0,0 +1,873 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+class BootstrapCollectionCheckboxesTest < ActionView::TestCase
+ include BootstrapForm::ActionViewExtensions::FormHelper
+
+ setup do
+ setup_test_fixture
+ Rails.application.config.bootstrap_form.group_around_collections = true
+ end
+
+ teardown do
+ Rails.application.config.bootstrap_form.group_around_collections = false
+ end
+
+ test "collection_check_boxes renders the form_group correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
+
This is a checkbox collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ label: "This is a checkbox collection", help: "With a help!")
+ end
+
+ if Rails::VERSION::MAJOR >= 8
+ test "collection_checkboxes renders the form_group correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
+
This is a checkbox collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_checkboxes(:misc, collection, :id, :street,
+ label: "This is a checkbox collection", help: "With a help!")
+ end
+ end
+
+ test "collection_check_boxes renders multiple checkboxes correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street)
+ end
+
+ test "collection_check_boxes renders multiple checkboxes contains unicode characters in IDs correctly" do
+ struct = Struct.new(:id, :name)
+ collection = [struct.new(1, "Foo"), struct.new("二", "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :name)
+ end
+
+ test "collection_check_boxes renders inline checkboxes correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street, inline: true)
+ end
+
+ test "collection_check_boxes renders with checked option correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: 1)
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: collection.first)
+ end
+
+ test "collection_check_boxes renders with multiple checked options correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: [1, 2])
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: collection)
+ end
+
+ test "collection_check_boxes sanitizes values when generating label `for`" do
+ collection = [Address.new(id: 1, street: "Foo St")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo St
+
+
+
+ HTML
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :street, :street)
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with labels defined by Proc :text_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ ooF
+
+
+
+
+
+ raB
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, proc { |a| a.street.reverse })
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with values defined by Proc :value_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street)
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with labels defined by lambda :text_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ ooF
+
+
+
+
+
+ raB
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, ->(a) { a.street.reverse })
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with values defined by lambda :value_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street)
+ end
+
+ test "collection_check_boxes renders with checked option correctly with Proc :value_method" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street, checked: "address_1")
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street, checked: collection.first)
+ end
+
+ test "collection_check_boxes renders with multiple checked options correctly with lambda :value_method" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street, checked: %w[address_1 address_2])
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street, checked: collection)
+ end
+
+ test "collection_check_boxes renders with include_hidden options correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street, include_hidden: false)
+ end
+
+ test "collection_check_boxes renders error after last check box" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ @user.errors.add(:misc, "a box must be checked")
+
+ expected = <<~HTML
+
+ HTML
+
+ actual = bootstrap_form_for(@user) do |f|
+ f.collection_check_boxes(:misc, collection, :id, :street)
+ end
+
+ assert_equivalent_html expected, actual
+ end
+
+ test "collection_check_boxes renders data attributes" do
+ collection = [
+ ["1", "Foo", { "data-city": "east" }],
+ ["2", "Bar", { "data-city": "west" }]
+ ]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :first, :second, include_hidden: false)
+ end
+
+ test "collection_check_boxes renders multiple check boxes with error correctly" do
+ @user.errors.add(:misc, "error for test")
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+ HTML
+
+ actual = bootstrap_form_for(@user) do |f|
+ f.collection_check_boxes(:misc, collection, :id, :street, checked: collection)
+ end
+ assert_equivalent_html expected, actual
+ end
+end
+
+class BootstrapLegacyCollectionCheckboxesTest < ActionView::TestCase
+ include BootstrapForm::ActionViewExtensions::FormHelper
+
+ setup do
+ setup_test_fixture
+ Rails.application.config.bootstrap_form.group_around_collections = false
+ end
+
+ teardown do
+ Rails.application.config.bootstrap_form.group_around_collections = true
+ end
+
+ test "collection_check_boxes renders the form_group correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
+
This is a checkbox collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ label: "This is a checkbox collection", help: "With a help!")
+ end
+
+ if Rails::VERSION::MAJOR >= 8
+ test "collection_checkboxes renders the form_group correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
+
This is a checkbox collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_checkboxes(:misc, collection, :id, :street,
+ label: "This is a checkbox collection", help: "With a help!")
+ end
+ end
+
+ test "collection_check_boxes renders multiple checkboxes correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street)
+ end
+
+ test "collection_check_boxes renders multiple checkboxes contains unicode characters in IDs correctly" do
+ struct = Struct.new(:id, :name)
+ collection = [struct.new(1, "Foo"), struct.new("二", "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :name)
+ end
+
+ test "collection_check_boxes renders inline checkboxes correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street, inline: true)
+ end
+
+ test "collection_check_boxes renders with checked option correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: 1)
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: collection.first)
+ end
+
+ test "collection_check_boxes renders with multiple checked options correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: [1, 2])
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street,
+ checked: collection)
+ end
+
+ test "collection_check_boxes sanitizes values when generating label `for`" do
+ collection = [Address.new(id: 1, street: "Foo St")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo St
+
+
+
+ HTML
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :street, :street)
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with labels defined by Proc :text_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ ooF
+
+
+
+
+
+ raB
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, proc { |a| a.street.reverse })
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with values defined by Proc :value_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street)
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with labels defined by lambda :text_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ ooF
+
+
+
+
+
+ raB
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, ->(a) { a.street.reverse })
+ end
+
+ test "collection_check_boxes renders multiple checkboxes with values defined by lambda :value_method correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street)
+ end
+
+ test "collection_check_boxes renders with checked option correctly with Proc :value_method" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street, checked: "address_1")
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street, checked: collection.first)
+ end
+
+ test "collection_check_boxes renders with multiple checked options correctly with lambda :value_method" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
+
Misc
+
+
+
+ Foo
+
+
+
+
+
+ Bar
+
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street, checked: %w[address_1 address_2])
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street, checked: collection)
+ end
+
+ test "collection_check_boxes renders with include_hidden options correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :id, :street, include_hidden: false)
+ end
+
+ test "collection_check_boxes renders error after last check box" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ @user.errors.add(:misc, "a box must be checked")
+
+ expected = <<~HTML
+
+ HTML
+
+ actual = bootstrap_form_for(@user) do |f|
+ f.collection_check_boxes(:misc, collection, :id, :street)
+ end
+
+ assert_equivalent_html expected, actual
+ end
+
+ test "collection_check_boxes renders data attributes" do
+ collection = [
+ ["1", "Foo", { "data-city": "east" }],
+ ["2", "Bar", { "data-city": "west" }]
+ ]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_check_boxes(:misc, collection, :first, :second, include_hidden: false)
+ end
+
+ test "collection_check_boxes renders multiple check boxes with error correctly" do
+ @user.errors.add(:misc, "error for test")
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+ HTML
+
+ actual = bootstrap_form_for(@user) do |f|
+ f.collection_check_boxes(:misc, collection, :id, :street, checked: collection)
+ end
+ assert_equivalent_html expected, actual
+ end
+end
diff --git a/test/bootstrap_collection_radio_buttons_test.rb b/test/bootstrap_collection_radio_buttons_test.rb
new file mode 100644
index 00000000..2c9344fe
--- /dev/null
+++ b/test/bootstrap_collection_radio_buttons_test.rb
@@ -0,0 +1,535 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+class BootstrapCollectionRadioButtonsTest < ActionView::TestCase
+ include BootstrapForm::ActionViewExtensions::FormHelper
+
+ setup do
+ setup_test_fixture
+ Rails.application.config.bootstrap_form.group_around_collections = true
+ end
+
+ teardown do
+ Rails.application.config.bootstrap_form.group_around_collections = false
+ end
+
+ test "collection_radio_buttons renders the form_group correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+
+ Foobar
+
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, :id, :street,
+ label: "This is a radio button collection", help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders multiple radios correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, :street)
+ end
+
+ test "collection_radio_buttons renders multiple radios with error correctly" do
+ @user.errors.add(:misc, "error for test")
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+ HTML
+
+ actual = bootstrap_form_for(@user) do |f|
+ f.collection_radio_buttons(:misc, collection, :id, :street)
+ end
+ assert_equivalent_html expected, actual
+ end
+
+ test "collection_radio_buttons renders inline radios correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, :street, inline: true)
+ end
+
+ test "collection_radio_buttons renders with checked option correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, :street, checked: 1)
+ end
+
+ test "collection_radio_buttons renders label defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ rabooF
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, :id, proc { |a| a.street.reverse },
+ label: "This is a radio button collection", help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders value defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street, label: "This is a radio button collection",
+ help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders multiple radios with label defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ ooF
+
+
+
+ raB
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, proc { |a| a.street.reverse })
+ end
+
+ test "collection_radio_buttons renders multiple radios with value defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, proc { |a| "address_#{a.id}" }, :street)
+ end
+
+ test "collection_radio_buttons renders label defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ rabooF
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, :id, ->(a) { a.street.reverse },
+ label: "This is a radio button collection", help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders value defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street, label: "This is a radio button collection",
+ help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders multiple radios with label defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ ooF
+
+
+
+ raB
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, ->(a) { a.street.reverse })
+ end
+
+ test "collection_radio_buttons renders multiple radios with value defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, ->(a) { "address_#{a.id}" }, :street)
+ end
+end
+
+class BootstrapCLegacyollectionRadioButtonsTest < ActionView::TestCase
+ include BootstrapForm::ActionViewExtensions::FormHelper
+
+ setup do
+ setup_test_fixture
+ Rails.application.config.bootstrap_form.group_around_collections = false
+ end
+
+ teardown do
+ Rails.application.config.bootstrap_form.group_around_collections = true
+ end
+
+ test "collection_radio_buttons renders the form_group correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+
+ Foobar
+
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, :id, :street,
+ label: "This is a radio button collection", help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders multiple radios correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, :street)
+ end
+
+ test "collection_radio_buttons renders multiple radios with error correctly" do
+ @user.errors.add(:misc, "error for test")
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+ HTML
+
+ actual = bootstrap_form_for(@user) do |f|
+ f.collection_radio_buttons(:misc, collection, :id, :street)
+ end
+ assert_equivalent_html expected, actual
+ end
+
+ test "collection_radio_buttons renders inline radios correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, :street, inline: true)
+ end
+
+ test "collection_radio_buttons renders with checked option correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, :street, checked: 1)
+ end
+
+ test "collection_radio_buttons renders label defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ rabooF
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, :id, proc { |a| a.street.reverse },
+ label: "This is a radio button collection", help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders value defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, proc { |a| "address_#{a.id}" },
+ :street, label: "This is a radio button collection",
+ help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders multiple radios with label defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ ooF
+
+
+
+ raB
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, proc { |a| a.street.reverse })
+ end
+
+ test "collection_radio_buttons renders multiple radios with value defined by Proc correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, proc { |a| "address_#{a.id}" }, :street)
+ end
+
+ test "collection_radio_buttons renders label defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ rabooF
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, :id, ->(a) { a.street.reverse },
+ label: "This is a radio button collection", help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders value defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foobar")]
+ expected = <<~HTML
+
+
This is a radio button collection
+
+
+ Foobar
+
+
With a help!
+
+ HTML
+
+ assert_equivalent_html expected,
+ @builder.collection_radio_buttons(:misc, collection, ->(a) { "address_#{a.id}" },
+ :street, label: "This is a radio button collection",
+ help: "With a help!")
+ end
+
+ test "collection_radio_buttons renders multiple radios with label defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ ooF
+
+
+
+ raB
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, :id, ->(a) { a.street.reverse })
+ end
+
+ test "collection_radio_buttons renders multiple radios with value defined by lambda correctly" do
+ collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")]
+ expected = <<~HTML
+
+
Misc
+
+
+ Foo
+
+
+
+ Bar
+
+
+ HTML
+
+ assert_equivalent_html expected, @builder.collection_radio_buttons(:misc, collection, ->(a) { "address_#{a.id}" }, :street)
+ end
+end
diff --git a/test/bootstrap_form_test.rb b/test/bootstrap_form_test.rb
index 6bd2deb0..22050769 100644
--- a/test/bootstrap_form_test.rb
+++ b/test/bootstrap_form_test.rb
@@ -5,7 +5,14 @@
class BootstrapFormTest < ActionView::TestCase
include BootstrapForm::ActionViewExtensions::FormHelper
- setup :setup_test_fixture
+ setup do
+ setup_test_fixture
+ Rails.application.config.bootstrap_form.group_around_collections = true
+ end
+
+ teardown do
+ Rails.application.config.bootstrap_form.group_around_collections = false
+ end
test "default-style forms" do
expected = <<~HTML
@@ -29,8 +36,8 @@ class BootstrapFormTest < ActionView::TestCase
I agree to the terms