view_component 2.53.0 → 2.55.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 +4 -4
- data/app/controllers/concerns/view_component/preview_actions.rb +97 -0
- data/app/controllers/view_components_controller.rb +1 -87
- data/docs/CHANGELOG.md +56 -1
- data/lib/view_component/base.rb +16 -1
- data/lib/view_component/compiler.rb +1 -0
- data/lib/view_component/polymorphic_slots.rb +6 -0
- data/lib/view_component/slotable_v2.rb +26 -6
- data/lib/view_component/test_helpers.rb +17 -2
- data/lib/view_component/translatable.rb +1 -3
- data/lib/view_component/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6460c94f3bb4f2b19cd5311d957d2187e437276c0c19b657834cccba39fb36f
|
4
|
+
data.tar.gz: c0dfc55481d2bcfcdc2f54ea4f4bacc94780e2dce39b77eaf5828490c1d0fc52
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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.
|
data/lib/view_component/base.rb
CHANGED
@@ -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.
|
@@ -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.
|
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
|
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.
|
125
|
+
# <% component.with_item(name: "Foo") do %>
|
120
126
|
# <p>One</p>
|
121
127
|
# <% end %>
|
122
128
|
#
|
123
|
-
# <% component.
|
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`:
|
23
|
-
"to
|
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
|
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?
|
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.
|
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-
|
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
|