view_component 2.37.0 → 2.41.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f97acb8cd2ac4c3c706207aa030cfb62af6d9066b9d05ed32b0946851132367
4
- data.tar.gz: aeceea89a252646b358b5a67883fc926bffd56d321e0e3237fbc1471970c6745
3
+ metadata.gz: ec24d635f8ce5341c3735ab855b9099fe2ea9179bb21a8fe79ba0f14f374ec44
4
+ data.tar.gz: fbc578b4554de38f01a4e92bbcf6ddfaf146ac04fb75e871f8d2378f8a19c053
5
5
  SHA512:
6
- metadata.gz: 8bc5f98ac1fc8ca620d2add5b59d5c0505489229cd0d8a7dcc9858774399c4e82e3dae419b5016b23a1228bec1e9e2e5a3e9d6980376439e5fb0cf02153ac8f9
7
- data.tar.gz: 4d7b0037a063825a777a94ca03e32e89abcadc04bb0c5ec2265f5eb8a1b62f7abb1e807ace75726365ab8baebdd774dd5fc757da2b0cd6ce51a5670b03309ff2
6
+ metadata.gz: 9ed08e30f614fc25aeb1c1826d159b312fba175a95a71966112069a27bc1da3255948b1c9504b06f55b6c476fda4418f13b3e42050664dc3146def0ff1ba0636
7
+ data.tar.gz: b02c7784cc31a3e5cfe5d6ec55879bbe832446557810a3884034e871effece891164424f4d4bf34664c3d8a239683405ea31d92b7351321ce0fc8c33c802f3a5
data/docs/CHANGELOG.md CHANGED
@@ -7,6 +7,88 @@ title: Changelog
7
7
 
8
8
  ## main
9
9
 
10
+ ## 2.41.0
11
+
12
+ * Add `sprockets-rails` development dependency to fix test suite failures when using rails@main.
13
+
14
+ *Blake Williams*
15
+
16
+ * Fix Ruby indentation warning.
17
+
18
+ *Blake Williams*
19
+
20
+ * Add `--parent` generator option to specify the parent class.
21
+ * Add config option `config.view_component.component_parent_class` to change it project-wide.
22
+
23
+ *Hans Lemuet*
24
+
25
+ * Update docs to add example for using Devise helpers in tests.
26
+
27
+ *Matthew Rider*
28
+
29
+ * Fix bug where `with_collection_parameter` did not inherit from parent component.
30
+
31
+ *Will Drexler*, *Christian Campoli*
32
+
33
+ * Allow query parameters in `with_request_url` test helper.
34
+
35
+ *Javi Martín*
36
+
37
+ * Add "how to render a component to a string" to FAQ.
38
+
39
+ *Hans Lemuet*
40
+
41
+ * Add `#render_in` to API docs.
42
+
43
+ *Hans Lemuet*
44
+
45
+ * Forward keyword arguments from slot wrapper to component instance.
46
+
47
+ *Cameron Dutro*
48
+
49
+ ## 2.40.0
50
+
51
+ * Replace antipatterns section in the documentation with best practices.
52
+
53
+ *Blake Williams*
54
+
55
+ * Add components to `rails stats` task.
56
+
57
+ *Nicolas Brousse*
58
+
59
+ * Fix bug when using Slim and writing a slot whose block evaluates to `nil`.
60
+
61
+ *Yousuf Jukaku*
62
+
63
+ * Add documentation for test helpers.
64
+
65
+ *Joel Hawksley*
66
+
67
+ ## 2.39.0
68
+
69
+ * Clarify documentation of `with_variant` as an override of Action Pack.
70
+
71
+ *Blake Williams*, *Cameron Dutro*, *Joel Hawksley*
72
+
73
+ * Update docs page to be called Javascript and CSS, rename Building ViewComponents to Guide.
74
+
75
+ *Joel Hawksley*
76
+
77
+ * Deprecate `Base#with_variant`.
78
+
79
+ *Cameron Dutro*
80
+
81
+ * Error out in the CI if docs/api.md has to be regenerated.
82
+
83
+ *Dany Marcoux*
84
+
85
+ ## 2.38.0
86
+
87
+ * Add `--stimulus` flag to the component generator. Generates a Stimulus controller alongside the component.
88
+ * Add config option `config.view_component.generate_stimulus_controller` to always generate a Stimulus controller.
89
+
90
+ *Sebastien Auriault*
91
+
10
92
  ## 2.37.0
11
93
 
12
94
  * Clarify slots example in docs to reduce naming confusion.
@@ -19,7 +101,7 @@ title: Changelog
19
101
 
20
102
  * Add test case for conflict with internal `@variant` variable.
21
103
 
22
- *David Backeus*
104
+ *David Backeus*
23
105
 
24
106
  * Document decision to not change naming convention recommendation to remove `-Component` suffix.
25
107
 
@@ -83,7 +165,7 @@ title: Changelog
83
165
 
84
166
  * Ensure consistent indentation with Rubocop.
85
167
 
86
- *Joel Hawksley
168
+ *Joel Hawksley*
87
169
 
88
170
  * Bump `activesupport` upper bound from `< 7.0` to `< 8.0`.
89
171
 
@@ -11,13 +11,21 @@ module ViewComponent
11
11
  private
12
12
 
13
13
  def destination
14
+ File.join(destination_directory, "#{destination_file_name}.html.#{engine_name}")
15
+ end
16
+
17
+ def destination_directory
14
18
  if options["sidecar"]
15
- File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component.html.#{engine_name}")
19
+ File.join(component_path, class_path, destination_file_name)
16
20
  else
17
- File.join(component_path, class_path, "#{file_name}_component.html.#{engine_name}")
21
+ File.join(component_path, class_path)
18
22
  end
19
23
  end
20
24
 
25
+ def destination_file_name
26
+ "#{file_name}_component"
27
+ end
28
+
21
29
  def file_name
22
30
  @_file_name ||= super.sub(/_component\z/i, "")
23
31
  end
@@ -25,5 +33,14 @@ module ViewComponent
25
33
  def component_path
26
34
  ViewComponent::Base.view_component_path
27
35
  end
36
+
37
+ def stimulus_controller
38
+ if options["stimulus"]
39
+ File.join(destination_directory, destination_file_name).
40
+ sub("#{component_path}/", "").
41
+ gsub("_", "-").
42
+ gsub("/", "--")
43
+ end
44
+ end
28
45
  end
29
46
  end
@@ -12,6 +12,9 @@ module Rails
12
12
  argument :attributes, type: :array, default: [], banner: "attribute"
13
13
  check_class_collision suffix: "Component"
14
14
  class_option :inline, type: :boolean, default: false
15
+ 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
17
+ class_option :sidecar, type: :boolean, default: false
15
18
 
16
19
  def create_component_file
17
20
  template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
@@ -21,6 +24,8 @@ module Rails
21
24
 
22
25
  hook_for :preview, type: :boolean
23
26
 
27
+ hook_for :stimulus, type: :boolean
28
+
24
29
  hook_for :template_engine do |instance, template_engine|
25
30
  instance.invoke template_engine, [instance.name]
26
31
  end
@@ -28,7 +33,9 @@ module Rails
28
33
  private
29
34
 
30
35
  def parent_class
31
- defined?(ApplicationComponent) ? "ApplicationComponent" : "ViewComponent::Base"
36
+ return options[:parent] if options[:parent]
37
+
38
+ ViewComponent::Base.component_parent_class || default_parent_class
32
39
  end
33
40
 
34
41
  def initialize_signature
@@ -44,6 +51,10 @@ module Rails
44
51
  def initialize_call_method_for_inline?
45
52
  options["inline"]
46
53
  end
54
+
55
+ def default_parent_class
56
+ defined?(ApplicationComponent) ? ApplicationComponent : ViewComponent::Base
57
+ end
47
58
  end
48
59
  end
49
60
  end
@@ -8,7 +8,7 @@ class <%= class_name %>Component < <%= parent_class %>
8
8
  <%- end -%>
9
9
  <%- if initialize_call_method_for_inline? -%>
10
10
  def call
11
- content_tag :h1, "Hello world!"
11
+ content_tag :h1, "Hello world!"<%= ", data: { controller: \"#{stimulus_controller}\" }" if options["stimulus"] %>
12
12
  end
13
13
  <%- end -%>
14
14
 
@@ -11,6 +11,7 @@ module Erb
11
11
  source_root File.expand_path("templates", __dir__)
12
12
  class_option :sidecar, type: :boolean, default: false
13
13
  class_option :inline, type: :boolean, default: false
14
+ class_option :stimulus, type: :boolean, default: false
14
15
 
15
16
  def engine_name
16
17
  "erb"
@@ -19,6 +20,14 @@ module Erb
19
20
  def copy_view_file
20
21
  super
21
22
  end
23
+
24
+ private
25
+
26
+ def data_attributes
27
+ if options["stimulus"]
28
+ " data-controller=\"#{stimulus_controller}\""
29
+ end
30
+ end
22
31
  end
23
32
  end
24
33
  end
@@ -1 +1 @@
1
- <div>Add <%= class_name %> template here</div>
1
+ <div<%= data_attributes %>>Add <%= class_name %> template here</div>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stimulus
4
+ module Generators
5
+ class ComponentGenerator < ::Rails::Generators::NamedBase
6
+ include ViewComponent::AbstractGenerator
7
+
8
+ source_root File.expand_path("templates", __dir__)
9
+ class_option :sidecar, type: :boolean, default: false
10
+
11
+ def create_stimulus_controller
12
+ template "component_controller.js", destination
13
+ end
14
+
15
+ private
16
+
17
+ def destination
18
+ if options["sidecar"]
19
+ File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.js")
20
+ else
21
+ File.join(component_path, class_path, "#{file_name}_component_controller.js")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ import { Controller } from "stimulus";
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ console.log("Hello, Stimulus!", this.element);
6
+ }
7
+ }
@@ -39,29 +39,12 @@ module ViewComponent
39
39
 
40
40
  # Entrypoint for rendering components.
41
41
  #
42
- # view_context: ActionView context from calling view
43
- # block: optional block to be captured within the view context
42
+ # - `view_context`: ActionView context from calling view
43
+ # - `block`: optional block to be captured within the view context
44
44
  #
45
- # returns HTML that has been escaped by the respective template handler
45
+ # Returns HTML that has been escaped by the respective template handler.
46
46
  #
47
- # Example subclass:
48
- #
49
- # app/components/my_component.rb:
50
- # class MyComponent < ViewComponent::Base
51
- # def initialize(title:)
52
- # @title = title
53
- # end
54
- # end
55
- #
56
- # app/components/my_component.html.erb
57
- # <span title="<%= @title %>">Hello, <%= content %>!</span>
58
- #
59
- # In use:
60
- # <%= render MyComponent.new(title: "greeting") do %>world<% end %>
61
- # returns:
62
- # <span title="greeting">Hello, world!</span>
63
- #
64
- # @private
47
+ # @return [String]
65
48
  def render_in(view_context, &block)
66
49
  self.class.compile(raise_errors: true)
67
50
 
@@ -218,9 +201,14 @@ module ViewComponent
218
201
 
219
202
  # Use the provided variant instead of the one determined by the current request.
220
203
  #
204
+ # @deprecated Will be removed in v3.0.0.
221
205
  # @param variant [Symbol] The variant to be used by the component.
222
206
  # @return [self]
223
207
  def with_variant(variant)
208
+ ActiveSupport::Deprecation.warn(
209
+ "`with_variant` is deprecated and will be removed in ViewComponent v3.0.0."
210
+ )
211
+
224
212
  @__vc_variant = variant
225
213
 
226
214
  self
@@ -278,6 +266,14 @@ module ViewComponent
278
266
  #
279
267
  mattr_accessor :show_previews_source, instance_writer: false, default: false
280
268
 
269
+ # Always generate a Stimulus controller alongside the component:
270
+ #
271
+ # config.view_component.generate_stimulus_controller = true
272
+ #
273
+ # Defaults to `false`.
274
+ #
275
+ mattr_accessor :generate_stimulus_controller, instance_writer: false, default: false
276
+
281
277
  # Path for component files
282
278
  #
283
279
  # config.view_component.view_component_path = "app/my_components"
@@ -285,6 +281,14 @@ module ViewComponent
285
281
  # Defaults to "app/components".
286
282
  mattr_accessor :view_component_path, instance_writer: false, default: "app/components"
287
283
 
284
+ # Parent class for generated components
285
+ #
286
+ # config.view_component.component_parent_class = "MyBaseComponent"
287
+ #
288
+ # Defaults to "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
289
+ mattr_accessor :component_parent_class,
290
+ instance_writer: false
291
+
288
292
  class << self
289
293
  # @private
290
294
  attr_accessor :source_location, :virtual_path
@@ -371,6 +375,9 @@ module ViewComponent
371
375
  %r{(.*#{Regexp.quote(ViewComponent::Base.view_component_path)})|(\.rb)}, ""
372
376
  )
373
377
 
378
+ # Set collection parameter to the extended component
379
+ child.with_collection_parameter provided_collection_parameter
380
+
374
381
  super
375
382
  end
376
383
 
@@ -8,6 +8,10 @@ module ViewComponent
8
8
  config.view_component = ActiveSupport::OrderedOptions.new
9
9
  config.view_component.preview_paths ||= []
10
10
 
11
+ rake_tasks do
12
+ load "view_component/rails/tasks/view_component.rake"
13
+ end
14
+
11
15
  initializer "view_component.set_configs" do |app|
12
16
  options = app.config.view_component
13
17
 
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ task stats: "view_component:statsetup"
4
+
5
+ namespace :view_component do
6
+ task statsetup: :environment do
7
+ require "rails/code_statistics"
8
+
9
+ ::STATS_DIRECTORIES << ["ViewComponents", ViewComponent::Base.view_component_path]
10
+ end
11
+ end
@@ -62,9 +62,9 @@ module ViewComponent
62
62
  view_context.capture(&@__vc_content_block)
63
63
  elsif defined?(@__vc_content_set_by_with_content)
64
64
  @__vc_content_set_by_with_content
65
- end
65
+ end
66
66
 
67
- @content
67
+ @content = @content.to_s
68
68
  end
69
69
 
70
70
  # Allow access to public component methods via the wrapper
@@ -86,8 +86,14 @@ module ViewComponent
86
86
  # end
87
87
  # end
88
88
  #
89
- def method_missing(symbol, *args, &block)
90
- @__vc_component_instance.public_send(symbol, *args, &block)
89
+ if RUBY_VERSION >= "2.7.0"
90
+ def method_missing(symbol, *args, **kwargs, &block)
91
+ @__vc_component_instance.public_send(symbol, *args, **kwargs, &block)
92
+ end
93
+ else
94
+ def method_missing(symbol, *args, &block)
95
+ @__vc_component_instance.public_send(symbol, *args, &block)
96
+ end
91
97
  end
92
98
 
93
99
  def html_safe?
@@ -27,8 +27,19 @@ module ViewComponent
27
27
  # :nocov:
28
28
  end
29
29
 
30
+ # @private
30
31
  attr_reader :rendered_component
31
32
 
33
+ # Render a component inline. Internally sets `page` to be a `Capybara::Node::Simple`,
34
+ # allowing for Capybara assertions to be used:
35
+ #
36
+ # ```ruby
37
+ # render_inline(MyComponent.new)
38
+ # assert_text("Hello, World!")
39
+ # ```
40
+ #
41
+ # @param component [ViewComponent::Base] The instance of the component to be rendered.
42
+ # @return [Nokogiri::HTML]
32
43
  def render_inline(component, **args, &block)
33
44
  @rendered_component =
34
45
  if Rails.version.to_f >= 6.1
@@ -40,10 +51,12 @@ module ViewComponent
40
51
  Nokogiri::HTML.fragment(@rendered_component)
41
52
  end
42
53
 
54
+ # @private
43
55
  def controller
44
56
  @controller ||= build_controller(Base.test_controller.constantize)
45
57
  end
46
58
 
59
+ # @private
47
60
  def request
48
61
  @request ||=
49
62
  begin
@@ -53,6 +66,15 @@ module ViewComponent
53
66
  end
54
67
  end
55
68
 
69
+ # Set the Action Pack request variant for the given block:
70
+ #
71
+ # ```ruby
72
+ # with_variant(:phone) do
73
+ # render_inline(MyComponent.new)
74
+ # end
75
+ # ```
76
+ #
77
+ # @param variant [Symbol] The variant to be set for the provided block.
56
78
  def with_variant(variant)
57
79
  old_variants = controller.view_context.lookup_context.variants
58
80
 
@@ -62,6 +84,16 @@ module ViewComponent
62
84
  controller.view_context.lookup_context.variants = old_variants
63
85
  end
64
86
 
87
+ # Set the controller to be used while executing the given block,
88
+ # allowing access to controller-specific methods:
89
+ #
90
+ # ```ruby
91
+ # with_controller_class(UsersController) do
92
+ # render_inline(MyComponent.new)
93
+ # end
94
+ # ```
95
+ #
96
+ # @param klass [ActionController::Base] The controller to be used.
65
97
  def with_controller_class(klass)
66
98
  old_controller = defined?(@controller) && @controller
67
99
 
@@ -71,17 +103,30 @@ module ViewComponent
71
103
  @controller = old_controller
72
104
  end
73
105
 
106
+ # Set the URL for the current request (such as when using request-dependent path helpers):
107
+ #
108
+ # ```ruby
109
+ # with_request_url("/users/42") do
110
+ # render_inline(MyComponent.new)
111
+ # end
112
+ # ```
113
+ #
114
+ # @param path [String] The path to set for the current request.
74
115
  def with_request_url(path)
75
116
  old_request_path_parameters = request.path_parameters
117
+ old_request_query_parameters = request.query_parameters
76
118
  old_controller = defined?(@controller) && @controller
77
119
 
78
120
  request.path_parameters = Rails.application.routes.recognize_path(path)
121
+ request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_query(path.split("?")[1]))
79
122
  yield
80
123
  ensure
81
124
  request.path_parameters = old_request_path_parameters
125
+ request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
82
126
  @controller = old_controller
83
127
  end
84
128
 
129
+ # @private
85
130
  def build_controller(klass)
86
131
  klass.new.tap { |c| c.request = request }.extend(Rails.application.routes.url_helpers)
87
132
  end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 37
6
+ MINOR = 41
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.37.0
4
+ version: 2.41.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-09 00:00:00.000000000 Z
11
+ date: 2021-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -226,6 +226,20 @@ dependencies:
226
226
  - - "~>"
227
227
  - !ruby/object:Gem::Version
228
228
  version: '4.0'
229
+ - !ruby/object:Gem::Dependency
230
+ name: sprockets-rails
231
+ requirement: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - "~>"
234
+ - !ruby/object:Gem::Version
235
+ version: 3.2.2
236
+ type: :development
237
+ prerelease: false
238
+ version_requirements: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - "~>"
241
+ - !ruby/object:Gem::Version
242
+ version: 3.2.2
229
243
  - !ruby/object:Gem::Dependency
230
244
  name: yard
231
245
  requirement: !ruby/object:Gem::Requirement
@@ -287,6 +301,8 @@ files:
287
301
  - lib/rails/generators/rspec/templates/component_spec.rb.tt
288
302
  - lib/rails/generators/slim/component_generator.rb
289
303
  - lib/rails/generators/slim/templates/component.html.slim.tt
304
+ - lib/rails/generators/stimulus/component_generator.rb
305
+ - lib/rails/generators/stimulus/templates/component_controller.js.tt
290
306
  - lib/rails/generators/test_unit/component_generator.rb
291
307
  - lib/rails/generators/test_unit/templates/component_test.rb.tt
292
308
  - lib/view_component.rb
@@ -301,6 +317,7 @@ files:
301
317
  - lib/view_component/preview.rb
302
318
  - lib/view_component/preview_template_error.rb
303
319
  - lib/view_component/previewable.rb
320
+ - lib/view_component/rails/tasks/view_component.rake
304
321
  - lib/view_component/render_component_helper.rb
305
322
  - lib/view_component/render_component_to_string_helper.rb
306
323
  - lib/view_component/render_monkey_patch.rb