-
Notifications
You must be signed in to change notification settings - Fork 434
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
Communicate Visit direction with html[data-turbo-visit-direction]
#1007
Conversation
@@ -15,6 +16,7 @@ export class History { | |||
if (!this.started) { | |||
addEventListener("popstate", this.onPopState, false) | |||
addEventListener("load", this.onPageLoad, false) | |||
this.currentIndex = history.state?.turbo?.restorationIndex || 0 |
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.
I wasn't aware of this until today, but browsers persist the state between refreshes (🤯). This means this feature should still work after a reload (e.g. after assets are invalidated and a user taps Back)
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.
TIL, fascinating.
Also /ht @matthewp for the inspiration |
@domchristie nice change 🙌 I'm wondering if we could also add an attribute to the body, similar to data-turbo-preview, with the navigation direction. That way we could customize animations with just css, no js or event handling required. |
With this in place, part of me wonders if the event detail is necessary, and whether we just use the data attribute? This might feel more Hotwire-y: where state lives at attributes in the DOM |
For consistency, I'd also suggest removing the isPreview detail too, as I think this also can be inferred from a data attribute (#926 (comment)) |
Yes, I think that makes sense. If you can make the change, that'd be great. I still need to spin the branch for a test, but the changes look 👌 |
Use html[data-turbo-visit-direction] attribute to communicate visit direction rather than using an event detail
html[data-turbo-visit-direction]
Demo app with build from this PR and directional View Transitions: https://github.com/domchristie/turbo_view_transition_test Screen.Recording.2023-10-03.at.18.53.00.mov |
(I should add, the test are flaky, but seem to pass, at least after a couple of automated retries) |
@afcapel friendly ping, I think this is ready |
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.
@domchristie looks great, just a few suggestions about the tests.
src/tests/functional/visit_tests.js
Outdated
waitUntilSelector(page, "[data-turbo-visit-direction='forward']") | ||
.then(() => waitUntilSelector(page, "html:not([data-turbo-visit-direction])")), | ||
page.click("#same-origin-link") | ||
]) |
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.
I think we can write these tests without relying on Promise.all
:
page.click("#same-origin-link")
await waitUntilSelector(page, "[data-turbo-visit-direction='forward']")
await waitUntilNoSelector(page, "[data-turbo-visit-direction]")
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.
I think I tried this and I don't this it worked because the attribute is added and removed quicker than the assertions can track.
If I recall correctly, in the time it takes for page.click
to resolve, the attribute it already added and removed, so waitUntilSelector(page, "[data-turbo-visit-direction='forward']")
never passes
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.
also line 226-227 ensures the attribute is added and removed in the correct order
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.
@domchristie what do you think of a660c7b?
The trick is that you don't await
for the page.click
, so it runs in parallel with the first waitUntilSelector
. Then we await
for the two assertions to ensure they happen one after the other.
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.
Huh, that works locally but seems very flaky on CI.
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.
I think our best option is to use the mutation logs so we are not so dependent on the exact assertion timing. With the mutation log, even if the attribute is no longer present in the page, we can still assert that it was present at some point.
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.
I think our best option is to use the mutation logs
This looks good to me 👍
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.
Cool, @domchristie if you can push those changes to your branch I'll merge.
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.
@afcapel I will do (I’ve just moved house, and will have a reliable internet connection next week)
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.
if you can push those changes to your branch I'll merge
@afcapel Done
@@ -15,6 +16,7 @@ export class History { | |||
if (!this.started) { | |||
addEventListener("popstate", this.onPopState, false) | |||
addEventListener("load", this.onPageLoad, false) | |||
this.currentIndex = history.state?.turbo?.restorationIndex || 0 |
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.
TIL, fascinating.
@afcapel "test" prefixes and typo fixed, and left a comment about the reason for |
Why remove them? Can't both coexist? Even HEY have a helper to know if it is turboPreview. If it was necessary to make the helper, wouldn't it be better to read the attribute directly from the event as well? https://app.hey.com/assets/helpers/turbo_helpers-5496333b991567941c18f8dd0485c0d0703e9c67.js |
@brunoprietog for me, it's about what feels right for the API. Turbo's event's details are pretty minimal, and as such, I think there's a good argument for more information in event details, but until that shape is decided upon, it feels preferable to stick with the data attribute approach only. Otherwise, if we decide on a different event API, we'd be introducing a breaking change. |
It's less dependent on the timing of the mutation. Even if the attribute is no longer present, we can still assert that it was present at some point.
Thanks @domchristie! |
…tion]` (hotwired#1007)" This reverts commit ac00359.
With this argument, it should #926 be reverted? I don't think there is much consistency. Some things are reported via HTML attributes and other things in the event detail. The most recent example is the renderMethod introduced in #1019. Lines 348 to 350 in ac00359
Other events that are emitted also provide quite a bit of information in the detail. As developers, isn't it more ergonomic to receive the information in the detail? Rather than having to make custom helpers to read this information from the HTML. I feel that exposing the information in the HTML is perfect for the CSS but not ergonomic for the event listeners. What feels wrong with exposing the information both ways? If something is still undecided, I think it would be good to document the approach and make the API consistent. What euristics will be used to define whether to expose via HTML attributes or via the event detail? |
@domchristie could you document this on turbo-site? If you don't have time I could do it on the weekend, if that's ok with you |
@brunoprietog I'm not opposed to including more information in event details, but the implementation of allowsImmediateRender(
{ element: newFrame }: Snapshot<FrameElement>,
_isPreview: boolean,
options: ViewRenderOptions<FrameElement>
) { Passing through The Turbo event system is reasonably complex, and often requires a chain of method calls to dispatch the event, so to have to include the single The HTML approach is already implemented, does not require these additional arguments, works with CSS selectors, and it's trivial to write a function to get the same information in JS (which has been the intention since Turbolinks was released).
With all that in mind, I'd suggest that if the information might be required in CSS and JavaScript, favour the HTML attribute approach over adding another event detail. In this way, a single API works for both cases without having to maintain the two approaches.
I'm a bit tight on time at the moment, so if you could do this, that'd be great. |
Introducued in hotwired/turbo#935 and hotwired/turbo#1007
Introduced in hotwired/turbo#935 and hotwired/turbo#1007
* Document view transitions Introduced in hotwired/turbo#935 and hotwired/turbo#1007 * Better css example Co-authored-by: Dom Christie <[email protected]> * Add data-turbo-visit-direction to attributes reference * Be more precise Co-authored-by: Dom Christie <[email protected]> * Document meta tag * Clarify it’s optional Co-authored-by: Kevin McConnell <[email protected]> --------- Co-authored-by: Dom Christie <[email protected]> Co-authored-by: Kevin McConnell <[email protected]>
…otwired#1007) * Add direction detail to turbo:visit events * Test restorationIndex is persisted between reloads * Add data-turbo-visit-direction to html element * Versatile Visit direction API. Use html[data-turbo-visit-direction] attribute to communicate visit direction rather than using an event detail * Remove "test" prefix from test description * Fix typos * Remove "test" prefix from test description * Clean up assertions * Read the mutation logs to assert the direction attribute change It's less dependent on the timing of the mutation. Even if the attribute is no longer present, we can still assert that it was present at some point. --------- Co-authored-by: Alberto Fernández-Capel <[email protected]>
This pull request adds
adirection
detail inturbo:visit
eventsdata-turbo-visit-direction
attributes to the HTML attribute during the lifecycle of a visit. This enables differentiation between back and forward history navigations and is useful for customising animations.A summary of the changes:
When a history entry is updated (pushState or replaceState):
restorationIndex
)When the history is popped (back or forward):
dispatch theapply the data attribute)turbo:visit
event with the direction detaildirection
can be:forward
for advance or forward restoration visitsback
for back restoration visitsnone
for replace visits