view_component 2.49.1 → 3.23.2

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 (63) 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 +108 -0
  6. data/app/controllers/view_components_controller.rb +1 -87
  7. data/app/controllers/view_components_system_test_controller.rb +30 -0
  8. data/app/helpers/preview_helper.rb +30 -12
  9. data/app/views/view_components/_preview_source.html.erb +3 -3
  10. data/app/views/view_components/preview.html.erb +2 -2
  11. data/docs/CHANGELOG.md +1653 -24
  12. data/lib/rails/generators/abstract_generator.rb +16 -10
  13. data/lib/rails/generators/component/component_generator.rb +8 -4
  14. data/lib/rails/generators/component/templates/component.rb.tt +3 -2
  15. data/lib/rails/generators/erb/component_generator.rb +1 -1
  16. data/lib/rails/generators/locale/component_generator.rb +4 -4
  17. data/lib/rails/generators/preview/component_generator.rb +17 -3
  18. data/lib/rails/generators/preview/templates/component_preview.rb.tt +5 -1
  19. data/lib/rails/generators/rspec/component_generator.rb +15 -3
  20. data/lib/rails/generators/rspec/templates/component_spec.rb.tt +3 -1
  21. data/lib/rails/generators/stimulus/component_generator.rb +8 -3
  22. data/lib/rails/generators/stimulus/templates/component_controller.ts.tt +9 -0
  23. data/lib/rails/generators/test_unit/templates/component_test.rb.tt +3 -1
  24. data/lib/view_component/base.rb +352 -196
  25. data/lib/view_component/capture_compatibility.rb +44 -0
  26. data/lib/view_component/collection.rb +28 -9
  27. data/lib/view_component/compiler.rb +162 -193
  28. data/lib/view_component/config.rb +225 -0
  29. data/lib/view_component/configurable.rb +17 -0
  30. data/lib/view_component/deprecation.rb +8 -0
  31. data/lib/view_component/engine.rb +74 -47
  32. data/lib/view_component/errors.rb +240 -0
  33. data/lib/view_component/inline_template.rb +55 -0
  34. data/lib/view_component/instrumentation.rb +10 -2
  35. data/lib/view_component/preview.rb +21 -19
  36. data/lib/view_component/rails/tasks/view_component.rake +11 -2
  37. data/lib/view_component/render_component_helper.rb +1 -0
  38. data/lib/view_component/render_component_to_string_helper.rb +1 -1
  39. data/lib/view_component/render_to_string_monkey_patch.rb +1 -1
  40. data/lib/view_component/rendering_component_helper.rb +1 -1
  41. data/lib/view_component/rendering_monkey_patch.rb +1 -1
  42. data/lib/view_component/slot.rb +119 -1
  43. data/lib/view_component/slotable.rb +393 -96
  44. data/lib/view_component/slotable_default.rb +20 -0
  45. data/lib/view_component/system_test_case.rb +13 -0
  46. data/lib/view_component/system_test_helpers.rb +27 -0
  47. data/lib/view_component/template.rb +134 -0
  48. data/lib/view_component/test_helpers.rb +208 -47
  49. data/lib/view_component/translatable.rb +51 -33
  50. data/lib/view_component/use_helpers.rb +42 -0
  51. data/lib/view_component/version.rb +5 -4
  52. data/lib/view_component/with_content_helper.rb +3 -8
  53. data/lib/view_component.rb +7 -12
  54. metadata +339 -57
  55. data/lib/rails/generators/component/USAGE +0 -13
  56. data/lib/view_component/content_areas.rb +0 -57
  57. data/lib/view_component/polymorphic_slots.rb +0 -73
  58. data/lib/view_component/preview_template_error.rb +0 -6
  59. data/lib/view_component/previewable.rb +0 -62
  60. data/lib/view_component/slot_v2.rb +0 -104
  61. data/lib/view_component/slotable_v2.rb +0 -307
  62. data/lib/view_component/template_error.rb +0 -9
  63. data/lib/yard/mattr_accessor_handler.rb +0 -19
@@ -3,9 +3,7 @@
3
3
  module ViewComponent
4
4
  module AbstractGenerator
5
5
  def copy_view_file
6
- unless options["inline"]
7
- template "component.html.#{engine_name}", destination
8
- end
6
+ template "component.html.#{engine_name}", destination unless options["inline"]
9
7
  end
10
8
 
11
9
  private
@@ -31,20 +29,28 @@ module ViewComponent
31
29
  end
32
30
 
33
31
  def component_path
34
- ViewComponent::Base.view_component_path
32
+ ViewComponent::Base.config.view_component_path
35
33
  end
36
34
 
37
35
  def stimulus_controller
38
- if options["stimulus"]
39
- File.join(destination_directory, destination_file_name).
40
- sub("#{component_path}/", "").
41
- gsub("_", "-").
42
- gsub("/", "--")
36
+ if stimulus?
37
+ File.join(destination_directory, destination_file_name)
38
+ .sub("#{component_path}/", "")
39
+ .tr("_", "-")
40
+ .gsub("/", "--")
43
41
  end
44
42
  end
45
43
 
46
44
  def sidecar?
47
- options["sidecar"] || ViewComponent::Base.generate_sidecar
45
+ options["sidecar"] || ViewComponent::Base.config.generate.sidecar
46
+ end
47
+
48
+ def stimulus?
49
+ options["stimulus"] || ViewComponent::Base.config.generate.stimulus_controller
50
+ end
51
+
52
+ def typescript?
53
+ options["typescript"] || ViewComponent::Base.config.generate.typescript
48
54
  end
49
55
  end
50
56
  end
@@ -11,14 +11,18 @@ module Rails
11
11
 
12
12
  argument :attributes, type: :array, default: [], banner: "attribute"
13
13
  check_class_collision suffix: "Component"
14
+
14
15
  class_option :inline, type: :boolean, default: false
16
+ class_option :locale, type: :boolean, default: ViewComponent::Base.config.generate.locale
15
17
  class_option :parent, type: :string, desc: "The parent class for the generated component"
16
- class_option :stimulus, type: :boolean, default: ViewComponent::Base.generate_stimulus_controller
18
+ class_option :preview, type: :boolean, default: ViewComponent::Base.config.generate.preview
17
19
  class_option :sidecar, type: :boolean, default: false
18
- class_option :locale, type: :boolean, default: ViewComponent::Base.generate_locale
20
+ class_option :stimulus, type: :boolean,
21
+ default: ViewComponent::Base.config.generate.stimulus_controller
22
+ class_option :skip_suffix, type: :boolean, default: false
19
23
 
20
24
  def create_component_file
21
- template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
25
+ template "component.rb", File.join(component_path, class_path, "#{file_name}#{options[:skip_suffix] ? "" : "_component"}.rb")
22
26
  end
23
27
 
24
28
  hook_for :test_framework
@@ -38,7 +42,7 @@ module Rails
38
42
  def parent_class
39
43
  return options[:parent] if options[:parent]
40
44
 
41
- ViewComponent::Base.component_parent_class || default_parent_class
45
+ ViewComponent::Base.config.component_parent_class || default_parent_class
42
46
  end
43
47
 
44
48
  def initialize_signature
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class <%= class_name %>Component < <%= parent_class %>
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %><%= options[:skip_suffix] ? "" : "Component" %> < <%= parent_class %>
4
5
  <%- if initialize_signature -%>
5
6
  def initialize(<%= initialize_signature %>)
6
7
  <%= initialize_body %>
@@ -11,5 +12,5 @@ class <%= class_name %>Component < <%= parent_class %>
11
12
  content_tag :h1, "Hello world!"<%= ", data: { controller: \"#{stimulus_controller}\" }" if options["stimulus"] %>
12
13
  end
13
14
  <%- end -%>
14
-
15
15
  end
16
+ <% end -%>
@@ -24,7 +24,7 @@ module Erb
24
24
  private
25
25
 
26
26
  def data_attributes
27
- if options["stimulus"]
27
+ if stimulus?
28
28
  " data-controller=\"#{stimulus_controller}\""
29
29
  end
30
30
  end
@@ -12,7 +12,7 @@ module Locale
12
12
  class_option :sidecar, type: :boolean, default: false
13
13
 
14
14
  def create_locale_file
15
- if ViewComponent::Base.generate_distinct_locale_files
15
+ if ViewComponent::Base.config.generate.distinct_locale_files
16
16
  I18n.available_locales.each do |locale|
17
17
  create_file destination(locale), translations_hash([locale]).to_yaml
18
18
  end
@@ -34,11 +34,11 @@ module Locale
34
34
  end
35
35
 
36
36
  def destination(locale = nil)
37
- extention = ".#{locale}" if locale
37
+ extension = ".#{locale}" if locale
38
38
  if sidecar?
39
- File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{extention}.yml")
39
+ File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{extension}.yml")
40
40
  else
41
- File.join(component_path, class_path, "#{file_name}_component#{extention}.yml")
41
+ File.join(component_path, class_path, "#{file_name}_component#{extension}.yml")
42
42
  end
43
43
  end
44
44
  end
@@ -4,14 +4,22 @@ module Preview
4
4
  module Generators
5
5
  class ComponentGenerator < ::Rails::Generators::NamedBase
6
6
  source_root File.expand_path("templates", __dir__)
7
+ class_option :preview_path, type: :string, desc: "Path for previews, required when multiple preview paths are configured", default: ViewComponent::Base.config.generate.preview_path
7
8
 
9
+ argument :attributes, type: :array, default: [], banner: "attribute"
8
10
  check_class_collision suffix: "ComponentPreview"
9
11
 
10
12
  def create_preview_file
11
- preview_paths = Rails.application.config.view_component.preview_paths
12
- return if preview_paths.count > 1
13
+ preview_paths = ViewComponent::Base.config.preview_paths
14
+ optional_path = options[:preview_path]
15
+ return if preview_paths.count > 1 && optional_path.blank?
16
+
17
+ path_prefix = if optional_path.present?
18
+ optional_path
19
+ else
20
+ preview_paths.one? ? preview_paths.first : "test/components/previews"
21
+ end
13
22
 
14
- path_prefix = preview_paths.one? ? preview_paths.first : "test/components/previews"
15
23
  template "component_preview.rb", File.join(path_prefix, class_path, "#{file_name}_component_preview.rb")
16
24
  end
17
25
 
@@ -20,6 +28,12 @@ module Preview
20
28
  def file_name
21
29
  @_file_name ||= super.sub(/_component\z/i, "")
22
30
  end
31
+
32
+ def render_signature
33
+ return if attributes.blank?
34
+
35
+ attributes.map { |attr| %(#{attr.name}: "#{attr.name}") }.join(", ")
36
+ end
23
37
  end
24
38
  end
25
39
  end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
1
4
  class <%= class_name %>ComponentPreview < ViewComponent::Preview
2
5
  def default
3
- render(<%= class_name %>Component.new)
6
+ render(<%= class_name %>Component.new<%= "(#{render_signature})" if render_signature %>)
4
7
  end
5
8
  end
9
+ <% end -%>
@@ -1,18 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/generators/abstract_generator"
4
+
3
5
  module Rspec
4
6
  module Generators
5
7
  class ComponentGenerator < ::Rails::Generators::NamedBase
8
+ include ViewComponent::AbstractGenerator
9
+
6
10
  source_root File.expand_path("templates", __dir__)
7
11
 
8
12
  def create_test_file
9
- template "component_spec.rb", File.join("spec/components", class_path, "#{file_name}_component_spec.rb")
13
+ template "component_spec.rb", File.join(spec_component_path, class_path, "#{file_name}_component_spec.rb")
10
14
  end
11
15
 
12
16
  private
13
17
 
14
- def file_name
15
- @_file_name ||= super.sub(/_component\z/i, "")
18
+ def spec_component_path
19
+ return "spec/components" unless ViewComponent::Base.config.generate.use_component_path_for_rspec_tests
20
+
21
+ configured_component_path = component_path
22
+ if configured_component_path.start_with?("app#{File::SEPARATOR}")
23
+ _app, *rest_of_path = Pathname.new(configured_component_path).each_filename.to_a
24
+ File.join("spec", *rest_of_path)
25
+ else
26
+ "spec/components"
27
+ end
16
28
  end
17
29
  end
18
30
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails_helper"
2
4
 
3
- RSpec.describe <%= class_name %>Component, type: :component do
5
+ RSpec.describe <%= namespaced? ? "#{namespace.name}::" : '' %><%= class_name %>Component, type: :component do
4
6
  pending "add some examples to (or delete) #{__FILE__}"
5
7
 
6
8
  # it "renders something useful" do
@@ -9,9 +9,10 @@ module Stimulus
9
9
 
10
10
  source_root File.expand_path("templates", __dir__)
11
11
  class_option :sidecar, type: :boolean, default: false
12
+ class_option :typescript, type: :boolean, default: false
12
13
 
13
14
  def create_stimulus_controller
14
- template "component_controller.js", destination
15
+ template "component_controller.#{filetype}", destination
15
16
  end
16
17
 
17
18
  def stimulus_module
@@ -22,11 +23,15 @@ module Stimulus
22
23
 
23
24
  private
24
25
 
26
+ def filetype
27
+ typescript? ? "ts" : "js"
28
+ end
29
+
25
30
  def destination
26
31
  if sidecar?
27
- File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.js")
32
+ File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.#{filetype}")
28
33
  else
29
- File.join(component_path, class_path, "#{file_name}_component_controller.js")
34
+ File.join(component_path, class_path, "#{file_name}_component_controller.#{filetype}")
30
35
  end
31
36
  end
32
37
 
@@ -0,0 +1,9 @@
1
+ import { Controller } from "<%= stimulus_module %>";
2
+
3
+ export default class extends Controller {
4
+ declare element: HTMLElement;
5
+
6
+ connect() {
7
+ console.log("Hello, Stimulus!", this.element);
8
+ }
9
+ }
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "test_helper"
2
4
 
3
- class <%= class_name %>ComponentTest < ViewComponent::TestCase
5
+ class <%= namespaced? ? "#{namespace.name}::" : '' %><%= class_name %>ComponentTest < ViewComponent::TestCase
4
6
  def test_component_renders_something_useful
5
7
  # assert_equal(
6
8
  # %(<span>Hello, components!</span>),