Skip to content

Commit 76fce07

Browse files
committed
Fix default layout handling for render component
1 parent 5e951a5 commit 76fce07

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Changes since the last non-beta release.
1515

1616
_Please add entries here for your pull requests that have not yet been released. Include LINKS for PRs and committers._
1717

18+
#### Fixed
19+
- Preserve default controller layouts for `render component:` after the Rails 8 render pipeline change reported in #1356.
20+
1821
## [3.3.0] - 2026-03-31
1922

2023
#### Added

lib/react/rails/railtie.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class Railtie < ::Rails::Railtie
6666
ActionController::Renderers.add :component do |component_name, options|
6767
renderer = ::React::Rails::ControllerRenderer.new(controller: self)
6868
html = renderer.call(component_name, options)
69-
render_options = options.merge(inline: html)
69+
render_options = Railtie.send(:component_render_options, options, html)
7070
render(render_options)
7171
end
7272
end
@@ -121,6 +121,13 @@ def self.append_react_build_to_assets_version!(assets, react_build)
121121
versioned_assets.version = [versioned_assets.version, "react-#{react_build}"].compact.join("-")
122122
end
123123

124+
def self.component_render_options(options, html)
125+
render_options = options.merge(inline: html)
126+
return render_options if render_options.key?(:layout)
127+
128+
render_options.merge(layout: true)
129+
end
130+
124131
def self.versioned_assets_for(assets)
125132
return assets if versioned_assets?(assets)
126133

@@ -134,7 +141,7 @@ def self.versioned_assets?(assets)
134141
assets.respond_to?(:version) && assets.respond_to?(:version=)
135142
end
136143

137-
private_class_method :versioned_assets_for, :versioned_assets?
144+
private_class_method :component_render_options, :versioned_assets_for, :versioned_assets?
138145
end
139146
end
140147
end
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class ComponentRendererController < ActionController::Base
6+
append_view_path File.expand_path("../../dummy/app/views", __dir__)
7+
layout "application"
8+
9+
def default_layout
10+
render component: "TodoList"
11+
end
12+
13+
def explicit_layout_false
14+
render component: "TodoList", layout: false
15+
end
16+
end
17+
18+
class ComponentRendererTest < ActionController::TestCase
19+
tests ComponentRendererController
20+
21+
FakeRenderer = Struct.new(:html, :calls) do
22+
def initialize(html)
23+
super(html, [])
24+
end
25+
26+
def call(component_name, options)
27+
calls << [component_name, options]
28+
html
29+
end
30+
end
31+
32+
setup do
33+
@routes = ActionDispatch::Routing::RouteSet.new
34+
@routes.draw do
35+
get "default_layout", to: "component_renderer#default_layout"
36+
get "explicit_layout_false", to: "component_renderer#explicit_layout_false"
37+
end
38+
end
39+
40+
test "render component uses the current layout by default" do # rubocop:disable Minitest/MultipleAssertions
41+
fake_renderer = FakeRenderer.new("<main>SSR</main>")
42+
43+
React::Rails::ControllerRenderer.stub(:new, ->(*) { fake_renderer }) do
44+
get :default_layout
45+
end
46+
47+
assert_response :success
48+
assert_match(%r{<title>Dummy</title>}, response.body)
49+
assert_match(%r{<main>SSR</main>}, response.body)
50+
assert_equal "TodoList", fake_renderer.calls.dig(0, 0)
51+
end
52+
53+
test "render component preserves explicit layout false" do # rubocop:disable Minitest/MultipleAssertions
54+
fake_renderer = FakeRenderer.new("<main>SSR</main>")
55+
56+
React::Rails::ControllerRenderer.stub(:new, ->(*) { fake_renderer }) do
57+
get :explicit_layout_false
58+
end
59+
60+
assert_response :success
61+
assert_no_match(%r{<title>Dummy</title>}, response.body)
62+
assert_match(%r{<main>SSR</main>}, response.body)
63+
assert_equal "TodoList", fake_renderer.calls.dig(0, 0)
64+
end
65+
end

test/react/rails/railtie_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,18 @@ class RailtieTest < ActionDispatch::IntegrationTest
4545
React::Rails::Railtie.append_react_build_to_assets_version!(assets, "development")
4646
end
4747
end
48+
49+
test "component render options default to using the current layout" do
50+
render_options = React::Rails::Railtie.send(:component_render_options, { status: :accepted }, "<div>SSR</div>")
51+
52+
assert_equal "<div>SSR</div>", render_options[:inline]
53+
assert render_options[:layout]
54+
assert_equal :accepted, render_options[:status]
55+
end
56+
57+
test "component render options preserve explicit layout overrides" do
58+
render_options = React::Rails::Railtie.send(:component_render_options, { layout: false }, "<div>SSR</div>")
59+
60+
refute render_options[:layout]
61+
end
4862
end

0 commit comments

Comments
 (0)