Skip to content

Commit 31db8f8

Browse files
mihaizaurusgraphierosautofix-ci[bot]
authored
fix: added scroll preservation opt in to the compare page (#2101)
Co-authored-by: Alec Lloyd Probert <55991794+graphieros@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 32ae83d commit 31db8f8

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

app/pages/compare.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import FacetBarChart from '~/components/Compare/FacetBarChart.vue'
55
66
definePageMeta({
77
name: 'compare',
8+
preserveScrollOnQuery: true,
89
})
910
1011
const { locale } = useI18n()

app/router.options.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import type { RouterConfig } from 'nuxt/schema'
22

33
export default {
4-
scrollBehavior(to, _from, savedPosition) {
4+
scrollBehavior(to, from, savedPosition) {
55
// If the browser has a saved position (e.g. back/forward navigation), restore it
66

77
if (savedPosition) {
88
return savedPosition
99
}
10+
11+
// Preserve the current viewport for query-only updates on pages that opt in,
12+
// such as compare where controls sync state to the URL in-place.
13+
if (to.path === from.path && to.hash === from.hash && to.meta.preserveScrollOnQuery === true) {
14+
return false
15+
}
16+
1017
// If navigating to a hash anchor, scroll to it
1118
if (to.hash) {
1219
const { scrollMargin } = to.meta

app/types/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@ declare module '#app' {
1010
* @default 70
1111
*/
1212
scrollMargin?: number
13+
/**
14+
* preserve scroll position when only query params change on same path/hash
15+
*/
16+
preserveScrollOnQuery?: boolean
1317
}
1418
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { describe, expect, it } from 'vitest'
2+
import routerOptions from '../../../app/router.options'
3+
4+
type ScrollBehavior = NonNullable<typeof routerOptions.scrollBehavior>
5+
type RouteArg = Parameters<ScrollBehavior>[0]
6+
7+
function createRoute(overrides: Partial<RouteArg> = {}) {
8+
return {
9+
path: '/',
10+
hash: '',
11+
query: {},
12+
meta: {},
13+
...overrides,
14+
} as RouteArg
15+
}
16+
17+
describe('router scrollBehavior', () => {
18+
it('restores saved position when available', () => {
19+
const savedPosition = { left: 12, top: 345 }
20+
21+
expect(routerOptions.scrollBehavior(createRoute(), createRoute(), savedPosition)).toEqual(
22+
savedPosition,
23+
)
24+
})
25+
26+
it('preserves scroll on query-only updates for pages that opt in', () => {
27+
const to = createRoute({
28+
path: '/compare',
29+
query: { packages: 'vue,nuxt', facets: 'downloads,license' },
30+
meta: { preserveScrollOnQuery: true },
31+
})
32+
const from = createRoute({
33+
path: '/compare',
34+
query: { packages: 'vue', facets: 'downloads' },
35+
meta: { preserveScrollOnQuery: true },
36+
})
37+
38+
expect(routerOptions.scrollBehavior(to, from, null)).toBe(false)
39+
})
40+
41+
it('does not preserve scroll on query-only updates without opt-in', () => {
42+
const to = createRoute({
43+
path: '/compare',
44+
query: { packages: 'vue,nuxt', facets: 'downloads,license' },
45+
})
46+
const from = createRoute({
47+
path: '/compare',
48+
query: { packages: 'vue', facets: 'downloads' },
49+
})
50+
51+
expect(routerOptions.scrollBehavior(to, from, null)).toEqual({ left: 0, top: 0 })
52+
})
53+
54+
it('scrolls to hash anchors', () => {
55+
const to = createRoute({
56+
hash: '#section-function',
57+
meta: { scrollMargin: 96 },
58+
})
59+
60+
expect(routerOptions.scrollBehavior(to, createRoute(), null)).toEqual({
61+
el: '#section-function',
62+
behavior: 'smooth',
63+
top: 96,
64+
})
65+
})
66+
67+
it('scrolls to top for regular navigations', () => {
68+
const to = createRoute({ path: '/compare', meta: { preserveScrollOnQuery: true } })
69+
const from = createRoute({ path: '/search' })
70+
71+
expect(routerOptions.scrollBehavior(to, from, null)).toEqual({ left: 0, top: 0 })
72+
})
73+
})

0 commit comments

Comments
 (0)