Repository navigation
Interactivity Router: dispatch navigation DOM events and add state.navigation.loading - #80302
Closed
fabiankaegy wants to merge 2 commits into
Closed
fabiankaegy wants to merge 2 commits into
fabiankaegy wants to merge 2 commits into
Conversation
…vigation.loading Scripts that aren't built on the Interactivity API (consent managers like OneTrust, tag managers like GTM, analytics, chat widgets) have no way to react to client-side navigations today. This adds two native window CustomEvents, wp-router-navigation-start and wp-router-navigation-end, so those scripts can hook in with a plain window.addEventListener() call. It also adds a public, documented state.navigation.loading boolean to the core/router store for code that is built on the Interactivity API, as the intended replacement for the deprecated state.navigation.hasStarted/ hasFinished properties. Both the events and the state flip are driven from the same code paths so they can never disagree. See the discussion in #70882. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Size Change: +147 B (0%) Total Size: 7.72 MB 📦 View Changed
|
…s e2e tests The two new navigation-events tests asserted the `wp-router-navigation-end` event's `title` detail against the block's own rendered heading (e.g. "Link 1"), but that detail is sourced from `document.title` (the browser tab title, e.g. "post title – site title"), which is a different string. Read `document.title` from the page instead of hardcoding the expected value so the assertion matches what the product code actually reports. Also confirmed via a controlled local run (reverting to the pre-PR `index.ts` and rebuilding) that the third failing test in the same CI shard, "should update the HTML only for the latest navigation", fails at the same rate on trunk's existing code, so it is pre-existing flakiness unrelated to this PR and is left untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Flaky tests detected in 77fb97f. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/29401907113
|
Member
|
Hey Fabian, I've published an alternative proposal: I'd love to know what you think. |
This file contains hidden or 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
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.
What?
See #70882
This is a prototype for a small piece of the Interactivity Router: a way for code outside the Interactivity API to know when a client-side navigation happens.
It adds two things:
windowCustomEvents dispatched by the router:wp-router-navigation-start(right before a navigation begins) andwp-router-navigation-end(once the DOM is patched, the title is updated, and history is updated). Both fire for regular link navigations and for back/forward restores from cache.state.navigation.loadingboolean on thecore/routerstore, meant as the replacement for the deprecatedstate.navigation.hasStarted/hasFinishedproperties that Luis mentioned in the thread were never meant to be public.Why?
There are basically two groups of consumers that want to know about client-side navigations:
hasStarted/hasFinishedwere used for before they got deprecated, andstate.navigation.loadingis meant to be their public, documented successor.watch(), and often the snippet comes straight from a vendor so we don't even control it.For group 2 there wasn't really a way to hook in at all. Turbo has
turbo:load, Astro hasastro:page-load, htmx hashtmx:afterSwap. A plain DOM event is about the smallest possible surface for this: no promises about internals, no state shape to maintain, just "a navigation happened, here's the URL."How?
Both the event dispatch and the
state.navigation.loadingflip happen from the same two helper functions,beginNavigation()andendNavigation(), inpackages/interactivity-router/src/index.ts. That way the state and the events are always driven from the same transition and can never end up disagreeing with each other.beginNavigation(url)is called once the router has decided a client-side navigation will happen (after theclientNavigationDisabledcheck passes), before the page is fetched. It setsstate.navigation.loading = trueand dispatcheswp-router-navigation-startwithdetail: { url }.endNavigation(url, referrer, title)is called after the DOM has been patched,document.titleupdated, and (for regular navigations) the history entry pushed. It setsstate.navigation.loading = falseand dispatcheswp-router-navigation-endwithdetail: { url, referrer, title }, wherereferreris the URL of the page being navigated away from.Both the
navigate()action and thepopstatelistener (used for back/forward restores from cache) call these two functions, so the events and the loading state behave consistently no matter how the navigation was triggered.The deprecated
hasStarted/hasFinishedgetters are untouched and still proxy through to the privatecore/router/privatestore exactly like before, so existing code using them keeps working (with the existing deprecation warning) until WP 7.1.Also updated the package README with a "Reacting to navigation from outside the Interactivity API" section and a description of
state.navigation.loading, plus a CHANGELOG entry under Unreleased.Testing Instructions
data-wp-router-region.url/referrer/title.view.js, import@wordpress/interactivity-routerand confirmstate.navigation.loadingistruewhile a navigation is in flight andfalseonce it settles.I added e2e coverage in
test/e2e/specs/interactivity/router-navigate.spec.tsfor both the events andstate.navigation.loading, covering a normal navigation and a back/forward restore. I wasn't able to run the e2e suite locally in this environment (wp-env-testneeds Docker running, and Docker wasn't reachable here), so I'd appreciate a check that CI is green on these. I did runnpm run lint:json the changed files andtsc -bon theinteractivity-routerpackage, both pass clean.Testing Instructions for Keyboard
No UI changes, nothing keyboard specific to test here.
Open questions
wp-router-navigation-endfires. The superseded one gets astartevent with no matchingend. That felt like the right call to match the existing "only the latest navigation wins" behavior, but flagging it in case folks want something different.beginNavigation/endNavigationinternally, happy to rename if there's a preferred convention.Use of AI Tools
This PR was written with Claude Code. I reviewed the implementation, the design decisions around tying the DOM events and
state.navigation.loadingto the same code path, and the tests before opening this PR.🤖 Generated with Claude Code