view_component 2.62.0 → 2.82.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 view_component might be problematic. Click here for more details.

Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/app/assets/vendor/prism.css +3 -195
  4. data/app/assets/vendor/prism.min.js +11 -11
  5. data/app/controllers/concerns/view_component/preview_actions.rb +6 -10
  6. data/app/controllers/view_components_system_test_controller.rb +7 -0
  7. data/app/helpers/preview_helper.rb +2 -2
  8. data/app/views/view_components/preview.html.erb +2 -2
  9. data/docs/CHANGELOG.md +383 -21
  10. data/lib/rails/generators/abstract_generator.rb +3 -5
  11. data/lib/rails/generators/component/component_generator.rb +5 -4
  12. data/lib/rails/generators/locale/component_generator.rb +1 -1
  13. data/lib/rails/generators/preview/component_generator.rb +10 -3
  14. data/lib/view_component/base.rb +41 -47
  15. data/lib/view_component/compiler.rb +57 -68
  16. data/lib/view_component/config.rb +176 -0
  17. data/lib/view_component/content_areas.rb +2 -3
  18. data/lib/view_component/deprecation.rb +2 -2
  19. data/lib/view_component/docs_builder_component.html.erb +1 -1
  20. data/lib/view_component/docs_builder_component.rb +1 -1
  21. data/lib/view_component/engine.rb +23 -29
  22. data/lib/view_component/polymorphic_slots.rb +11 -5
  23. data/lib/view_component/render_component_helper.rb +1 -0
  24. data/lib/view_component/slotable.rb +2 -3
  25. data/lib/view_component/slotable_v2.rb +48 -8
  26. data/lib/view_component/system_test_case.rb +13 -0
  27. data/lib/view_component/system_test_helpers.rb +27 -0
  28. data/lib/view_component/test_helpers.rb +65 -15
  29. data/lib/view_component/translatable.rb +1 -1
  30. data/lib/view_component/version.rb +1 -1
  31. data/lib/view_component.rb +6 -3
  32. metadata +50 -18
  33. data/lib/view_component/previewable.rb +0 -62
  34. data/lib/view_component/render_preview_helper.rb +0 -50
@@ -4,9 +4,11 @@ require "action_view"
4
4
  require "active_support/configurable"
5
5
  require "view_component/collection"
6
6
  require "view_component/compile_cache"
7
+ require "view_component/compiler"
8
+ require "view_component/config"
7
9
  require "view_component/content_areas"
8
10
  require "view_component/polymorphic_slots"
9
- require "view_component/previewable"
11
+ require "view_component/preview"
10
12
  require "view_component/slotable"
11
13
  require "view_component/slotable_v2"
12
14
  require "view_component/translatable"
@@ -14,10 +16,23 @@ require "view_component/with_content_helper"
14
16
 
15
17
  module ViewComponent
16
18
  class Base < ActionView::Base
17
- include ActiveSupport::Configurable
19
+ class << self
20
+ delegate(*ViewComponent::Config.defaults.keys, to: :config)
21
+
22
+ # Returns the current config.
23
+ #
24
+ # @return [ViewComponent::Config]
25
+ def config
26
+ @config ||= ViewComponent::Config.defaults
27
+ end
28
+
29
+ # Replaces the entire config. You shouldn't need to use this directly
30
+ # unless you're building a `ViewComponent::Config` elsewhere.
31
+ attr_writer :config
32
+ end
33
+
18
34
  include ViewComponent::ContentAreas
19
35
  include ViewComponent::PolymorphicSlots
20
- include ViewComponent::Previewable
21
36
  include ViewComponent::SlotableV2
22
37
  include ViewComponent::Translatable
23
38
  include ViewComponent::WithContentHelper
@@ -51,15 +66,6 @@ module ViewComponent
51
66
  self.__vc_original_view_context = view_context
52
67
  end
53
68
 
54
- # EXPERIMENTAL: This API is experimental and may be removed at any time.
55
- # Hook for allowing components to do work as part of the compilation process.
56
- #
57
- # For example, one might compile component-specific assets at this point.
58
- # @private TODO: add documentation
59
- def self._after_compile
60
- # noop
61
- end
62
-
63
69
  # @!macro [attach] deprecated_generate_mattr_accessor
64
70
  # @method generate_$1
65
71
  # @deprecated Use `#generate.$1` instead. Will be removed in v3.0.0.
@@ -113,11 +119,9 @@ module ViewComponent
113
119
  @current_template = self
114
120
 
115
121
  if block && defined?(@__vc_content_set_by_with_content)
116
- raise ArgumentError.new(
117
- "It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
122
+ raise ArgumentError, "It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
118
123
  "which means that ViewComponent doesn't know which content to use.\n\n" \
119
124
  "To fix this issue, use either `with_content` or a block."
120
- )
121
125
  end
122
126
 
123
127
  @__vc_content_evaluated = false
@@ -126,7 +130,7 @@ module ViewComponent
126
130
  before_render
127
131
 
128
132
  if render?
129
- render_template_for(@__vc_variant).to_s + _output_postamble
133
+ render_template_for(@__vc_variant).to_s + output_postamble
130
134
  else
131
135
  ""
132
136
  end
@@ -149,10 +153,10 @@ module ViewComponent
149
153
  nil
150
154
  end
151
155
 
152
- # EXPERIMENTAL: Optional content to be returned after the rendered template.
156
+ # Optional content to be returned after the rendered template.
153
157
  #
154
158
  # @return [String]
155
- def _output_postamble
159
+ def output_postamble
156
160
  ""
157
161
  end
158
162
 
@@ -262,9 +266,7 @@ module ViewComponent
262
266
  # @private
263
267
  def format
264
268
  # Ruby 2.6 throws a warning without checking `defined?`, 2.7 doesn't
265
- if defined?(@__vc_variant)
266
- @__vc_variant
267
- end
269
+ @__vc_variant if defined?(@__vc_variant)
268
270
  end
269
271
 
270
272
  # Use the provided variant instead of the one determined by the current request.
@@ -313,11 +315,9 @@ module ViewComponent
313
315
  # config.view_component.test_controller = "MyTestController"
314
316
  # ```
315
317
  #
316
- # Defaults to ApplicationController. Can also be configured on a per-test
317
- # basis using `with_controller_class`.
318
+ # Defaults to `nil`. If this is falsy, `"ApplicationController"` is used. Can also be
319
+ # configured on a per-test basis using `with_controller_class`.
318
320
  #
319
- mattr_accessor :test_controller
320
- @@test_controller = "ApplicationController"
321
321
 
322
322
  # Set if render monkey patches should be included or not in Rails <6.1:
323
323
  #
@@ -325,7 +325,6 @@ module ViewComponent
325
325
  # config.view_component.render_monkey_patch_enabled = false
326
326
  # ```
327
327
  #
328
- mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
329
328
 
330
329
  # Path for component files
331
330
  #
@@ -333,9 +332,8 @@ module ViewComponent
333
332
  # config.view_component.view_component_path = "app/my_components"
334
333
  # ```
335
334
  #
336
- # Defaults to `app/components`.
335
+ # Defaults to `nil`. If this is falsy, `app/components` is used.
337
336
  #
338
- mattr_accessor :view_component_path, instance_writer: false, default: "app/components"
339
337
 
340
338
  # Parent class for generated components
341
339
  #
@@ -346,7 +344,6 @@ module ViewComponent
346
344
  # Defaults to nil. If this is falsy, generators will use
347
345
  # "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
348
346
  #
349
- mattr_accessor :component_parent_class, instance_writer: false
350
347
 
351
348
  # Configuration for generators.
352
349
  #
@@ -397,21 +394,19 @@ module ViewComponent
397
394
  # ```
398
395
  #
399
396
  # Defaults to `false`.
400
- mattr_accessor :generate, instance_writer: false, default: ActiveSupport::OrderedOptions.new(false)
401
397
 
402
398
  class << self
403
399
  # @private
404
400
  attr_accessor :source_location, :virtual_path
405
401
 
406
- # EXPERIMENTAL: This API is experimental and may be removed at any time.
407
402
  # Find sidecar files for the given extensions.
408
403
  #
409
404
  # The provided array of extensions is expected to contain
410
- # strings starting without the "dot", example: `["erb", "haml"]`.
405
+ # strings starting without the dot, example: `["erb", "haml"]`.
411
406
  #
412
407
  # For example, one might collect sidecar CSS files that need to be compiled.
413
- # @private TODO: add documentation
414
- def _sidecar_files(extensions)
408
+ # @param extensions [Array<String>] Extensions of which to return matching sidecar files.
409
+ def sidecar_files(extensions)
415
410
  return [] unless source_location
416
411
 
417
412
  extensions = extensions.join(",")
@@ -489,8 +484,8 @@ module ViewComponent
489
484
 
490
485
  # If Rails application is loaded, add application url_helpers to the component context
491
486
  # we need to check this to use this gem as a dependency
492
- if defined?(Rails) && Rails.application
493
- child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
487
+ if defined?(Rails) && Rails.application && !(child < Rails.application.routes.url_helpers)
488
+ child.include Rails.application.routes.url_helpers
494
489
  end
495
490
 
496
491
  # Derive the source location of the component Ruby file from the call stack.
@@ -500,7 +495,7 @@ module ViewComponent
500
495
 
501
496
  # Removes the first part of the path and the extension.
502
497
  child.virtual_path = child.source_location.gsub(
503
- %r{(.*#{Regexp.quote(ViewComponent::Base.view_component_path)})|(\.rb)}, ""
498
+ /(.*#{Regexp.quote(ViewComponent::Base.config.view_component_path)})|(\.rb)/, ""
504
499
  )
505
500
 
506
501
  # Set collection parameter to the extended component
@@ -585,26 +580,22 @@ module ViewComponent
585
580
  parameter = validate_default ? collection_parameter : provided_collection_parameter
586
581
 
587
582
  return unless parameter
588
- return if initialize_parameter_names.include?(parameter)
583
+ return if initialize_parameter_names.include?(parameter) || splatted_keyword_argument_present?
589
584
 
590
585
  # If Ruby can't parse the component class, then the initalize
591
586
  # parameters will be empty and ViewComponent will not be able to render
592
587
  # the component.
593
588
  if initialize_parameters.empty?
594
- raise ArgumentError.new(
595
- "The #{self} initializer is empty or invalid." \
589
+ raise ArgumentError, "The #{self} initializer is empty or invalid." \
596
590
  "It must accept the parameter `#{parameter}` to render it as a collection.\n\n" \
597
591
  "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
598
592
  "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
599
- )
600
593
  end
601
594
 
602
- raise ArgumentError.new(
603
- "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
595
+ raise ArgumentError, "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
604
596
  "which is required in order to render it as a collection.\n\n" \
605
597
  "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
606
598
  "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
607
- )
608
599
  end
609
600
 
610
601
  # Ensure the component initializer doesn't define
@@ -614,10 +605,8 @@ module ViewComponent
614
605
  def validate_initialization_parameters!
615
606
  return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
616
607
 
617
- raise ViewComponent::ComponentError.new(
618
- "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
608
+ raise ViewComponent::ComponentError, "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
619
609
  "public ViewComponent method. To fix this issue, rename the parameter."
620
- )
621
610
  end
622
611
 
623
612
  # @private
@@ -647,6 +636,11 @@ module ViewComponent
647
636
 
648
637
  private
649
638
 
639
+ def splatted_keyword_argument_present?
640
+ initialize_parameters.flatten.include?(:keyrest) &&
641
+ !initialize_parameters.include?([:keyrest, :**]) # Un-named splatted keyword args don't count!
642
+ end
643
+
650
644
  def initialize_parameter_names
651
645
  return attribute_names.map(&:to_sym) if respond_to?(:attribute_names)
652
646
 
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "concurrent-ruby"
4
+
3
5
  module ViewComponent
4
6
  class Compiler
5
- # Lock required to be obtained before compiling the component
6
- attr_reader :__vc_compiler_lock
7
-
8
7
  # Compiler mode. Can be either:
9
8
  # * development (a blocking mode which ensures thread safety when redefining the `call` method for components,
10
9
  # default in Rails development and test mode)
@@ -16,7 +15,7 @@ module ViewComponent
16
15
 
17
16
  def initialize(component_class)
18
17
  @component_class = component_class
19
- @__vc_compiler_lock = Monitor.new
18
+ @redefinition_lock = Mutex.new
20
19
  end
21
20
 
22
21
  def compiled?
@@ -32,46 +31,39 @@ module ViewComponent
32
31
  return if component_class == ViewComponent::Base
33
32
 
34
33
  component_class.superclass.compile(raise_errors: raise_errors) if should_compile_superclass?
34
+ subclass_instance_methods = component_class.instance_methods(false)
35
35
 
36
- with_lock do
37
- CompileCache.invalidate_class!(component_class)
38
-
39
- subclass_instance_methods = component_class.instance_methods(false)
40
-
41
- if subclass_instance_methods.include?(:with_content) && raise_errors
42
- raise ViewComponent::ComponentError.new(
43
- "#{component_class} implements a reserved method, `#with_content`.\n\n" \
44
- "To fix this issue, change the name of the method."
45
- )
46
- end
47
-
48
- if template_errors.present?
49
- raise ViewComponent::TemplateError.new(template_errors) if raise_errors
36
+ if subclass_instance_methods.include?(:with_content) && raise_errors
37
+ raise ViewComponent::ComponentError.new(
38
+ "#{component_class} implements a reserved method, `#with_content`.\n\n" \
39
+ "To fix this issue, change the name of the method."
40
+ )
41
+ end
50
42
 
51
- return false
52
- end
43
+ if template_errors.present?
44
+ raise ViewComponent::TemplateError.new(template_errors) if raise_errors
53
45
 
54
- if subclass_instance_methods.include?(:before_render_check)
55
- ViewComponent::Deprecation.warn(
56
- "`#before_render_check` will be removed in v3.0.0.\n\n" \
57
- "To fix this issue, use `#before_render` instead."
58
- )
59
- end
46
+ return false
47
+ end
60
48
 
61
- if raise_errors
62
- component_class.validate_initialization_parameters!
63
- component_class.validate_collection_parameter!
64
- end
49
+ if subclass_instance_methods.include?(:before_render_check)
50
+ ViewComponent::Deprecation.deprecation_warning(
51
+ "`before_render_check`", :"`before_render`"
52
+ )
53
+ end
65
54
 
66
- templates.each do |template|
67
- # Remove existing compiled template methods,
68
- # as Ruby warns when redefining a method.
69
- method_name = call_method_name(template[:variant])
55
+ if raise_errors
56
+ component_class.validate_initialization_parameters!
57
+ component_class.validate_collection_parameter!
58
+ end
70
59
 
71
- if component_class.instance_methods.include?(method_name.to_sym)
72
- component_class.send(:undef_method, method_name.to_sym)
73
- end
60
+ templates.each do |template|
61
+ # Remove existing compiled template methods,
62
+ # as Ruby warns when redefining a method.
63
+ method_name = call_method_name(template[:variant])
74
64
 
65
+ redefinition_lock.synchronize do
66
+ component_class.silence_redefinition_of_method(method_name)
75
67
  # rubocop:disable Style/EvalWithLocation
76
68
  component_class.class_eval <<-RUBY, template[:path], 0
77
69
  def #{method_name}
@@ -80,35 +72,22 @@ module ViewComponent
80
72
  RUBY
81
73
  # rubocop:enable Style/EvalWithLocation
82
74
  end
75
+ end
83
76
 
84
- define_render_template_for
85
-
86
- component_class.build_i18n_backend
87
- component_class._after_compile
77
+ define_render_template_for
88
78
 
89
- CompileCache.register(component_class)
90
- end
91
- end
79
+ component_class.build_i18n_backend
92
80
 
93
- def with_lock(&block)
94
- if development?
95
- __vc_compiler_lock.synchronize(&block)
96
- else
97
- block.call
98
- end
81
+ CompileCache.register(component_class)
99
82
  end
100
83
 
101
84
  private
102
85
 
103
- attr_reader :component_class
86
+ attr_reader :component_class, :redefinition_lock
104
87
 
105
88
  def define_render_template_for
106
- if component_class.instance_methods.include?(:render_template_for)
107
- component_class.send(:undef_method, :render_template_for)
108
- end
109
-
110
89
  variant_elsifs = variants.compact.uniq.map do |variant|
111
- "elsif variant.to_sym == :#{variant}\n #{call_method_name(variant)}"
90
+ "elsif variant.to_sym == :'#{variant}'\n #{call_method_name(variant)}"
112
91
  end.join("\n")
113
92
 
114
93
  body = <<-RUBY
@@ -120,15 +99,8 @@ module ViewComponent
120
99
  end
121
100
  RUBY
122
101
 
123
- if development?
124
- component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
125
- def render_template_for(variant = nil)
126
- self.class.compiler.with_lock do
127
- #{body}
128
- end
129
- end
130
- RUBY
131
- else
102
+ redefinition_lock.synchronize do
103
+ component_class.silence_redefinition_of_method(:render_template_for)
132
104
  component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
133
105
  def render_template_for(variant = nil)
134
106
  #{body}
@@ -186,6 +158,19 @@ module ViewComponent
186
158
  "There can only be a template file or inline render method per variant."
187
159
  end
188
160
 
161
+ uniq_variants = variants.compact.uniq
162
+ normalized_variants = uniq_variants.map { |variant| normalized_variant_name(variant) }
163
+
164
+ colliding_variants = uniq_variants.select do |variant|
165
+ normalized_variants.count(normalized_variant_name(variant)) > 1
166
+ end
167
+
168
+ unless colliding_variants.empty?
169
+ errors <<
170
+ "Colliding templates #{colliding_variants.sort.map { |v| "'#{v}'" }.to_sentence} " \
171
+ "found in #{component_class}."
172
+ end
173
+
189
174
  errors
190
175
  end
191
176
  end
@@ -195,11 +180,11 @@ module ViewComponent
195
180
  begin
196
181
  extensions = ActionView::Template.template_handler_extensions
197
182
 
198
- component_class._sidecar_files(extensions).each_with_object([]) do |path, memo|
183
+ component_class.sidecar_files(extensions).each_with_object([]) do |path, memo|
199
184
  pieces = File.basename(path).split(".")
200
185
  memo << {
201
186
  path: path,
202
- variant: pieces.second.split("+").second&.to_sym,
187
+ variant: pieces[1..-2].join(".").split("+").second&.to_sym,
203
188
  handler: pieces.last
204
189
  }
205
190
  end
@@ -257,12 +242,16 @@ module ViewComponent
257
242
 
258
243
  def call_method_name(variant)
259
244
  if variant.present? && variants.include?(variant)
260
- "call_#{variant}"
245
+ "call_#{normalized_variant_name(variant)}"
261
246
  else
262
247
  "call"
263
248
  end
264
249
  end
265
250
 
251
+ def normalized_variant_name(variant)
252
+ variant.to_s.gsub("-", "__").gsub(".", "___")
253
+ end
254
+
266
255
  def should_compile_superclass?
267
256
  development? &&
268
257
  templates.empty? &&
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "view_component/deprecation"
4
+
5
+ module ViewComponent
6
+ class Config
7
+ class << self
8
+ # `new` without any arguments initializes the default configuration, but
9
+ # it's important to differentiate in case that's no longer the case in
10
+ # future.
11
+ alias_method :default, :new
12
+
13
+ def defaults
14
+ ActiveSupport::OrderedOptions.new.merge!({
15
+ generate: default_generate_options,
16
+ preview_controller: "ViewComponentsController",
17
+ preview_route: "/rails/view_components",
18
+ show_previews_source: false,
19
+ instrumentation_enabled: false,
20
+ render_monkey_patch_enabled: true,
21
+ view_component_path: "app/components",
22
+ component_parent_class: nil,
23
+ show_previews: Rails.env.development? || Rails.env.test?,
24
+ preview_paths: default_preview_paths,
25
+ test_controller: "ApplicationController",
26
+ default_preview_layout: nil
27
+ })
28
+ end
29
+
30
+ # @!attribute generate
31
+ # @return [ActiveSupport::OrderedOptions]
32
+ # The subset of configuration options relating to generators.
33
+ #
34
+ # All options under this namespace default to `false` unless otherwise
35
+ # stated.
36
+ #
37
+ # #### `#sidecar`
38
+ #
39
+ # Always generate a component with a sidecar directory:
40
+ #
41
+ # config.view_component.generate.sidecar = true
42
+ #
43
+ # #### `#stimulus_controller`
44
+ #
45
+ # Always generate a Stimulus controller alongside the component:
46
+ #
47
+ # config.view_component.generate.stimulus_controller = true
48
+ #
49
+ # #### `#locale`
50
+ #
51
+ # Always generate translations file alongside the component:
52
+ #
53
+ # config.view_component.generate.locale = true
54
+ #
55
+ # #### `#distinct_locale_files`
56
+ #
57
+ # Always generate as many translations files as available locales:
58
+ #
59
+ # config.view_component.generate.distinct_locale_files = true
60
+ #
61
+ # One file will be generated for each configured `I18n.available_locales`,
62
+ # falling back to `[:en]` when no `available_locales` is defined.
63
+ #
64
+ # #### `#preview`
65
+ #
66
+ # Always generate a preview alongside the component:
67
+ #
68
+ # config.view_component.generate.preview = true
69
+ #
70
+ # #### #preview_path
71
+ #
72
+ # Path to generate preview:
73
+ #
74
+ # config.view_component.generate.preview_path = "test/components/previews"
75
+ #
76
+ # Required when there is more than one path defined in preview_paths.
77
+ # Defaults to `""`. If this is blank, the generator will use
78
+ # `ViewComponent.config.preview_paths` if defined,
79
+ # `"test/components/previews"` otherwise
80
+
81
+ # @!attribute preview_controller
82
+ # @return [String]
83
+ # The controller used for previewing components.
84
+ # Defaults to `ViewComponentsController`.
85
+
86
+ # @!attribute preview_route
87
+ # @return [String]
88
+ # The entry route for component previews.
89
+ # Defaults to `"/rails/view_components"`.
90
+
91
+ # @!attribute show_previews_source
92
+ # @return [Boolean]
93
+ # Whether to display source code previews in component previews.
94
+ # Defaults to `false`.
95
+
96
+ # @!attribute instrumentation_enabled
97
+ # @return [Boolean]
98
+ # Whether ActiveSupport notifications are enabled.
99
+ # Defaults to `false`.
100
+
101
+ # @!attribute render_monkey_patch_enabled
102
+ # @return [Boolean] Whether the #render method should be monkey patched.
103
+ # If this is disabled, use `#render_component` or
104
+ # `#render_component_to_string` instead.
105
+ # Defaults to `true`.
106
+
107
+ # @!attribute view_component_path
108
+ # @return [String]
109
+ # The path in which components, their templates, and their sidecars should
110
+ # be stored.
111
+ # Defaults to `"app/components"`.
112
+
113
+ # @!attribute component_parent_class
114
+ # @return [String]
115
+ # The parent class from which generated components will inherit.
116
+ # Defaults to `nil`. If this is falsy, generators will use
117
+ # `"ApplicationComponent"` if defined, `"ViewComponent::Base"` otherwise.
118
+
119
+ # @!attribute show_previews
120
+ # @return [Boolean]
121
+ # Whether component previews are enabled.
122
+ # Defaults to `true` in development and test environments.
123
+
124
+ # @!attribute preview_paths
125
+ # @return [Array<String>]
126
+ # The locations in which component previews will be looked up.
127
+ # Defaults to `['test/component/previews']` relative to your Rails root.
128
+
129
+ # @!attribute preview_path
130
+ # @deprecated Use #preview_paths instead. Will be removed in v3.0.0.
131
+
132
+ # @!attribute test_controller
133
+ # @return [String]
134
+ # The controller used for testing components.
135
+ # Can also be configured on a per-test basis using `#with_controller_class`.
136
+ # Defaults to `ApplicationController`.
137
+
138
+ # @!attribute default_preview_layout
139
+ # @return [String]
140
+ # A custom default layout used for the previews index page and individual
141
+ # previews.
142
+ # Defaults to `nil`. If this is falsy, `"component_preview"` is used.
143
+
144
+ def default_preview_paths
145
+ return [] unless defined?(Rails.root) && Dir.exist?("#{Rails.root}/test/components/previews")
146
+
147
+ ["#{Rails.root}/test/components/previews"]
148
+ end
149
+
150
+ def default_generate_options
151
+ options = ActiveSupport::OrderedOptions.new(false)
152
+ options.preview_path = ""
153
+ options
154
+ end
155
+ end
156
+
157
+ def initialize
158
+ @config = self.class.defaults
159
+ end
160
+
161
+ def preview_path
162
+ preview_paths
163
+ end
164
+
165
+ def preview_path=(new_value)
166
+ ViewComponent::Deprecation.deprecation_warning("`preview_path`", :"`preview_paths`")
167
+ self.preview_paths = Array.wrap(new_value)
168
+ end
169
+
170
+ delegate_missing_to :config
171
+
172
+ private
173
+
174
+ attr_reader :config
175
+ end
176
+ end
@@ -31,9 +31,8 @@ module ViewComponent
31
31
 
32
32
  class_methods do
33
33
  def with_content_areas(*areas)
34
- ViewComponent::Deprecation.warn(
35
- "`with_content_areas` is deprecated and will be removed in ViewComponent v3.0.0.\n\n" \
36
- "Use slots (https://viewcomponent.org/guide/slots.html) instead."
34
+ ViewComponent::Deprecation.deprecation_warning(
35
+ "`with_content_areas`", "use slots (https://viewcomponent.org/guide/slots.html) instead"
37
36
  )
38
37
 
39
38
  if areas.include?(:content)
@@ -3,6 +3,6 @@
3
3
  require "active_support/deprecation"
4
4
 
5
5
  module ViewComponent
6
- DEPRECATION_HORIZON = 3
7
- Deprecation = ActiveSupport::Deprecation.new(DEPRECATION_HORIZON.to_s, "ViewComponent")
6
+ DEPRECATION_HORIZON = "3.0.0"
7
+ Deprecation = ActiveSupport::Deprecation.new(DEPRECATION_HORIZON, "ViewComponent")
8
8
  end
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  layout: default
3
- title: API
3
+ title: API reference
4
4
  nav_order: 3
5
5
  ---
6
6
 
@@ -57,7 +57,7 @@ module ViewComponent
57
57
 
58
58
  def call
59
59
  <<~DOCS.chomp
60
- #{separator}#{signature_or_name}#{types}#{suffix}
60
+ `#{separator}#{signature_or_name}`#{types}#{suffix}
61
61
 
62
62
  #{docstring_and_deprecation_text}
63
63
  DOCS