diff --git a/CHANGELOG.md b/CHANGELOG.md index 32cf03c1c..1d7938944 100644 --- a/CHANGELOG.md +++ b/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! diff --git a/lib/bootstrap_form/form_builder.rb b/lib/bootstrap_form/form_builder.rb index d89c96643..80a4833b8 100644 --- a/lib/bootstrap_form/form_builder.rb +++ b/lib/bootstrap_form/form_builder.rb @@ -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) @@ -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 @@ -142,6 +157,10 @@ 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] @@ -149,12 +168,16 @@ def radio_button_with_bootstrap(name, value, *args) 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 @@ -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 @@ -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) @@ -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 diff --git a/test/bootstrap_form_group_test.rb b/test/bootstrap_form_group_test.rb index 26a0a62fb..60cbaddda 100644 --- a/test/bootstrap_form_group_test.rb +++ b/test/bootstrap_form_group_test.rb @@ -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 = %{
} + 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 = %{
} + assert_equivalent_xml expected, @builder.text_field(:email, label: {text: 'Email Address'}, id: "super_user_email") + end + test "hiding a label" do expected = %{
} assert_equivalent_xml expected, @builder.text_field(:email, hide_label: true) diff --git a/test/bootstrap_form_test.rb b/test/bootstrap_form_test.rb index b2bcd5c32..3f0b73d2d 100644 --- a/test/bootstrap_form_test.rb +++ b/test/bootstrap_form_test.rb @@ -43,10 +43,26 @@ def setup end test "bootstrap_form_tag allows an empty name for checkboxes" do - expected = %{
} + expected = %{
} 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 = %{
} + 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 = %{
} + 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 = %{
} + 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?