simple_form 3.4.0 → 4.1.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.

Potentially problematic release.


This version of simple_form might be problematic. Click here for more details.

Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +62 -0
  3. data/README.md +208 -36
  4. data/lib/generators/simple_form/install_generator.rb +1 -0
  5. data/lib/generators/simple_form/templates/README +3 -3
  6. data/lib/generators/simple_form/templates/_form.html.erb +2 -0
  7. data/lib/generators/simple_form/templates/_form.html.haml +2 -0
  8. data/lib/generators/simple_form/templates/_form.html.slim +1 -0
  9. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +15 -5
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +358 -73
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +20 -8
  12. data/lib/simple_form/action_view_extensions/builder.rb +1 -0
  13. data/lib/simple_form/action_view_extensions/form_helper.rb +1 -0
  14. data/lib/simple_form/components/errors.rb +15 -2
  15. data/lib/simple_form/components/hints.rb +1 -0
  16. data/lib/simple_form/components/html5.rb +1 -0
  17. data/lib/simple_form/components/label_input.rb +2 -1
  18. data/lib/simple_form/components/labels.rb +11 -4
  19. data/lib/simple_form/components/maxlength.rb +4 -13
  20. data/lib/simple_form/components/min_max.rb +1 -0
  21. data/lib/simple_form/components/minlength.rb +5 -14
  22. data/lib/simple_form/components/pattern.rb +1 -0
  23. data/lib/simple_form/components/placeholders.rb +2 -1
  24. data/lib/simple_form/components/readonly.rb +1 -0
  25. data/lib/simple_form/components.rb +1 -0
  26. data/lib/simple_form/error_notification.rb +1 -0
  27. data/lib/simple_form/form_builder.rb +81 -26
  28. data/lib/simple_form/helpers/autofocus.rb +1 -0
  29. data/lib/simple_form/helpers/disabled.rb +1 -0
  30. data/lib/simple_form/helpers/readonly.rb +1 -0
  31. data/lib/simple_form/helpers/required.rb +1 -0
  32. data/lib/simple_form/helpers/validators.rb +2 -1
  33. data/lib/simple_form/helpers.rb +1 -0
  34. data/lib/simple_form/i18n_cache.rb +1 -0
  35. data/lib/simple_form/inputs/base.rb +24 -2
  36. data/lib/simple_form/inputs/block_input.rb +1 -0
  37. data/lib/simple_form/inputs/boolean_input.rb +4 -2
  38. data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
  39. data/lib/simple_form/inputs/collection_input.rb +3 -2
  40. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +2 -1
  41. data/lib/simple_form/inputs/collection_select_input.rb +1 -0
  42. data/lib/simple_form/inputs/color_input.rb +14 -0
  43. data/lib/simple_form/inputs/date_time_input.rb +1 -0
  44. data/lib/simple_form/inputs/file_input.rb +1 -0
  45. data/lib/simple_form/inputs/grouped_collection_select_input.rb +1 -0
  46. data/lib/simple_form/inputs/hidden_input.rb +1 -0
  47. data/lib/simple_form/inputs/numeric_input.rb +1 -0
  48. data/lib/simple_form/inputs/password_input.rb +1 -0
  49. data/lib/simple_form/inputs/priority_input.rb +1 -0
  50. data/lib/simple_form/inputs/range_input.rb +1 -0
  51. data/lib/simple_form/inputs/string_input.rb +2 -1
  52. data/lib/simple_form/inputs/text_input.rb +1 -0
  53. data/lib/simple_form/inputs.rb +2 -0
  54. data/lib/simple_form/map_type.rb +1 -0
  55. data/lib/simple_form/railtie.rb +1 -0
  56. data/lib/simple_form/tags.rb +1 -0
  57. data/lib/simple_form/version.rb +2 -1
  58. data/lib/simple_form/wrappers/builder.rb +1 -0
  59. data/lib/simple_form/wrappers/leaf.rb +2 -1
  60. data/lib/simple_form/wrappers/many.rb +1 -0
  61. data/lib/simple_form/wrappers/root.rb +2 -0
  62. data/lib/simple_form/wrappers/single.rb +2 -1
  63. data/lib/simple_form/wrappers.rb +1 -0
  64. data/lib/simple_form.rb +58 -7
  65. data/test/action_view_extensions/builder_test.rb +6 -5
  66. data/test/action_view_extensions/form_helper_test.rb +3 -2
  67. data/test/components/custom_components_test.rb +62 -0
  68. data/test/components/label_test.rb +33 -4
  69. data/test/form_builder/association_test.rb +27 -2
  70. data/test/form_builder/button_test.rb +1 -0
  71. data/test/form_builder/error_notification_test.rb +1 -0
  72. data/test/form_builder/error_test.rb +12 -0
  73. data/test/form_builder/general_test.rb +67 -3
  74. data/test/form_builder/hint_test.rb +6 -0
  75. data/test/form_builder/input_field_test.rb +30 -10
  76. data/test/form_builder/label_test.rb +9 -3
  77. data/test/form_builder/wrapper_test.rb +24 -4
  78. data/test/generators/simple_form_generator_test.rb +4 -3
  79. data/test/inputs/boolean_input_test.rb +17 -0
  80. data/test/inputs/collection_check_boxes_input_test.rb +30 -14
  81. data/test/inputs/collection_radio_buttons_input_test.rb +40 -24
  82. data/test/inputs/collection_select_input_test.rb +40 -39
  83. data/test/inputs/color_input_test.rb +10 -0
  84. data/test/inputs/datetime_input_test.rb +7 -16
  85. data/test/inputs/disabled_test.rb +14 -0
  86. data/test/inputs/discovery_test.rb +22 -0
  87. data/test/inputs/file_input_test.rb +1 -0
  88. data/test/inputs/general_test.rb +3 -2
  89. data/test/inputs/grouped_collection_select_input_test.rb +11 -10
  90. data/test/inputs/hidden_input_test.rb +1 -0
  91. data/test/inputs/numeric_input_test.rb +2 -1
  92. data/test/inputs/priority_input_test.rb +3 -10
  93. data/test/inputs/readonly_test.rb +1 -0
  94. data/test/inputs/required_test.rb +1 -0
  95. data/test/inputs/string_input_test.rb +10 -16
  96. data/test/inputs/text_input_test.rb +1 -0
  97. data/test/simple_form_test.rb +1 -0
  98. data/test/support/discovery_inputs.rb +8 -0
  99. data/test/support/misc_helpers.rb +22 -1
  100. data/test/support/mock_controller.rb +7 -1
  101. data/test/support/models.rb +52 -16
  102. data/test/test_helper.rb +2 -0
  103. metadata +40 -47
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  class Leaf
@@ -11,7 +12,7 @@ module SimpleForm
11
12
  def render(input)
12
13
  method = input.method(@namespace)
13
14
 
14
- if method.arity == 0
15
+ if method.arity.zero?
15
16
  ActiveSupport::Deprecation.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: @namespace })
16
17
 
17
18
  method.call
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # A wrapper is an object that holds several components and render them.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # `Root` is the root wrapper for all components. It is special cased to
@@ -29,6 +30,7 @@ module SimpleForm
29
30
  end
30
31
  css << (options[:wrapper_error_class] || @defaults[:error_class]) if input.has_errors?
31
32
  css << (options[:wrapper_hint_class] || @defaults[:hint_class]) if input.has_hint?
33
+ css << (options[:wrapper_valid_class] || @defaults[:valid_class]) if input.valid?
32
34
  css.compact
33
35
  end
34
36
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # `Single` is an optimization for a wrapper that has only one component.
@@ -19,7 +20,7 @@ module SimpleForm
19
20
  private
20
21
 
21
22
  def html_options(options)
22
- [:label, :input].include?(namespace) ? {} : super
23
+ %i[label input].include?(namespace) ? {} : super
23
24
  end
24
25
  end
25
26
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  autoload :Builder, 'simple_form/wrappers/builder'
data/lib/simple_form.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'action_view'
3
+ require 'action_pack'
2
4
  require 'simple_form/action_view_extensions/form_helper'
3
5
  require 'simple_form/action_view_extensions/builder'
4
6
  require 'active_support/core_ext/hash/slice'
@@ -58,11 +60,11 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
58
60
 
59
61
  # Series of attemps to detect a default label method for collection.
60
62
  mattr_accessor :collection_label_methods
61
- @@collection_label_methods = [:to_label, :name, :title, :to_s]
63
+ @@collection_label_methods = %i[to_label name title to_s]
62
64
 
63
65
  # Series of attemps to detect a default value method for collection.
64
66
  mattr_accessor :collection_value_methods
65
- @@collection_value_methods = [:id, :to_s]
67
+ @@collection_value_methods = %i[id to_s]
66
68
 
67
69
  # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
68
70
  mattr_accessor :collection_wrapper_tag
@@ -84,7 +86,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
84
86
 
85
87
  # How the label text should be generated altogether with the required text.
86
88
  mattr_accessor :label_text
87
- @@label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
89
+ @@label_text = ->(label, required, explicit_label) { "#{required} #{label}" }
88
90
 
89
91
  # You can define the class to be used on all labels. Defaults to none.
90
92
  mattr_accessor :label_class
@@ -108,7 +110,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
108
110
 
109
111
  # You can define which elements should obtain additional classes.
110
112
  mattr_accessor :generate_additional_classes_for
111
- @@generate_additional_classes_for = [:wrapper, :label, :input]
113
+ @@generate_additional_classes_for = %i[wrapper label input]
112
114
 
113
115
  # Whether attributes are required by default or not.
114
116
  mattr_accessor :required_by_default
@@ -120,7 +122,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
120
122
 
121
123
  # Collection of methods to detect if a file type was given.
122
124
  mattr_accessor :file_methods
123
- @@file_methods = [:mounted_as, :file?, :public_filename]
125
+ @@file_methods = %i[mounted_as file? public_filename attached?]
124
126
 
125
127
  # Custom mappings for input types. This should be a hash containing a regexp
126
128
  # to match as key, and the input type that will be used when the field name
@@ -163,7 +165,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
163
165
 
164
166
  # Cache SimpleForm inputs discovery.
165
167
  mattr_accessor :cache_discovery
166
- @@cache_discovery = defined?(Rails) && !Rails.env.development?
168
+ @@cache_discovery = defined?(Rails.env) && !Rails.env.development?
167
169
 
168
170
  # Adds a class to each generated button, mostly for compatiblity.
169
171
  mattr_accessor :button_class
@@ -198,6 +200,12 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
198
200
  mattr_accessor :i18n_scope
199
201
  @@i18n_scope = 'simple_form'
200
202
 
203
+ mattr_accessor :input_field_error_class
204
+ @@input_field_error_class = nil
205
+
206
+ mattr_accessor :input_field_valid_class
207
+ @@input_field_valid_class = nil
208
+
201
209
  # Retrieves a given wrapper
202
210
  def self.wrapper(name)
203
211
  @@wrappers[name.to_s] or raise WrapperNotFound, "Couldn't find wrapper with name #{name}"
@@ -227,7 +235,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
227
235
  SimpleForm::Wrappers::Root.new(builder.to_a, options)
228
236
  end
229
237
 
230
- wrappers class: :input, hint_class: :field_with_hint, error_class: :field_with_errors do |b|
238
+ wrappers class: :input, hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
231
239
  b.use :html5
232
240
 
233
241
  b.use :min_max
@@ -263,6 +271,49 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
263
271
  @@configured = true
264
272
  yield self
265
273
  end
274
+
275
+ # Includes a component to be used by Simple Form. Methods defined in a
276
+ # component will be exposed to be used in the wrapper as Simple::Components
277
+ #
278
+ # Examples
279
+ #
280
+ # # The application needs to tell where the components will be.
281
+ # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
282
+ #
283
+ # # Create a custom component in the path specified above.
284
+ # # lib/components/input_group_component.rb
285
+ # module InputGroupComponent
286
+ # def prepend
287
+ # ...
288
+ # end
289
+ #
290
+ # def append
291
+ # ...
292
+ # end
293
+ # end
294
+ #
295
+ # SimpleForm.setup do |config|
296
+ # # Create a wrapper using the custom component.
297
+ # config.wrappers :input_group, tag: :div, error_class: :error do |b|
298
+ # b.use :label
299
+ # b.optional :prepend
300
+ # b.use :input
301
+ # b.use :append
302
+ # end
303
+ # end
304
+ #
305
+ # # Using the custom component in the form.
306
+ # <%= simple_form_for @blog, wrapper: input_group do |f| %>
307
+ # <%= f.input :title, prepend: true %>
308
+ # <% end %>
309
+ #
310
+ def self.include_component(component)
311
+ if Module === component
312
+ SimpleForm::Inputs::Base.include(component)
313
+ else
314
+ raise TypeError, "SimpleForm.include_component expects a module but got: #{component.class}"
315
+ end
316
+ end
266
317
  end
267
318
 
268
319
  require 'simple_form/railtie' if defined?(Rails)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class BuilderTest < ActionView::TestCase
@@ -36,7 +37,7 @@ class BuilderTest < ActionView::TestCase
36
37
  end
37
38
 
38
39
  test "collection radio handles camelized collection values for labels correctly" do
39
- with_collection_radio_buttons @user, :active, ['Yes', 'No'], :to_s, :to_s
40
+ with_collection_radio_buttons @user, :active, %w[Yes No], :to_s, :to_s
40
41
 
41
42
  assert_select 'form label.collection_radio_buttons[for=user_active_yes]', 'Yes'
42
43
  assert_select 'form label.collection_radio_buttons[for=user_active_no]', 'No'
@@ -243,7 +244,7 @@ class BuilderTest < ActionView::TestCase
243
244
 
244
245
  test "collection radio with block helpers does not leak the template" do
245
246
  with_concat_form_for(@user) do |f|
246
- collection_input = f.collection_radio_buttons :active, [true, false], :to_s, :to_s do |b|
247
+ collection_input = f.collection_radio_buttons :active, [true, false], :to_s, :to_s do |b|
247
248
  b.label(class: b.object) { b.radio_button + b.text }
248
249
  end
249
250
  concat collection_input
@@ -283,7 +284,7 @@ class BuilderTest < ActionView::TestCase
283
284
  end
284
285
 
285
286
  test "collection check box handles camelized collection values for labels correctly" do
286
- with_collection_check_boxes @user, :active, ['Yes', 'No'], :to_s, :to_s
287
+ with_collection_check_boxes @user, :active, %w[Yes No], :to_s, :to_s
287
288
 
288
289
  assert_select 'form label.collection_check_boxes[for=user_active_yes]', 'Yes'
289
290
  assert_select 'form label.collection_check_boxes[for=user_active_no]', 'No'
@@ -317,7 +318,7 @@ class BuilderTest < ActionView::TestCase
317
318
 
318
319
  test "collection check boxes accepts selected string values as :checked option" do
319
320
  collection = (1..3).map { |i| [i, "Category #{i}"] }
320
- with_collection_check_boxes :user, :category_ids, collection, :first, :last, checked: ['1', '3']
321
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, checked: %w[1 3]
321
322
 
322
323
  assert_select 'input[type=checkbox][value="1"][checked=checked]'
323
324
  assert_select 'input[type=checkbox][value="3"][checked=checked]'
@@ -541,7 +542,7 @@ class BuilderTest < ActionView::TestCase
541
542
 
542
543
  test "collection check boxes with block helpers does not leak the template" do
543
544
  with_concat_form_for(@user) do |f|
544
- collection_input = f.collection_check_boxes :active, [true, false], :to_s, :to_s do |b|
545
+ collection_input = f.collection_check_boxes :active, [true, false], :to_s, :to_s do |b|
545
546
  b.label(class: b.object) { b.check_box + b.text }
546
547
  end
547
548
  concat collection_input
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class FormHelperTest < ActionView::TestCase
@@ -151,7 +152,7 @@ class FormHelperTest < ActionView::TestCase
151
152
  end
152
153
 
153
154
  test 'SimpleForm for swaps default action view field_error_proc' do
154
- expected_error_proc = lambda {}
155
+ expected_error_proc = -> {}
155
156
  swap SimpleForm, field_error_proc: expected_error_proc do
156
157
  simple_form_for :user do |f|
157
158
  assert_equal expected_error_proc, ::ActionView::Base.field_error_proc
@@ -161,7 +162,7 @@ class FormHelperTest < ActionView::TestCase
161
162
 
162
163
  private
163
164
 
164
- def swap_field_error_proc(expected_error_proc = lambda {})
165
+ def swap_field_error_proc(expected_error_proc = -> {})
165
166
  swap ActionView::Base, field_error_proc: expected_error_proc do
166
167
  yield
167
168
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ # Module that represents a custom component.
6
+ module Numbers
7
+ def number(wrapper_options = nil)
8
+ @number ||= options[:number].to_s.html_safe
9
+ end
10
+ end
11
+
12
+ # Module that represents a custom component.
13
+ module InputGroup
14
+ def prepend(wrapper_options = nil)
15
+ span_tag = content_tag(:span, options[:prepend], class: 'input-group-text')
16
+ template.content_tag(:div, span_tag, class: 'input-group-prepend')
17
+ end
18
+
19
+ def append(wrapper_options = nil)
20
+ span_tag = content_tag(:span, options[:append], class: 'input-group-text')
21
+ template.content_tag(:div, span_tag, class: 'input-group-append')
22
+ end
23
+ end
24
+
25
+ class CustomComponentsTest < ActionView::TestCase
26
+ test 'includes the custom components' do
27
+ SimpleForm.include_component Numbers
28
+
29
+ custom_wrapper = SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
30
+ b.use :number, wrap_with: { tag: 'div', class: 'number' }
31
+ end
32
+
33
+ with_form_for @user, :name, number: 1, wrapper: custom_wrapper
34
+
35
+ assert_select 'div.number', text: '1'
36
+ end
37
+
38
+ test 'includes custom components and use it as optional in the wrapper' do
39
+ SimpleForm.include_component InputGroup
40
+
41
+ custom_wrapper = SimpleForm.build tag: :div, class: 'custom_wrapper' do |b|
42
+ b.use :label
43
+ b.optional :prepend
44
+ b.use :input
45
+ b.use :append
46
+ end
47
+
48
+ with_form_for @user, :name, prepend: true, wrapper: custom_wrapper
49
+
50
+ assert_select 'div.input-group-prepend > span.input-group-text'
51
+ assert_select 'div.input-group-append > span.input-group-text'
52
+ end
53
+
54
+ test 'raises a TypeError when the component is not a Module' do
55
+ component = 'MyComponent'
56
+
57
+ exception = assert_raises TypeError do
58
+ SimpleForm.include_component(component)
59
+ end
60
+ assert_equal exception.message, "SimpleForm.include_component expects a module but got: String"
61
+ end
62
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -177,7 +178,7 @@ class IsolatedLabelTest < ActionView::TestCase
177
178
  end
178
179
 
179
180
  test 'label does not have css class from type when generate_additional_classes_for does not include :label' do
180
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
181
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
181
182
  with_label_for @user, :name, :string
182
183
  assert_no_select 'label.string'
183
184
  with_label_for @user, :description, :text
@@ -192,7 +193,7 @@ class IsolatedLabelTest < ActionView::TestCase
192
193
  end
193
194
 
194
195
  test 'label does not generate empty css class' do
195
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
196
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
196
197
  with_label_for @user, :name, :string
197
198
  assert_no_select 'label[class]'
198
199
  end
@@ -206,7 +207,7 @@ class IsolatedLabelTest < ActionView::TestCase
206
207
  end
207
208
 
208
209
  test 'label does not obtain required from ActiveModel::Validations when generate_additional_classes_for does not include :label' do
209
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
210
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
210
211
  with_label_for @validating_user, :name, :string
211
212
  assert_no_select 'label.required'
212
213
  with_label_for @validating_user, :status, :string
@@ -248,6 +249,15 @@ class IsolatedLabelTest < ActionView::TestCase
248
249
  end
249
250
  end
250
251
 
252
+ test 'label uses custom i18n scope to find required text' do
253
+ store_translations(:en, my_scope: { required: { text: 'Pflichtfeld' } }) do
254
+ swap SimpleForm, i18n_scope: :my_scope do
255
+ with_label_for @user, :name, :string
256
+ assert_select 'form label abbr[title="Pflichtfeld"]', '*'
257
+ end
258
+ end
259
+ end
260
+
251
261
  test 'label uses i18n to find required mark' do
252
262
  store_translations(:en, simple_form: { required: { mark: '*-*' } }) do
253
263
  with_label_for @user, :name, :string
@@ -255,6 +265,15 @@ class IsolatedLabelTest < ActionView::TestCase
255
265
  end
256
266
  end
257
267
 
268
+ test 'label uses custom i18n scope to find required mark' do
269
+ store_translations(:en, my_scope: { required: { mark: '!!' } }) do
270
+ swap SimpleForm, i18n_scope: :my_scope do
271
+ with_label_for @user, :name, :string
272
+ assert_select 'form label abbr', '!!'
273
+ end
274
+ end
275
+ end
276
+
258
277
  test 'label uses i18n to find required string tag' do
259
278
  store_translations(:en, simple_form: { required: { html: '<span class="required" title="requerido">*</span>' } }) do
260
279
  with_label_for @user, :name, :string
@@ -263,6 +282,16 @@ class IsolatedLabelTest < ActionView::TestCase
263
282
  end
264
283
  end
265
284
 
285
+ test 'label uses custom i18n scope to find required string tag' do
286
+ store_translations(:en, my_scope: { required: { html: '<span class="mandatory" title="Pflichtfeld">!!</span>' } }) do
287
+ swap SimpleForm, i18n_scope: :my_scope do
288
+ with_label_for @user, :name, :string
289
+ assert_no_select 'form label abbr'
290
+ assert_select 'form label span.mandatory[title=Pflichtfeld]', '!!'
291
+ end
292
+ end
293
+ end
294
+
266
295
  test 'label allows overwriting input id' do
267
296
  with_label_for @user, :name, :string, input_html: { id: 'my_new_id' }
268
297
  assert_select 'label[for=my_new_id]'
@@ -289,7 +318,7 @@ class IsolatedLabelTest < ActionView::TestCase
289
318
  end
290
319
 
291
320
  test 'label includes for attribute for select collection' do
292
- with_label_for @user, :sex, :select, collection: [:male, :female]
321
+ with_label_for @user, :sex, :select, collection: %i[male female]
293
322
  assert_select 'label[for=user_sex]'
294
323
  end
295
324
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -14,6 +15,12 @@ class AssociationTest < ActionView::TestCase
14
15
  end
15
16
  end
16
17
 
18
+ test 'builder association works with decorated object responsive to #to_model' do
19
+ assert_nothing_raised do
20
+ with_association_for @decorated_user, :company
21
+ end
22
+ end
23
+
17
24
  test 'builder association with a block calls simple_fields_for' do
18
25
  simple_form_for @user do |f|
19
26
  f.association :posts do |posts_form|
@@ -121,6 +128,15 @@ class AssociationTest < ActionView::TestCase
121
128
  assert_no_select 'form select option[value="2"]'
122
129
  end
123
130
 
131
+ test 'builder allows collection to have a scope with parameter' do
132
+ with_association_for @user, :special_tags
133
+ assert_select 'form select.select#user_special_tag_ids'
134
+ assert_select 'form select[multiple=multiple]'
135
+ assert_select 'form select option[value="1"]', 'Tag 1'
136
+ assert_no_select 'form select option[value="2"]'
137
+ assert_no_select 'form select option[value="3"]'
138
+ end
139
+
124
140
  test 'builder marks the record which already belongs to the user' do
125
141
  @user.company_id = 2
126
142
  with_association_for @user, :company, as: :radio_buttons
@@ -154,6 +170,15 @@ class AssociationTest < ActionView::TestCase
154
170
  end
155
171
  end
156
172
 
173
+ test 'builder does not call where if the given association does not respond to it' do
174
+ with_association_for @user, :friends
175
+ assert_select 'form select.select#user_friend_ids'
176
+ assert_select 'form select[multiple=multiple]'
177
+ assert_select 'form select option[value="1"]', 'Friend 1'
178
+ assert_select 'form select option[value="2"]', 'Friend 2'
179
+ assert_select 'form select option[value="3"]', 'Friend 3'
180
+ end
181
+
157
182
  test 'builder does not call order if the given association does not respond to it' do
158
183
  with_association_for @user, :pictures
159
184
  assert_select 'form select.select#user_picture_ids'
@@ -210,12 +235,12 @@ class AssociationTest < ActionView::TestCase
210
235
  end
211
236
 
212
237
  test 'builder with collection support does not change the options hash' do
213
- options = { as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li}
238
+ options = { as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li }
214
239
  with_association_for @user, :tags, options
215
240
 
216
241
  assert_select 'form ul', count: 1
217
242
  assert_select 'form ul li', count: 3
218
- assert_equal({ as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li},
243
+ assert_equal({ as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li },
219
244
  options)
220
245
  end
221
246
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  # Tests for f.error and f.full_error
@@ -35,6 +36,11 @@ class ErrorTest < ActionView::TestCase
35
36
  assert_select 'span.error', "cannot be blank"
36
37
  end
37
38
 
39
+ test 'error generates messages with decorated object responsive to #to_model' do
40
+ with_error_for @decorated_user, :name
41
+ assert_select 'span.error', "cannot be blank"
42
+ end
43
+
38
44
  test 'error generates messages for attribute with one error when using first' do
39
45
  swap SimpleForm, error_method: :first do
40
46
  with_error_for @user, :age
@@ -218,6 +224,12 @@ class ErrorTest < ActionView::TestCase
218
224
  assert_no_select 'span.error'
219
225
  end
220
226
 
227
+ test 'input with custom error works when form does not use a model' do
228
+ with_form_for :user, :active, error: "Super User Active! cannot be blank"
229
+
230
+ assert_select 'span.error'
231
+ end
232
+
221
233
  test 'input with custom error works when using full_error component' do
222
234
  swap_wrapper :default, custom_wrapper_with_full_error do
223
235
  error_text = "Super User Name! cannot be blank"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -30,6 +31,12 @@ class FormBuilderTest < ActionView::TestCase
30
31
  end
31
32
  end
32
33
 
34
+ test 'builder works with decorated object responsive to #to_model' do
35
+ assert_nothing_raised do
36
+ with_form_for @decorated_user, :name
37
+ end
38
+ end
39
+
33
40
  test 'builder input allows a block to configure input' do
34
41
  with_form_for @user, :name do
35
42
  text_field_tag :foo, :bar, id: :cool
@@ -49,7 +56,7 @@ class FormBuilderTest < ActionView::TestCase
49
56
  test 'builder does not override custom input mappings for custom collection' do
50
57
  swap SimpleForm, input_mappings: { /gender$/ => :check_boxes } do
51
58
  with_concat_form_for @user do |f|
52
- f.input :gender, collection: [:male, :female]
59
+ f.input :gender, collection: %i[male female]
53
60
  end
54
61
 
55
62
  assert_no_select 'select option', 'Male'
@@ -58,7 +65,7 @@ class FormBuilderTest < ActionView::TestCase
58
65
  end
59
66
 
60
67
  test 'builder allows to skip input_type class' do
61
- swap SimpleForm, generate_additional_classes_for: [:label, :wrapper] do
68
+ swap SimpleForm, generate_additional_classes_for: %i[label wrapper] do
62
69
  with_form_for @user, :post_count
63
70
  assert_no_select "form input#user_post_count.integer"
64
71
  assert_select "form input#user_post_count"
@@ -119,9 +126,40 @@ class FormBuilderTest < ActionView::TestCase
119
126
 
120
127
  test 'builder generates text areas for text columns' do
121
128
  with_form_for @user, :description
129
+ assert_no_select 'form input#user_description.string'
122
130
  assert_select 'form textarea#user_description.text'
123
131
  end
124
132
 
133
+ test 'builder generates text areas for text columns when hinted' do
134
+ with_form_for @user, :description, as: :text
135
+ assert_no_select 'form input#user_description.string'
136
+ assert_select 'form textarea#user_description.text'
137
+ end
138
+
139
+ test 'builder generates text field for text columns when hinted' do
140
+ with_form_for @user, :description, as: :string
141
+ assert_no_select 'form textarea#user_description.text'
142
+ assert_select 'form input#user_description.string'
143
+ end
144
+
145
+ test 'builder generates text areas for hstore columns' do
146
+ with_form_for @user, :hstore
147
+ assert_no_select 'form input#user_hstore.string'
148
+ assert_select 'form textarea#user_hstore.text'
149
+ end
150
+
151
+ test 'builder generates text areas for json columns' do
152
+ with_form_for @user, :json
153
+ assert_no_select 'form input#user_json.string'
154
+ assert_select 'form textarea#user_json.text'
155
+ end
156
+
157
+ test 'builder generates text areas for jsonb columns' do
158
+ with_form_for @user, :jsonb
159
+ assert_no_select 'form input#user_jsonb.string'
160
+ assert_select 'form textarea#user_jsonb.text'
161
+ end
162
+
125
163
  test 'builder generates a checkbox for boolean columns' do
126
164
  with_form_for @user, :active
127
165
  assert_select 'form input[type=checkbox]#user_active.boolean'
@@ -146,6 +184,11 @@ class FormBuilderTest < ActionView::TestCase
146
184
  end
147
185
  end
148
186
 
187
+ test 'builder generates string fields for citext columns' do
188
+ with_form_for @user, :citext
189
+ assert_select 'form input#user_citext.string'
190
+ end
191
+
149
192
  test 'builder generates password fields for columns that matches password' do
150
193
  with_form_for @user, :password
151
194
  assert_select 'form input#user_password.password'
@@ -199,6 +242,16 @@ class FormBuilderTest < ActionView::TestCase
199
242
  test 'builder generates file for file columns' do
200
243
  @user.avatar = MiniTest::Mock.new
201
244
  @user.avatar.expect(:public_filename, true)
245
+ @user.avatar.expect(:!, false)
246
+
247
+ with_form_for @user, :avatar
248
+ assert_select 'form input#user_avatar.file'
249
+ end
250
+
251
+ test 'builder generates file for activestorage entries' do
252
+ @user.avatar = MiniTest::Mock.new
253
+ @user.avatar.expect(:attached?, false)
254
+ @user.avatar.expect(:!, false)
202
255
 
203
256
  with_form_for @user, :avatar
204
257
  assert_select 'form input#user_avatar.file'
@@ -207,6 +260,7 @@ class FormBuilderTest < ActionView::TestCase
207
260
  test 'builder generates file for attributes that are real db columns but have file methods' do
208
261
  @user.home_picture = MiniTest::Mock.new
209
262
  @user.home_picture.expect(:mounted_as, true)
263
+ @user.home_picture.expect(:!, false)
210
264
 
211
265
  with_form_for @user, :home_picture
212
266
  assert_select 'form input#user_home_picture.file'
@@ -217,15 +271,25 @@ class FormBuilderTest < ActionView::TestCase
217
271
  assert_select 'form select#user_age.select'
218
272
  end
219
273
 
274
+ test 'builder does not generate url fields for columns that contain only the letters url' do
275
+ with_form_for @user, :hourly
276
+ assert_no_select 'form input#user_url.string.url'
277
+ assert_select 'form input#user_hourly.string'
278
+ end
279
+
220
280
  test 'builder allows overriding default input type for text' do
221
281
  with_form_for @user, :name, as: :text
222
282
  assert_no_select 'form input#user_name'
223
283
  assert_select 'form textarea#user_name.text'
284
+ end
224
285
 
286
+ test 'builder allows overriding default input type for radio_buttons' do
225
287
  with_form_for @user, :active, as: :radio_buttons
226
288
  assert_no_select 'form input[type=checkbox]'
227
289
  assert_select 'form input.radio_buttons[type=radio]', count: 2
290
+ end
228
291
 
292
+ test 'builder allows overriding default input type for string' do
229
293
  with_form_for @user, :born_at, as: :string
230
294
  assert_no_select 'form select'
231
295
  assert_select 'form input#user_born_at.string'
@@ -365,7 +429,7 @@ class FormBuilderTest < ActionView::TestCase
365
429
  end
366
430
 
367
431
  # DEFAULT OPTIONS
368
- [:input, :input_field].each do |method|
432
+ %i[input input_field].each do |method|
369
433
  test "builder receives a default argument and pass it to the inputs when calling '#{method}'" do
370
434
  with_concat_form_for @user, defaults: { input_html: { class: 'default_class' } } do |f|
371
435
  f.public_send(method, :name)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  # Tests for f.hint
@@ -18,6 +19,11 @@ class HintTest < ActionView::TestCase
18
19
  assert_select 'span.hint', 'Use with care...'
19
20
  end
20
21
 
22
+ test 'hint is generated with decorated object responsive to #to_model' do
23
+ with_hint_for @decorated_user, :name, hint: 'Use with care...'
24
+ assert_select 'span.hint', 'Use with care...'
25
+ end
26
+
21
27
  test 'hint does not modify the options hash' do
22
28
  options = { hint: 'Use with care...' }
23
29
  with_hint_for @user, :name, options