-
Notifications
You must be signed in to change notification settings - Fork 435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve Turbo Frame navigation bug #1231
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
seanpdoyle
force-pushed
the
link-with-frame-child
branch
6 times, most recently
from
March 27, 2024 12:53
5bf750b
to
c729a70
Compare
The scenario --- Imagine a List-Details style page layout, with navigation links on the left and the contents of the page on the right: ```html <turbo-frame id="list"> <a href="/articles/1" data-turbo-frame="details"> Article #1 <turbo-frame id="preview_article_1" src="/articles/1/preview"> Some preview text </turbo-frame> </a> <!-- ... --> <a href="/?page=2">Next Page</a> </turbo-frame> <turbo-frame id="details"> <h1>Details</h1> </turbo-frame> ``` The `#list` element is a `<turbo-frame>` to handle pagination, and the `#details` element is a `<turbo-frame>` as well. The `<a>` elements within the `#list` frame drive the `#details` frame through their `[data-turbo-frame="details"]`. The `<a>` element nests a third kind of `<turbo-frame>` that is specific to the resource being linked to. It asynchronously loads in preview content with a `[src]` attribute. The problem --- Clicking the `Article #1` text within the `<a>` element drives the `#details` frame as expected. However, clicking the `Some preview text` within the `<a>` element's nested `<turbo-frame>` element navigates the entire page. This is demonstrated in the following reproduction script: ```ruby require "bundler/inline" gemfile(true) do source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails" gem "propshaft" gem "puma" gem "sqlite3" gem "turbo-rails" gem "capybara" gem "cuprite", "~> 0.9", require: "capybara/cuprite" end ENV["DATABASE_URL"] = "sqlite3::memory:" ENV["RAILS_ENV"] = "test" require "active_record/railtie" require "action_controller/railtie" require "action_view/railtie" require "action_cable/engine" require "rails/test_unit/railtie" class App < Rails::Application config.load_defaults Rails::VERSION::STRING.to_f config.root = __dir__ config.hosts << "example.org" config.eager_load = false config.session_store :cookie_store, key: "cookie_store_key" config.secret_key_base = "secret_key_base" config.consider_all_requests_local = true config.action_cable.cable = {"adapter" => "async"} config.turbo.draw_routes = false Rails.logger = config.logger = Logger.new($stdout) routes.append do root to: "application#index" end end Rails.application.initialize! ActiveRecord::Schema.define do create_table :messages, force: true do |t| t.text :body, null: false end end class Message < ActiveRecord::Base end class ApplicationController < ActionController::Base include Rails.application.routes.url_helpers class_attribute :template, default: DATA.read def index render inline: template, formats: :html end end class ApplicationSystemTestCase < ActionDispatch::SystemTestCase driven_by :cuprite, using: :chrome, screen_size: [1400, 1400], options: { js_errors: true, headless: false } end Capybara.configure do |config| config.server = :puma, {Silent: true} config.default_normalize_ws = true end require "rails/test_help" class TurboSystemTest < ApplicationSystemTestCase test "reproduces bug" do visit root_path binding.irb click_link "Drive #details to ?key=1" end end __END__ <!DOCTYPE html> <html> <head> <%= csrf_meta_tags %> <script type="importmap"> { "imports": { "@hotwired/turbo-rails": "<%= asset_path("turbo.js") %>" } } </script> <script type="module"> import "@hotwired/turbo-rails" </script> <style> body { display: grid; grid-template-areas: "list details" "list details"; grid-template-columns: 150px 1fr; grid-template-rows: 50px 1fr; height: 100vh; } #list { display: flex; flex-direction: column; grid-area: list; overflow-y: scroll; } #details { grid-area: details; } </style> <meta name="turbo-prefetch" content="false"> </head> <body> <turbo-frame id="list"> <% 1.upto(5).each do |key| %> <%= link_to({key:}, data: {turbo_frame: "details"}) do %> <turbo-frame id="frame_<%= key %>"> Drive #details to <%= {key:}.to_query %> </turbo-frame> <% end %> <% end %> </turbo-frame> <turbo-frame id="details"> <%= params.fetch(:key, "0") %> </turbo-frame> </body> </html> ``` The solution --- When observing `click` events, utilize the `findLinkFromClickTarget` to find the nearest `<a>` element to the `click` target so that **that** element's ancestors are used to determine which `<turbo-frame>` to target instead of risking the possibility of using one of its **descendants**.
seanpdoyle
force-pushed
the
link-with-frame-child
branch
from
March 27, 2024 12:54
c729a70
to
513a0e0
Compare
@afcapel could you review this change? |
@jorgemanrubia are you able to review this change? |
jorgemanrubia
approved these changes
Apr 22, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @seanpdoyle
cc @afcapel
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The scenario
Imagine a List-Details style page layout, with navigation links on the left and the contents of the page on the right:
The
#list
element is a<turbo-frame>
to handle pagination, and the#details
element is a<turbo-frame>
as well. The<a>
elements within the#list
frame drive the#details
frame through their[data-turbo-frame="details"]
. The<a>
element nests a third kind of<turbo-frame>
that is specific to the resource being linked to. It asynchronously loads in preview content with a[src]
attribute.The problem
Clicking the
Article #1
text within the<a>
element drives the#details
frame as expected.However, clicking the
Some preview text
within the<a>
element's nested<turbo-frame>
element navigates the entire page.This is demonstrated in the following reproduction script:
Copy-paste into `bug.rb`, then execute `ruby bug.rb`
The solution
When observing
click
events, utilize thefindLinkFromClickTarget
to find the nearest<a>
element to theclick
target so that that element's ancestors are used to determine which<turbo-frame>
to target instead of risking the possibility of using one of its descendants.