Skip to content

Comments

fix(react-router,solid-router,vue-router): correct preload triggers for "intent" option#6747

Merged
Sheraff merged 8 commits intomainfrom
refactor-link-timeout-external-ref
Feb 24, 2026
Merged

fix(react-router,solid-router,vue-router): correct preload triggers for "intent" option#6747
Sheraff merged 8 commits intomainfrom
refactor-link-timeout-external-ref

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Feb 23, 2026

Summary by CodeRabbit

  • Refactor
    • Link preloading now only occurs for the explicit "intent" mode and is debounced per interaction to avoid duplicate or premature preloads.
  • New Features
    • Added a configurable preloadDelay prop on Link to control preload timing.
  • Tests
    • Added tests covering Link preload behavior across preload modes and user interactions (focus, hover, touchstart).
  • Chores
    • Removed an internal public type from router package exports.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removed the exported LinkCurrentTargetElement type; replaced per-element preload timeouts with a module-level WeakMap debounce in solid- and vue-router adapters; tightened react-router to only preload when preload === 'intent'; added/updated tests for intent vs non-intent preload behavior.

Changes

Cohort / File(s) Summary
Core type removal
packages/router-core/src/index.ts, packages/router-core/src/link.ts
Removed the exported LinkCurrentTargetElement type (it previously included a preloadTimeout field).
Solid adapter
packages/solid-router/src/link.tsx, packages/solid-router/tests/link.test.tsx
Replaced per-element preloadTimeout with module-level WeakMap (timeoutMap); updated enqueueIntentPreload/handleLeave to use `event.currentTarget
Vue adapter
packages/vue-router/src/link.tsx, packages/vue-router/tests/link.test.tsx
Applied same WeakMap debounce and event-target handling as Solid; updated cleanup logic; added tests for intent and non-intent preload values and test usage of preloadDelay.
React adapter
packages/react-router/src/link.tsx, packages/react-router/tests/link.test.tsx
Gated preloading to only run when preload === 'intent' (previously any truthy); added tests asserting preloading occurs only for intent.
Tests (shared)
packages/*/tests/link.test.tsx
Added/expanded parameterized tests ensuring preload triggers only for preload="intent" and not for `undefined

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant LinkComponent as Link
participant TimeoutMap as WeakMap
participant Router as Router.preloadRoute

User->>LinkComponent: focus / hover / touchstart
LinkComponent->>TimeoutMap: check existing timeout for event target
alt no existing timeout and preload === 'intent'
LinkComponent->>TimeoutMap: set debounce timeout
TimeoutMap-->>LinkComponent: on timeout trigger
LinkComponent->>Router: preloadRoute(...)
else existing timeout or preload !== 'intent'
LinkComponent-->>User: no-op
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • birkskyum
  • nlynzaad

Poem

🐰 I hopped through code with nimble feet,
Timers moved to maps — no DOM defeat.
Intent now waits, then softly calls,
Debounced and tidy across the walls.
Tests clap their paws — the change is sweet.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(react-router,solid-router,vue-router): correct preload triggers for "intent" option' accurately describes a key behavioral change in the PR, but the primary changes involve removing a type (LinkCurrentTargetElement) and refactoring timeout handling to use WeakMaps instead of attaching properties to DOM elements. The title focuses on the behavioral fix for preload triggers while the substantial refactoring work of avoiding foreign properties on DOM elements is not mentioned.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-link-timeout-external-ref

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Feb 23, 2026

View your CI Pipeline Execution ↗ for commit 135abf1

Command Status Duration Result
nx run tanstack-router-e2e-bundle-size:build --... ✅ Succeeded 1m 28s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-24 12:09:17 UTC

@github-actions
Copy link

github-actions bot commented Feb 23, 2026

Bundle Size Benchmarks

  • Commit: a0ea0e913bf9
  • Measured at: 2026-02-24T12:01:23.535Z
  • Baseline source: history:09405b22ccff
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 86.54 KiB +6 B (+0.01%) 272.17 KiB 75.21 KiB ▇██▆▆▆▁▁▁▁▁▁
react-router.full 89.55 KiB +6 B (+0.01%) 282.51 KiB 77.88 KiB ███▇▆▆▁▁▁▁▁▁
solid-router.minimal 35.85 KiB +44 B (+0.12%) 107.29 KiB 32.22 KiB ███▆▆▆▁▁▁▁▁▂
solid-router.full 40.18 KiB +35 B (+0.09%) 120.34 KiB 36.05 KiB ███▇▆▆▁▁▂▂▂▂
vue-router.minimal 51.71 KiB +52 B (+0.10%) 147.26 KiB 46.49 KiB █▇▇▆▆▆▁▁▁▁▁▃
vue-router.full 56.51 KiB +27 B (+0.05%) 162.85 KiB 50.75 KiB █▇▇▆▆▆▁▁▁▁▁▂
react-start.minimal 99.08 KiB +4 B (+0.00%) 311.31 KiB 85.74 KiB ▇▇▇▆▆█▁▁▁▁▁▁
react-start.full 102.42 KiB +1 B (+0.00%) 321.08 KiB 88.53 KiB ███▆▅▆▁▁▁▁▁▁
solid-start.minimal 48.16 KiB +36 B (+0.07%) 144.86 KiB 42.60 KiB █▇▇▆▅█▁▁▂▂▂▃
solid-start.full 53.62 KiB +42 B (+0.08%) 160.77 KiB 47.26 KiB ███▆▆█▁▁▁▁▁▃

Trend sparkline is historical gzip bytes ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 23, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@6747

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@6747

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@6747

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@6747

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@6747

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@6747

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@6747

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@6747

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@6747

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@6747

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@6747

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@6747

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@6747

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@6747

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@6747

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@6747

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@6747

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@6747

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@6747

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@6747

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@6747

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@6747

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@6747

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@6747

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@6747

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@6747

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6747

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@6747

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@6747

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@6747

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@6747

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@6747

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@6747

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6747

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-devtools@6747

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-ssr-query@6747

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6747

@tanstack/vue-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@6747

@tanstack/vue-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@6747

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@6747

commit: 1464f1d

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/solid-router/tests/link.test.tsx (1)

4484-4486: Misleading test name for the undefined case.

test.each stringifies values, so undefined renders as 'Link.preload="undefined"' in the test output. This implies the preload prop is explicitly set to "undefined", whereas the test actually omits the prop entirely (via {} spread). A label override makes the intent clear:

✏️ Proposed fix
- test.each([undefined, false, 'render', 'viewport'] as const)(
-   'Link.preload="%s" should not preload on focus, hover, or touchstart',
+ test.each([
+   [undefined, 'not set'],
+   [false, 'false'],
+   ['render', 'render'],
+   ['viewport', 'viewport'],
+ ] as const)(
+   'Link.preload="%s" should not preload on focus, hover, or touchstart',
+   async (preloadMode, _label) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-router/tests/link.test.tsx` around lines 4484 - 4486, The test
title for test.each([undefined, false, 'render', 'viewport']...) misleadingly
shows Link.preload="undefined" for the case where the prop is omitted; update
the test data to include explicit labels so the case where preload is omitted is
shown as e.g. "no prop" (or "omitted") instead of "undefined" — modify the
invocation of test.each used with the test name 'Link.preload="%s" should not
preload on focus, hover, or touchstart' to supply tuples of [value, label] or
otherwise provide a custom label for the undefined case (referencing the
existing test.each and the preloadMode variable/Link.preload mention) so the
test output reflects the intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/solid-router/tests/link.test.tsx`:
- Around line 4535-4537: The test uses waitFor around a negative assertion on
preloadRouteSpy which can false-pass because waitFor resolves immediately if the
assertion is already true; change the pattern to first await sleep(...) to drain
timers (consistent with other tests) then perform a direct synchronous
expect(preloadRouteSpy).toHaveBeenCalledTimes(baselineCalls) so asynchronous
preloads scheduled with defaultPreloadDelay: 0 are allowed to run before the
assertion; update the assertion code that currently references waitFor and
preloadRouteSpy accordingly.

In `@packages/vue-router/src/link.tsx`:
- Around line 362-390: The intent preload should bypass scheduling when
preloadDelay.value === 0 so events fire immediately and cannot be cancelled by
handleLeave; in enqueueIntentPreload (and where delay-based scheduling happens)
add an early branch: if preloadDelay.value === 0 then call doPreload() and
return (do not touch timeoutMap or setTimeout). Keep existing guards
(options.disabled, preload.value !== 'intent') but ensure zero-delay fast path
avoids storing timeouts so quick focus/hover/touch can't be deduped/cancelled.

---

Nitpick comments:
In `@packages/solid-router/tests/link.test.tsx`:
- Around line 4484-4486: The test title for test.each([undefined, false,
'render', 'viewport']...) misleadingly shows Link.preload="undefined" for the
case where the prop is omitted; update the test data to include explicit labels
so the case where preload is omitted is shown as e.g. "no prop" (or "omitted")
instead of "undefined" — modify the invocation of test.each used with the test
name 'Link.preload="%s" should not preload on focus, hover, or touchstart' to
supply tuples of [value, label] or otherwise provide a custom label for the
undefined case (referencing the existing test.each and the preloadMode
variable/Link.preload mention) so the test output reflects the intent.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8875f10 and 2431c47.

📒 Files selected for processing (6)
  • packages/react-router/src/link.tsx
  • packages/react-router/tests/link.test.tsx
  • packages/solid-router/src/link.tsx
  • packages/solid-router/tests/link.test.tsx
  • packages/vue-router/src/link.tsx
  • packages/vue-router/tests/link.test.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/vue-router/tests/link.test.tsx (2)

4563-4613: No coverage for handleLeave debounce cancellation — the core behavioral change of this PR.

The PR replaces per-element preloadTimeout properties with a module-level WeakMap-based timeoutMap. The most important invariant of that change is that a mouseleave or blur event cancels a pending preload timer before it fires. The intent test only validates the positive path (events trigger preloads); it never verifies that leaving the element before the timer fires suppresses the preload.

A minimal coverage addition, e.g.:

// Verify that mouseleave cancels a pending intent preload
const router2 = createRouter({
  routeTree: rootRoute.addChildren([aboutRoute, indexRoute]),
  defaultPreload: false,
  defaultPreloadDelay: 50, // non-zero so we can interleave leave before fire
  history,
})
vi.useFakeTimers()
const spy2 = vi.spyOn(router2, 'preloadRoute')
render(<RouterProvider router={router2} />)
const link2 = await screen.findByRole('link', { name: 'About Link' })

fireEvent.mouseOver(link2)   // schedules timer
fireEvent.mouseLeave(link2)  // should cancel it
vi.runAllTimers()
expect(spy2).not.toHaveBeenCalled()
vi.useRealTimers()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-router/tests/link.test.tsx` around lines 4563 - 4613, The test
currently only asserts positive preload calls but misses verifying that
handleLeave cancels pending intent preloads; add a second case that creates a
router with defaultPreloadDelay > 0, uses vi.useFakeTimers(), spies
router.preloadRoute, renders RouterProvider, finds the Link, fires mouseOver to
schedule a timer, then fires mouseLeave (or blur) to cancel it, advances timers
(vi.runAllTimers or equivalent) and asserts preloadRoute was not called, finally
call vi.useRealTimers(); reference symbols: timeoutMap/handleLeave in the module
and router.preloadRoute and Link component in the test.

4554-4559: Negative assertion using sleep(1) may be flaky under load.

With defaultPreloadDelay: 0, the intent handler schedules setTimeout(fn, 0). await sleep(1) relies on real wall-clock time to allow that 0ms callback to fire before checking. This is sufficient in most environments but can race under heavy CI load.

Consider using fake timers for a deterministic boundary:

♻️ Suggested refactor using fake timers
+      vi.useFakeTimers()

       fireEvent.focus(aboutLink)
       fireEvent.mouseOver(aboutLink)
       fireEvent.touchStart(aboutLink)

-      await sleep(1)
+      vi.runAllTimers()
       expect(preloadRouteSpy).toHaveBeenCalledTimes(baselineCalls)
+
+      vi.useRealTimers()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-router/tests/link.test.tsx` around lines 4554 - 4559, The
negative assertion using sleep(1) is flaky; switch the test to use fake timers
instead of waiting real time: enable fake timers (e.g., jest.useFakeTimers()),
trigger the events on aboutLink (fireEvent.focus/mouseOver/touchStart), then
advance or run timers deterministically (e.g., jest.advanceTimersByTime(0) or
jest.runOnlyPendingTimers()) to let the defaultPreloadDelay: 0 scheduled
callback run, and finally assert preloadRouteSpy has been called the expected
number of times; restore real timers after the test. Ensure you update the test
surrounding preloadRouteSpy and any setup/teardown to use
jest.useFakeTimers()/jest.useRealTimers().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/vue-router/tests/link.test.tsx`:
- Around line 4563-4613: The test currently only asserts positive preload calls
but misses verifying that handleLeave cancels pending intent preloads; add a
second case that creates a router with defaultPreloadDelay > 0, uses
vi.useFakeTimers(), spies router.preloadRoute, renders RouterProvider, finds the
Link, fires mouseOver to schedule a timer, then fires mouseLeave (or blur) to
cancel it, advances timers (vi.runAllTimers or equivalent) and asserts
preloadRoute was not called, finally call vi.useRealTimers(); reference symbols:
timeoutMap/handleLeave in the module and router.preloadRoute and Link component
in the test.
- Around line 4554-4559: The negative assertion using sleep(1) is flaky; switch
the test to use fake timers instead of waiting real time: enable fake timers
(e.g., jest.useFakeTimers()), trigger the events on aboutLink
(fireEvent.focus/mouseOver/touchStart), then advance or run timers
deterministically (e.g., jest.advanceTimersByTime(0) or
jest.runOnlyPendingTimers()) to let the defaultPreloadDelay: 0 scheduled
callback run, and finally assert preloadRouteSpy has been called the expected
number of times; restore real timers after the test. Ensure you update the test
surrounding preloadRouteSpy and any setup/teardown to use
jest.useFakeTimers()/jest.useRealTimers().

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2431c47 and 52ba894.

📒 Files selected for processing (3)
  • packages/react-router/tests/link.test.tsx
  • packages/solid-router/tests/link.test.tsx
  • packages/vue-router/tests/link.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-router/tests/link.test.tsx
  • packages/solid-router/tests/link.test.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/vue-router/tests/link.test.tsx (1)

4507-4561: LGTM — minor: the 'render' baseline guard is missing for 'viewport'

The if (preloadMode === 'render') block correctly waits for the initial render-triggered call before capturing baselineCalls. For 'viewport' this is safe only because the getIntersectionObserverMock helper never auto-fires the intersection callback — an implicit assumption. If the mock is ever updated to simulate visible elements, preloadRoute would be called asynchronously after findByRole returns but before or during sleep(100), and the baseline would be stale.

Consider symmetrically guarding both:

♻️ Suggested guard for `viewport` and `render` modes
-    if (preloadMode === 'render') {
+    if (preloadMode === 'render' || preloadMode === 'viewport') {
       await waitFor(() =>
         expect(preloadRouteSpy.mock.calls.length).toBeGreaterThan(0),
       )
     }

Or, if the intent is to assert that 'viewport' mode never calls preloadRoute until the IntersectionObserver fires (i.e., baselineCalls should always be 0 for viewport), document that assumption with a comment:

+    // Note: the IntersectionObserver mock does not auto-fire the callback,
+    // so viewport preload is never triggered during render in this test environment.
     const baselineCalls = preloadRouteSpy.mock.calls.length
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-router/tests/link.test.tsx` around lines 4507 - 4561, The test
misses the same async baseline guard for preloadMode === 'viewport' as it has
for 'render', which can make baselineCalls stale if getIntersectionObserverMock
ever auto-fires; update the conditional that waits for an initial
render-triggered preload to cover both modes (e.g., if (preloadMode === 'render'
|| preloadMode === 'viewport') await waitFor(() =>
expect(preloadRouteSpy.mock.calls.length).toBeGreaterThan(0))); touch the test's
preloadMode check around preloadRouteSpy and/or add a brief comment referencing
getIntersectionObserverMock to document the assumption if you prefer to keep the
original behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/vue-router/tests/link.test.tsx`:
- Around line 4507-4561: The test misses the same async baseline guard for
preloadMode === 'viewport' as it has for 'render', which can make baselineCalls
stale if getIntersectionObserverMock ever auto-fires; update the conditional
that waits for an initial render-triggered preload to cover both modes (e.g., if
(preloadMode === 'render' || preloadMode === 'viewport') await waitFor(() =>
expect(preloadRouteSpy.mock.calls.length).toBeGreaterThan(0))); touch the test's
preloadMode check around preloadRouteSpy and/or add a brief comment referencing
getIntersectionObserverMock to document the assumption if you prefer to keep the
original behavior.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52ba894 and 1464f1d.

📒 Files selected for processing (3)
  • packages/react-router/tests/link.test.tsx
  • packages/solid-router/tests/link.test.tsx
  • packages/vue-router/tests/link.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-router/tests/link.test.tsx
  • packages/solid-router/tests/link.test.tsx

@Sheraff Sheraff changed the title refactor(solid-router,vue-router): avoid attaching foreign properties to DOM elements fix(react-router,solid-router,vue-router): correct preload triggers for "intent" option Feb 24, 2026
@Sheraff Sheraff merged commit f58d457 into main Feb 24, 2026
6 of 7 checks passed
@Sheraff Sheraff deleted the refactor-link-timeout-external-ref branch February 24, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant