diff options
author | Carlos Antonio da Silva <carlosantoniodasilva@gmail.com> | 2012-01-31 16:31:17 -0200 |
---|---|---|
committer | Carlos Antonio da Silva <carlosantoniodasilva@gmail.com> | 2012-02-02 09:40:22 -0200 |
commit | 7af0ec75d0d933d75f9a63a63bbbde554f206721 (patch) | |
tree | 481d56735c6c1a5d0efb6faa2fb30b435789587f | |
parent | 9323fb60572815fc63fa618a5bd17b68cf8c8e35 (diff) | |
download | rails-7af0ec75d0d933d75f9a63a63bbbde554f206721.tar.gz rails-7af0ec75d0d933d75f9a63a63bbbde554f206721.tar.bz2 rails-7af0ec75d0d933d75f9a63a63bbbde554f206721.zip |
Add collection_check_boxes helper
[Carlos Antonio da Silva + Rafael Mendonça França]
4 files changed, 262 insertions, 1 deletions
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 5ca2f2abdc..96519a7428 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -195,6 +195,10 @@ module ActionView Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block) end + def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block) + Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block) + end + # Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt> diff --git a/actionpack/lib/action_view/helpers/tags.rb b/actionpack/lib/action_view/helpers/tags.rb index fa19cf2dba..c480799fe3 100644 --- a/actionpack/lib/action_view/helpers/tags.rb +++ b/actionpack/lib/action_view/helpers/tags.rb @@ -5,6 +5,7 @@ module ActionView autoload :Base autoload :CheckBox + autoload :CollectionCheckBoxes autoload :CollectionRadioButtons autoload :CollectionSelect autoload :DateSelect diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb new file mode 100644 index 0000000000..d8f7a1fff6 --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -0,0 +1,30 @@ +module ActionView + module Helpers + module Tags + class CollectionCheckBoxes < CollectionRadioButtons + delegate :check_box, :label, :to => :@template_object + + def render + rendered_collection = render_collection( + @method_name, @collection, @value_method, @text_method, @options, @html_options + ) do |value, text, default_html_options| + default_html_options[:multiple] = true + + if block_given? + yield sanitize_attribute_name(@method_name, value), text, value, default_html_options + else + check_box(@object_name, @method_name, default_html_options, value, nil) + + label(@object_name, sanitize_attribute_name(@method_name, value), text, :class => "collection_check_boxes") + end + end + + # Prepend a hidden field to make sure something will be sent back to the + # server if all checkboxes are unchecked. + hidden = @template_object.hidden_field_tag("#{@object_name}[#{@method_name}][]", "", :id => nil) + + wrap_rendered_collection(rendered_collection + hidden, @options) + end + end + end + end +end diff --git a/actionpack/test/template/form_collections_helper_test.rb b/actionpack/test/template/form_collections_helper_test.rb index 7af634936d..e8ec274a04 100644 --- a/actionpack/test/template/form_collections_helper_test.rb +++ b/actionpack/test/template/form_collections_helper_test.rb @@ -1,5 +1,8 @@ require 'abstract_unit' +class Tag < Struct.new(:id, :name) +end + class FormCollectionsHelperTest < ActionView::TestCase def assert_no_select(selector, value = nil) assert_select(selector, :text => value, :count => 0) @@ -9,7 +12,11 @@ class FormCollectionsHelperTest < ActionView::TestCase concat collection_radio_buttons(*args, &block) end - # COLLECTION RADIO + def with_collection_check_boxes(*args, &block) + concat collection_check_boxes(*args, &block) + end + + # COLLECTION RADIO BUTTONS test 'collection radio accepts a collection and generate inputs from value method' do with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s @@ -171,4 +178,223 @@ class FormCollectionsHelperTest < ActionView::TestCase assert_select 'label[for=user_active_true] > input#user_active_true[type=radio]' assert_select 'label[for=user_active_false] > input#user_active_false[type=radio]' end + + # COLLECTION CHECK BOXES + test 'collection check boxes accepts a collection and generate a serie of checkboxes for value method' do + collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')] + with_collection_check_boxes :user, :tag_ids, collection, :id, :name + + assert_select 'input#user_tag_ids_1[type=checkbox][value=1]' + assert_select 'input#user_tag_ids_2[type=checkbox][value=2]' + end + + test 'collection check boxes generates only one hidden field for the entire collection, to ensure something will be sent back to the server when posting an empty collection' do + collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')] + with_collection_check_boxes :user, :tag_ids, collection, :id, :name + + assert_select "input[type=hidden][name='user[tag_ids][]'][value=]", :count => 1 + end + + test 'collection check boxes accepts a collection and generate a serie of checkboxes with labels for label method' do + collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')] + with_collection_check_boxes :user, :tag_ids, collection, :id, :name + + assert_select 'label.collection_check_boxes[for=user_tag_ids_1]', 'Tag 1' + assert_select 'label.collection_check_boxes[for=user_tag_ids_2]', 'Tag 2' + end + + test 'collection check boxes handles camelized collection values for labels correctly' do + with_collection_check_boxes :user, :active, ['Yes', 'No'], :to_s, :to_s + + assert_select 'label.collection_check_boxes[for=user_active_yes]', 'Yes' + assert_select 'label.collection_check_boxes[for=user_active_no]', 'No' + end + + test 'colection check box should sanitize collection values for labels correctly' do + with_collection_check_boxes :user, :name, ['$0.99', '$1.99'], :to_s, :to_s + assert_select 'label.collection_check_boxes[for=user_name_099]', '$0.99' + assert_select 'label.collection_check_boxes[for=user_name_199]', '$1.99' + end + + test 'collection check boxes accepts selected values as :checked option' do + collection = (1..3).map{|i| [i, "Tag #{i}"] } + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, :checked => [1, 3] + + assert_select 'input[type=checkbox][value=1][checked=checked]' + assert_select 'input[type=checkbox][value=3][checked=checked]' + assert_no_select 'input[type=checkbox][value=2][checked=checked]' + end + + test 'collection check boxes accepts a single checked value' do + collection = (1..3).map{|i| [i, "Tag #{i}"] } + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, :checked => 3 + + assert_select 'input[type=checkbox][value=3][checked=checked]' + assert_no_select 'input[type=checkbox][value=1][checked=checked]' + assert_no_select 'input[type=checkbox][value=2][checked=checked]' + end + + test 'collection check boxes accepts selected values as :checked option and override the model values' do + skip "check with fields for" + collection = (1..3).map{|i| [i, "Tag #{i}"] } + :user.tag_ids = [2] + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, :checked => [1, 3] + + assert_select 'input[type=checkbox][value=1][checked=checked]' + assert_select 'input[type=checkbox][value=3][checked=checked]' + assert_no_select 'input[type=checkbox][value=2][checked=checked]' + end + + test 'collection check boxes accepts multiple disabled items' do + collection = (1..3).map{|i| [i, "Tag #{i}"] } + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, :disabled => [1, 3] + + assert_select 'input[type=checkbox][value=1][disabled=disabled]' + assert_select 'input[type=checkbox][value=3][disabled=disabled]' + assert_no_select 'input[type=checkbox][value=2][disabled=disabled]' + end + + test 'collection check boxes accepts single disable item' do + collection = (1..3).map{|i| [i, "Tag #{i}"] } + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, :disabled => 1 + + assert_select 'input[type=checkbox][value=1][disabled=disabled]' + assert_no_select 'input[type=checkbox][value=3][disabled=disabled]' + assert_no_select 'input[type=checkbox][value=2][disabled=disabled]' + end + + test 'collection check boxes accepts a proc to disabled items' do + collection = (1..3).map{|i| [i, "Tag #{i}"] } + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, :disabled => proc { |i| i.first == 1 } + + assert_select 'input[type=checkbox][value=1][disabled=disabled]' + assert_no_select 'input[type=checkbox][value=3][disabled=disabled]' + assert_no_select 'input[type=checkbox][value=2][disabled=disabled]' + end + + test 'collection check boxes accepts html options' do + collection = [[1, 'Tag 1'], [2, 'Tag 2']] + with_collection_check_boxes :user, :tag_ids, collection, :first, :last, {}, :class => 'check' + + assert_select 'input.check[type=checkbox][value=1]' + assert_select 'input.check[type=checkbox][value=2]' + end + + test 'collection check boxes with fields for' do + skip "test collection check boxes with fields for (and radio buttons as well)" + collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')] + concat(form_for(:user) do |f| + f.fields_for(:post) do |p| + p.collection_check_boxes :tag_ids, collection, :id, :name + end + end) + + assert_select 'input#user_post_tag_ids_1[type=checkbox][value=1]' + assert_select 'input#user_post_tag_ids_2[type=checkbox][value=2]' + + assert_select 'label.collection_check_boxes[for=user_post_tag_ids_1]', 'Tag 1' + assert_select 'label.collection_check_boxes[for=user_post_tag_ids_2]', 'Tag 2' + end + + test 'collection check boxeses wraps the collection in the given collection wrapper tag' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => :ul + + assert_select 'ul input[type=checkbox]', :count => 2 + end + + test 'collection check boxeses does not render any wrapper tag by default' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s + + assert_select 'input[type=checkbox]', :count => 2 + assert_no_select 'ul' + end + + test 'collection check boxeses does not wrap the collection when given falsy values' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => false + + assert_select 'input[type=checkbox]', :count => 2 + assert_no_select 'ul' + end + + test 'collection check boxeses uses the given class for collection wrapper tag' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, + :collection_wrapper_tag => :ul, :collection_wrapper_class => "items-list" + + assert_select 'ul.items-list input[type=checkbox]', :count => 2 + end + + test 'collection check boxeses uses no class for collection wrapper tag when no wrapper tag is given' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, + :collection_wrapper_class => "items-list" + + assert_select 'input[type=checkbox]', :count => 2 + assert_no_select 'ul' + assert_no_select '.items-list' + end + + test 'collection check boxeses uses no class for collection wrapper tag by default' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => :ul + + assert_select 'ul' + assert_no_select 'ul[class]' + end + + test 'collection check boxeses wrap items in a span tag by default' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s + + assert_select 'span input[type=checkbox]', :count => 2 + end + + test 'collection check boxeses wraps each item in the given item wrapper tag' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => :li + + assert_select 'li input[type=checkbox]', :count => 2 + end + + test 'collection check boxeses does not wrap each item when given explicitly falsy value' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => false + + assert_select 'input[type=checkbox]' + assert_no_select 'span input[type=checkbox]' + end + + test 'collection check boxeses uses the given class for item wrapper tag' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, + :item_wrapper_tag => :li, :item_wrapper_class => "inline" + + assert_select "li.inline input[type=checkbox]", :count => 2 + end + + test 'collection check boxeses uses no class for item wrapper tag when no wrapper tag is given' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, + :item_wrapper_tag => nil, :item_wrapper_class => "inline" + + assert_select 'input[type=checkbox]', :count => 2 + assert_no_select 'li' + assert_no_select '.inline' + end + + test 'collection check boxeses uses no class for item wrapper tag by default' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s, + :item_wrapper_tag => :li + + assert_select "li", :count => 2 + assert_no_select "li[class]" + end + + test 'collection check boxes does not wrap input inside the label' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s + + assert_select 'input[type=checkbox] + label' + assert_no_select 'label input' + end + + test 'collection check boxes accepts a block to render the radio and label as required' do + with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s do |label_for, text, value, html_options| + label(:user, label_for, text) { check_box(:user, :active, html_options, value) } + end + + assert_select 'label[for=user_active_true] > input#user_active_true[type=checkbox]' + assert_select 'label[for=user_active_false] > input#user_active_false[type=checkbox]' + end end |