mirror of https://github.com/rails/rails
Serialize aria- namespaced list attributes
Summary === Prior to this commit, calls passing `aria: { labelledby: [...] }` serialized the `aria-labelledby` Array value as JSON. This commit introduces special case logic to serialize `aria-` prefixed `TrueClass`, `FalseClass`, `Hash`, and `Array` values more appropriately. An element's [`aria-labelledby` attribute][aria-labelledby] and [`aria-describedby` attribute][aria-describedby] can accept a space-delimited list of identifier values (much like the [`class` attribute][class] accepts a space delimited [`DOMTokenList` value][DOMTokenList]). Similarly, there are [no boolean `aria-` attributes][aria-attributes] (only `true`, `false`, or undefined), so this commit serializes `true` to `"true"` and `false` to `"false"`. Testing --- This change moves an assertion _outside_ of a loop over `["aria", :aria]`. Prior to this change, the second assertion within the loop wasn't utilizing the iterated value as a Hash key. That is to say: `aria:` (where an `aria` local variable is declared) is not equivalent an equivalent syntax to `aria =>`. Since the migration to `**options` in response to Ruby 2.7 deprecations, invoking `tag.a("aria" => {...})` incorrectly coerces the `"aria" => {...}` has to be the `TagBuilder#a` method `content = nil` ordered argument, instead of its `options` keyword arguments. This commit does not modify that behavior, but it _does_ move the assertion outside the block so that it isn't run unnecessarily. [aria-labelledby]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute [aria-describedby]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-describedby_attribute [aria-attributes]: https://www.w3.org/TR/wai-aria-1.1/#propcharacteristic_value [class]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class [DOMTokenList]: https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList [class_names]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-class_names
This commit is contained in:
parent
43daedcb7d
commit
8b19d66fc6
|
@ -1,3 +1,20 @@
|
||||||
|
* ARIA Array and Hash attributes are treated as space separated `DOMTokenList`
|
||||||
|
values. This is useful when declaring lists of label text identifiers in
|
||||||
|
`aria-labelledby` or `aria-describedby`.
|
||||||
|
|
||||||
|
tag.input type: 'checkbox', name: 'published', aria: {
|
||||||
|
invalid: @post.errors[:published].any?,
|
||||||
|
labelledby: ['published_context', 'published_label'],
|
||||||
|
describedby: { published_errors: @post.errors[:published].any? }
|
||||||
|
}
|
||||||
|
#=> <input
|
||||||
|
type="checkbox" name="published" aria-invalid="true"
|
||||||
|
aria-labelledby="published_context published_label"
|
||||||
|
aria-describedby="published_errors"
|
||||||
|
>
|
||||||
|
|
||||||
|
*Sean Doyle*
|
||||||
|
|
||||||
* Remove deprecated `escape_whitelist` from `ActionView::Template::Handlers::ERB`.
|
* Remove deprecated `escape_whitelist` from `ActionView::Template::Handlers::ERB`.
|
||||||
|
|
||||||
*Rafael Mendonça França*
|
*Rafael Mendonça França*
|
||||||
|
|
|
@ -26,11 +26,13 @@ module ActionView
|
||||||
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
|
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
|
||||||
BOOLEAN_ATTRIBUTES.freeze
|
BOOLEAN_ATTRIBUTES.freeze
|
||||||
|
|
||||||
TAG_PREFIXES = ["aria", "data", :aria, :data].to_set.freeze
|
ARIA_PREFIXES = ["aria", :aria].to_set.freeze
|
||||||
|
DATA_PREFIXES = ["data", :data].to_set.freeze
|
||||||
|
|
||||||
TAG_TYPES = {}
|
TAG_TYPES = {}
|
||||||
TAG_TYPES.merge! BOOLEAN_ATTRIBUTES.index_with(:boolean)
|
TAG_TYPES.merge! BOOLEAN_ATTRIBUTES.index_with(:boolean)
|
||||||
TAG_TYPES.merge! TAG_PREFIXES.index_with(:prefix)
|
TAG_TYPES.merge! DATA_PREFIXES.index_with(:data)
|
||||||
|
TAG_TYPES.merge! ARIA_PREFIXES.index_with(:aria)
|
||||||
TAG_TYPES.freeze
|
TAG_TYPES.freeze
|
||||||
|
|
||||||
PRE_CONTENT_STRINGS = Hash.new { "" }
|
PRE_CONTENT_STRINGS = Hash.new { "" }
|
||||||
|
@ -72,9 +74,18 @@ module ActionView
|
||||||
sep = " "
|
sep = " "
|
||||||
options.each_pair do |key, value|
|
options.each_pair do |key, value|
|
||||||
type = TAG_TYPES[key]
|
type = TAG_TYPES[key]
|
||||||
if type == :prefix && value.is_a?(Hash)
|
if type == :data && value.is_a?(Hash)
|
||||||
value.each_pair do |k, v|
|
value.each_pair do |k, v|
|
||||||
next if v.nil?
|
next if v.nil?
|
||||||
|
output << sep
|
||||||
|
output << prefix_tag_option(key, k, v, escape)
|
||||||
|
end
|
||||||
|
elsif type == :aria && value.is_a?(Hash)
|
||||||
|
value.each_pair do |k, v|
|
||||||
|
next if v.nil?
|
||||||
|
|
||||||
|
v = (v.is_a?(Array) || v.is_a?(Hash)) ? safe_join(TagHelper.build_tag_values(v), " ") : v.to_s
|
||||||
|
|
||||||
output << sep
|
output << sep
|
||||||
output << prefix_tag_option(key, k, v, escape)
|
output << prefix_tag_option(key, k, v, escape)
|
||||||
end
|
end
|
||||||
|
@ -165,8 +176,8 @@ module ActionView
|
||||||
# tag.input type: 'text', disabled: true
|
# tag.input type: 'text', disabled: true
|
||||||
# # => <input type="text" disabled="disabled">
|
# # => <input type="text" disabled="disabled">
|
||||||
#
|
#
|
||||||
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
|
# HTML5 <tt>data-*</tt> and <tt>aria-*</tt> attributes can be set with a
|
||||||
# pointing to a hash of sub-attributes.
|
# single +data+ or +aria+ key pointing to a hash of sub-attributes.
|
||||||
#
|
#
|
||||||
# To play nicely with JavaScript conventions, sub-attributes are dasherized.
|
# To play nicely with JavaScript conventions, sub-attributes are dasherized.
|
||||||
#
|
#
|
||||||
|
|
|
@ -441,11 +441,12 @@ class TagHelperTest < ActionView::TestCase
|
||||||
|
|
||||||
def test_aria_attributes
|
def test_aria_attributes
|
||||||
["aria", :aria].each { |aria|
|
["aria", :aria].each { |aria|
|
||||||
assert_dom_equal '<a aria-a-float="3.14" aria-a-big-decimal="-123.456" aria-a-number="1" aria-array="[1,2,3]" aria-hash="{"key":"value"}" aria-string-with-quotes="double"quote"party"" aria-string="hello" aria-symbol="foo" />',
|
assert_dom_equal '<a aria-a-float="3.14" aria-a-big-decimal="-123.456" aria-a-number="1" aria-truthy="true" aria-falsey="false" aria-array="1 2 3" aria-hash="a b" aria-tokens="a b" aria-string-with-quotes="double"quote"party"" aria-string="hello" aria-symbol="foo" />',
|
||||||
tag("a", aria => { a_float: 3.14, a_big_decimal: BigDecimal("-123.456"), a_number: 1, string: "hello", symbol: :foo, array: [1, 2, 3], hash: { key: "value" }, string_with_quotes: 'double"quote"party"' })
|
tag("a", aria => { nil: nil, a_float: 3.14, a_big_decimal: BigDecimal("-123.456"), a_number: 1, truthy: true, falsey: false, string: "hello", symbol: :foo, array: [1, 2, 3], hash: { a: true, b: "truthy", falsey: false, nil: nil }, tokens: ["a", { b: true, c: false }], string_with_quotes: 'double"quote"party"' })
|
||||||
assert_dom_equal '<a aria-a-float="3.14" aria-a-big-decimal="-123.456" aria-a-number="1" aria-array="[1,2,3]" aria-hash="{"key":"value"}" aria-string-with-quotes="double"quote"party"" aria-string="hello" aria-symbol="foo" />',
|
|
||||||
tag.a(aria: { a_float: 3.14, a_big_decimal: BigDecimal("-123.456"), a_number: 1, string: "hello", symbol: :foo, array: [1, 2, 3], hash: { key: "value" }, string_with_quotes: 'double"quote"party"' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_dom_equal '<a aria-a-float="3.14" aria-a-big-decimal="-123.456" aria-a-number="1" aria-truthy="true" aria-falsey="false" aria-array="1 2 3" aria-hash="a b" aria-tokens="a b" aria-string-with-quotes="double"quote"party"" aria-string="hello" aria-symbol="foo" />',
|
||||||
|
tag.a(aria: { nil: nil, a_float: 3.14, a_big_decimal: BigDecimal("-123.456"), a_number: 1, truthy: true, falsey: false, string: "hello", symbol: :foo, array: [1, 2, 3], hash: { a: true, b: "truthy", falsey: false, nil: nil }, tokens: ["a", { b: true, c: false }], string_with_quotes: 'double"quote"party"' })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_link_to_data_nil_equal
|
def test_link_to_data_nil_equal
|
||||||
|
|
Loading…
Reference in New Issue