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

Fix name and id for checkbox/radio #251

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
@@ -1,8 +1,10 @@
## [Pending Release][]

Bugfixes:
- Your contribution here!

- Fix id and name generation for fields when using `bootstrap_form_tag` to use the same conventions as in Rails (Fix #266, #193, #241)
- In `form_group`, we allow the hash for label to include a `for` parameter
- In `form_group`, use the id specified for the input as the value of `for` parameter in the matching label (Fix #213)

Features:
- The project is now tested against and compatible with the following Rails versions 4.0, 4.1, 4.2 and 5.0 (#278).
- Your contribution here!
Expand Down
63 changes: 48 additions & 15 deletions lib/bootstrap_form/form_builder.rb
Expand Up @@ -108,8 +108,19 @@ def time_zone_select_with_bootstrap(method, priority_zones = nil, options = {},

bootstrap_method_alias :time_zone_select

def hidden_field_with_bootstrap(method, options = {})
if acts_like_form_tag
options[:id] ||= method.to_s
options[:name] ||= method.to_s
end
hidden_field_without_bootstrap(method, options)
end

bootstrap_method_alias :hidden_field

def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_value = "0", &block)
options = options.symbolize_keys!
options = convert_form_tag_options(name, options) if acts_like_form_tag
check_box_options = options.except(:label, :label_class, :help, :inline)

html = check_box_without_bootstrap(name, check_box_options, checked_value, unchecked_value)
Expand All @@ -128,12 +139,16 @@ def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_
disabled_class = " disabled" if options[:disabled]
label_class = options[:label_class]

label_opts = { class: label_class }
# only add +for+ key if not nil otherwise it prevent standard behaviour of label method
label_opts[:for] = options[:id] unless (options[:id].nil? || options[:id].empty?)

if options[:inline]
label_class = " #{label_class}" if label_class
label(label_name, html, class: "checkbox-inline#{disabled_class}#{label_class}")
label_opts[:class] = "checkbox-inline#{disabled_class} #{label_class}".strip
label(label_name, html, label_opts)
else
content_tag(:div, class: "checkbox#{disabled_class}") do
label(label_name, html, class: label_class)
label(label_name, html, label_opts)
end
end
end
Expand All @@ -142,19 +157,27 @@ def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_

def radio_button_with_bootstrap(name, value, *args)
options = args.extract_options!.symbolize_keys!
if acts_like_form_tag
options[:id] ||= "#{name}_#{value}"
options[:name] ||= name
end
args << options.except(:label, :label_class, :help, :inline)

html = radio_button_without_bootstrap(name, value, *args) + " " + options[:label]

disabled_class = " disabled" if options[:disabled]
label_class = options[:label_class]

label_opts = { value: value, class: label_class }
# only add +for+ key if not nil otherwise it prevent standard behaviour of label method
label_opts[:for] = options[:id] unless (options[:id].nil? || options[:id].empty?)

if options[:inline]
label_class = " #{label_class}" if label_class
label(name, html, class: "radio-inline#{disabled_class}#{label_class}", value: value)
label_opts[:class] = "radio-inline#{disabled_class} #{label_class}".strip
label(name, html, label_opts)
else
content_tag(:div, class: "radio#{disabled_class}") do
label(name, html, value: value, class: label_class)
label(name, html, label_opts)
end
end
end
Expand Down Expand Up @@ -196,7 +219,10 @@ def form_group(*args, &block)
options[:class] = ["form-group", options[:class]].compact.join(' ')
options[:class] << " #{error_class}" if has_error?(name)
options[:class] << " #{feedback_class}" if options[:icon]


# allow to use a string for label in form_group, like for other tags
options[:label] = { text: options[:label] } if options[:label].is_a?(String)

content_tag(:div, options.except(:id, :label, :help, :icon, :label_col, :control_col, :layout)) do
label = generate_label(options[:id], name, options[:label], options[:label_col], options[:layout]) if options[:label]
control = capture(&block).to_s
Expand Down Expand Up @@ -310,7 +336,13 @@ def form_group_builder(method, options, html_options = nil)
control_classes = css_options.delete(:control_class) { control_class }
css_options[:class] = [control_classes, css_options[:class]].compact.join(" ")

options = convert_form_tag_options(method, options) if acts_like_form_tag
if acts_like_form_tag
options = convert_form_tag_options(method, options)
# fix name and id for input/select tag when there is no object to refer to
html_options ||= {}
html_options[:name] ||= options[:name]
html_options[:id] ||= options[:id]
end

wrapper_class = css_options.delete(:wrapper_class)
wrapper_options = css_options.delete(:wrapper)
Expand All @@ -334,23 +366,24 @@ def form_group_builder(method, options, html_options = nil)
end

unless options.delete(:skip_label)
if options[:label].is_a?(Hash)
label_text = options[:label].delete(:text)
label_for = css_options[:id] # fix: set proper for information when an id is set for the control
if options[:label].is_a?(String)
label_text = options.delete(:label)
elsif options[:label].is_a?(Hash)
label_text = options[:label].delete(:text)
label_class = options[:label].delete(:class)
label_for = options[:label].delete(:for) if options[:label].has_key?(:for) # fix: preserve for information
options.delete(:label)
end
label_class ||= options.delete(:label_class)
label_class = hide_class if options.delete(:hide_label)

if options[:label].is_a?(String)
label_text ||= options.delete(:label)
end


form_group_options.merge!(label: {
text: label_text,
class: label_class,
skip_required: options.delete(:skip_required)
})
form_group_options[:label][:for] = label_for unless label_for.nil? # fix label for
end

form_group(method, form_group_options) do
Expand Down
10 changes: 10 additions & 0 deletions test/bootstrap_form_group_test.rb
Expand Up @@ -17,6 +17,16 @@ def setup
assert_equivalent_xml expected, @builder.text_field(:email, label: {text: 'Email Address'})
end

test "preserving the label for via the html_options label hash" do
expected = %{<div class="form-group"><label class="control-label required" for="super_user_email">Email Address</label><input class="form-control" id="super_user_email" name="user[email]" type="text" value="steve@example.com" /></div>}
assert_equivalent_xml expected, @builder.text_field(:email, label: {text: 'Email Address', for: 'super_user_email'}, id: "super_user_email")
end

test "properly generate the label for if we give a specific id" do
expected = %{<div class="form-group"><label class="control-label required" for="super_user_email">Email Address</label><input class="form-control" id="super_user_email" name="user[email]" type="text" value="steve@example.com" /></div>}
assert_equivalent_xml expected, @builder.text_field(:email, label: {text: 'Email Address'}, id: "super_user_email")
end

test "hiding a label" do
expected = %{<div class="form-group"><label class="sr-only control-label required" for="user_email">Email</label><input class="form-control" id="user_email" name="user[email]" type="text" value="steve@example.com" /></div>}
assert_equivalent_xml expected, @builder.text_field(:email, hide_label: true)
Expand Down
18 changes: 17 additions & 1 deletion test/bootstrap_form_test.rb
Expand Up @@ -43,10 +43,26 @@ def setup
end

test "bootstrap_form_tag allows an empty name for checkboxes" do
expected = %{<form accept-charset="UTF-8" action="/users" method="post" role="form"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /></div><div class="checkbox"><label for="_misc"><input name="[misc]" type="hidden" value="0" /><input id="_misc" name="[misc]" type="checkbox" value="1" /> Misc</label></div></form>}
expected = %{<form accept-charset="UTF-8" action="/users" method="post" role="form"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /></div><div class="checkbox"><label for="misc"><input name="misc" type="hidden" value="0" /><input id="misc" name="misc" type="checkbox" value="1" /> Misc</label></div></form>}
assert_equivalent_xml expected, bootstrap_form_tag(url: '/users') { |f| f.check_box :misc }
end

test "bootstrap_form_tag use proper rails naming convention for select" do
expected = %{<form accept-charset=\"UTF-8\" action=\"/users\" method=\"post\" role=\"form\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /></div><div class=\"form-group\"><label class=\"control-label\" for=\"misc\">Misc</label><select class=\"form-control\" id=\"misc\" name=\"misc\"><option value=\"0\">0</option>
<option value=\"1\">1</option></select></div></form>}
assert_equivalent_xml expected, bootstrap_form_tag(url: '/users') { |f| f.select :misc, ['0', '1'] }
end

test "bootstrap_form_tag use proper rails naming convention for radio button" do
expected = %{<form accept-charset=\"UTF-8\" action=\"/users\" method=\"post\" role=\"form\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /></div><div class=\"radio\"><label for=\"misc_1\"><input id=\"misc_1\" name=\"misc\" type=\"radio\" value=\"1\" /> </label></div></form>}
assert_equivalent_xml expected, bootstrap_form_tag(url: '/users') { |f| f.radio_button :misc, 1 }
end

test "bootstrap_form_tag use proper rails naming convention for hidden field" do
expected = %{<form accept-charset=\"UTF-8\" action=\"/users\" method=\"post\" role=\"form\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /></div><input id=\"email\" name=\"email\" type=\"hidden\" value=\"test@yahoo.com\" /></form>}
assert_equivalent_xml expected, bootstrap_form_tag(url: '/users') { |f| f.hidden_field :email, value: 'test@yahoo.com' }
end

test "errors display correctly and inline_errors are turned off by default when label_errors is true" do
@user.email = nil
@user.valid?
Expand Down