forme 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abf3346d90f5f4eddb69a0c0c2a63a2fcee549cf2b928bec7ab8a9505a8135aa
4
- data.tar.gz: 577dccded6b59dae158e5275b7bed4dcb87a9756b29d2e3f367accca1f32313b
3
+ metadata.gz: 581902a62cc974ce05fecf52a4b7debd41b5d89f5ba151c1c03a2f0589f15e7e
4
+ data.tar.gz: 5252e5dca95690ddf46341e443954058644f41c67e2109d7692a6dfb8c3b3ee8
5
5
  SHA512:
6
- metadata.gz: 35dbc13c7de3c746a229b0f4d6026a0dfc3b8fa9dda3952ae3aa2117c6b1ad843ec07c3c40ab754cd2ae91c3ffea83cb281ee48539b886b48b832f2f1f887bab
7
- data.tar.gz: 39fdd8a5b87777f1fd1831adf05c100624619e7e1cdf09d9e207d8118a8ab154971f6d229d7db1c3141faf423816a68a66b67001fd4e0b29462af2a545493e09
6
+ metadata.gz: 39c198d2db64a06eacf30e1ed2e5af10bce818fe45f81c17ab8fcd4db7b1693afb3c2e82d013615105dd71907e96eead22104340068b04361492b853fe03da65
7
+ data.tar.gz: 33bfcf058c9ffc34f9581f3aa78e924b6764b034c56495ebaebd241eb07f5984e4330ddbab3f3a934cc77e66412e01c07decbf81bee07bb10c5d829eb00dff6f
data/CHANGELOG CHANGED
@@ -1,3 +1,25 @@
1
+ === 1.10.0 (2019-05-13)
2
+
3
+ * Make readonly formatter ignore hidden inputs (jeremyevans)
4
+
5
+ * Add :select_labels for date inputs using :as=>:select to use labels for the inputs for better accessibility (jeremyevans)
6
+
7
+ * Add :after_legend error_handler for adding error message after legend when using :legend labeler (jeremyevans)
8
+
9
+ * Add aria-describedby to all inputs with errors where possible for better accessibility (jeremyevans)
10
+
11
+ * Add aria-invalid to all inputs with errors for better accessibility (jeremyevans)
12
+
13
+ * Support :fieldset wrapper and :legend labeler, can be used for accessible radioset/checkboxset (jeremyevans)
14
+
15
+ * Support :tag_label_attr option for radioset and checkbox set for label attributes for each radio/checkbox label (jeremyevans)
16
+
17
+ * Support custom :error_handler in radioset and checkboxset inputs (jeremyevans)
18
+
19
+ * Support custom :labeler in radioset and checkboxset inputs (jeremyevans)
20
+
21
+ * Avoid calling Proc.new with an implicit block, which is deprecated starting in ruby 2.7 (jeremyevans)
22
+
1
23
  === 1.9.0 (2018-11-16)
2
24
 
3
25
  * Automatically add maxlength attributes to text and textarea inputs in the Sequel plugin based on maximum database column length (jeremyevans)
@@ -822,6 +822,8 @@ creates multiple select options. Options:
822
822
  for the select field and string to use a string
823
823
  (:date default: <tt>[:year, '-', :month, '-', :day]</tt>)
824
824
  (:datetime default: <tt>[:year, '-', :month, '-', :day, ' ', :hour, ':', :minute, ':', :second]</tt>)
825
+ :select_labels :: The labels to use for the select boxes. Should be a hash keyed by the
826
+ symbol used in order. By default, no labels are used.
825
827
  :select_options :: The options to use for the select boxes. Should be a hash keyed by the
826
828
  symbol used in order (e.g. <tt>{:year=>1970..2020}</tt>)
827
829
 
@@ -862,6 +864,7 @@ the following options:
862
864
 
863
865
  :tag_wrapper :: The wrapper transformer for individual tags in the set
864
866
  :tag_labeler :: The labeler transformer for individual tags in the set
867
+ :tag_label_attr :: The attributes to use for labels for individual tags in the set
865
868
 
866
869
  === :radioset
867
870
 
@@ -980,7 +983,9 @@ Forme ships with a bunch of built-in transformers that you can use:
980
983
 
981
984
  === +error_handler+
982
985
 
986
+ :after_legend :: designed for usage with :legend labeler, putting error message after legend, adding error for first input in the set
983
987
  :default :: modifies tag to add an error class and adds a span with the error message
988
+ :set :: default error_handler for checkboxset and radioset inputs, that adds an error to the last input in the set
984
989
 
985
990
  This supports the following options:
986
991
 
@@ -998,8 +1003,10 @@ This supports the following options:
998
1003
 
999
1004
  :default :: uses implicit labels, where the tag is a child of the label tag
1000
1005
  :explicit :: uses explicit labels with the for attribute, where tag is a sibling of the label tag
1006
+ :legend :: adds a legend before the tags, mostly useful for accessible checkboxset and radioset inputs
1007
+ :span :: default labeler for checkboxset and radioset inputs that adds a span before the tags
1001
1008
 
1002
- Both of these respect the following options:
1009
+ The :default and :explicit labelers respect the following options:
1003
1010
 
1004
1011
  :label_position :: Can be set to :before or :after to place the label before or after the the input.
1005
1012
  :label_attr :: A hash of attributes to use for the label tag
@@ -1008,6 +1015,7 @@ Both of these respect the following options:
1008
1015
 
1009
1016
  :default :: returns tag without wrapping
1010
1017
  :div :: wraps tag in div tag
1018
+ :fieldset :: wraps tags in a fieldset, mostly useful for accessible checkboxset and radioset inputs
1011
1019
  :fieldset_ol :: same as :li, but also sets +inputs_wrapper+ to :fieldset_ol
1012
1020
  :li :: wraps tag in li tag
1013
1021
  :ol :: same as :li, but also sets +inputs_wrapper+ to :ol
@@ -214,7 +214,7 @@ module Forme
214
214
  yield
215
215
  end
216
216
  else
217
- form.tag(:fieldset, attr, &Proc.new)
217
+ form.tag(:fieldset, attr, &block)
218
218
  end
219
219
  end
220
220
  end
@@ -59,7 +59,7 @@ module Forme
59
59
  ins = opts[:inputs]
60
60
  button = opts[:button]
61
61
  if ins || button
62
- block = Proc.new do |form|
62
+ block = proc do |form|
63
63
  form._inputs(ins, opts) if ins
64
64
  yield form if block_given?
65
65
  form.emit(form.button(button)) if button
@@ -11,10 +11,55 @@ module Forme
11
11
 
12
12
  # Return tag with error message span tag after it.
13
13
  def call(tag, input)
14
+ [tag, error_tag(input)]
15
+ end
16
+
17
+ private
18
+
19
+ def error_tag(input)
14
20
  attr = input.opts[:error_attr]
15
21
  attr = attr ? attr.dup : {}
16
22
  Forme.attr_classes(attr, 'error_message')
17
- [tag, input.tag(:span, attr, input.opts[:error])]
23
+
24
+ if id = input.opts[:error_id]
25
+ unless attr['id'] || attr[:id]
26
+ attr['id'] = id
27
+ end
28
+ end
29
+
30
+ input.tag(:span, attr, input.opts[:error])
31
+ end
32
+ end
33
+
34
+ class ErrorHandler::Set < ErrorHandler
35
+ Forme.register_transformer(:error_handler, :set, new)
36
+
37
+ def call(tag, input)
38
+ return super unless last_input = input.opts[:last_input]
39
+
40
+ last_input.opts[:error] = input.opts[:error]
41
+ last_input.opts[:error_attr] = input.opts[:error_attr] if input.opts[:error_attr]
42
+ last_input.opts[:error_handler] = :default
43
+
44
+ tag
45
+ end
46
+ end
47
+
48
+ class ErrorHandler::AfterLegend < ErrorHandler
49
+ Forme.register_transformer(:error_handler, :after_legend, new)
50
+
51
+ def call(tag, input)
52
+ return super unless tag.is_a?(Array)
53
+ return super unless tag.first.is_a?(Tag)
54
+ return super unless tag.first.type == :legend
55
+
56
+ first_input = input.opts[:first_input]
57
+ attr = first_input.opts[:attr] ||= {}
58
+ Forme.attr_classes(attr, 'error')
59
+ attr['aria-invalid'] = 'true'
60
+ attr['aria-describedby'] = input.opts[:error_id] = "#{first_input.opts[:id]}_error_message"
61
+
62
+ tag.insert(1, error_tag(input))
18
63
  end
19
64
  end
20
65
  end
@@ -61,7 +61,6 @@ module Forme
61
61
  @attr = attr ? attr.dup : {}
62
62
  @opts = input.opts
63
63
  normalize_options
64
-
65
64
  tag = if html = input.opts[:html]
66
65
  html = html.call(input) if html.respond_to?(:call)
67
66
  form.raw(html)
@@ -158,9 +157,10 @@ module Forme
158
157
  ops = ops.merge(@opts[:select_options]) if @opts[:select_options]
159
158
  first_input = true
160
159
  format = DATE_SELECT_FORMAT
160
+ @opts[:select_labels] ||= {}
161
161
  order.map do |x|
162
162
  next x if x.is_a?(String)
163
- opts = @opts.merge(:label=>nil, :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :value=>values[x], :options=>ops[x].map{|y| [sprintf(format, y), y]})
163
+ opts = @opts.merge(:label=>@opts[:select_labels][x], :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :value=>values[x], :options=>ops[x].map{|y| [sprintf(format, y), y]})
164
164
  opts[:id] = if first_input
165
165
  first_input = false
166
166
  id
@@ -221,23 +221,22 @@ module Forme
221
221
  key = @opts[:key]
222
222
  name = @opts[:name]
223
223
  id = @opts[:id]
224
- if @opts[:error]
225
- @opts[:set_error] = @opts.delete(:error)
226
- end
227
- if @opts[:label]
228
- @opts[:set_label] = @opts.delete(:label)
229
- end
224
+ @opts[:labeler] ||= :span
225
+ @opts[:error_handler] ||= :set
230
226
 
231
227
  tag_wrapper = Forme.transformer(:tag_wrapper, @opts.delete(:tag_wrapper), @input.form_opts) || :default
232
228
  tag_labeler = Forme.transformer(:labeler, @opts.delete(:tag_labeler), @input.form_opts) || :default
233
229
  wrapper = @opts.fetch(:wrapper){@opts[:wrapper] = @input.form_opts[:set_wrapper] || @input.form_opts[:wrapper]}
234
230
  wrapper = Forme.transformer(:wrapper, wrapper)
231
+ tag_label_attr = @opts[:tag_label_attr] || @opts[:label_attr]
235
232
 
236
- tags = process_select_optgroups(:_format_set_optgroup) do |label, value, sel, attrs|
233
+ first_input = nil
234
+ last_input = nil
235
+ ret = process_select_optgroups(:_format_set_optgroup) do |label, value, sel, attrs|
237
236
  value = label if value.nil?
238
237
  label_attr = {:class=>:option}
239
- label_attr.merge!(@opts[:label_attr]) if @opts[:label_attr]
240
- r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>label_attr, :wrapper=>tag_wrapper, :labeler=>tag_labeler)
238
+ label_attr.merge!(tag_label_attr) if tag_label_attr
239
+ r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>label_attr, :wrapper=>tag_wrapper, :labeler=>tag_labeler, :error=>nil, :error_attr=>nil)
241
240
  if r_opts[:value].nil?
242
241
  r_opts[:value] = value unless value.nil?
243
242
  end
@@ -255,31 +254,15 @@ module Forme
255
254
  r_opts[:key_id] ||= value
256
255
  end
257
256
 
258
- form._input(type, r_opts)
259
- end
260
-
261
- if @opts[:set_error]
262
- _add_set_error(tags)
257
+ input = form._input(type, r_opts)
258
+ first_input ||= input
259
+ last_input = input
263
260
  end
264
261
 
265
- tags.unshift(set_label) if @opts[:set_label]
266
-
267
- tags
268
- end
262
+ @opts[:first_input] = first_input
263
+ @opts[:last_input] = last_input
269
264
 
270
- def set_label
271
- form._tag(:span, {:class=>:label}, @opts[:set_label])
272
- end
273
-
274
- def _add_set_error(tags)
275
- if (last_input = tags.last) && last_input.is_a?(Input)
276
- last_input.opts[:error] = @opts[:set_error]
277
- last_input.opts[:error_attr] = @opts[:error_attr] if @opts[:error_attr]
278
- else
279
- attr = @opts[:error_attr] || {}
280
- Forme.attr_classes(attr, 'error_message')
281
- tags << form._tag(:span, attr, [@opts[:set_error]])
282
- end
265
+ ret
283
266
  end
284
267
 
285
268
  # Formats a textarea. Respects the following options:
@@ -321,7 +304,19 @@ module Forme
321
304
  handle_errors_option
322
305
 
323
306
  Forme.attr_classes(@attr, @opts[:class]) if @opts.has_key?(:class)
324
- Forme.attr_classes(@attr, 'error') if @opts[:error]
307
+
308
+ if @opts[:error]
309
+ Forme.attr_classes(@attr, 'error')
310
+ @attr["aria-invalid"] = "true"
311
+ if @opts.fetch(:error_handler, true)
312
+ unless @opts[:error_id]
313
+ if id = @attr[:id] || @attr['id']
314
+ error_id = @attr['aria-describedby'] ||= "#{id}_error_message"
315
+ @opts[:error_id] = error_id
316
+ end
317
+ end
318
+ end
319
+ end
325
320
 
326
321
  if data = opts[:data]
327
322
  data.each do |k, v|
@@ -540,8 +535,11 @@ module Forme
540
535
  end
541
536
 
542
537
  # Use a span with text instead of an input field.
538
+ # For hidden inputs, do not show anything
543
539
  def _format_input(type)
544
- tag(:span, {'class'=>'readonly-text'}, @attr[:value])
540
+ unless type.to_s == 'hidden'
541
+ tag(:span, {'class'=>'readonly-text'}, @attr[:value])
542
+ end
545
543
  end
546
544
 
547
545
  # Disabled radio button inputs.
@@ -10,7 +10,7 @@ module Forme
10
10
  # Wrap the inputs in a <fieldset>. If the :legend
11
11
  # option is given, add a <legend> tag as the first
12
12
  # child of the fieldset.
13
- def call(form, opts)
13
+ def call(form, opts, &block)
14
14
  attr = opts[:attr] ? opts[:attr].dup : {}
15
15
  Forme.attr_classes(attr, 'inputs')
16
16
  if legend = opts[:legend]
@@ -19,7 +19,7 @@ module Forme
19
19
  yield
20
20
  end
21
21
  else
22
- form.tag(:fieldset, attr, &Proc.new)
22
+ form.tag(:fieldset, attr, &block)
23
23
  end
24
24
  end
25
25
  end
@@ -77,4 +77,23 @@ module Forme
77
77
  t
78
78
  end
79
79
  end
80
+
81
+ class Labeler::Span
82
+ Forme.register_transformer(:labeler, :span, new)
83
+
84
+ def call(tag, input)
85
+ label_attr = input.opts[:label_attr]
86
+ label_attr = label_attr ? label_attr.dup : {}
87
+ Forme.attr_classes(label_attr, "label")
88
+ [input.tag(:span, label_attr, input.opts[:label]), tag]
89
+ end
90
+ end
91
+
92
+ class Labeler::Legend
93
+ Forme.register_transformer(:labeler, :legend, new)
94
+
95
+ def call(tag, input)
96
+ [input.tag(:legend, input.opts[:label_attr], input.opts[:label]), tag]
97
+ end
98
+ end
80
99
  end
@@ -23,7 +23,7 @@ module Forme
23
23
  input.tag(@type, input.opts[:wrapper_attr], super)
24
24
  end
25
25
 
26
- [:li, :p, :div, :span, :td].each do |x|
26
+ [:li, :p, :div, :span, :td, :fieldset].each do |x|
27
27
  Forme.register_transformer(:wrapper, x, new(x))
28
28
  end
29
29
  end
@@ -6,7 +6,7 @@ module Forme
6
6
  MAJOR = 1
7
7
 
8
8
  # The minor version of Forme, updated for new feature releases of Forme.
9
- MINOR = 9
9
+ MINOR = 10
10
10
 
11
11
  # The patch version of Forme, updated only for bug fixes from the last
12
12
  # feature release.
@@ -116,13 +116,13 @@ describe "Forme plain forms" do
116
116
 
117
117
  it "should consider form's :errors hash based on the :key option" do
118
118
  @f.opts[:errors] = { 'foo' => 'must be present' }
119
- @f.input(:text, :key=>"foo").to_s.must_equal "<input class=\"error\" id=\"foo\" name=\"foo\" type=\"text\"/><span class=\"error_message\">must be present</span>"
119
+ @f.input(:text, :key=>"foo").to_s.must_equal "<input aria-describedby=\"foo_error_message\" aria-invalid=\"true\" class=\"error\" id=\"foo\" name=\"foo\" type=\"text\"/><span class=\"error_message\" id=\"foo_error_message\">must be present</span>"
120
120
  end
121
121
 
122
122
  it "should consider form's :errors hash based on the :key option when using namespaces" do
123
123
  @f.opts[:errors] = { 'bar' => { 'foo' => 'must be present' } }
124
124
  @f.with_opts(:namespace=>['bar']) do
125
- @f.input(:text, :key=>"foo").to_s.must_equal "<input class=\"error\" id=\"bar_foo\" name=\"bar[foo]\" type=\"text\"/><span class=\"error_message\">must be present</span>"
125
+ @f.input(:text, :key=>"foo").to_s.must_equal "<input aria-describedby=\"bar_foo_error_message\" aria-invalid=\"true\" class=\"error\" id=\"bar_foo\" name=\"bar[foo]\" type=\"text\"/><span class=\"error_message\" id=\"bar_foo_error_message\">must be present</span>"
126
126
  end
127
127
  end
128
128
 
@@ -301,6 +301,10 @@ describe "Forme plain forms" do
301
301
  @f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5)).to_s.must_equal %{<select id="bar" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select>}
302
302
  end
303
303
 
304
+ it "should use labels for select boxes for dates if the :as=>:select and :select_labels options are given" do
305
+ @f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5), :select_labels=>{:year=>'Y', :month=>'M', :day=>'D'}, :labeler=>:explicit).to_s.must_equal %{<label class="label-before" for="bar">Y</label><select id="bar" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<label class="label-before" for="bar_month">M</label><select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<label class="label-before" for="bar_day">D</label><select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select>}
306
+ end
307
+
304
308
  it "should allow ordering date select boxes via :order" do
305
309
  @f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5), :order=>[:month, '/', :day, '/', :year]).to_s.must_equal %{<select id="bar" name="foo[month]">#{sel(1..12, 6)}</select>/<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select>/<select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>}
306
310
  end
@@ -433,7 +437,11 @@ describe "Forme plain forms" do
433
437
  end
434
438
 
435
439
  it "should create set of radio buttons with :error and :error_attr options" do
436
- @f.input(:radioset, :options=>[1, 2, 3], :selected=>2, :error=>'foo', :error_attr=>{'bar'=>'baz'}).to_s.must_equal '<label class="option"><input type="radio" value="1"/> 1</label><label class="option"><input checked="checked" type="radio" value="2"/> 2</label><label class="option"><input class="error" type="radio" value="3"/> 3</label><span bar="baz" class="error_message">foo</span>'
440
+ @f.input(:radioset, :options=>[1, 2, 3], :selected=>2, :error=>'foo', :error_attr=>{'bar'=>'baz'}).to_s.must_equal '<label class="option"><input type="radio" value="1"/> 1</label><label class="option"><input checked="checked" type="radio" value="2"/> 2</label><label class="option"><input aria-invalid="true" class="error" type="radio" value="3"/> 3</label><span bar="baz" class="error_message">foo</span>'
441
+ end
442
+
443
+ it "should support custom error_handler for set of radio buttons" do
444
+ @f.input(:radioset, :options=>[1, 2, 3], :selected=>2, :error=>'foo', :error_attr=>{'bar'=>'baz'}, :error_handler=>lambda{|tag, input| input.tag(:div, {}, tag)}).to_s.must_equal '<div><label class="option"><input type="radio" value="1"/> 1</label><label class="option"><input checked="checked" type="radio" value="2"/> 2</label><label class="option"><input type="radio" value="3"/> 3</label></div>'
437
445
  end
438
446
 
439
447
  it "should create set of checkbox buttons" do
@@ -457,10 +465,26 @@ describe "Forme plain forms" do
457
465
  @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :label=>'foo').to_s.must_equal '<span class="label">foo</span><label class="option"><input type="checkbox" value="1"/> a</label><label class="option"><input type="checkbox" value="2"/> b</label><label class="option"><input type="checkbox" value="3"/> c</label>'
458
466
  end
459
467
 
468
+ it "should support fieldset/legend for checkboxsets" do
469
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :label=>'foo', :labeler=>:legend, :wrapper=>:fieldset).to_s.must_equal '<fieldset><legend>foo</legend><label class="option"><input type="checkbox" value="1"/> a</label><label class="option"><input type="checkbox" value="2"/> b</label><label class="option"><input type="checkbox" value="3"/> c</label></fieldset>'
470
+ end
471
+
472
+ it "should support legend with attributes for checkboxsets" do
473
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :label=>'foo', :label_attr=>{:class=>"baz"}, :tag_label_attr=>{:class=>"bar"}, :labeler=>:legend, :wrapper=>:fieldset).to_s.must_equal '<fieldset><legend class="baz">foo</legend><label class="bar"><input type="checkbox" value="1"/> a</label><label class="bar"><input type="checkbox" value="2"/> b</label><label class="bar"><input type="checkbox" value="3"/> c</label></fieldset>'
474
+ end
475
+
476
+ it "should support legend with attributes for checkboxsets, handling errors with :error_handler=>:after_legend" do
477
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :id=>:quux, :label=>'foo', :label_attr=>{:class=>"baz"}, :tag_label_attr=>{:class=>"bar"}, :labeler=>:legend, :wrapper=>:fieldset, :error=>'bar2', :error_handler=>:after_legend).to_s.must_equal '<fieldset><legend class="baz">foo</legend><span class="error_message" id="quux_1_error_message">bar2</span><label class="bar"><input aria-describedby="quux_1_error_message" aria-invalid="true" class="error" id="quux_1" type="checkbox" value="1"/> a</label><label class="bar"><input id="quux_2" type="checkbox" value="2"/> b</label><label class="bar"><input id="quux_3" type="checkbox" value="3"/> c</label></fieldset>'
478
+ end
479
+
460
480
  it "should support :tag_labeler for checkboxsets" do
461
481
  @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :tag_labeler=>:explicit).to_s.must_equal '<input type="checkbox" value="1"/><label class="option label-after">a</label><input type="checkbox" value="2"/><label class="option label-after">b</label><input type="checkbox" value="3"/><label class="option label-after">c</label>'
462
482
  end
463
483
 
484
+ it "should support custom :labeler for checkboxsets" do
485
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :label=>'foo', :labeler=>lambda{|tag, input| input.tag(:div, {}, tag)}).to_s.must_equal '<div><label class="option"><input type="checkbox" value="1"/> a</label><label class="option"><input type="checkbox" value="2"/> b</label><label class="option"><input type="checkbox" value="3"/> c</label></div>'
486
+ end
487
+
464
488
  it "should create set of checkbox buttons with options and values with hashes" do
465
489
  @f.input(:checkboxset, :options=>[[:a, {:attr=>{:foo=>1}}], [:b, {:class=>'foo', :value=>2}], [:c, {:id=>:baz}]], :selected=>2).to_s.must_equal '<label class="option"><input foo="1" type="checkbox" value="a"/> a</label><label class="option"><input checked="checked" class="foo" type="checkbox" value="2"/> b</label><label class="option"><input id="baz" type="checkbox" value="c"/> c</label>'
466
490
  end
@@ -491,7 +515,7 @@ describe "Forme plain forms" do
491
515
  end
492
516
 
493
517
  it "should respect the :error option for checkbox sets" do
494
- @f.input(:checkboxset, :options=>[1, 2, 3], :error=>'foo', :value=>2).to_s.must_equal '<label class="option"><input type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="2"/> 2</label><label class="option"><input class="error" type="checkbox" value="3"/> 3</label><span class="error_message">foo</span>'
518
+ @f.input(:checkboxset, :options=>[1, 2, 3], :error=>'foo', :value=>2).to_s.must_equal '<label class="option"><input type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="2"/> 2</label><label class="option"><input aria-invalid="true" class="error" type="checkbox" value="3"/> 3</label><span class="error_message">foo</span>'
495
519
  end
496
520
 
497
521
  it "should create set of checkbox buttons with fieldsets and legends for optgroups" do
@@ -558,19 +582,23 @@ describe "Forme plain forms" do
558
582
  end
559
583
 
560
584
  it "should automatically note the input has errors if :error option is used" do
561
- @f.input(:text, :error=>'Bad Stuff!', :value=>'foo').to_s.must_equal '<input class="error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
585
+ @f.input(:text, :error=>'Bad Stuff!', :value=>'foo').to_s.must_equal '<input aria-invalid="true" class="error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
562
586
  end
563
587
 
564
588
  it "should add an error message after the label" do
565
- @f.input(:text, :error=>'Bad Stuff!', :value=>'foo', :label=>"Foo").to_s.must_equal '<label>Foo: <input class="error" type="text" value="foo"/></label><span class="error_message">Bad Stuff!</span>'
589
+ @f.input(:text, :error=>'Bad Stuff!', :value=>'foo', :label=>"Foo").to_s.must_equal '<label>Foo: <input aria-invalid="true" class="error" type="text" value="foo"/></label><span class="error_message">Bad Stuff!</span>'
566
590
  end
567
591
 
568
592
  it "should add to existing :class option if :error option is used" do
569
- @f.input(:text, :error=>'Bad Stuff!', :class=>'bar', :value=>'foo').to_s.must_equal '<input class="bar error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
593
+ @f.input(:text, :error=>'Bad Stuff!', :class=>'bar', :value=>'foo').to_s.must_equal '<input aria-invalid="true" class="bar error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
570
594
  end
571
595
 
572
596
  it "should respect :error_attr option for setting the attributes for the error message span" do
573
- @f.input(:text, :error=>'Bad Stuff!', :value=>'foo', :error_attr=>{:class=>'foo'}).to_s.must_equal '<input class="error" type="text" value="foo"/><span class="foo error_message">Bad Stuff!</span>'
597
+ @f.input(:text, :error=>'Bad Stuff!', :value=>'foo', :error_attr=>{:class=>'foo'}).to_s.must_equal '<input aria-invalid="true" class="error" type="text" value="foo"/><span class="foo error_message">Bad Stuff!</span>'
598
+ end
599
+
600
+ it "should use aria-describedby and aria-invalid tags for errors with where the id attribute can be determined" do
601
+ @f.input(:text, :error=>'Bad Stuff!', :id=>:bar, :value=>'foo', :error_attr=>{:class=>'foo'}).to_s.must_equal '<input aria-describedby="bar_error_message" aria-invalid="true" class="error" id="bar" type="text" value="foo"/><span class="foo error_message" id="bar_error_message">Bad Stuff!</span>'
574
602
  end
575
603
 
576
604
  it "#open should return an opening tag" do
@@ -696,7 +724,7 @@ describe "Forme plain forms" do
696
724
  end
697
725
 
698
726
  it "inputs should have helper displayed inside wrapper, after error" do
699
- @f.input(:text, :help=>"List type of foo", :error=>'bad', :wrapper=>:li).to_s.must_equal '<li><input class="error" type="text"/><span class="error_message">bad</span><span class="helper">List type of foo</span></li>'
727
+ @f.input(:text, :help=>"List type of foo", :error=>'bad', :wrapper=>:li).to_s.must_equal '<li><input aria-invalid="true" class="error" type="text"/><span class="error_message">bad</span><span class="helper">List type of foo</span></li>'
700
728
  end
701
729
 
702
730
  it "inputs should accept a :formatter option to use a custom formatter" do
@@ -729,7 +757,7 @@ describe "Forme plain forms" do
729
757
  end
730
758
 
731
759
  it "inputs should accept a :error_handler option to use a custom error_handler" do
732
- @f.input(:textarea, :error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}, :error=>'bar', :id=>:foo).to_s.must_equal '<textarea class="error" id="foo"></textarea>!!! bar'
760
+ @f.input(:textarea, :error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}, :error=>'bar', :id=>:foo).to_s.must_equal '<textarea aria-describedby="foo_error_message" aria-invalid="true" class="error" id="foo"></textarea>!!! bar'
733
761
  end
734
762
 
735
763
  it "#inputs should accept a :inputs_wrapper option to use a custom inputs_wrapper" do
@@ -745,7 +773,7 @@ describe "Forme plain forms" do
745
773
  end
746
774
 
747
775
  it "inputs should accept a :error_handler=>nil option to not use an error_handler" do
748
- @f.input(:textarea, :error_handler=>nil, :error=>'bar', :id=>:foo).to_s.must_equal '<textarea class="error" id="foo"></textarea>'
776
+ @f.input(:textarea, :error_handler=>nil, :error=>'bar', :id=>:foo).to_s.must_equal '<textarea aria-invalid="true" class="error" id="foo"></textarea>'
749
777
  end
750
778
 
751
779
  it "#inputs should accept a :inputs_wrapper=>nil option to not use an inputs_wrapper" do
@@ -819,7 +847,7 @@ describe "Forme custom" do
819
847
  end
820
848
 
821
849
  it "error_handlers can be specified as a proc" do
822
- Forme::Form.new(:error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}).input(:textarea, :name=>'foo', :error=>'bar').to_s.must_equal '<textarea class="error" name="foo"></textarea>!!! bar'
850
+ Forme::Form.new(:error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}).input(:textarea, :name=>'foo', :error=>'bar').to_s.must_equal '<textarea aria-invalid="true" class="error" name="foo"></textarea>!!! bar'
823
851
  end
824
852
 
825
853
  it "wrappers can be specified as a proc" do
@@ -854,6 +882,10 @@ describe "Forme built-in custom" do
854
882
  Forme::Form.new(:formatter=>:readonly).input(:select, :label=>"Foo", :options=>[1, 2, 3], :value=>2).to_s.must_equal "<label>Foo: <span>2</span></label>"
855
883
  end
856
884
 
885
+ it "formatter: readonly removes hidden inputs" do
886
+ Forme::Form.new(:formatter=>:readonly).input(:hidden, :value=>"Bar").to_s.must_equal ""
887
+ end
888
+
857
889
  it "formatter: readonly formats text into paragraphs for textarea inputs" do
858
890
  Forme::Form.new(:formatter=>:readonly).input(:textarea, :label=>"Foo", :value=>"\n Bar\nBaz\n\nQuuz\n\n1\n2 \n").to_s.must_equal "<label>Foo: <div class=\"readonly-textarea\"><p> Bar<br />Baz</p><p>Quuz</p><p>1<br />2 </p></div></label>"
859
891
  end
@@ -877,7 +909,7 @@ describe "Forme built-in custom" do
877
909
  end
878
910
 
879
911
  it "labeler: explicit should handle tags with errors" do
880
- Forme::Form.new(:labeler=>:explicit).input(:text, :error=>'Bad Stuff!', :value=>'f', :id=>'foo', :label=>'bar').to_s.must_equal '<label class="label-before" for="foo">bar</label><input class="error" id="foo" type="text" value="f"/><span class="error_message">Bad Stuff!</span>'
912
+ Forme::Form.new(:labeler=>:explicit).input(:text, :error=>'Bad Stuff!', :value=>'f', :id=>'foo', :label=>'bar').to_s.must_equal '<label class="label-before" for="foo">bar</label><input aria-describedby="foo_error_message" aria-invalid="true" class="error" id="foo" type="text" value="f"/><span class="error_message" id="foo_error_message">Bad Stuff!</span>'
881
913
  end
882
914
 
883
915
  it "wrapper: li wraps tag in an li" do
@@ -915,7 +947,7 @@ describe "Forme built-in custom" do
915
947
  end
916
948
 
917
949
  it "wrapper: trtd should use at most 2 td tags" do
918
- Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'Foo', :error=>'Bar').to_s.must_equal '<tr><td><label class="label-before" for="foo">Foo</label></td><td><textarea class="error" id="foo"></textarea><span class="error_message">Bar</span></td></tr>'
950
+ Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'Foo', :error=>'Bar').to_s.must_equal '<tr><td><label class="label-before" for="foo">Foo</label></td><td><textarea aria-describedby="foo_error_message" aria-invalid="true" class="error" id="foo"></textarea><span class="error_message" id="foo_error_message">Bar</span></td></tr>'
919
951
  end
920
952
 
921
953
  it "wrapper: trtd should handle inputs with label after" do
@@ -20,7 +20,15 @@ rescue LoadError
20
20
  end
21
21
 
22
22
  class FormeRodaTest < Roda
23
- use Rack::Session::Cookie, :secret => "__a_very_long_string__"
23
+ opts[:check_dynamic_arity] = opts[:check_arity] = :warn
24
+
25
+ if defined?(Roda::RodaVersionNumber) && Roda::RodaVersionNumber >= 30100
26
+ require 'roda/session_middleware'
27
+ opts[:sessions_convert_symbols] = true
28
+ use RodaSessionMiddleware, :secret=>SecureRandom.random_bytes(64), :key=>'rack.session'
29
+ else
30
+ use Rack::Session::Cookie, :secret => "__a_very_long_string__"
31
+ end
24
32
 
25
33
  def erb(s, opts={})
26
34
  render(opts.merge(:inline=>s))
@@ -134,7 +134,7 @@ describe "Forme Sequel::Model forms" do
134
134
 
135
135
  it "should handle errors on radio buttons for boolean fields if :as=>:radio is used" do
136
136
  @ab.errors.add(:platinum, 'foo')
137
- @b.input(:platinum, :as=>:radio).to_s.must_equal '<span class="label">Platinum</span><label class="option"><input id="album_platinum_yes" name="album[platinum]" type="radio" value="t"/> Yes</label><label class="option"><input checked="checked" class="error" id="album_platinum_no" name="album[platinum]" type="radio" value="f"/> No</label><span class="error_message">foo</span>'
137
+ @b.input(:platinum, :as=>:radio).to_s.must_equal '<span class="label">Platinum</span><label class="option"><input id="album_platinum_yes" name="album[platinum]" type="radio" value="t"/> Yes</label><label class="option"><input aria-describedby="album_platinum_no_error_message" aria-invalid="true" checked="checked" class="error" id="album_platinum_no" name="album[platinum]" type="radio" value="f"/> No</label><span class="error_message" id="album_platinum_no_error_message">foo</span>'
138
138
  end
139
139
 
140
140
  it "should handle Raw :label options if :as=>:radio is used" do
@@ -272,8 +272,8 @@ describe "Forme Sequel::Model forms" do
272
272
 
273
273
  it "should handle an error message on the underlying column for pg_array_to_many associations" do
274
274
  @ab.errors.add(:atag_ids, 'tis not valid')
275
- @b.input(:atags).to_s.must_equal '<label>Atags: <select class="error" id="album_atag_ids" multiple="multiple" name="album[atag_ids][]"><option selected="selected" value="1">s</option><option selected="selected" value="2">t</option><option value="3">u</option></select></label><span class="error_message">tis not valid</span>'
276
- @b.input(:atags, :as=>:checkbox).to_s.must_equal '<span class="label">Atags</span><label class="option"><input checked="checked" id="album_atag_ids_1" name="album[atag_ids][]" type="checkbox" value="1"/> s</label><label class="option"><input checked="checked" id="album_atag_ids_2" name="album[atag_ids][]" type="checkbox" value="2"/> t</label><label class="option"><input class="error" id="album_atag_ids_3" name="album[atag_ids][]" type="checkbox" value="3"/> u</label><span class="error_message">tis not valid</span>'
275
+ @b.input(:atags).to_s.must_equal '<label>Atags: <select aria-describedby="album_atag_ids_error_message" aria-invalid="true" class="error" id="album_atag_ids" multiple="multiple" name="album[atag_ids][]"><option selected="selected" value="1">s</option><option selected="selected" value="2">t</option><option value="3">u</option></select></label><span class="error_message" id="album_atag_ids_error_message">tis not valid</span>'
276
+ @b.input(:atags, :as=>:checkbox).to_s.must_equal '<span class="label">Atags</span><label class="option"><input checked="checked" id="album_atag_ids_1" name="album[atag_ids][]" type="checkbox" value="1"/> s</label><label class="option"><input checked="checked" id="album_atag_ids_2" name="album[atag_ids][]" type="checkbox" value="2"/> t</label><label class="option"><input aria-describedby="album_atag_ids_3_error_message" aria-invalid="true" class="error" id="album_atag_ids_3" name="album[atag_ids][]" type="checkbox" value="3"/> u</label><span class="error_message" id="album_atag_ids_3_error_message">tis not valid</span>'
277
277
  end
278
278
 
279
279
  it "should use a regular select box for *_to_many associations if multiple if false" do
@@ -318,7 +318,7 @@ describe "Forme Sequel::Model forms" do
318
318
 
319
319
  it "should handle errors on methods not backed by columns" do
320
320
  @ab.errors.add(:artist_name, 'foo')
321
- @b.input(:artist_name).to_s.must_equal '<label>Artist name: <input class="error" id="album_artist_name" name="album[artist_name]" type="text" value="a"/></label><span class="error_message">foo</span>'
321
+ @b.input(:artist_name).to_s.must_equal '<label>Artist name: <input aria-describedby="album_artist_name_error_message" aria-invalid="true" class="error" id="album_artist_name" name="album[artist_name]" type="text" value="a"/></label><span class="error_message" id="album_artist_name_error_message">foo</span>'
322
322
  end
323
323
 
324
324
  it "should respect a :type option with a schema type as the input type for methods not backed by columns" do
@@ -333,12 +333,12 @@ describe "Forme Sequel::Model forms" do
333
333
 
334
334
  it "should correctly show an error message if there is one" do
335
335
  @ab.errors.add(:name, 'tis not valid')
336
- @b.input(:name).to_s.must_equal '<label>Name: <input class="error" id="album_name" maxlength="255" name="album[name]" type="text" value="b"/></label><span class="error_message">tis not valid</span>'
336
+ @b.input(:name).to_s.must_equal '<label>Name: <input aria-describedby="album_name_error_message" aria-invalid="true" class="error" id="album_name" maxlength="255" name="album[name]" type="text" value="b"/></label><span class="error_message" id="album_name_error_message">tis not valid</span>'
337
337
  end
338
338
 
339
339
  it "should correctly show an error message for many_to_one associations if there is one" do
340
340
  @ab.errors.add(:artist_id, 'tis not valid')
341
- @b.input(:artist).to_s.must_equal '<label>Artist: <select class="error" id="album_artist_id" name="album[artist_id]"><option value=""></option><option selected="selected" value="1">a</option><option value="2">d</option></select></label><span class="error_message">tis not valid</span>'
341
+ @b.input(:artist).to_s.must_equal '<label>Artist: <select aria-describedby="album_artist_id_error_message" aria-invalid="true" class="error" id="album_artist_id" name="album[artist_id]"><option value=""></option><option selected="selected" value="1">a</option><option value="2">d</option></select></label><span class="error_message" id="album_artist_id_error_message">tis not valid</span>'
342
342
  end
343
343
 
344
344
  it "should raise an error for unhandled associations" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forme
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-16 00:00:00.000000000 Z
11
+ date: 2019-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -220,8 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
220
  - !ruby/object:Gem::Version
221
221
  version: '0'
222
222
  requirements: []
223
- rubyforge_project:
224
- rubygems_version: 2.7.6
223
+ rubygems_version: 3.0.3
225
224
  signing_key:
226
225
  specification_version: 4
227
226
  summary: HTML forms library