Skip to content
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

Allow Turbo to reload the page instead of morphing #40

Merged
merged 9 commits into from
Dec 25, 2024
Merged
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ gem "rubocop-rails-omakase", require: false

gem "propshaft"
gem "importmap-rails"
gem "turbo-rails"
gem "stimulus-rails"
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ GEM
stringio (3.1.2)
thor (1.3.2)
timeout (0.4.3)
turbo-rails (2.0.11)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.6.0)
Expand Down Expand Up @@ -283,6 +286,7 @@ DEPENDENCIES
rubocop-rails-omakase
sqlite3
stimulus-rails
turbo-rails

BUNDLED WITH
2.5.23
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ You can set configuration options on your `development.rb`. For example:
config.hotwire.spark.html_paths += %w[ lib ]
```

| Name | Description |
|------------------|------------------------------------------------------------------------------------------------------------------------------|
| `html_paths` | Paths where file changes trigger a content refresh. By default: `app/controllers`, `app/helpers`, `app/models`, `app/views`. |
| `css_paths` | Paths where file changes trigger a CSS refresh. By default: `app/assets/stylesheets` or `app/assets/builds` if exists. |
| `stimulus_paths` | Paths where file changes trigger a Stimulus controller refresh. By default: `app/javascript/controllers`. |
| `enabled` | Enable or disable live reloading. By default, it's only enabled in `development`. |
| `logging` | Show logs in the browser console when reloading happens. It's false by default. |
| Name | Description |
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `html_paths` | Paths where file changes trigger a content refresh. By default: `app/controllers`, `app/helpers`, `app/models`, `app/views`. |
| `css_paths` | Paths where file changes trigger a CSS refresh. By default: `app/assets/stylesheets` or `app/assets/builds` if exists. |
| `stimulus_paths` | Paths where file changes trigger a Stimulus controller refresh. By default: `app/javascript/controllers`. |
| `enabled` | Enable or disable live reloading. By default, it's only enabled in `development`. |
| `logging` | Show logs in the browser console when reloading happens. It's false by default. |
| `html_reload_method` | Reload html by morphing (`"morph"`) or with a Turbo page reload (`"replace"`). By default `"morph"`. Use `"replace"` if your app is incompatible with morphing. |

## License

Expand Down
49 changes: 39 additions & 10 deletions app/assets/javascripts/hotwire_spark.js
Original file line number Diff line number Diff line change
Expand Up @@ -1387,7 +1387,7 @@ var HotwireSpark = (function () {
})();

function log() {
if (HotwireSpark.config.loggingEnabled) {
if (HotwireSpark$1.config.loggingEnabled) {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
Expand Down Expand Up @@ -1470,16 +1470,16 @@ var HotwireSpark = (function () {
}
}

class HtmlReloader {
class MorphHtmlReloader {
static async reload() {
return new HtmlReloader().reload();
return new MorphHtmlReloader().reload();
}
async reload() {
const reloadedDocument = await this.#reloadHtml();
const reloadedDocument = await this.#reloadWithMorph();
await this.#reloadStimulus(reloadedDocument);
}
async #reloadHtml() {
log("Reload html...");
async #reloadWithMorph() {
log("Reload html with morph...");
const reloadedDocument = await reloadHtmlDocument();
this.#updateBody(reloadedDocument.body);
return reloadedDocument;
Expand Down Expand Up @@ -1548,6 +1548,32 @@ var HotwireSpark = (function () {
}
}

class ReplaceHtmlReloader {
static async reload() {
return new ReplaceHtmlReloader().reload();
}
async reload() {
await this.#reloadWithTurbo();
}
#reloadWithTurbo() {
log("Reload html with Turbo...");
this.#maintainScrollPosition();
return new Promise(resolve => {
document.addEventListener("turbo:load", () => resolve(document), {
once: true
});
window.Turbo.visit(window.location);
});
}
#maintainScrollPosition() {
document.addEventListener("turbo:render", () => {
Turbo.navigator.currentVisit.scrolled = true;
}, {
once: true
});
}
}

consumer.subscriptions.create({
channel: "Hotwire::Spark::Channel"
}, {
Expand Down Expand Up @@ -1579,6 +1605,7 @@ var HotwireSpark = (function () {
}
},
reloadHtml() {
const HtmlReloader = HotwireSpark.config.htmlReloadMethod == "morph" ? MorphHtmlReloader : ReplaceHtmlReloader;
return HtmlReloader.reload();
},
reloadCss(fileName) {
Expand All @@ -1589,15 +1616,17 @@ var HotwireSpark = (function () {
}
});

const HotwireSpark = {
const HotwireSpark$1 = {
config: {
loggingEnabled: false
loggingEnabled: false,
htmlReloadMethod: "morph"
}
};
document.addEventListener("DOMContentLoaded", function () {
HotwireSpark.config.loggingEnabled = getConfigurationProperty("logging");
HotwireSpark$1.config.loggingEnabled = getConfigurationProperty("logging");
HotwireSpark$1.config.htmlReloadMethod = getConfigurationProperty("html-reload-method");
});

return HotwireSpark;
return HotwireSpark$1;

})();
2 changes: 1 addition & 1 deletion app/assets/javascripts/hotwire_spark.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/assets/javascripts/hotwire_spark.min.js.map

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion app/javascript/hotwire/spark/channels/monitoring_channel.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import consumer from "./consumer"
import { assetNameFromPath } from "../helpers.js";
import { HtmlReloader } from "../reloaders/html_reloader.js";
import { MorphHtmlReloader } from "../reloaders/morph_html_reloader.js";
import { CssReloader } from "../reloaders/css_reloader.js";
import { StimulusReloader } from "../reloaders/stimulus_reloader.js";
import { ReplaceHtmlReloader } from "../reloaders/replace_html_reloader.js";

consumer.subscriptions.create({ channel: "Hotwire::Spark::Channel" }, {
connected() {
Expand Down Expand Up @@ -33,6 +34,9 @@ consumer.subscriptions.create({ channel: "Hotwire::Spark::Channel" }, {
},

reloadHtml() {
const HtmlReloader = HotwireSpark.config.htmlReloadMethod == "morph"
? MorphHtmlReloader
: ReplaceHtmlReloader
return HtmlReloader.reload()
},

Expand Down
4 changes: 3 additions & 1 deletion app/javascript/hotwire/spark/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { getConfigurationProperty } from "./helpers.js";

const HotwireSpark = {
config: {
loggingEnabled: false
loggingEnabled: false,
htmlReloadMethod: "morph"
}
}

document.addEventListener("DOMContentLoaded", function() {
HotwireSpark.config.loggingEnabled = getConfigurationProperty("logging");
HotwireSpark.config.htmlReloadMethod = getConfigurationProperty("html-reload-method");
})

export default HotwireSpark
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Idiomorph } from "idiomorph/dist/idiomorph.esm.js"
import { log } from "../logger.js"
import { reloadHtmlDocument } from "../helpers.js"
import { log } from "../logger.js"
import { StimulusReloader } from "./stimulus_reloader.js"

export class HtmlReloader {
export class MorphHtmlReloader {
static async reload() {
return new HtmlReloader().reload()
return new MorphHtmlReloader().reload()
}

async reload() {
const reloadedDocument = await this.#reloadHtml()
const reloadedDocument = await this.#reloadWithMorph()
await this.#reloadStimulus(reloadedDocument)
}

async #reloadHtml() {
log("Reload html...")
async #reloadWithMorph() {
log("Reload html with morph...")

const reloadedDocument = await reloadHtmlDocument()
this.#updateBody(reloadedDocument.body)
Expand Down
28 changes: 28 additions & 0 deletions app/javascript/hotwire/spark/reloaders/replace_html_reloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { log } from "../logger.js"

export class ReplaceHtmlReloader {
static async reload() {
return new ReplaceHtmlReloader().reload()
}

async reload() {
await this.#reloadWithTurbo()
}

#reloadWithTurbo() {
log("Reload html with Turbo...")

this.#maintainScrollPosition()

return new Promise(resolve => {
document.addEventListener("turbo:load", () => resolve(document), { once: true })
window.Turbo.visit(window.location)
})
}

#maintainScrollPosition() {
document.addEventListener("turbo:render", () => {
Turbo.navigator.currentVisit.scrolled = true
}, { once: true })
}
}
1 change: 1 addition & 0 deletions lib/hotwire-spark.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Hotwire::Spark
mattr_accessor :html_paths, default: []
mattr_accessor :stimulus_paths, default: []
mattr_accessor :logging, default: false
mattr_accessor :html_reload_method, default: "morph"

mattr_accessor :enabled, default: Rails.env.development?

Expand Down
16 changes: 10 additions & 6 deletions lib/hotwire/spark/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,18 @@ def view_helpers
end

def inject_options(html)
if Hotwire::Spark.logging
html.sub("</head>", "#{logging_option}</head>")
else
html
end
html.sub("</head>", "#{options}</head>")
end

def options
[ logging_option, html_reload_method_option ].compact.join("\n")
end

def logging_option
view_helpers.tag.meta(name: "hotwire-spark:logging", content: "true")
view_helpers.tag.meta(name: "hotwire-spark:logging", content: "true") if Hotwire::Spark.logging
end

def html_reload_method_option
view_helpers.tag.meta(name: "hotwire-spark:html-reload-method", content: Hotwire::Spark.html_reload_method)
end
end
1 change: 1 addition & 0 deletions test/dummy/app/javascript/application.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"
10 changes: 10 additions & 0 deletions test/dummy/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
<%# Includes all stylesheet files in app/assets/stylesheets %>
<%= stylesheet_link_tag :app %>
<%= javascript_importmap_tags %>

<script>
let firstLoad = true
document.addEventListener("turbo:load", (event) => {
if (!firstLoad) {
document.body.setAttribute("data-turbo-navigated", "")
}
firstLoad = false
})
</script>
</head>

<body>
Expand Down
1 change: 1 addition & 0 deletions test/dummy/config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
12 changes: 0 additions & 12 deletions test/html_reload_test.rb

This file was deleted.

17 changes: 17 additions & 0 deletions test/morph_reload_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "application_system_test_case"

class MorphReloadTest < ApplicationSystemTestCase
setup do
Hotwire::Spark.html_reload_method = "morph"
end

test "reload HTML changes" do
visit root_path
assert_no_text "This was morphed"

edit_file "app/views/home/show.html.erb", replace: "_REPLACE_", with: "This was morphed!"

assert_text "This was morphed"
assert_no_css "[data-turbo-navigated]"
end
end
17 changes: 17 additions & 0 deletions test/replace_reload_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "application_system_test_case"

class ReplaceReloadTest < ApplicationSystemTestCase
setup do
Hotwire::Spark.html_reload_method = "replace"
end

test "reload HTML changes" do
visit root_path
assert_no_text "This is pretty amazing"

edit_file "app/views/home/show.html.erb", replace: "cool", with: "amazing"

assert_text "This is pretty amazing"
assert_css "[data-turbo-navigated]"
end
end
1 change: 1 addition & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ActiveSupport::TestCase

setup do
reload_rails_reloader
Hotwire::Spark.html_reload_method = "morph"
end

private
Expand Down
Loading