simple_form 2.0.0 → 3.5.1

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 +7 -0
  2. data/CHANGELOG.md +97 -198
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +572 -296
  5. data/lib/generators/simple_form/install_generator.rb +17 -7
  6. data/lib/generators/simple_form/templates/README +3 -4
  7. data/lib/generators/simple_form/templates/_form.html.erb +1 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +1 -0
  9. data/lib/generators/simple_form/templates/config/initializers/{simple_form.rb.tt → simple_form.rb} +57 -63
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +155 -0
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +111 -0
  12. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +14 -7
  13. data/lib/simple_form/action_view_extensions/builder.rb +5 -305
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +18 -20
  15. data/lib/simple_form/components/errors.rb +30 -3
  16. data/lib/simple_form/components/hints.rb +10 -3
  17. data/lib/simple_form/components/html5.rb +17 -3
  18. data/lib/simple_form/components/label_input.rb +21 -2
  19. data/lib/simple_form/components/labels.rb +16 -11
  20. data/lib/simple_form/components/maxlength.rb +19 -12
  21. data/lib/simple_form/components/min_max.rb +4 -2
  22. data/lib/simple_form/components/minlength.rb +48 -0
  23. data/lib/simple_form/components/pattern.rb +5 -4
  24. data/lib/simple_form/components/placeholders.rb +3 -2
  25. data/lib/simple_form/components/readonly.rb +3 -2
  26. data/lib/simple_form/components.rb +15 -11
  27. data/lib/simple_form/error_notification.rb +4 -3
  28. data/lib/simple_form/form_builder.rb +283 -105
  29. data/lib/simple_form/helpers/autofocus.rb +1 -0
  30. data/lib/simple_form/helpers/disabled.rb +1 -0
  31. data/lib/simple_form/helpers/readonly.rb +1 -0
  32. data/lib/simple_form/helpers/required.rb +1 -0
  33. data/lib/simple_form/helpers/validators.rb +4 -3
  34. data/lib/simple_form/helpers.rb +7 -6
  35. data/lib/simple_form/i18n_cache.rb +1 -0
  36. data/lib/simple_form/inputs/base.rb +76 -23
  37. data/lib/simple_form/inputs/block_input.rb +3 -2
  38. data/lib/simple_form/inputs/boolean_input.rb +55 -16
  39. data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
  40. data/lib/simple_form/inputs/collection_input.rb +41 -18
  41. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +11 -19
  42. data/lib/simple_form/inputs/collection_select_input.rb +5 -2
  43. data/lib/simple_form/inputs/date_time_input.rb +23 -12
  44. data/lib/simple_form/inputs/file_input.rb +5 -2
  45. data/lib/simple_form/inputs/grouped_collection_select_input.rb +16 -3
  46. data/lib/simple_form/inputs/hidden_input.rb +5 -2
  47. data/lib/simple_form/inputs/numeric_input.rb +4 -8
  48. data/lib/simple_form/inputs/password_input.rb +6 -4
  49. data/lib/simple_form/inputs/priority_input.rb +5 -2
  50. data/lib/simple_form/inputs/range_input.rb +2 -1
  51. data/lib/simple_form/inputs/string_input.rb +6 -4
  52. data/lib/simple_form/inputs/text_input.rb +6 -3
  53. data/lib/simple_form/inputs.rb +20 -17
  54. data/lib/simple_form/map_type.rb +1 -0
  55. data/lib/simple_form/railtie.rb +15 -0
  56. data/lib/simple_form/tags.rb +69 -0
  57. data/lib/simple_form/version.rb +2 -1
  58. data/lib/simple_form/wrappers/builder.rb +12 -35
  59. data/lib/simple_form/wrappers/leaf.rb +29 -0
  60. data/lib/simple_form/wrappers/many.rb +12 -7
  61. data/lib/simple_form/wrappers/root.rb +7 -4
  62. data/lib/simple_form/wrappers/single.rb +12 -3
  63. data/lib/simple_form/wrappers.rb +3 -1
  64. data/lib/simple_form.rb +118 -63
  65. data/test/action_view_extensions/builder_test.rb +230 -164
  66. data/test/action_view_extensions/form_helper_test.rb +107 -39
  67. data/test/components/label_test.rb +105 -87
  68. data/test/form_builder/association_test.rb +131 -62
  69. data/test/form_builder/button_test.rb +15 -14
  70. data/test/form_builder/error_notification_test.rb +11 -10
  71. data/test/form_builder/error_test.rb +188 -34
  72. data/test/form_builder/general_test.rb +247 -102
  73. data/test/form_builder/hint_test.rb +59 -32
  74. data/test/form_builder/input_field_test.rb +138 -25
  75. data/test/form_builder/label_test.rb +84 -13
  76. data/test/form_builder/wrapper_test.rb +236 -33
  77. data/test/generators/simple_form_generator_test.rb +15 -4
  78. data/test/inputs/boolean_input_test.rb +147 -13
  79. data/test/inputs/collection_check_boxes_input_test.rb +166 -71
  80. data/test/inputs/collection_radio_buttons_input_test.rb +229 -113
  81. data/test/inputs/collection_select_input_test.rb +222 -85
  82. data/test/inputs/datetime_input_test.rb +134 -47
  83. data/test/inputs/disabled_test.rb +62 -21
  84. data/test/inputs/discovery_test.rb +70 -10
  85. data/test/inputs/file_input_test.rb +4 -3
  86. data/test/inputs/general_test.rb +90 -26
  87. data/test/inputs/grouped_collection_select_input_test.rb +88 -23
  88. data/test/inputs/hidden_input_test.rb +7 -5
  89. data/test/inputs/numeric_input_test.rb +56 -46
  90. data/test/inputs/priority_input_test.rb +31 -16
  91. data/test/inputs/readonly_test.rb +68 -27
  92. data/test/inputs/required_test.rb +63 -18
  93. data/test/inputs/string_input_test.rb +76 -51
  94. data/test/inputs/text_input_test.rb +21 -8
  95. data/test/simple_form_test.rb +9 -0
  96. data/test/support/discovery_inputs.rb +39 -2
  97. data/test/support/misc_helpers.rb +176 -20
  98. data/test/support/mock_controller.rb +13 -7
  99. data/test/support/models.rb +187 -71
  100. data/test/test_helper.rb +38 -39
  101. metadata +53 -39
  102. data/lib/simple_form/core_ext/hash.rb +0 -16
  103. data/test/support/mock_response.rb +0 -14
@@ -1,11 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class GroupedCollectionSelectInput < CollectionInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  label_method, value_method = detect_collection_methods
7
+
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
6
10
  @builder.grouped_collection_select(attribute_name, grouped_collection,
7
11
  group_method, group_label_method, value_method, label_method,
8
- input_options, input_html_options)
12
+ input_options, merged_input_options)
9
13
  end
10
14
 
11
15
  private
@@ -19,7 +23,7 @@ module SimpleForm
19
23
 
20
24
  # Sample collection
21
25
  def collection
22
- @collection ||= grouped_collection.first.try(:send, group_method) || []
26
+ @collection ||= grouped_collection.map { |collection| collection.try(:send, group_method) }.detect(&:present?) || []
23
27
  end
24
28
 
25
29
  def group_method
@@ -36,6 +40,15 @@ module SimpleForm
36
40
 
37
41
  label
38
42
  end
43
+
44
+ def detect_method_from_class(collection_classes)
45
+ return {} if collection_classes.empty?
46
+
47
+ sample = collection_classes.first
48
+
49
+ { label: SimpleForm.collection_label_methods.find { |m| sample.instance_methods.include?(m) },
50
+ value: SimpleForm.collection_value_methods.find { |m| sample.instance_methods.include?(m) } }
51
+ end
39
52
  end
40
53
  end
41
54
  end
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class HiddenInput < Base
4
5
  disable :label, :errors, :hint, :required
5
6
 
6
- def input
7
- @builder.hidden_field(attribute_name, input_html_options)
7
+ def input(wrapper_options = nil)
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.hidden_field(attribute_name, merged_input_options)
8
11
  end
9
12
 
10
13
  private
@@ -1,23 +1,19 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class NumericInput < Base
4
5
  enable :placeholder, :min_max
5
6
 
6
- def input
7
- add_size!
7
+ def input(wrapper_options = nil)
8
8
  input_html_classes.unshift("numeric")
9
9
  if html5?
10
10
  input_html_options[:type] ||= "number"
11
11
  input_html_options[:step] ||= integer? ? 1 : "any"
12
12
  end
13
- @builder.text_field(attribute_name, input_html_options)
14
- end
15
13
 
16
- private
14
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
17
15
 
18
- # Rails adds the size attr by default, if the :size key does not exist.
19
- def add_size!
20
- input_html_options[:size] ||= nil
16
+ @builder.text_field(attribute_name, merged_input_options)
21
17
  end
22
18
  end
23
19
  end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class PasswordInput < Base
4
- enable :placeholder, :maxlength
5
+ enable :placeholder, :maxlength, :minlength
5
6
 
6
- def input
7
- add_size!
8
- @builder.password_field(attribute_name, input_html_options)
7
+ def input(wrapper_options = nil)
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.password_field(attribute_name, merged_input_options)
9
11
  end
10
12
  end
11
13
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class PriorityInput < CollectionSelectInput
4
- def input
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
+
5
8
  @builder.send(:"#{input_type}_select", attribute_name, input_priority,
6
- input_options, input_html_options)
9
+ input_options, merged_input_options)
7
10
  end
8
11
 
9
12
  def input_priority
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class RangeInput < NumericInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  if html5?
6
7
  input_html_options[:type] ||= "range"
7
8
  input_html_options[:step] ||= 1
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class StringInput < Base
4
- enable :placeholder, :maxlength, :pattern
5
+ enable :placeholder, :maxlength, :minlength, :pattern
5
6
 
6
- def input
7
+ def input(wrapper_options = nil)
7
8
  unless string?
8
9
  input_html_classes.unshift("string")
9
10
  input_html_options[:type] ||= input_type if html5?
10
11
  end
11
12
 
12
- add_size!
13
- @builder.text_field(attribute_name, input_html_options)
13
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
14
+
15
+ @builder.text_field(attribute_name, merged_input_options)
14
16
  end
15
17
 
16
18
  private
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class TextInput < Base
4
- enable :placeholder, :maxlength
5
+ enable :placeholder, :maxlength, :minlength
5
6
 
6
- def input
7
- @builder.text_area(attribute_name, input_html_options)
7
+ def input(wrapper_options = nil)
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.text_area(attribute_name, merged_input_options)
8
11
  end
9
12
  end
10
13
  end
@@ -1,21 +1,24 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
- autoload :Base, 'simple_form/inputs/base'
4
- autoload :BlockInput, 'simple_form/inputs/block_input'
5
- autoload :BooleanInput, 'simple_form/inputs/boolean_input'
6
- autoload :CollectionCheckBoxesInput, 'simple_form/inputs/collection_check_boxes_input'
7
- autoload :CollectionInput, 'simple_form/inputs/collection_input'
8
- autoload :CollectionRadioButtonsInput, 'simple_form/inputs/collection_radio_buttons_input'
9
- autoload :CollectionSelectInput, 'simple_form/inputs/collection_select_input'
10
- autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
11
- autoload :FileInput, 'simple_form/inputs/file_input'
12
- autoload :GroupedCollectionSelectInput, 'simple_form/inputs/grouped_collection_select_input'
13
- autoload :HiddenInput, 'simple_form/inputs/hidden_input'
14
- autoload :NumericInput, 'simple_form/inputs/numeric_input'
15
- autoload :PasswordInput, 'simple_form/inputs/password_input'
16
- autoload :PriorityInput, 'simple_form/inputs/priority_input'
17
- autoload :RangeInput, 'simple_form/inputs/range_input'
18
- autoload :StringInput, 'simple_form/inputs/string_input'
19
- autoload :TextInput, 'simple_form/inputs/text_input'
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Base
7
+ autoload :BlockInput
8
+ autoload :BooleanInput
9
+ autoload :CollectionCheckBoxesInput
10
+ autoload :CollectionInput
11
+ autoload :CollectionRadioButtonsInput
12
+ autoload :CollectionSelectInput
13
+ autoload :DateTimeInput
14
+ autoload :FileInput
15
+ autoload :GroupedCollectionSelectInput
16
+ autoload :HiddenInput
17
+ autoload :NumericInput
18
+ autoload :PasswordInput
19
+ autoload :PriorityInput
20
+ autoload :RangeInput
21
+ autoload :StringInput
22
+ autoload :TextInput
20
23
  end
21
24
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_support/core_ext/class/attribute'
2
3
 
3
4
  module SimpleForm
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/railtie'
3
+
4
+ module SimpleForm
5
+ class Railtie < Rails::Railtie
6
+ config.eager_load_namespaces << SimpleForm
7
+
8
+ config.after_initialize do
9
+ unless SimpleForm.configured?
10
+ warn '[Simple Form] Simple Form is not configured in the application and will use the default values.' +
11
+ ' Use `rails generate simple_form:install` to generate the Simple Form configuration.'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ module SimpleForm
3
+ module Tags
4
+ module CollectionExtensions
5
+ private
6
+
7
+ def render_collection
8
+ item_wrapper_tag = @options.fetch(:item_wrapper_tag, :span)
9
+ item_wrapper_class = @options[:item_wrapper_class]
10
+
11
+ @collection.map do |item|
12
+ value = value_for_collection(item, @value_method)
13
+ text = value_for_collection(item, @text_method)
14
+ default_html_options = default_html_options_for_collection(item, value)
15
+ additional_html_options = option_html_attributes(item)
16
+
17
+ rendered_item = yield item, value, text, default_html_options.merge(additional_html_options)
18
+
19
+ if @options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
20
+ label_options = default_html_options.slice(:index, :namespace)
21
+ label_options['class'] = @options[:item_label_class]
22
+ rendered_item = @template_object.label(@object_name, sanitize_attribute_name(value), rendered_item, label_options)
23
+ end
24
+
25
+ item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, class: item_wrapper_class) : rendered_item
26
+ end.join.html_safe
27
+ end
28
+
29
+ def wrap_rendered_collection(collection)
30
+ wrapper_tag = @options[:collection_wrapper_tag]
31
+
32
+ if wrapper_tag
33
+ wrapper_class = @options[:collection_wrapper_class]
34
+ @template_object.content_tag(wrapper_tag, collection, class: wrapper_class)
35
+ else
36
+ collection
37
+ end
38
+ end
39
+ end
40
+
41
+ class CollectionRadioButtons < ActionView::Helpers::Tags::CollectionRadioButtons
42
+ include CollectionExtensions
43
+
44
+ def render
45
+ wrap_rendered_collection(super)
46
+ end
47
+
48
+ private
49
+
50
+ def render_component(builder)
51
+ builder.radio_button + builder.label(class: "collection_radio_buttons")
52
+ end
53
+ end
54
+
55
+ class CollectionCheckBoxes < ActionView::Helpers::Tags::CollectionCheckBoxes
56
+ include CollectionExtensions
57
+
58
+ def render
59
+ wrap_rendered_collection(super)
60
+ end
61
+
62
+ private
63
+
64
+ def render_component(builder)
65
+ builder.check_box + builder.label(class: "collection_check_boxes")
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
- VERSION = "2.0.0".freeze
3
+ VERSION = "3.5.1".freeze
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # Provides the builder syntax for components. The builder provides
@@ -12,18 +13,18 @@ module SimpleForm
12
13
  # b.optional :placeholder
13
14
  #
14
15
  # # Use a component with specific wrapper options
15
- # b.use :error, :wrap_with => { :tag => "span", :class => "error" }
16
+ # b.use :error, wrap_with: { tag: "span", class: "error" }
16
17
  #
17
18
  # # Use a set of components by wrapping them in a tag+class.
18
- # b.wrapper :tag => "div", :class => "another" do |ba|
19
+ # b.wrapper tag: "div", class: "another" do |ba|
19
20
  # ba.use :label
20
21
  # ba.use :input
21
22
  # end
22
23
  #
23
24
  # # Use a set of components by wrapping them in a tag+class.
24
25
  # # This wrapper is identified by :label_input, which means it can
25
- # # be turned off on demand with `f.input :name, :label_input => false`
26
- # b.wrapper :label_input, :tag => "div", :class => "another" do |ba|
26
+ # # be turned off on demand with `f.input :name, label_input: false`
27
+ # b.wrapper :label_input, tag: "div", class: "another" do |ba|
27
28
  # ba.use :label
28
29
  # ba.use :input
29
30
  # end
@@ -32,7 +33,7 @@ module SimpleForm
32
33
  # The builder also accepts default options at the root level. This is usually
33
34
  # used if you want a component to be disabled by default:
34
35
  #
35
- # config.wrappers :hint => false do |b|
36
+ # config.wrappers hint: false do |b|
36
37
  # b.use :hint
37
38
  # b.use :label_input
38
39
  # end
@@ -45,44 +46,20 @@ module SimpleForm
45
46
  @components = []
46
47
  end
47
48
 
48
- def use(name, options=nil, &block)
49
- if block_given?
50
- ActiveSupport::Deprecation.warn "Passing a block to use is deprecated. " \
51
- "Please use wrapper instead of use."
52
- return wrapper(name, options, &block)
53
- end
54
-
55
- if options && options.keys != [:wrap_with]
56
- ActiveSupport::Deprecation.warn "Passing :tag, :class and others to use is deprecated. " \
57
- "Please invoke b.use #{name.inspect}, :wrap_with => #{options.inspect} instead."
58
- options = { :wrap_with => options }
59
- end
60
-
49
+ def use(name, options = {})
61
50
  if options && wrapper = options[:wrap_with]
62
- @components << Single.new(name, wrapper)
51
+ @components << Single.new(name, wrapper, options.except(:wrap_with))
63
52
  else
64
- @components << name
53
+ @components << Leaf.new(name, options)
65
54
  end
66
55
  end
67
56
 
68
- def optional(name, options=nil, &block)
69
- if block_given?
70
- ActiveSupport::Deprecation.warn "Passing a block to optional is deprecated. " \
71
- "Please use wrapper instead of optional."
72
- return wrapper(name, options, &block)
73
- end
74
-
75
- if options && options.keys != [:wrap_with]
76
- ActiveSupport::Deprecation.warn "Passing :tag, :class and others to optional is deprecated. " \
77
- "Please invoke b.optional #{name.inspect}, :wrap_with => #{options.inspect} instead."
78
- options = { :wrap_with => options }
79
- end
80
-
57
+ def optional(name, options = {}, &block)
81
58
  @options[name] = false
82
- use(name, options, &block)
59
+ use(name, options)
83
60
  end
84
61
 
85
- def wrapper(name, options=nil)
62
+ def wrapper(name, options = nil)
86
63
  if block_given?
87
64
  name, options = nil, name if name.is_a?(Hash)
88
65
  builder = self.class.new(@options)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module SimpleForm
3
+ module Wrappers
4
+ class Leaf
5
+ attr_reader :namespace
6
+
7
+ def initialize(namespace, options = {})
8
+ @namespace = namespace
9
+ @options = options
10
+ end
11
+
12
+ def render(input)
13
+ method = input.method(@namespace)
14
+
15
+ if method.arity.zero?
16
+ ActiveSupport::Deprecation.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: @namespace })
17
+
18
+ method.call
19
+ else
20
+ method.call(@options)
21
+ end
22
+ end
23
+
24
+ def find(name)
25
+ self if @namespace == name
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,7 +1,8 @@
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.
4
- # A component may either be a symbol or any object that responds to `render`.
5
+ # A component may be any object that responds to `render`.
5
6
  # This API allows inputs/components to be easily wrapped, removing the
6
7
  # need to modify the code only to wrap input in an extra tag.
7
8
  #
@@ -10,14 +11,13 @@ module SimpleForm
10
11
  # on demand on input generation.
11
12
  class Many
12
13
  attr_reader :namespace, :defaults, :components
13
- alias :to_sym :namespace
14
14
 
15
- def initialize(namespace, components, defaults={})
15
+ def initialize(namespace, components, defaults = {})
16
16
  @namespace = namespace
17
17
  @components = components
18
18
  @defaults = defaults
19
19
  @defaults[:tag] = :div unless @defaults.key?(:tag)
20
- @defaults[:class] = Array.wrap(@defaults[:class])
20
+ @defaults[:class] = Array(@defaults[:class])
21
21
  end
22
22
 
23
23
  def render(input)
@@ -25,8 +25,8 @@ module SimpleForm
25
25
  options = input.options
26
26
 
27
27
  components.each do |component|
28
- next if options[component] == false
29
- rendered = component.respond_to?(:render) ? component.render(input) : input.send(component)
28
+ next if options[component.namespace] == false
29
+ rendered = component.render(input)
30
30
  content.safe_concat rendered.to_s if rendered
31
31
  end
32
32
 
@@ -51,16 +51,21 @@ module SimpleForm
51
51
 
52
52
  def wrap(input, options, content)
53
53
  return content if options[namespace] == false
54
+ return if defaults[:unless_blank] && content.empty?
54
55
 
55
56
  tag = (namespace && options[:"#{namespace}_tag"]) || @defaults[:tag]
56
57
  return content unless tag
57
58
 
58
59
  klass = html_classes(input, options)
59
- opts = options[:"#{namespace}_html"] || {}
60
+ opts = html_options(options)
60
61
  opts[:class] = (klass << opts[:class]).join(' ').strip unless klass.empty?
61
62
  input.template.content_tag(tag, content, opts)
62
63
  end
63
64
 
65
+ def html_options(options)
66
+ (@defaults[:html] || {}).merge(options[:"#{namespace}_html"] || {})
67
+ end
68
+
64
69
  def html_classes(input, options)
65
70
  @defaults[:class].dup
66
71
  end
@@ -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
@@ -17,17 +18,19 @@ module SimpleForm
17
18
 
18
19
  # Provide a fallback if name cannot be found.
19
20
  def find(name)
20
- super || SimpleForm::Wrappers::Many.new(name, [name])
21
+ super || SimpleForm::Wrappers::Many.new(name, [Leaf.new(name)])
21
22
  end
22
23
 
23
24
  private
24
25
 
25
26
  def html_classes(input, options)
26
- css = options[:wrapper_class] ? Array.wrap(options[:wrapper_class]) : @defaults[:class]
27
- css += SimpleForm.additional_classes_for(:wrapper) { input.html_classes }
27
+ css = options[:wrapper_class] ? Array(options[:wrapper_class]) : @defaults[:class]
28
+ css += SimpleForm.additional_classes_for(:wrapper) do
29
+ input.additional_classes + [input.input_class]
30
+ end
28
31
  css << (options[:wrapper_error_class] || @defaults[:error_class]) if input.has_errors?
29
32
  css << (options[:wrapper_hint_class] || @defaults[:hint_class]) if input.has_hint?
30
- css
33
+ css.compact
31
34
  end
32
35
  end
33
36
  end
@@ -1,18 +1,27 @@
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.
4
5
  class Single < Many
5
- def initialize(name, options={})
6
- super(name, [name], options)
6
+ def initialize(name, wrapper_options = {}, options = {})
7
+ @component = Leaf.new(name, options)
8
+
9
+ super(name, [@component], wrapper_options)
7
10
  end
8
11
 
9
12
  def render(input)
10
13
  options = input.options
11
14
  if options[namespace] != false
12
- content = input.send(namespace)
15
+ content = @component.render(input)
13
16
  wrap(input, options, content) if content
14
17
  end
15
18
  end
19
+
20
+ private
21
+
22
+ def html_options(options)
23
+ %i[label input].include?(namespace) ? {} : super
24
+ end
16
25
  end
17
26
  end
18
27
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  autoload :Builder, 'simple_form/wrappers/builder'
4
5
  autoload :Many, 'simple_form/wrappers/many'
5
6
  autoload :Root, 'simple_form/wrappers/root'
6
7
  autoload :Single, 'simple_form/wrappers/single'
8
+ autoload :Leaf, 'simple_form/wrappers/leaf'
7
9
  end
8
- end
10
+ end