Open seanpdoyle opened 7 months ago
This level of detail feels necessary, since there has been some surprise around links and forms not morphing.
Below is a self-contained Turbo Rails application that morphs with form submissions and link clicks. The system test covering scroll preservation passes. If the data: {turbo_action: "replace"}
options are omitted, it fails:
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "rails"
gem "propshaft"
gem "sqlite3"
gem "turbo-rails"
gem "capybara"
gem "cuprite", "~> 0.9", require: "capybara/cuprite"
end
require "active_record/railtie"
# require "active_storage/engine"
require "action_controller/railtie"
require "action_view/railtie"
# require "action_mailer/railtie"
# require "active_job/railtie"
# require "action_cable/engine"
# require "action_mailbox/engine"
# require "action_text/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.turbo.draw_routes = false
Rails.logger = config.logger = Logger.new($stdout)
routes.append do
post "/" => "application#create"
root to: "application#index"
end
end
$template = DATA.read
class ApplicationController < ActionController::Base
include Rails.application.routes.url_helpers
def index
render inline: $template, formats: :html
end
def create
redirect_to root_url(count: params[:count].to_i + 1)
end
end
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :cuprite, using: :chrome, screen_size: [1400, 1400], options: { js_errors: true }
end
Capybara.configure do |config|
config.server = :webrick
config.default_normalize_ws = true
end
ENV["DATABASE_URL"] = "sqlite3::memory:"
ENV["RAILS_ENV"] ||= "test"
Rails.application.initialize!
require "rails/test_help"
class TurboSystemTest < ApplicationSystemTestCase
test "reproduces bug" do
visit root_path
scroll_to find_button("Morph")
assert_scroll_preserved do
click_button "Morph"
assert_text "Count 1"
end
assert_scroll_preserved do
click_button "Morph"
assert_text "Count 2"
end
assert_scroll_preserved do
click_button "Morph"
assert_text "Count 3"
end
assert_scroll_preserved do
click_link "Morph"
assert_text "Count 4"
end
assert_scroll_preserved do
click_link "Morph"
assert_text "Count 5"
end
end
def assert_scroll_preserved(&block)
assert_no_changes -> { evaluate_script("window.scrollY") }, &block
end
end
__END__
<html>
<head>
<script type="importmap">
{
"imports": {
"@hotwired/turbo-rails": "<%= asset_path("turbo.js") %>"
}
}
</script>
<script type="module">
import "@hotwired/turbo-rails"
</script>
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
<%= yield :head %>
</head>
<body>
<p style="margin-bottom: 100vh;">Count <%= params.fetch(:count, 0) %><p>
<%= button_to "Morph", root_path, method: "post",
data: {turbo_action: "replace"},
params: {count: params[:count].to_i} %>
<%= link_to "Morph", root_path(count: params[:count].to_i + 1),
data: {turbo_action: "replace"} %>
</body>
</html>
Expand upon the Page Refresh sections explaining how to morph and preserve scrolling.
Page Refreshes
A "page refresh" is a application visit with a
"replace"
action to a URL with a whose pathname matches the current URL path. Page refreshes can be initiated by driving the page with a link, or by redirecting after a form submission. In either case, the elements must have a[data-turbo-action="replace"]
attribute: