-
Notifications
You must be signed in to change notification settings - Fork 430
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
Don't break out of frames when frame missing #863
Conversation
We need the allowlist concept to go along with this, so you can deal with "session expired" cases without having to write a custom turbo:frame-missing handler. |
@dhh yep, just decided to split that piece into its own PR. We've been chatting about a couple of different ways to specify that allowlist, so I'm opening a 2nd PR for that part now. |
Cools cools 👌 |
This changes the default behaviour when a frame response is missing its expected `<turbo-frame>` element. Previously, when the response was missing its frame, we would trigger a `turbo:frame-missing` event, and then (provided that event wasn't cancelled) perform a full page visit to the requested URL. However there are cases where the full reload makes things worse: - If the frame contents were non-critical, reloading the page can turn a minor bug into a major one. - It can mask some bugs where frames were intend to explicitly navigate out of the frame (`target="_top"`), by incurring a second request that loads the page that makes it seem as if it's working corrects. - It leaves the user at a URL that may never be capable of rendering a valid response (since that URL was only intended to serve a particular frame). That means refreshing the page is no help in getting back to a working state. - It can lose other temporary state on a page, like form values. With this change, we no longer perform the full page visit. Instead, we handle a missing frame by doing two things: - Write a short error message into the frame, so that the problem is visible on the page. - Throw an exception, which should make the problem quite obvious in development, and which allows it to be easily gathered by exception monitoring tools in production. We keep the `turbo:frame-missing` event exactly as before, so applications can still hook in to perform alternative behaviour if they want.
Also remove `invalidate` that has effectively been replaced.
e2542eb
to
00a481e
Compare
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@hotwired/turbo](https://turbo.hotwired.dev) ([source](https://togithub.com/hotwired/turbo)) | [`7.2.5` -> `7.3.0`](https://renovatebot.com/diffs/npm/@hotwired%2fturbo/7.2.5/7.3.0) | [![age](https://badges.renovateapi.com/packages/npm/@hotwired%2fturbo/7.3.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@hotwired%2fturbo/7.3.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@hotwired%2fturbo/7.3.0/compatibility-slim/7.2.5)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@hotwired%2fturbo/7.3.0/confidence-slim/7.2.5)](https://docs.renovatebot.com/merge-confidence/) | | [sass](https://togithub.com/sass/dart-sass) | [`1.58.0` -> `1.58.3`](https://renovatebot.com/diffs/npm/sass/1.58.0/1.58.3) | [![age](https://badges.renovateapi.com/packages/npm/sass/1.58.3/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/sass/1.58.3/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/sass/1.58.3/compatibility-slim/1.58.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/sass/1.58.3/confidence-slim/1.58.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>hotwired/turbo</summary> ### [`v7.3.0`](https://togithub.com/hotwired/turbo/releases/tag/v7.3.0) [Compare Source](https://togithub.com/hotwired/turbo/compare/v7.2.5...v7.3.0) #### What's Changed - Don't break out of frames when frame missing by [@​kevinmcconnell](https://togithub.com/kevinmcconnell) in [https://github.com/hotwired/turbo/pull/863](https://togithub.com/hotwired/turbo/pull/863) - Respect `turbo-visit-control` for frame requests by [@​kevinmcconnell](https://togithub.com/kevinmcconnell) in [https://github.com/hotwired/turbo/pull/867](https://togithub.com/hotwired/turbo/pull/867) - Allow changing the submitter text during form submission by [@​afcapel](https://togithub.com/afcapel) in [https://github.com/hotwired/turbo/pull/869](https://togithub.com/hotwired/turbo/pull/869) - Form submissions from frames should clear cache by [@​kevinmcconnell](https://togithub.com/kevinmcconnell) in [https://github.com/hotwired/turbo/pull/882](https://togithub.com/hotwired/turbo/pull/882) - Deprecate `[data-turbo-cache=false]` in favor of `[data-turbo-temporary]` by [@​packagethief](https://togithub.com/packagethief) in [https://github.com/hotwired/turbo/pull/871](https://togithub.com/hotwired/turbo/pull/871) - `resume()` does not require `value` by [@​dahlbyk](https://togithub.com/dahlbyk) in [https://github.com/hotwired/turbo/pull/854](https://togithub.com/hotwired/turbo/pull/854) - Rename `isIdempotent` to `isSafe` by [@​kevinmcconnell](https://togithub.com/kevinmcconnell) in [https://github.com/hotwired/turbo/pull/883](https://togithub.com/hotwired/turbo/pull/883) **Full Changelog**: hotwired/turbo@v7.2.5...v7.3.0 </details> <details> <summary>sass/dart-sass</summary> ### [`v1.58.3`](https://togithub.com/sass/dart-sass/blob/HEAD/CHANGELOG.md#​1583) [Compare Source](https://togithub.com/sass/dart-sass/compare/1.58.2...1.58.3) - No user-visible changes. ### [`v1.58.2`](https://togithub.com/sass/dart-sass/blob/HEAD/CHANGELOG.md#​1582) [Compare Source](https://togithub.com/sass/dart-sass/compare/1.58.1...1.58.2) ##### Command Line Interface - Add a timestamp to messages printed in `--watch` mode. - Print better `calc()`-based suggestions for `/`-as-division expression that contain calculation-incompatible constructs like unary minus. ### [`v1.58.1`](https://togithub.com/sass/dart-sass/blob/HEAD/CHANGELOG.md#​1581) [Compare Source](https://togithub.com/sass/dart-sass/compare/1.58.0...1.58.1) - Emit a unitless hue when serializing `hsl()` colors. The `deg` unit is incompatible with IE, and while that officially falls outside our compatibility policy, it's better to lean towards greater compatibility. </details> --- ### Configuration 📅 **Schedule**: Branch creation - "before 6am on wednesday" in timezone Australia/Sydney, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/google/osv.dev). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xMjUuMSIsInVwZGF0ZWRJblZlciI6IjM0LjE0Ni4yIn0=-->
Hey there, Love the idea behind the change, seems like a great way to enforce the correct patterns when implementing frames! However, as bullet number two:
This change rendered most of our application essentially unusable as our (seemingly) external links now just return the explained Do you by chance have any strategies for identifying which frames/links will now be broken by this? We just finished rewriting our entire UI with Turbo and it's looking like the only way to make sure is by checking every single one... Any help is appreciated! As mentioned, we love the change but were not expecting such an impact to our application. |
Hi @jonathanloos, yeah, I totally understand! Thanks for bringing this up. It's hard to make this change without some disruption because the behaviour we want is a bit stricter than what we had in 7.2. (Prior to 7.2 it was stricter too). In 7.2. we have been emitting console warnings to try to highlight where the requests were missing their frames. But it's very easy to miss those, so it's totally understandable that you could end up with links missing frames/targets without noticing. (This situation was a big part of the motivation for the change.) One idea that might help would be to use a global handler to catch any That should keep your app working as it did before, but now you can also see what's coming in to the error monitoring to find any links/frames that need to be updated, and you can make those changes gradually instead of all at once. Something along these lines should work for that: document.addEventListener("turbo:frame-missing", function(event) {
// TODO: Report this event somewhere...
// Visit the response
event.preventDefault()
event.detail.visit(event.detail.response)
}) Hope that helps! |
@kevinmcconnell we'll be taking a look into that! We appreciate the quick response as well 🙏 Definitely need to pay closer attention to the console warnings.. |
Hey @kevinmcconnell ! Once again thank you for the idea behind mitigating the issues. Our team has successfully implemented the patch and are tracking the offending DOM elements using Ahoy. That way we can gather info on which turbo frames are offending and use it as a todo list in the future. For reference and anyone also looking to track, here's the basis of our implementation (very similar to the code above!). import { Controller } from '@hotwired/stimulus'
import ahoy from 'ahoy.js'
export default class extends Controller {
connect () {
document.addEventListener('turbo:frame-missing', this.handleMissingFrame)
}
handleMissingFrame (event) {
this.trackAhoy(event)
// Visit the response
event.preventDefault()
event.detail.visit(event.detail.response)
}
trackAhoy (event) {
ahoy.track('turbo_frame_missing', event.target.id)
}
} Note we had to do some solid debouncing and event listener disconnecting in a stimulus controller to make sure we didn't get duplicates all over the place. Hope that helps! Thanks again. |
@jonathanloos great! I'm glad to hear this worked out. Thanks for letting me know! 🙏 |
Howdy! Just spent a couple hours integrating this (and its companion) (and/with the Turbo-Rails companion, hotwired/turbo-rails#428) into an app with a partner of mine and we had one particularly subtle hiccup I wanted to note for others / consideration. In 7.2 (or at least in RC3, which we were running previously), if a frame requests something and the response didn't contain the matching frame, that same response ended up being painted. It wasn't requested again. I believe the idea with the new API is, "use There's this 'escape hatch': document.addEventListener("turbo:frame-missing", function(event) {
event.preventDefault()
event.detail.visit(event.detail.response)
}) Which, based on the way As we found after a couple hours of digging and experimenting is that this In short, the solution we found for a frame 'breaking out' to a full page load, but using the singular response it already received as the content for that new page load (e.g. not requesting again) is to use the escape hatch above plus forcing the layout to be our application layout (which will have a matching class ApplicationController < ActionController::Base
# ...
layout -> { "application" if turbo_frame_request? }
# ...
end which should override the custom layout introduced and used by default for frame responses via hotwired/turbo-rails#428. This is sort of a half Turbo and half Turbo-Rails concern so hopefully this is an okay place to post it, just wanted to put forth this use case for consideration and thoughts as it seemed like the |
…javascript error (#402) * Update Gemfile * update * note to remove chromedriver locally for tests to pass * fix found by consulting hotwired/turbo#863
This changes the default behaviour when a frame response is missing its expected
<turbo-frame>
element.Previously, when the response was missing its frame, we would trigger a
turbo:frame-missing
event, and then (provided that event wasn't cancelled) perform a full page visit to the requested URL.However there are cases where the full reload makes things worse:
target="_top"
), by incurring a second request that loads the page that makes it seem as if it's working corrects.With this change, we no longer perform the full page visit. Instead, we handle a missing frame by doing two things:
We keep the
turbo:frame-missing
event exactly as before, so applications can still hook in to perform alternative behaviour if they want.