Add support for conditional values to TagBuilder
Adds support for conditional values to TagBuilder, extracting logic we use in the GitHub application, inspired by https://github.com/JedWatson/classnames. It’s common practice to conditionally apply CSS classes in Rails views. This can lead to messy string interpolation, often using ternaries: ```ruby content_tag( "My username", class: "always #{'sometimes' if current_user.special?} another" ) ``` By adding support for hashes to TagBuilder, we can instead write the following: ```ruby content_tag( "My username", class: ["always", "another", { 'sometimes' => current_user.special? }] ) ``` cc @JedWatson
This commit is contained in:
parent
60af9db374
commit
f1c63d8673
|
@ -1,3 +1,7 @@
|
|||
* Add support for conditional values to TagBuilder.
|
||||
|
||||
*Joel Hawksley*
|
||||
|
||||
* Fix `select_tag` so that it doesn't change `options` when `include_blank` is present.
|
||||
|
||||
*Younes SERRAJ*
|
||||
|
|
|
@ -85,7 +85,9 @@ module ActionView
|
|||
end
|
||||
|
||||
def tag_option(key, value, escape)
|
||||
if value.is_a?(Array)
|
||||
case value
|
||||
when Array, Hash
|
||||
value = build_tag_values(value)
|
||||
value = escape ? safe_join(value, " ") : value.join(" ")
|
||||
else
|
||||
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s.dup
|
||||
|
@ -95,6 +97,26 @@ module ActionView
|
|||
end
|
||||
|
||||
private
|
||||
|
||||
def build_tag_values(*args)
|
||||
tag_values = []
|
||||
|
||||
args.each do |tag_value|
|
||||
case tag_value
|
||||
when String
|
||||
tag_values << tag_value if tag_value.present?
|
||||
when Hash
|
||||
tag_value.each do |key, val|
|
||||
tag_values << key if val
|
||||
end
|
||||
when Array
|
||||
tag_values << build_tag_values(*tag_value).presence
|
||||
end
|
||||
end
|
||||
|
||||
tag_values.compact.flatten
|
||||
end
|
||||
|
||||
def prefix_tag_option(prefix, key, value, escape)
|
||||
key = "#{prefix}-#{key.to_s.dasherize}"
|
||||
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
|
||||
|
@ -233,6 +255,9 @@ module ActionView
|
|||
#
|
||||
# tag("div", data: { name: 'Stephen', city_state: %w(Chicago IL) })
|
||||
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
|
||||
#
|
||||
# tag("div", class: { highlight: current_user.admin? })
|
||||
# # => <div class="highlight" />
|
||||
def tag(name = nil, options = nil, open = false, escape = true)
|
||||
if name.nil?
|
||||
tag_builder
|
||||
|
@ -260,6 +285,8 @@ module ActionView
|
|||
# # => <div class="strong"><p>Hello world!</p></div>
|
||||
# content_tag(:div, "Hello world!", class: ["strong", "highlight"])
|
||||
# # => <div class="strong highlight">Hello world!</div>
|
||||
# content_tag(:div, "Hello world!", class: ["strong", { highlight: current_user.admin? }])
|
||||
# # => <div class="strong highlight">Hello world!</div>
|
||||
# content_tag("select", options, multiple: true)
|
||||
# # => <select multiple="multiple">...options...</select>
|
||||
#
|
||||
|
|
|
@ -239,6 +239,68 @@ class TagHelperTest < ActionView::TestCase
|
|||
assert_equal '<p class="">limelight</p>', str
|
||||
end
|
||||
|
||||
def test_content_tag_with_conditional_hash_classes
|
||||
str = content_tag("p", "limelight", class: { "song": true, "play": false })
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", class: [{ "song": true }, { "play": false }])
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", class: { song: true, play: false })
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", class: [{ song: true}, nil, false])
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", class: ["song", { foo: false}])
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", class: { "song": true, "play": true })
|
||||
assert_equal "<p class=\"song play\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", class: { "song": false, "play": false })
|
||||
assert_equal '<p class="">limelight</p>', str
|
||||
end
|
||||
|
||||
def test_tag_builder_with_conditional_hash_classes
|
||||
str = tag.p "limelight", class: { "song": true, "play": false }
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: [{ "song": true }, { "play": false }]
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: { song: true, play: false }
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: [{ song: true}, nil, false]
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: ["song", { foo: false}]
|
||||
assert_equal "<p class=\"song\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: { "song": true, "play": true }
|
||||
assert_equal "<p class=\"song play\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: { "song": false, "play": false }
|
||||
assert_equal '<p class="">limelight</p>', str
|
||||
end
|
||||
|
||||
def test_content_tag_with_unescaped_conditional_hash_classes
|
||||
str = content_tag("p", "limelight", { class: { "song": true, "play>": true } }, false)
|
||||
assert_equal "<p class=\"song play>\">limelight</p>", str
|
||||
|
||||
str = content_tag("p", "limelight", { class: ["song", { "play>": true }] }, false)
|
||||
assert_equal "<p class=\"song play>\">limelight</p>", str
|
||||
end
|
||||
|
||||
def test_tag_builder_with_unescaped_conditional_hash_classes
|
||||
str = tag.p "limelight", class: { "song": true, "play>": true }, escape_attributes: false
|
||||
assert_equal "<p class=\"song play>\">limelight</p>", str
|
||||
|
||||
str = tag.p "limelight", class: ["song", { "play>": true }], escape_attributes: false
|
||||
assert_equal "<p class=\"song play>\">limelight</p>", str
|
||||
end
|
||||
|
||||
def test_content_tag_with_data_attributes
|
||||
assert_dom_equal '<p data-number="1" data-string="hello" data-string-with-quotes="double"quote"party"">limelight</p>',
|
||||
content_tag("p", "limelight", data: { number: 1, string: "hello", string_with_quotes: 'double"quote"party"' })
|
||||
|
|
Loading…
Reference in New Issue