diff --git a/app/controllers/turbo/frames/frame_request.rb b/app/controllers/turbo/frames/frame_request.rb index 11ca08eb..e9399e63 100644 --- a/app/controllers/turbo/frames/frame_request.rb +++ b/app/controllers/turbo/frames/frame_request.rb @@ -1,19 +1,27 @@ # Turbo frame requests are requests made from within a turbo frame with the intention of replacing the content of just # that frame, not the whole page. They are automatically tagged as such by the Turbo Frame JavaScript, which adds a -# Turbo-Frame header to the request. When that header is detected by the controller, we ensure that any -# template layout is skipped (since we're only working on an in-page frame, thus can skip the weight of the layout), and -# that the etag for the page is changed (such that a cache for a layout-less request isn't served on a normal request -# and vice versa). +# Turbo-Frame header to the request. # -# This is merely a rendering optimization. Everything would still work just fine if we rendered everything including the layout. -# Turbo Frames knows how to fish out the relevant frame regardless. +# When that header is detected by the controller, we substitute our own minimal layout in place of the +# application-supplied layout (since we're only working on an in-page frame, thus can skip the weight of the layout). We +# use a minimal layout, rather than avoid the layout entirely, so that it's still possible to render content into the +# head. +# +# Accordingly, we ensure that the etag for the page is changed, such that a cache for a minimal-layout request isn't +# served on a normal request and vice versa. +# +# This is merely a rendering optimization. Everything would still work just fine if we rendered everything including the +# full layout. Turbo Frames knows how to fish out the relevant frame regardless. +# +# The layout used is turbo_rails/frame.html.erb. If there's a need to customize this layout, an application can +# supply its own (such as app/views/layouts/turbo_rails/frame.html.erb) which will be used instead. # # This module is automatically included in ActionController::Base. module Turbo::Frames::FrameRequest extend ActiveSupport::Concern included do - layout -> { false if turbo_frame_request? } + layout -> { "turbo_rails/frame" if turbo_frame_request? } etag { :frame if turbo_frame_request? } end diff --git a/app/views/layouts/turbo_rails/frame.html.erb b/app/views/layouts/turbo_rails/frame.html.erb new file mode 100644 index 00000000..0171671e --- /dev/null +++ b/app/views/layouts/turbo_rails/frame.html.erb @@ -0,0 +1,8 @@ + + + <%= yield :head %> + + + <%= yield %> + + diff --git a/test/dummy/app/views/trays/show.html.erb b/test/dummy/app/views/trays/show.html.erb index 125250cb..9ab284b8 100644 --- a/test/dummy/app/views/trays/show.html.erb +++ b/test/dummy/app/views/trays/show.html.erb @@ -1,3 +1,7 @@ +<% content_for :head do %> + +<% end %> +
This is a tray!
<%= @frame_id %>
diff --git a/test/frames/frame_request_controller_test.rb b/test/frames/frame_request_controller_test.rb index 3ba6fa83..d3c8d0fa 100644 --- a/test/frames/frame_request_controller_test.rb +++ b/test/frames/frame_request_controller_test.rb @@ -1,7 +1,7 @@ require "test_helper" class Turbo::FrameRequestControllerTest < ActionDispatch::IntegrationTest - test "frame requests are rendered without a layout" do + test "frame requests are rendered with a minimal layout" do get tray_path(id: 1) assert_select "title", count: 1 @@ -9,6 +9,22 @@ class Turbo::FrameRequestControllerTest < ActionDispatch::IntegrationTest assert_select "title", count: 0 end + test "frame request layout includes `head` content" do + get tray_path(id: 1), headers: { "Turbo-Frame" => "true" } + + assert_select "head", count: 1 + assert_select "meta[name=test][content=present]" + end + + test "frame request layout can be overridden" do + with_prepended_view_path "test/frames/views" do + get tray_path(id: 1), headers: { "Turbo-Frame" => "true" } + end + + assert_select "meta[name=test][content=present]" + assert_select "meta[name=alternative][content=present]" + end + test "frame requests get a unique etag" do get tray_path(id: 1) etag_without_frame = @response.headers["ETag"] @@ -28,4 +44,13 @@ class Turbo::FrameRequestControllerTest < ActionDispatch::IntegrationTest get tray_path(id: 1), headers: { "Turbo-Frame" => turbo_frame_request_id } assert_match /#{turbo_frame_request_id}/, @response.body end + + private + def with_prepended_view_path(path, &block) + previous_view_paths = ApplicationController.view_paths + ApplicationController.prepend_view_path path + yield + ensure + ApplicationController.view_paths = previous_view_paths + end end diff --git a/test/frames/views/layouts/turbo_rails/frame.html.erb b/test/frames/views/layouts/turbo_rails/frame.html.erb new file mode 100644 index 00000000..0fbebe4b --- /dev/null +++ b/test/frames/views/layouts/turbo_rails/frame.html.erb @@ -0,0 +1,9 @@ + + + + <%= yield :head %> + + + <%= yield %> + + \ No newline at end of file