From 554b6da1a2a4596cdcd77a4aba222a036e83d334 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Sun, 31 Jul 2022 18:01:03 -0400 Subject: [PATCH] "Break out" of a frame from the server Closes https://github.com/hotwired/turbo/issues/257 Closes https://github.com/hotwired/turbo/pull/397 Follow-up to https://github.com/hotwired/turbo/issues/257#issuecomment-1143999625 https://github.com/hotwired/turbo/issues/257#issuecomment-1144629360 Depends on https://github.com/hotwired/turbo/pull/660 Introduces the `Turbo::Stream::Redirect` concern to override the [redirect_to][] routing helper. When called with a `turbo_frame:` option, the `redirect_to` helper with check whether the request was made with the Turbo Stream `Accept:` header. When it's absent, the response will redirect with a typical HTTP status code and location. When present, the controller will respond with a `` element that invokes [Turbo.visit($URL, { frame: $TURBO_FRAME })][hotwired/turbo#649] where `$URL` is set to the redirect's path or URL, and `$TURBO_FRAME` is set to the `turbo_frame:` argument. This enables server-side actions to navigate the entire page with a `turbo_frame: "_top"` option. Incidentally, it also enables a frame request to navigate _a different_ frame. Typically, an HTTP that would result in a redirect nets two requests: the first submission, then the subsequent GET request to follow the redirect. In the case of a "break out", the same number of requests are made: the first submission, then the subsequent GET made by the `Turbo.visit` call. Once the `Turbo.visit` call is made, the script removes its ancestor ` + + diff --git a/lib/turbo/engine.rb b/lib/turbo/engine.rb index 9234f563..49fc3923 100644 --- a/lib/turbo/engine.rb +++ b/lib/turbo/engine.rb @@ -36,7 +36,9 @@ class Engine < Rails::Engine initializer "turbo.helpers", before: :load_config_initializers do ActiveSupport.on_load(:action_controller_base) do - include Turbo::Streams::TurboStreamsTagBuilder, Turbo::Frames::FrameRequest, Turbo::Native::Navigation + include Turbo::Streams::TurboStreamsTagBuilder, Turbo::Frames::FrameRequest, Turbo::Native::Navigation, + Turbo::Streams::Redirect + helper Turbo::Engine.helpers end end diff --git a/package.json b/package.json index e55517a9..2e0a9996 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "release": "npm publish && git commit -am \"$npm_package_name v$npm_package_version\" && git push" }, "dependencies": { - "@hotwired/turbo": "^7.1.0", + "@hotwired/turbo": "https://github.com/seanpdoyle/turbo.git#stream-renderer", "@rails/actioncable": "^7.0" }, "devDependencies": { diff --git a/test/dummy/app/controllers/articles_controller.rb b/test/dummy/app/controllers/articles_controller.rb index d7e7e864..e512a3bc 100644 --- a/test/dummy/app/controllers/articles_controller.rb +++ b/test/dummy/app/controllers/articles_controller.rb @@ -16,11 +16,17 @@ def update end def create - @article = Article.new + @article = Article.new article_params - @article.update! article_params + if @article.save + redirect_to articles_url, turbo_frame: "_top" + else + render :new, status: :unprocessable_entity + end + end - redirect_to articles_url + def new + @article = Article.new end private diff --git a/test/dummy/app/views/articles/new.html.erb b/test/dummy/app/views/articles/new.html.erb new file mode 100644 index 00000000..7f0634ca --- /dev/null +++ b/test/dummy/app/views/articles/new.html.erb @@ -0,0 +1,17 @@ +
+ New Article + + <%= turbo_frame_tag @article do %> + <%= form_with model: @article do |form| %> + <%= form.label :body %> + <%= form.text_area :body, aria: { describedby: (dom_id(@article, :errors) if @article.errors[:body].any?) } %> + <% if @article.errors[:body].any? %> +

+ <%= @article.errors[:body].to_sentence %> +

+ <% end %> + + <%= form.button %> + <% end %> + <% end %> +
diff --git a/test/streams/redirect_test.rb b/test/streams/redirect_test.rb new file mode 100644 index 00000000..b3d5eca8 --- /dev/null +++ b/test/streams/redirect_test.rb @@ -0,0 +1,9 @@ +require "turbo_test" + +class Turbo::Streams::RedirectTest < ActionDispatch::IntegrationTest + test "html requests respond with a redirect HTTP status" do + post articles_path, params: { article: { body: "A valid value" } } + + assert_redirected_to articles_url + end +end diff --git a/test/system/frames_test.rb b/test/system/frames_test.rb new file mode 100644 index 00000000..ed723021 --- /dev/null +++ b/test/system/frames_test.rb @@ -0,0 +1,24 @@ +require "application_system_test_case" + +class FramesTest < ApplicationSystemTestCase + test "can render an invalid submission within a frame" do + visit new_article_path + toggle_disclosure "New Article" + click_on "Create Article" + + within_disclosure "New Article" do + assert_field "Body", described_by: "can't be blank" + end + end + + test "can redirect the entire page after a valid submission within a frame" do + visit new_article_path + toggle_disclosure "New Article" + fill_in "Body", with: "An article's body" + click_on "Create Article" + + assert_no_selector :disclosure, "New Article" + assert_no_field "Body" + assert_text "An article's body" + end +end diff --git a/yarn.lock b/yarn.lock index 777aff5d..146574d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,10 +23,9 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@hotwired/turbo@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.1.0.tgz#27e44e0e3dc5bd1d4bda0766d579cf5a14091cd7" - integrity sha512-Q8kGjqwPqER+CtpQudbH+3Zgs2X4zb6pBAlr6NsKTXadg45pAOvxI9i4QpuHbwSzR2+x87HUm+rot9F/Pe8rxA== +"@hotwired/turbo@https://github.com/seanpdoyle/turbo.git#stream-renderer": + version "7.2.0-beta.1" + resolved "https://github.com/seanpdoyle/turbo.git#73b38ce91f833257d3da32145f27a059626d30fe" "@rails/actioncable@^7.0": version "7.0.1"