diff --git a/CHANGELOG.md b/CHANGELOG.md index bb7b0c9b..ce4e35c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Changes since the last non-beta release. _Please add entries here for your pull requests that have not yet been released. Include LINKS for PRs and committers._ +#### Fixed +- Preserve default controller layouts for `render component:` after the Rails 8 render pipeline change reported in #1356. + ## [3.3.0] - 2026-03-31 #### Added diff --git a/lib/react/rails/railtie.rb b/lib/react/rails/railtie.rb index 90e856a1..6aa11650 100644 --- a/lib/react/rails/railtie.rb +++ b/lib/react/rails/railtie.rb @@ -66,7 +66,7 @@ class Railtie < ::Rails::Railtie ActionController::Renderers.add :component do |component_name, options| renderer = ::React::Rails::ControllerRenderer.new(controller: self) html = renderer.call(component_name, options) - render_options = options.merge(inline: html) + render_options = Railtie.component_render_options(options, html) render(render_options) end end @@ -121,6 +121,13 @@ def self.append_react_build_to_assets_version!(assets, react_build) versioned_assets.version = [versioned_assets.version, "react-#{react_build}"].compact.join("-") end + def self.component_render_options(options, html) + render_options = options.merge(inline: html) + return render_options if render_options.key?(:layout) + + render_options.merge(layout: true) + end + def self.versioned_assets_for(assets) return assets if versioned_assets?(assets) diff --git a/test/react/rails/component_renderer_test.rb b/test/react/rails/component_renderer_test.rb new file mode 100644 index 00000000..19d3b080 --- /dev/null +++ b/test/react/rails/component_renderer_test.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require "test_helper" + +class ComponentRendererController < ActionController::Base + append_view_path File.expand_path("../../dummy/app/views", __dir__) + layout "application" + + def default_layout + render component: "TodoList" + end + + def explicit_layout_false + render component: "TodoList", layout: false + end +end + +class ComponentRendererTest < ActionController::TestCase + tests ComponentRendererController + + FakeRenderer = Struct.new(:html, :calls) do + def initialize(html) + super(html, []) + end + + def call(component_name, options) + calls << [component_name, options] + html + end + end + + setup do + @routes = ActionDispatch::Routing::RouteSet.new + @routes.draw do + get "default_layout", to: "component_renderer#default_layout" + get "explicit_layout_false", to: "component_renderer#explicit_layout_false" + end + end + + test "render component uses the current layout by default" do # rubocop:disable Minitest/MultipleAssertions + fake_renderer = FakeRenderer.new("
SSR
") + + React::Rails::ControllerRenderer.stub(:new, ->(*) { fake_renderer }) do + get :default_layout + end + + assert_response :success + assert_match(%r{Dummy}, response.body) + assert_match(%r{
SSR
}, response.body) + assert_equal "TodoList", fake_renderer.calls.dig(0, 0) + end + + test "render component preserves explicit layout false" do # rubocop:disable Minitest/MultipleAssertions + fake_renderer = FakeRenderer.new("
SSR
") + + React::Rails::ControllerRenderer.stub(:new, ->(*) { fake_renderer }) do + get :explicit_layout_false + end + + assert_response :success + assert_no_match(%r{Dummy}, response.body) + assert_match(%r{
SSR
}, response.body) + assert_equal "TodoList", fake_renderer.calls.dig(0, 0) + end +end diff --git a/test/react/rails/railtie_test.rb b/test/react/rails/railtie_test.rb index 1bc0540c..dbf6ce6f 100644 --- a/test/react/rails/railtie_test.rb +++ b/test/react/rails/railtie_test.rb @@ -45,4 +45,24 @@ class RailtieTest < ActionDispatch::IntegrationTest React::Rails::Railtie.append_react_build_to_assets_version!(assets, "development") end end + + test "component render options default to using the current layout" do + render_options = React::Rails::Railtie.component_render_options({ status: :accepted }, "
SSR
") + + assert_equal "
SSR
", render_options[:inline] + assert render_options[:layout] + assert_equal :accepted, render_options[:status] + end + + test "component render options preserve explicit layout overrides" do + render_options = React::Rails::Railtie.component_render_options({ layout: false }, "
SSR
") + + refute render_options[:layout] + end + + test "component render options preserve a named layout override" do + render_options = React::Rails::Railtie.component_render_options({ layout: "admin" }, "
SSR
") + + assert_equal "admin", render_options[:layout] + end end