view_component 2.53.0 → 2.55.0

Sign up to get free protection for your applications and to get access to all the features.

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: 0d505afaa4cb16810628e814c69bb2d00bc76498f6c681173d7811f44cd87c8e
4
- data.tar.gz: 2bc859d6034fed19e34739cf1844679bcf83332c7e227c487ddb7364e8166f4b
3
+ metadata.gz: c6460c94f3bb4f2b19cd5311d957d2187e437276c0c19b657834cccba39fb36f
4
+ data.tar.gz: c0dfc55481d2bcfcdc2f54ea4f4bacc94780e2dce39b77eaf5828490c1d0fc52
5
5
  SHA512:
6
- metadata.gz: 75dcf204fd8e3dfad8a1da18de6d96d43fd779f1c9a73ca66b76f0db1ac7be04cf04447c9eaa901d32bc674d7eb7fa79fd5572bff08652a892a7e96f5186faaa
7
- data.tar.gz: 7be68adaa9de981a4fb12451ebcc19875f5754fa4798eef60f98702b1c7396bb598a1f6ae77b768bbe55e5b4f77c33f6b9db7156fa18f04c0a63075c54fdd7cc
6
+ metadata.gz: 6ffed42bf70668cf3dbf811f5f24e4a9e2e6cca7fdaea3674f8323c08a60b0f7c49f94c9a1d0bb1c962fb04c0c6dbdb219891116b70385bb8d3fe5f650d86f91
7
+ data.tar.gz: 416501060d316e680dbf7f65543aa9a57b693e13bf8a9ebd8c244794155b33702863b22723d140bb85df560e4b18d746f3d534a9a98f4698ced8435cecce3375
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module PreviewActions
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ prepend_view_path File.expand_path("../../../views", __dir__)
9
+
10
+ around_action :set_locale, only: :previews
11
+ before_action :find_preview, only: :previews
12
+ before_action :require_local!, unless: :show_previews?
13
+
14
+ if respond_to?(:content_security_policy)
15
+ content_security_policy(false)
16
+ end
17
+ end
18
+
19
+ def index
20
+ @previews = ViewComponent::Preview.all
21
+ @page_title = "Component Previews"
22
+ render "view_components/index", **determine_layout
23
+ end
24
+
25
+ def previews
26
+ if params[:path] == @preview.preview_name
27
+ @page_title = "Component Previews for #{@preview.preview_name}"
28
+ render "view_components/previews", **determine_layout
29
+ else
30
+ prepend_application_view_paths
31
+ prepend_preview_examples_view_path
32
+ @example_name = File.basename(params[:path])
33
+ @render_args = @preview.render_args(@example_name, params: params.permit!)
34
+ layout = determine_layout(@render_args[:layout], prepend_views: false)[:layout]
35
+ locals = @render_args[:locals]
36
+ opts = {}
37
+ opts[:layout] = layout if layout.present? || layout == false
38
+ opts[:locals] = locals if locals.present?
39
+ render "view_components/preview", opts # rubocop:disable GitHub/RailsControllerRenderLiteral
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def default_preview_layout # :doc:
46
+ ViewComponent::Base.default_preview_layout
47
+ end
48
+
49
+ def show_previews? # :doc:
50
+ ViewComponent::Base.show_previews
51
+ end
52
+
53
+ def find_preview # :doc:
54
+ candidates = []
55
+ params[:path].to_s.scan(%r{/|$}) { candidates << $` }
56
+ preview = candidates.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
57
+
58
+ if preview
59
+ @preview = ViewComponent::Preview.find(preview)
60
+ else
61
+ raise AbstractController::ActionNotFound, "Component preview '#{params[:path]}' not found."
62
+ end
63
+ end
64
+
65
+ def set_locale
66
+ I18n.with_locale(params[:locale] || I18n.default_locale) do
67
+ yield
68
+ end
69
+ end
70
+
71
+ # Returns either {} or {layout: value} depending on configuration
72
+ def determine_layout(layout_override = nil, prepend_views: true)
73
+ return {} unless defined?(Rails.root)
74
+
75
+ layout_declaration = {}
76
+
77
+ if !layout_override.nil?
78
+ # Allow component-level override, even if false (thus no layout rendered)
79
+ layout_declaration[:layout] = layout_override
80
+ elsif default_preview_layout.present?
81
+ layout_declaration[:layout] = default_preview_layout
82
+ end
83
+
84
+ prepend_application_view_paths if layout_declaration[:layout].present? && prepend_views
85
+
86
+ layout_declaration
87
+ end
88
+
89
+ def prepend_application_view_paths
90
+ prepend_view_path Rails.root.join("app/views") if defined?(Rails.root)
91
+ end
92
+
93
+ def prepend_preview_examples_view_path
94
+ prepend_view_path(ViewComponent::Base.preview_paths)
95
+ end
96
+ end
97
+ end
@@ -3,91 +3,5 @@
3
3
  require "rails/application_controller"
4
4
 
5
5
  class ViewComponentsController < Rails::ApplicationController # :nodoc:
6
- prepend_view_path File.expand_path("../views", __dir__)
7
-
8
- around_action :set_locale, only: :previews
9
- before_action :find_preview, only: :previews
10
- before_action :require_local!, unless: :show_previews?
11
-
12
- if respond_to?(:content_security_policy)
13
- content_security_policy(false)
14
- end
15
-
16
- def index
17
- @previews = ViewComponent::Preview.all
18
- @page_title = "Component Previews"
19
- render "view_components/index", **determine_layout
20
- end
21
-
22
- def previews
23
- if params[:path] == @preview.preview_name
24
- @page_title = "Component Previews for #{@preview.preview_name}"
25
- render "view_components/previews", **determine_layout
26
- else
27
- prepend_application_view_paths
28
- prepend_preview_examples_view_path
29
- @example_name = File.basename(params[:path])
30
- @render_args = @preview.render_args(@example_name, params: params.permit!)
31
- layout = determine_layout(@render_args[:layout], prepend_views: false)[:layout]
32
- locals = @render_args[:locals]
33
- opts = {}
34
- opts[:layout] = layout if layout.present? || layout == false
35
- opts[:locals] = locals if locals.present?
36
- render "view_components/preview", opts # rubocop:disable GitHub/RailsControllerRenderLiteral
37
- end
38
- end
39
-
40
- private
41
-
42
- def default_preview_layout # :doc:
43
- ViewComponent::Base.default_preview_layout
44
- end
45
-
46
- def show_previews? # :doc:
47
- ViewComponent::Base.show_previews
48
- end
49
-
50
- def find_preview # :doc:
51
- candidates = []
52
- params[:path].to_s.scan(%r{/|$}) { candidates << $` }
53
- preview = candidates.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
54
-
55
- if preview
56
- @preview = ViewComponent::Preview.find(preview)
57
- else
58
- raise AbstractController::ActionNotFound, "Component preview '#{params[:path]}' not found."
59
- end
60
- end
61
-
62
- def set_locale
63
- I18n.with_locale(params[:locale] || I18n.default_locale) do
64
- yield
65
- end
66
- end
67
-
68
- # Returns either {} or {layout: value} depending on configuration
69
- def determine_layout(layout_override = nil, prepend_views: true)
70
- return {} unless defined?(Rails.root)
71
-
72
- layout_declaration = {}
73
-
74
- if !layout_override.nil?
75
- # Allow component-level override, even if false (thus no layout rendered)
76
- layout_declaration[:layout] = layout_override
77
- elsif default_preview_layout.present?
78
- layout_declaration[:layout] = default_preview_layout
79
- end
80
-
81
- prepend_application_view_paths if layout_declaration[:layout].present? && prepend_views
82
-
83
- layout_declaration
84
- end
85
-
86
- def prepend_application_view_paths
87
- prepend_view_path Rails.root.join("app/views") if defined?(Rails.root)
88
- end
89
-
90
- def prepend_preview_examples_view_path
91
- prepend_view_path(ViewComponent::Base.preview_paths)
92
- end
6
+ include ViewComponent::PreviewActions
93
7
  end
data/docs/CHANGELOG.md CHANGED
@@ -9,6 +9,53 @@ title: Changelog
9
9
 
10
10
  ## main
11
11
 
12
+ ## 2.55.0
13
+
14
+ * Add `render_parent` convenience method to avoid confusion between `<%= super %>` and `<% super %>` in template code.
15
+
16
+ *Cameron Dutro*
17
+
18
+ * Add note about discouraging inheritance.
19
+
20
+ *Joel Hawksley*
21
+
22
+ * Clean up grammar in documentation.
23
+
24
+ *Joel Hawksley*
25
+
26
+ * The ViewComponent team at GitHub is hiring! We're looking for a Rails engineer with accessibility experience: [https://boards.greenhouse.io/github/jobs/4020166](https://boards.greenhouse.io/github/jobs/4020166). Reach out to joelhawksley@github.com with any questions!
27
+
28
+ * The ViewComponent team is hosting a happy hour at RailsConf. Join us for snacks, drinks, and stickers: [https://www.eventbrite.com/e/viewcomponent-happy-hour-tickets-304168585427](https://www.eventbrite.com/e/viewcomponent-happy-hour-tickets-304168585427)
29
+
30
+ ## 2.54.1
31
+
32
+ * Update docs dependencies.
33
+
34
+ *Joel Hawksley*
35
+
36
+ * Resolve warning in slots API.
37
+ * Raise in the test environment when ViewComponent code emits a warning.
38
+
39
+ *Blake Williams*
40
+
41
+ ## 2.54.0
42
+
43
+ * Add `with_*` slot API for defining slots. Note: we plan to deprecate the non `with_*` API for slots in an upcoming release.
44
+
45
+ *Blake Williams*
46
+
47
+ * Add QuickNode to list of companies that use ViewComponent.
48
+
49
+ *Luc Castera*
50
+
51
+ * Include the `Translatable` module by default.
52
+
53
+ *Elia Schito*
54
+
55
+ * Update docs dependencies.
56
+
57
+ *Joel Hawksley*
58
+
12
59
  ## 2.53.0
13
60
 
14
61
  * Add support for relative I18n scopes to translations.
@@ -31,7 +78,7 @@ title: Changelog
31
78
 
32
79
  *Jason Swett*
33
80
 
34
- * Add Bearer to list of companies that heavily rely on ViewComponent.
81
+ * Add Bearer to list of companies that use ViewComponent.
35
82
 
36
83
  *Yaroslav Shmarov*
37
84
 
@@ -39,6 +86,10 @@ title: Changelog
39
86
 
40
87
  *Joel Hawksley*
41
88
 
89
+ * Enable rendering arbitrary block contents in the view context in tests.
90
+
91
+ *Cameron Dutro*
92
+
42
93
  ## 2.52.0
43
94
 
44
95
  * Add ADR for separate slot getter/setter API.
@@ -160,6 +211,10 @@ title: Changelog
160
211
 
161
212
  *Peter Goldstein*
162
213
 
214
+ * Move preview logic to module for easier app integration.
215
+
216
+ *Sammy Henningsson*
217
+
163
218
  ## 2.48.0
164
219
 
165
220
  * Correct path in example test command in Contributing docs.
@@ -17,6 +17,7 @@ module ViewComponent
17
17
  include ViewComponent::ContentAreas
18
18
  include ViewComponent::Previewable
19
19
  include ViewComponent::SlotableV2
20
+ include ViewComponent::Translatable
20
21
  include ViewComponent::WithContentHelper
21
22
 
22
23
  ViewContextCalledBeforeRenderError = Class.new(StandardError)
@@ -69,7 +70,7 @@ module ViewComponent
69
70
  @view_context = view_context
70
71
  self.__vc_original_view_context ||= view_context
71
72
 
72
- @output_buffer = ActionView::OutputBuffer.new unless @global_buffer_in_use
73
+ @output_buffer = ActionView::OutputBuffer.new unless defined?(@global_buffer_in_use) && @global_buffer_in_use
73
74
 
74
75
  @lookup_context ||= view_context.lookup_context
75
76
 
@@ -116,6 +117,20 @@ module ViewComponent
116
117
  render_template_for(@__vc_variant).to_s + _output_postamble
117
118
  end
118
119
 
120
+ # Subclass components that call `super` inside their template code will cause a
121
+ # double render if they accidentally emit the result:
122
+ #
123
+ # <%= super %> # double-renders
124
+ #
125
+ # <% super %> # does not double-render
126
+ #
127
+ # Calls `super`, returning nil to avoid rendering the result twice.
128
+ def render_parent
129
+ mtd = @__vc_variant ? "call_#{@__vc_variant}" : "call"
130
+ method(mtd).super_method.call
131
+ nil
132
+ end
133
+
119
134
  # :nocov:
120
135
  def render_template_for(variant = nil)
121
136
  # Force compilation here so the compiler always redefines render_template_for.
@@ -77,6 +77,7 @@ module ViewComponent
77
77
 
78
78
  define_render_template_for
79
79
 
80
+ component_class.build_i18n_backend
80
81
  component_class._after_compile
81
82
 
82
83
  CompileCache.register(component_class)
@@ -45,10 +45,16 @@ module ViewComponent
45
45
  "#{slot_name}_#{poly_type}"
46
46
  end
47
47
 
48
+ # Deprecated: Will be removed in 3.0
48
49
  define_method(setter_name) do |*args, &block|
49
50
  set_polymorphic_slot(slot_name, poly_type, *args, &block)
50
51
  end
51
52
  ruby2_keywords(setter_name.to_sym) if respond_to?(:ruby2_keywords, true)
53
+
54
+ define_method("with_#{setter_name}") do |*args, &block|
55
+ set_polymorphic_slot(slot_name, poly_type, *args, &block)
56
+ end
57
+ ruby2_keywords(:"with_#{setter_name}") if respond_to?(:ruby2_keywords, true)
52
58
  end
53
59
 
54
60
  self.registered_slots[slot_name] = {
@@ -61,20 +61,26 @@ module ViewComponent
61
61
  # = Setting sub-component content
62
62
  #
63
63
  # Consumers of the component can render a sub-component by calling a
64
- # helper method with the same name as the slot.
64
+ # helper method with the same name as the slot prefixed with `with_`.
65
65
  #
66
66
  # <%= render_inline(MyComponent.new) do |component| %>
67
- # <% component.header(classes: "Foo") do %>
67
+ # <% component.with_header(classes: "Foo") do %>
68
68
  # <p>Bar</p>
69
69
  # <% end %>
70
70
  # <% end %>
71
71
  def renders_one(slot_name, callable = nil)
72
72
  validate_singular_slot_name(slot_name)
73
73
 
74
+ define_method :"with_#{slot_name}" do |*args, &block|
75
+ set_slot(slot_name, nil, *args, &block)
76
+ end
77
+ ruby2_keywords(:"with_#{slot_name}") if respond_to?(:ruby2_keywords, true)
78
+
74
79
  define_method slot_name do |*args, &block|
75
80
  if args.empty? && block.nil?
76
81
  get_slot(slot_name)
77
82
  else
83
+ # Deprecated: Will remove in 3.0
78
84
  set_slot(slot_name, nil, *args, &block)
79
85
  end
80
86
  end
@@ -112,15 +118,15 @@ module ViewComponent
112
118
  # = Setting sub-component content
113
119
  #
114
120
  # Consumers of the component can set the content of a slot by calling a
115
- # helper method with the same name as the slot. The method can be
116
- # called multiple times to append to the slot.
121
+ # helper method with the same name as the slot prefixed with `with_`. The
122
+ # method can be called multiple times to append to the slot.
117
123
  #
118
124
  # <%= render_inline(MyComponent.new) do |component| %>
119
- # <% component.item(name: "Foo") do %>
125
+ # <% component.with_item(name: "Foo") do %>
120
126
  # <p>One</p>
121
127
  # <% end %>
122
128
  #
123
- # <% component.item(name: "Bar") do %>
129
+ # <% component.with_item(name: "Bar") do %>
124
130
  # <p>two</p>
125
131
  # <% end %>
126
132
  # <% end %>
@@ -132,17 +138,31 @@ module ViewComponent
132
138
  # Define setter for singular names
133
139
  # for example `renders_many :items` allows fetching all tabs with
134
140
  # `component.tabs` and setting a tab with `component.tab`
141
+ #
142
+ # Deprecated: Will remove in 3.0
135
143
  define_method singular_name do |*args, &block|
136
144
  set_slot(slot_name, nil, *args, &block)
137
145
  end
138
146
  ruby2_keywords(singular_name.to_sym) if respond_to?(:ruby2_keywords, true)
139
147
 
148
+ define_method :"with_#{singular_name}" do |*args, &block|
149
+ set_slot(slot_name, nil, *args, &block)
150
+ end
151
+ ruby2_keywords(:"with_#{singular_name}") if respond_to?(:ruby2_keywords, true)
152
+
153
+ define_method :"with_#{slot_name}" do |collection_args = nil, &block|
154
+ collection_args.map do |args|
155
+ set_slot(slot_name, nil, **args, &block)
156
+ end
157
+ end
158
+
140
159
  # Instantiates and and adds multiple slots forwarding the first
141
160
  # argument to each slot constructor
142
161
  define_method slot_name do |collection_args = nil, &block|
143
162
  if collection_args.nil? && block.nil?
144
163
  get_slot(slot_name)
145
164
  else
165
+ # Deprecated: Will remove in 3.0
146
166
  collection_args.map do |args|
147
167
  set_slot(slot_name, nil, **args, &block)
148
168
  end
@@ -19,8 +19,8 @@ module ViewComponent
19
19
  # :nocov:
20
20
  if ENV["DEBUG"]
21
21
  warn(
22
- "WARNING in `ViewComponent::TestHelpers`: You must add `capybara` " \
23
- "to your Gemfile to use Capybara assertions."
22
+ "WARNING in `ViewComponent::TestHelpers`: Add `capybara` " \
23
+ "to Gemfile to use Capybara assertions."
24
24
  )
25
25
  end
26
26
 
@@ -51,6 +51,21 @@ module ViewComponent
51
51
  Nokogiri::HTML.fragment(@rendered_component)
52
52
  end
53
53
 
54
+ # Execute the given block in the view context. Internally sets `page` to be a
55
+ # `Capybara::Node::Simple`, allowing for Capybara assertions to be used:
56
+ #
57
+ # ```ruby
58
+ # render_in_view_context do
59
+ # render(MyComponent.new)
60
+ # end
61
+ #
62
+ # assert_text("Hello, World!")
63
+ # ```
64
+ def render_in_view_context(&block)
65
+ @rendered_component = controller.view_context.instance_exec(&block)
66
+ Nokogiri::HTML.fragment(@rendered_component)
67
+ end
68
+
54
69
  # @private
55
70
  def controller
56
71
  @controller ||= build_controller(Base.test_controller.constantize)
@@ -20,9 +20,7 @@ module ViewComponent
20
20
  @i18n_scope ||= virtual_path.sub(%r{^/}, "").gsub(%r{/_?}, ".")
21
21
  end
22
22
 
23
- def _after_compile
24
- super
25
-
23
+ def build_i18n_backend
26
24
  return if CompileCache.compiled? self
27
25
 
28
26
  if (translation_files = _sidecar_files(%w[yml yaml])).any?
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 53
6
+ MINOR = 55
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.53.0
4
+ version: 2.55.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: 2022-04-20 00:00:00.000000000 Z
11
+ date: 2022-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -293,6 +293,7 @@ files:
293
293
  - README.md
294
294
  - app/assets/vendor/prism.css
295
295
  - app/assets/vendor/prism.min.js
296
+ - app/controllers/concerns/view_component/preview_actions.rb
296
297
  - app/controllers/view_components_controller.rb
297
298
  - app/helpers/preview_helper.rb
298
299
  - app/views/test_mailer/test_email.html.erb