diff options
author | Carlos Antonio da Silva <carlosantoniodasilva@gmail.com> | 2012-01-31 15:45:59 -0200 |
---|---|---|
committer | Carlos Antonio da Silva <carlosantoniodasilva@gmail.com> | 2012-02-02 09:40:22 -0200 |
commit | 9323fb60572815fc63fa618a5bd17b68cf8c8e35 (patch) | |
tree | 70017e5388cc205a7b326684d839ae56d78e7c01 | |
parent | 3ccbc619e4e1614c94d19cc9d12bbe7f7826cbdf (diff) | |
download | rails-9323fb60572815fc63fa618a5bd17b68cf8c8e35.tar.gz rails-9323fb60572815fc63fa618a5bd17b68cf8c8e35.tar.bz2 rails-9323fb60572815fc63fa618a5bd17b68cf8c8e35.zip |
Create collection_radio_buttons helper
[Carlos Antonio da Silva + Rafael Mendonça França]
4 files changed, 279 insertions, 15 deletions
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index c4cdfef4a2..5ca2f2abdc 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -191,6 +191,10 @@ module ActionView Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render end + def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block) + Tags::CollectionRadioButtons.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 e874d4ca42..fa19cf2dba 100644 --- a/actionpack/lib/action_view/helpers/tags.rb +++ b/actionpack/lib/action_view/helpers/tags.rb @@ -4,27 +4,28 @@ module ActionView extend ActiveSupport::Autoload autoload :Base + autoload :CheckBox + autoload :CollectionRadioButtons + autoload :CollectionSelect + autoload :DateSelect + autoload :DatetimeSelect + autoload :EmailField + autoload :FileField + autoload :GroupedCollectionSelect + autoload :HiddenField autoload :Label - autoload :TextField + autoload :NumberField autoload :PasswordField - autoload :HiddenField - autoload :FileField + autoload :RadioButton + autoload :RangeField autoload :SearchField + autoload :Select autoload :TelField - autoload :UrlField - autoload :EmailField - autoload :NumberField - autoload :RangeField autoload :TextArea - autoload :CheckBox - autoload :RadioButton - autoload :Select - autoload :CollectionSelect - autoload :GroupedCollectionSelect - autoload :TimeZoneSelect - autoload :DateSelect + autoload :TextField autoload :TimeSelect - autoload :DatetimeSelect + autoload :TimeZoneSelect + autoload :UrlField end end end diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb new file mode 100644 index 0000000000..eea8c41dc0 --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -0,0 +1,85 @@ +module ActionView + module Helpers + module Tags + class CollectionRadioButtons < CollectionSelect + delegate :radio_button, :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| + if block_given? + yield sanitize_attribute_name(@method_name, value), text, value, default_html_options + else + radio_button(@object_name, @method_name, value, default_html_options) + + label(@object_name, sanitize_attribute_name(@method_name, value), text, :class => "collection_radio_buttons") + end + end + + wrap_rendered_collection(rendered_collection, @options) + end + + private + + # Generate default options for collection helpers, such as :checked and + # :disabled. + def default_html_options_for_collection(item, value, options, html_options) #:nodoc: + html_options = html_options.dup + + [:checked, :selected, :disabled].each do |option| + next unless options[option] + + + accept = if options[option].respond_to?(:call) + options[option].call(item) + else + Array(options[option]).include?(value) + end + + if accept + html_options[option] = true + elsif option == :checked + html_options[option] = false + end + end + + html_options + end + + def sanitize_attribute_name(attribute, value) #:nodoc: + "#{attribute}_#{value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase}" + end + + def render_collection(attribute, collection, value_method, text_method, options={}, html_options={}) #:nodoc: + item_wrapper_tag = options.fetch(:item_wrapper_tag, :span) + item_wrapper_class = options[:item_wrapper_class] + + collection.map do |item| + value = value_for_collection(item, value_method) + text = value_for_collection(item, text_method) + default_html_options = default_html_options_for_collection(item, value, options, html_options) + + rendered_item = yield value, text, default_html_options + + item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, :class => item_wrapper_class) : rendered_item + end.join.html_safe + end + + def value_for_collection(item, value) #:nodoc: + value.respond_to?(:call) ? value.call(item) : item.send(value) + end + + def wrap_rendered_collection(collection, options) + wrapper_tag = options[:collection_wrapper_tag] + + if wrapper_tag + wrapper_class = options[:collection_wrapper_class] + @template_object.content_tag(wrapper_tag, collection, :class => wrapper_class) + else + collection + end + 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 new file mode 100644 index 0000000000..7af634936d --- /dev/null +++ b/actionpack/test/template/form_collections_helper_test.rb @@ -0,0 +1,174 @@ +require 'abstract_unit' + +class FormCollectionsHelperTest < ActionView::TestCase + def assert_no_select(selector, value = nil) + assert_select(selector, :text => value, :count => 0) + end + + def with_collection_radio_buttons(*args, &block) + concat collection_radio_buttons(*args, &block) + end + + # COLLECTION RADIO + 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 + + assert_select 'input[type=radio][value=true]#user_active_true' + assert_select 'input[type=radio][value=false]#user_active_false' + end + + test 'collection radio accepts a collection and generate inputs from label method' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s + + assert_select 'label.collection_radio_buttons[for=user_active_true]', 'true' + assert_select 'label.collection_radio_buttons[for=user_active_false]', 'false' + end + + test 'collection radio handles camelized collection values for labels correctly' do + with_collection_radio_buttons :user, :active, ['Yes', 'No'], :to_s, :to_s + + assert_select 'label.collection_radio_buttons[for=user_active_yes]', 'Yes' + assert_select 'label.collection_radio_buttons[for=user_active_no]', 'No' + end + + test 'colection radio should sanitize collection values for labels correctly' do + with_collection_radio_buttons :user, :name, ['$0.99', '$1.99'], :to_s, :to_s + assert_select 'label.collection_radio_buttons[for=user_name_099]', '$0.99' + assert_select 'label.collection_radio_buttons[for=user_name_199]', '$1.99' + end + + test 'collection radio accepts checked item' do + with_collection_radio_buttons :user, :active, [[1, true], [0, false]], :last, :first, :checked => true + + assert_select 'input[type=radio][value=true][checked=checked]' + assert_no_select 'input[type=radio][value=false][checked=checked]' + end + + test 'collection radio accepts multiple disabled items' do + collection = [[1, true], [0, false], [2, 'other']] + with_collection_radio_buttons :user, :active, collection, :last, :first, :disabled => [true, false] + + assert_select 'input[type=radio][value=true][disabled=disabled]' + assert_select 'input[type=radio][value=false][disabled=disabled]' + assert_no_select 'input[type=radio][value=other][disabled=disabled]' + end + + test 'collection radio accepts single disable item' do + collection = [[1, true], [0, false]] + with_collection_radio_buttons :user, :active, collection, :last, :first, :disabled => true + + assert_select 'input[type=radio][value=true][disabled=disabled]' + assert_no_select 'input[type=radio][value=false][disabled=disabled]' + end + + test 'collection radio accepts html options as input' do + collection = [[1, true], [0, false]] + with_collection_radio_buttons :user, :active, collection, :last, :first, {}, :class => 'special-radio' + + assert_select 'input[type=radio][value=true].special-radio#user_active_true' + assert_select 'input[type=radio][value=false].special-radio#user_active_false' + end + + test 'collection radio wraps the collection in the given collection wrapper tag' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => :ul + + assert_select 'ul input[type=radio]', :count => 2 + end + + test 'collection radio does not render any wrapper tag by default' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s + + assert_select 'input[type=radio]', :count => 2 + assert_no_select 'ul' + end + + test 'collection radio does not wrap the collection when given falsy values' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => false + + assert_select 'input[type=radio]', :count => 2 + assert_no_select 'ul' + end + + test 'collection radio uses the given class for collection wrapper tag' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, + :collection_wrapper_tag => :ul, :collection_wrapper_class => "items-list" + + assert_select 'ul.items-list input[type=radio]', :count => 2 + end + + test 'collection radio uses no class for collection wrapper tag when no wrapper tag is given' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, + :collection_wrapper_class => "items-list" + + assert_select 'input[type=radio]', :count => 2 + assert_no_select 'ul' + assert_no_select '.items-list' + end + + test 'collection radio uses no class for collection wrapper tag by default' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => :ul + + assert_select 'ul' + assert_no_select 'ul[class]' + end + + test 'collection radio wrap items in a span tag by default' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s + + assert_select 'span input[type=radio][value=true]#user_active_true + label' + assert_select 'span input[type=radio][value=false]#user_active_false + label' + end + + test 'collection radio wraps each item in the given item wrapper tag' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => :li + + assert_select 'li input[type=radio]', :count => 2 + end + + test 'collection radio does not wrap each item when given explicitly falsy value' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => false + + assert_select 'input[type=radio]' + assert_no_select 'span input[type=radio]' + end + + test 'collection radio uses the given class for item wrapper tag' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, + :item_wrapper_tag => :li, :item_wrapper_class => "inline" + + assert_select "li.inline input[type=radio]", :count => 2 + end + + test 'collection radio uses no class for item wrapper tag when no wrapper tag is given' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s, + :item_wrapper_tag => nil, :item_wrapper_class => "inline" + + assert_select 'input[type=radio]', :count => 2 + assert_no_select 'li' + assert_no_select '.inline' + end + + test 'collection radio uses no class for item wrapper tag by default' do + with_collection_radio_buttons :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 radio does not wrap input inside the label' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s + + assert_select 'input[type=radio] + label' + assert_no_select 'label input' + end + + test 'collection radio accepts a block to render the radio and label as required' do + with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s do |label_for, text, value, html_options| + concat label(:user, label_for, text) { radio_button(:user, :active, value, html_options) } + end + + 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 +end |