simple_form 4.0.1 → 5.0.3

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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +61 -8
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +43 -52
  5. data/lib/generators/simple_form/templates/README +2 -3
  6. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +1 -7
  7. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +26 -25
  8. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +1 -1
  9. data/lib/simple_form.rb +22 -5
  10. data/lib/simple_form/components/errors.rb +5 -1
  11. data/lib/simple_form/form_builder.rb +30 -10
  12. data/lib/simple_form/inputs.rb +2 -0
  13. data/lib/simple_form/inputs/base.rb +4 -1
  14. data/lib/simple_form/inputs/boolean_input.rb +1 -0
  15. data/lib/simple_form/inputs/collection_check_boxes_input.rb +1 -1
  16. data/lib/simple_form/inputs/collection_input.rb +1 -1
  17. data/lib/simple_form/inputs/color_input.rb +14 -0
  18. data/lib/simple_form/inputs/priority_input.rb +0 -4
  19. data/lib/simple_form/inputs/rich_text_area_input.rb +12 -0
  20. data/lib/simple_form/inputs/string_input.rb +1 -1
  21. data/lib/simple_form/tags.rb +6 -2
  22. data/lib/simple_form/version.rb +1 -1
  23. data/lib/simple_form/wrappers/root.rb +8 -3
  24. data/test/action_view_extensions/builder_test.rb +22 -4
  25. data/test/form_builder/association_test.rb +6 -0
  26. data/test/form_builder/error_test.rb +6 -0
  27. data/test/form_builder/general_test.rb +23 -19
  28. data/test/form_builder/input_field_test.rb +3 -9
  29. data/test/form_builder/label_test.rb +1 -1
  30. data/test/form_builder/wrapper_test.rb +8 -1
  31. data/test/inputs/boolean_input_test.rb +8 -0
  32. data/test/inputs/collection_check_boxes_input_test.rb +8 -0
  33. data/test/inputs/collection_radio_buttons_input_test.rb +8 -0
  34. data/test/inputs/collection_select_input_test.rb +6 -0
  35. data/test/inputs/color_input_test.rb +10 -0
  36. data/test/inputs/datetime_input_test.rb +2 -12
  37. data/test/inputs/disabled_test.rb +13 -0
  38. data/test/inputs/discovery_test.rb +21 -0
  39. data/test/inputs/priority_input_test.rb +6 -14
  40. data/test/inputs/rich_text_area_input_test.rb +15 -0
  41. data/test/inputs/string_input_test.rb +8 -15
  42. data/test/support/discovery_inputs.rb +7 -0
  43. data/test/support/misc_helpers.rb +8 -2
  44. data/test/support/models.rb +29 -6
  45. data/test/test_helper.rb +7 -4
  46. metadata +40 -35
@@ -19,7 +19,7 @@ class LabelTest < ActionView::TestCase
19
19
  assert_select 'label.string[for=user_name]', /Name/
20
20
  end
21
21
 
22
- test 'builder generates a label for the boolean attrbiute' do
22
+ test 'builder generates a label for the boolean attribute' do
23
23
  with_label_for @user, :name, as: :boolean
24
24
  assert_select 'label.boolean[for=user_name]', /Name/
25
25
  assert_no_select 'label[as=boolean]'
@@ -59,6 +59,13 @@ class WrapperTest < ActionView::TestCase
59
59
  assert_no_select 'input.is-invalid'
60
60
  end
61
61
 
62
+ test 'wrapper does not determine if valid class is needed when it is set to nil' do
63
+ @user.instance_eval { undef errors }
64
+ with_form_for @user, :name, wrapper: custom_wrapper_with_input_valid_class(valid_class: nil)
65
+
66
+ assert_no_select 'div.field_without_errors'
67
+ end
68
+
62
69
  test 'wrapper adds hint class for attribute with a hint' do
63
70
  with_form_for @user, :name, hint: 'hint'
64
71
  assert_select 'div.field_with_hint'
@@ -161,7 +168,7 @@ class WrapperTest < ActionView::TestCase
161
168
  test 'custom wrappers can have full error message on attributes' do
162
169
  swap_wrapper :default, custom_wrapper_with_full_error do
163
170
  with_form_for @user, :name
164
- assert_select 'span.error', "Name cannot be blank"
171
+ assert_select 'span.error', "Super User Name! cannot be blank"
165
172
  end
166
173
  end
167
174
 
@@ -107,6 +107,14 @@ class BooleanInputTest < ActionView::TestCase
107
107
  end
108
108
  end
109
109
 
110
+ test 'input boolean with nested generates a disabled hidden field with the form attribute when it is given' do
111
+ swap SimpleForm, boolean_style: :nested do
112
+ with_input_for @user, :active, :boolean, input_html: { form: 'form_id' }
113
+
114
+ assert_select "input[type=hidden][form=form_id]+ label.boolean > input.boolean"
115
+ end
116
+ end
117
+
110
118
  test 'input accepts changing boolean style to nested through given options' do
111
119
  with_input_for @user, :active, :boolean, boolean_style: :nested
112
120
  assert_select 'label[for=user_active]', 'Active'
@@ -316,4 +316,12 @@ class CollectionCheckBoxesInputTest < ActionView::TestCase
316
316
  assert_select 'span.checkbox > label', '200'
317
317
  end
318
318
  end
319
+
320
+ test 'input check boxes with inline style support label custom classes' do
321
+ swap SimpleForm, boolean_style: :inline do
322
+ with_input_for @user, :gender, :check_boxes, collection: %i[male female], item_label_class: 'beautiful-label'
323
+
324
+ assert_select 'label.beautiful-label', count: 2
325
+ end
326
+ end
319
327
  end
@@ -439,4 +439,12 @@ class CollectionRadioButtonsInputTest < ActionView::TestCase
439
439
  assert_select 'span.radio > label', '200'
440
440
  end
441
441
  end
442
+
443
+ test 'input check boxes with inline style support label custom classes' do
444
+ swap SimpleForm, boolean_style: :inline do
445
+ with_input_for @user, :gender, :radio_buttons, collection: %i[male female], item_label_class: 'beautiful-label'
446
+
447
+ assert_select 'label.beautiful-label', count: 2
448
+ end
449
+ end
442
450
  end
@@ -284,6 +284,12 @@ class CollectionSelectInputTest < ActionView::TestCase
284
284
  assert_select 'select[required]'
285
285
  end
286
286
 
287
+ test "collection input generated aria-label should contain 'true'" do
288
+ with_input_for @user, :age, :select, collection: 18..30, prompt: "Please select foo"
289
+ assert_select 'select.required'
290
+ assert_select 'select[aria-required=true]'
291
+ end
292
+
287
293
  test 'collection input with select type does not generate required html attribute without blank option' do
288
294
  with_input_for @user, :name, :select, include_blank: false, collection: %w[Jose Carlos]
289
295
  assert_select 'select.required'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ColorInputTest < ActionView::TestCase
6
+ test 'input generates a color field' do
7
+ with_input_for @user, :favorite_color, :color
8
+ assert_select 'input[type=color].color#user_favorite_color'
9
+ end
10
+ end
@@ -6,12 +6,7 @@ require 'test_helper'
6
6
  class DateTimeInputWithHtml5Test < ActionView::TestCase
7
7
  test 'input generates a datetime input for datetime attributes if HTML5 compatibility is explicitly enbled' do
8
8
  with_input_for @user, :created_at, :datetime, html5: true
9
-
10
- if ActionPack::VERSION::STRING >= '5'
11
- assert_select 'input[type="datetime-local"]'
12
- elsif ActionPack::VERSION::STRING < '5'
13
- assert_select 'input[type="datetime"]'
14
- end
9
+ assert_select 'input[type="datetime-local"]'
15
10
  end
16
11
 
17
12
  test 'input generates a datetime select for datetime attributes' do
@@ -80,12 +75,7 @@ class DateTimeInputWithoutHtml5Test < ActionView::TestCase
80
75
  test 'input generates a datetime input for datetime attributes if HTML5 compatibility is explicitly enabled' do
81
76
  swap_wrapper do
82
77
  with_input_for @user, :created_at, :datetime, html5: true
83
-
84
- if ActionPack::VERSION::STRING >= '5'
85
- assert_select 'input[type="datetime-local"]'
86
- elsif ActionPack::VERSION::STRING < '5'
87
- assert_select 'input[type="datetime"]'
88
- end
78
+ assert_select 'input[type="datetime-local"]'
89
79
  end
90
80
  end
91
81
 
@@ -76,4 +76,17 @@ class DisabledTest < ActionView::TestCase
76
76
  with_input_for @user, :created_at, :datetime
77
77
  assert_no_select 'select.datetime.disabled[disabled]'
78
78
  end
79
+
80
+ test 'input_field collection allows disabled select' do
81
+ with_input_field_for @user, :description, collection: ['foo', 'bar'], disabled: true
82
+ assert_select 'select[disabled]'
83
+ assert_no_select 'option[disabled]'
84
+ end
85
+
86
+ test 'input_field collection allows individual disabled options' do
87
+ with_input_field_for @user, :description, collection: ['foo', 'bar'], disabled: 'bar'
88
+ assert_no_select 'select[disabled]'
89
+ assert_no_select 'option[disabled][value=foo]'
90
+ assert_select 'option[disabled][value=bar]'
91
+ end
79
92
  end
@@ -15,6 +15,7 @@ class DiscoveryTest < ActionView::TestCase
15
15
  Object.send :remove_const, :CustomizedInput
16
16
  Object.send :remove_const, :DeprecatedInput
17
17
  Object.send :remove_const, :CollectionSelectInput
18
+ Object.send :remove_const, :FileInput
18
19
  CustomInputs.send :remove_const, :CustomizedInput
19
20
  CustomInputs.send :remove_const, :PasswordInput
20
21
  CustomInputs.send :remove_const, :NumericInput
@@ -109,6 +110,26 @@ class DiscoveryTest < ActionView::TestCase
109
110
  end
110
111
  end
111
112
 
113
+ test 'does not duplicate the html classes giving a extra class' do
114
+ discovery do
115
+ swap SimpleForm, input_class: 'custom-default-input-class' do
116
+ with_form_for @user, :active, as: :select
117
+ assert_select 'form select#user_active.select' do
118
+ # Make sure class list contains 'chosen' only once.
119
+ assert_select ":match('class', ?)", /^(?!.*\bchosen\b.*\bchosen\b).*\bchosen\b.*$/
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ test 'new inputs can override the default input_html_classes' do
126
+ discovery do
127
+ with_form_for @user, :avatar, as: :file
128
+ assert_no_select 'form input[type=file]#user_avatar.file.file-upload'
129
+ assert_select 'form input[type=file]#user_avatar.file-upload'
130
+ end
131
+ end
132
+
112
133
  test 'inputs method without wrapper_options are deprecated' do
113
134
  discovery do
114
135
  assert_deprecated do
@@ -6,22 +6,14 @@ class PriorityInputTest < ActionView::TestCase
6
6
  test 'input generates a country select field' do
7
7
  with_input_for @user, :country, :country
8
8
  assert_select 'select#user_country'
9
- if ActionPack::VERSION::STRING >= '5'
10
- assert_select 'select option[value=BR]', 'Brazil'
11
- elsif ActionPack::VERSION::STRING < '5'
12
- assert_select 'select option[value=Brazil]', 'Brazil'
13
- end
9
+ assert_select 'select option[value=BR]', 'Brazil'
14
10
  assert_no_select 'select option[value=""][disabled=disabled]'
15
11
  end
16
12
 
17
13
  test 'input generates a country select with SimpleForm default' do
18
14
  swap SimpleForm, country_priority: [ 'Brazil' ] do
19
15
  with_input_for @user, :country, :country
20
- if ActionPack::VERSION::STRING >= '5'
21
- assert_select 'select option[value="---------------"][disabled=disabled]'
22
- elsif ActionPack::VERSION::STRING < '5'
23
- assert_select 'select option[value=""][disabled=disabled]'
24
- end
16
+ assert_select 'select option[value="---------------"][disabled=disabled]'
25
17
  end
26
18
  end
27
19
 
@@ -44,15 +36,15 @@ class PriorityInputTest < ActionView::TestCase
44
36
  assert_no_select 'select option[value=""]', /^$/
45
37
  end
46
38
 
47
- test 'priority input does not generate invalid required html attribute' do
39
+ test 'priority input does generate select element with required html attribute' do
48
40
  with_input_for @user, :country, :country
49
41
  assert_select 'select.required'
50
- assert_no_select 'select[required]'
42
+ assert_select 'select[required]'
51
43
  end
52
44
 
53
- test 'priority input does not generate invalid aria-required html attribute' do
45
+ test 'priority input does generate select element with aria-required html attribute' do
54
46
  with_input_for @user, :country, :country
55
47
  assert_select 'select.required'
56
- assert_no_select 'select[aria-required]'
48
+ assert_select 'select[aria-required]'
57
49
  end
58
50
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+ require 'test_helper'
4
+
5
+ class RichTextAreaInputTest < ActionView::TestCase
6
+ test 'input generates a text area for text attributes' do
7
+ with_input_for @user, :description, :text
8
+ assert_select 'textarea.text#user_description'
9
+ end
10
+
11
+ test 'input generates a text area for text attributes that accept placeholder' do
12
+ with_input_for @user, :description, :text, placeholder: 'Put in some text'
13
+ assert_select 'textarea.text[placeholder="Put in some text"]'
14
+ end
15
+ end
@@ -8,6 +8,11 @@ class StringInputTest < ActionView::TestCase
8
8
  assert_select "input#user_name[type=text][name='user[name]'][value='New in SimpleForm!']"
9
9
  end
10
10
 
11
+ test 'input maps text field to citext attribute' do
12
+ with_input_for @user, :name, :citext
13
+ assert_select "input#user_name[type=text][name='user[name]'][value='New in SimpleForm!']"
14
+ end
15
+
11
16
  test 'input generates a password field for password attributes' do
12
17
  with_input_for @user, :password, :password
13
18
  assert_select "input#user_password.password[type=password][name='user[password]']"
@@ -38,18 +43,6 @@ class StringInputTest < ActionView::TestCase
38
43
  assert_select 'input.string[minlength="5"]'
39
44
  end
40
45
 
41
- if ActionPack::VERSION::STRING < '5'
42
- test 'input does not get maxlength from validation when tokenizer present' do
43
- with_input_for @validating_user, :action, :string
44
- assert_no_select 'input.string[maxlength]'
45
- end
46
-
47
- test 'input does not get minlength from validation when tokenizer present' do
48
- with_input_for @validating_user, :action, :string
49
- assert_no_select 'input.string[minlength]'
50
- end
51
- end
52
-
53
46
  test 'input gets maxlength from validation when :is option present' do
54
47
  with_input_for @validating_user, :home_picture, :string
55
48
  assert_select 'input.string[maxlength="12"]'
@@ -88,12 +81,12 @@ class StringInputTest < ActionView::TestCase
88
81
 
89
82
  test 'input infers pattern from attributes' do
90
83
  with_input_for @other_validating_user, :country, :string, pattern: true
91
- assert_select 'input[pattern="\w+"]'
84
+ assert_select "input:match('pattern', ?)", /\w+/
92
85
  end
93
86
 
94
87
  test 'input infers pattern from attributes using proc' do
95
88
  with_input_for @other_validating_user, :name, :string, pattern: true
96
- assert_select 'input[pattern="\w+"]'
89
+ assert_select "input:match('pattern', ?)", /\w+/
97
90
  end
98
91
 
99
92
  test 'input does not infer pattern from attributes if root default is false' do
@@ -105,7 +98,7 @@ class StringInputTest < ActionView::TestCase
105
98
 
106
99
  test 'input uses given pattern from attributes' do
107
100
  with_input_for @other_validating_user, :country, :string, input_html: { pattern: "\\d+" }
108
- assert_select 'input[pattern="\d+"]'
101
+ assert_select "input:match('pattern', ?)", /\\d+/
109
102
  end
110
103
 
111
104
  test 'input does not use pattern if model has :without validation option' do
@@ -37,6 +37,13 @@ class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
37
37
  end
38
38
  end
39
39
 
40
+ class FileInput < SimpleForm::Inputs::FileInput
41
+ def input_html_classes
42
+ super.delete_if { |html_class| html_class == :file }
43
+ super.push('file-upload')
44
+ end
45
+ end
46
+
40
47
  module CustomInputs
41
48
  class CustomizedInput < SimpleForm::Inputs::StringInput
42
49
  def input_html_classes
@@ -213,8 +213,8 @@ module MiscHelpers
213
213
  end
214
214
  end
215
215
 
216
- def custom_wrapper_with_input_valid_class
217
- SimpleForm.build tag: :div, class: "custom_wrapper", valid_class: :field_without_errors do |b|
216
+ def custom_wrapper_with_input_valid_class(valid_class: :field_without_errors)
217
+ SimpleForm.build tag: :div, class: "custom_wrapper", valid_class: valid_class do |b|
218
218
  b.use :label
219
219
  b.use :input, class: 'inline-class', valid_class: 'is-valid'
220
220
  end
@@ -255,6 +255,12 @@ module MiscHelpers
255
255
  f.input(attribute_name, options.merge(as: type))
256
256
  end
257
257
  end
258
+
259
+ def with_input_field_for(object, *args)
260
+ with_concat_form_for(object) do |f|
261
+ f.input_field(*args)
262
+ end
263
+ end
258
264
  end
259
265
 
260
266
  class CustomFormBuilder < SimpleForm::FormBuilder
@@ -76,7 +76,11 @@ Friend = Struct.new(:id, :name) do
76
76
  end
77
77
  end
78
78
 
79
- class Tag < Company; end
79
+ class Tag < Company
80
+ def group_method
81
+ ["category-1"]
82
+ end
83
+ end
80
84
 
81
85
  TagGroup = Struct.new(:id, :name, :tags)
82
86
 
@@ -91,7 +95,7 @@ class User
91
95
  :post_count, :lock_version, :amount, :attempts, :action, :credit_card, :gender,
92
96
  :extra_special_company_id, :pictures, :picture_ids, :special_pictures,
93
97
  :special_picture_ids, :uuid, :friends, :friend_ids, :special_tags, :special_tag_ids,
94
- :citext, :hstore, :json, :jsonb
98
+ :citext, :hstore, :json, :jsonb, :hourly, :favorite_color
95
99
 
96
100
  def self.build(extra_attributes = {})
97
101
  attributes = {
@@ -200,7 +204,7 @@ class User
200
204
 
201
205
  def self.human_attribute_name(attribute, options = {})
202
206
  case attribute
203
- when 'name'
207
+ when 'name', :name
204
208
  'Super User Name!'
205
209
  when 'description'
206
210
  'User Description!'
@@ -277,9 +281,6 @@ class ValidatingUser < User
277
281
  only_integer: true
278
282
  validates_length_of :name, maximum: 25, minimum: 5
279
283
  validates_length_of :description, in: 15..50
280
- if ActionPack::VERSION::STRING < '5'
281
- validates_length_of :action, maximum: 10, tokenizer: ->(str) { str.scan(/\w+/) }
282
- end
283
284
  validates_length_of :home_picture, is: 12
284
285
 
285
286
  def min_amount
@@ -332,3 +333,25 @@ end
332
333
 
333
334
  class UserNumber1And2 < User
334
335
  end
336
+
337
+ class UserWithAttachment < User
338
+ def avatar_attachment
339
+ OpenStruct.new
340
+ end
341
+
342
+ def avatars_attachments
343
+ OpenStruct.new
344
+ end
345
+
346
+ def remote_cover_url
347
+ "/uploads/cover.png"
348
+ end
349
+
350
+ def profile_image_attacher
351
+ OpenStruct.new
352
+ end
353
+
354
+ def portrait_file_name
355
+ "portrait.png"
356
+ end
357
+ end
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
- require 'bundler/setup'
3
-
4
2
  require 'minitest/autorun'
5
3
 
6
4
  require 'active_model'
7
5
  require 'action_controller'
8
6
  require 'action_view'
7
+
9
8
  ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
10
9
 
11
10
  require 'action_view/template'
12
-
13
11
  require 'action_view/test_case'
14
12
 
15
13
  module Rails
@@ -41,10 +39,14 @@ if ActiveSupport::TestCase.respond_to?(:test_order=)
41
39
  ActiveSupport::TestCase.test_order = :random
42
40
  end
43
41
 
42
+ require "rails/test_unit/line_filtering"
43
+
44
44
  class ActionView::TestCase
45
45
  include MiscHelpers
46
46
  include SimpleForm::ActionViewExtensions::FormHelper
47
47
 
48
+ extend Rails::LineFiltering
49
+
48
50
  setup :set_controller
49
51
  setup :setup_users
50
52
 
@@ -58,7 +60,7 @@ class ActionView::TestCase
58
60
 
59
61
  @validating_user = ValidatingUser.build({
60
62
  name: 'Tester McTesterson',
61
- description: 'A test user of the most distinguised caliber',
63
+ description: 'A test user of the most distinguished caliber',
62
64
  home_picture: 'Home picture',
63
65
  age: 19,
64
66
  amount: 15,
@@ -89,4 +91,5 @@ class ActionView::TestCase
89
91
  alias :validating_user_path :user_path
90
92
  alias :validating_users_path :user_path
91
93
  alias :other_validating_user_path :user_path
94
+ alias :user_with_attachment_path :user_path
92
95
  end