diff --git a/Gemfile.lock b/Gemfile.lock index 6f2565291..cf2e08ec6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - view_component (3.24.0) + view_component (3.25.0) activesupport (>= 5.2.0, < 8.2) concurrent-ruby (~> 1) method_source (~> 1.0) diff --git a/app/helpers/preview_helper.rb b/app/helpers/preview_helper.rb index bac7557bc..db8d1637a 100644 --- a/app/helpers/preview_helper.rb +++ b/app/helpers/preview_helper.rb @@ -23,7 +23,7 @@ def prism_js_source_url end def find_template_data_for_preview_source(lookup_context:, template_identifier:) - template = lookup_context.find_template(template_identifier) + template = find_template_for_preview_source(lookup_context, template_identifier) if Rails.version.to_f >= 6.1 || template.source.present? { @@ -61,6 +61,14 @@ def find_template_data_for_preview_source(lookup_context:, template_identifier:) private + def find_template_for_preview_source(lookup_context, template_identifier) + if lookup_context.respond_to?(:find_template) + lookup_context.find_template(template_identifier) + else + lookup_context.find(template_identifier) + end + end + def prism_language_name_by_template(template:) language = template.identifier.split(".").last diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7f898ee77..61fe6a9a8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,14 @@ nav_order: 6 ## main +* Fix rendering partials from components rendered as a collection when the original view context is explicitly stashed as `nil`. + + *Justin Coyne, Joel Hawksley* + +* Restore compatibility with Rails main after `ActionView::Template.template_handler_extensions` and `ActionView::LookupContext#find_template` were removed. + + *Joel Hawksley* + ## 3.25.0 * Support Rails `render_in` options signature. Rails [#50623](https://github.com/rails/rails/pull/50623) changed the `render_in` signature from `render_in(view_context, &block)` to `render_in(view_context, **options, &block)`. `ViewComponent::Base#render_in`, `ViewComponent::Collection#render_in`, and `ViewComponent::Instrumentation#render_in` now accept `**options`, restoring compatibility with Rails main and silencing the deprecation warning. diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index d7e98ab94..a5e6626b4 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -88,7 +88,7 @@ def render_in(view_context, **options, &block) @view_context = view_context self.__vc_original_view_context = if instance_variable_defined?(:@__vc_pending_original_view_context) - remove_instance_variable(:@__vc_pending_original_view_context) + remove_instance_variable(:@__vc_pending_original_view_context) || view_context else view_context end diff --git a/lib/view_component/compiler.rb b/lib/view_component/compiler.rb index c4ab88df4..ae5ba61c0 100644 --- a/lib/view_component/compiler.rb +++ b/lib/view_component/compiler.rb @@ -170,7 +170,7 @@ def gather_templates @templates ||= begin templates = @component.sidecar_files( - ActionView::Template.template_handler_extensions + template_handler_extensions ).map do |path| # Extract format and variant from template filename this_format, variant = @@ -225,5 +225,13 @@ def gather_templates templates end end + + def template_handler_extensions + if ActionView::Template.respond_to?(:template_handler_extensions) + ActionView::Template.template_handler_extensions + else + ActionView::Template::Handlers.extensions.map(&:to_s) + end + end end end diff --git a/test/sandbox/test/collection_test.rb b/test/sandbox/test/collection_test.rb index 7d1b56d1c..01ab43f48 100644 --- a/test/sandbox/test/collection_test.rb +++ b/test/sandbox/test/collection_test.rb @@ -22,6 +22,20 @@ def call end end + # Renders a plain partial, which routes through `ViewComponent::Base#render` + # and so depends on `__vc_original_view_context` being set. + class PartialRenderingComponent < ViewComponent::Base + with_collection_parameter :item + + def initialize(item: nil) + @item = item + end + + def call + render partial: "integration_examples/test_partial" + end + end + def setup @products = [Product.new(name: "Radio clock"), Product.new(name: "Mints")] @collection = ProductComponent.with_collection(@products, notice: "secondhand") @@ -46,5 +60,15 @@ def test_supports_collection_with_spacer_component render_inline(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new)) assert_selector("hr", count: 1) end + + # A collection rendered from a plain view (rather than from inside another + # component) never has `set_original_view_context` called on it, so its own + # `__vc_original_view_context` is nil. That nil must not be forwarded to the + # child components, or rendering a partial from one raises NoMethodError. + def test_supports_components_that_render_partials + render_inline(PartialRenderingComponent.with_collection([1, 2])) + + assert_text("hello,partial world!", count: 2) + end end end