Skip to content

Commit

Permalink
feat: Add scroll option support for React SPA adapter (#911)
Browse files Browse the repository at this point in the history
  • Loading branch information
franky47 authored Feb 13, 2025
1 parent bf56cb7 commit 223609e
Show file tree
Hide file tree
Showing 18 changed files with 129 additions and 1 deletion.
11 changes: 11 additions & 0 deletions packages/e2e/next/cypress/e2e/shared/scroll.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { testScroll } from 'e2e-shared/specs/scroll.cy'

testScroll({
path: '/app/scroll',
nextJsRouter: 'app'
})

testScroll({
path: '/pages/scroll',
nextJsRouter: 'pages'
})
10 changes: 10 additions & 0 deletions packages/e2e/next/src/app/app/(shared)/scroll/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Scroll } from 'e2e-shared/specs/scroll'
import { Suspense } from 'react'

export default function Page() {
return (
<Suspense>
<Scroll />
</Suspense>
)
}
3 changes: 3 additions & 0 deletions packages/e2e/next/src/pages/pages/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Scroll } from 'e2e-shared/specs/scroll'

export default Scroll
3 changes: 3 additions & 0 deletions packages/e2e/react-router/v6/cypress/e2e/shared/scroll.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { testScroll } from 'e2e-shared/specs/scroll.cy'

testScroll({ path: '/scroll' })
1 change: 1 addition & 0 deletions packages/e2e/react-router/v6/src/react-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const router = createBrowserRouter(
<Route path="fog-of-war/result" lazy={load(import('./routes/fog-of-war.result'))} />
<Route path="conditional-rendering/useQueryState" lazy={load(import('./routes/conditional-rendering.useQueryState'))} />
<Route path="conditional-rendering/useQueryStates" lazy={load(import('./routes/conditional-rendering.useQueryStates'))} />
<Route path="scroll" lazy={load(import('./routes/scroll'))} />

<Route path="render-count/:hook/:shallow/:history/:startTransition/no-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.no-loader'))} />
<Route path="render-count/:hook/:shallow/:history/:startTransition/sync-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader'))} />
Expand Down
3 changes: 3 additions & 0 deletions packages/e2e/react-router/v6/src/routes/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Scroll } from 'e2e-shared/specs/scroll'

export default Scroll
1 change: 1 addition & 0 deletions packages/e2e/react-router/v7/app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default [
route('/fog-of-war/result', './routes/fog-of-war.result.tsx'),
route('/conditional-rendering/useQueryState', './routes/conditional-rendering.useQueryState.tsx'),
route('/conditional-rendering/useQueryStates', './routes/conditional-rendering.useQueryStates.tsx'),
route('/scroll', './routes/scroll.tsx'),

route('/render-count/:hook/:shallow/:history/:startTransition/no-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.no-loader.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/sync-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader.tsx'),
Expand Down
3 changes: 3 additions & 0 deletions packages/e2e/react-router/v7/app/routes/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Scroll } from 'e2e-shared/specs/scroll'

export default Scroll
3 changes: 3 additions & 0 deletions packages/e2e/react-router/v7/cypress/e2e/shared/scroll.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { testScroll } from 'e2e-shared/specs/scroll.cy'

testScroll({ path: '/scroll' })
3 changes: 3 additions & 0 deletions packages/e2e/react/cypress/e2e/shared/scroll.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { testScroll } from 'e2e-shared/specs/scroll.cy'

testScroll({ path: '/scroll' })
1 change: 1 addition & 0 deletions packages/e2e/react/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const routes: Record<string, React.LazyExoticComponent<() => JSX.Element>> = {
'/referential-stability/useQueryStates': lazy(() => import('./routes/referential-stability.useQueryStates')),
'/conditional-rendering/useQueryState': lazy(() => import('./routes/conditional-rendering.useQueryState')),
'/conditional-rendering/useQueryStates': lazy(() => import('./routes/conditional-rendering.useQueryStates')),
'/scroll': lazy(() => import('./routes/scroll')),

'/render-count/useQueryState/true/replace/false': lazy(() => import('./routes/render-count')),
'/render-count/useQueryState/true/replace/true': lazy(() => import('./routes/render-count')),
Expand Down
3 changes: 3 additions & 0 deletions packages/e2e/react/src/routes/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Scroll } from 'e2e-shared/specs/scroll'

export default Scroll
3 changes: 3 additions & 0 deletions packages/e2e/remix/app/routes/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Scroll } from 'e2e-shared/specs/scroll'

export default Scroll
3 changes: 3 additions & 0 deletions packages/e2e/remix/cypress/e2e/shared/scroll.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { testScroll } from 'e2e-shared/specs/scroll.cy'

testScroll({ path: '/scroll' })
2 changes: 1 addition & 1 deletion packages/e2e/shared/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function defineConfig(config: Config) {
video: false,
fixturesFolder: false,
testIsolation: true,
defaultCommandTimeout: 500,
defaultCommandTimeout: 1000,
setupNodeEvents(on) {
cypressTerminalReport(on)
},
Expand Down
18 changes: 18 additions & 0 deletions packages/e2e/shared/specs/scroll.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createTest } from '../create-test'

export const testScroll = createTest('scroll', ({ path }) => {
it('does not scroll to the top of the page by default (scroll: false)', () => {
cy.visit(path + '?scroll=false')
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
cy.get('#not-at-the-top').should('be.visible')
cy.get('button').click()
cy.get('#not-at-the-top').should('be.visible')
})
it('scrolls to the top of the page when setting scroll: true', () => {
cy.visit(path + '?scroll=true')
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
cy.get('#not-at-the-top').should('be.visible')
cy.get('button').click()
cy.get('#at-the-top').should('be.visible')
})
})
56 changes: 56 additions & 0 deletions packages/e2e/shared/specs/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use client'

import { parseAsBoolean, useQueryState } from 'nuqs'
import { useEffect, useState } from 'react'

export function Scroll() {
return (
<>
<ScrollDetector />
<div style={{ height: '200vh' }} />
<ScrollAction />
</>
)
}

function ScrollDetector() {
const [atTheTop, setAtTheTop] = useState(false)

useEffect(() => {
const controller = new AbortController()
window.addEventListener('scroll', () => setAtTheTop(window.scrollY === 0), {
signal: controller.signal
})
return () => controller.abort()
}, [])

return (
<span
id={atTheTop ? 'at-the-top' : 'not-at-the-top'}
style={{ position: 'fixed', top: 8 }}
>
{atTheTop ? null : 'not '}at the top
</span>
)
}

function ScrollAction() {
const [scroll] = useQueryState('scroll', parseAsBoolean.withDefault(false))
const [, setState] = useQueryState('test', {
scroll
})

useEffect(() => {
document.getElementById('scroll-to-me')?.scrollIntoView()
}, [])

return (
<button
id="scroll-to-me"
onClick={() => setState('pass')}
style={{ marginInline: 'auto', display: 'block' }}
>
Test
</button>
)
}
3 changes: 3 additions & 0 deletions packages/nuqs/src/adapters/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ function updateUrl(search: URLSearchParams, options: AdapterOptions) {
options.history === 'push' ? history.pushState : history.replaceState
method.call(history, history.state, '', url)
emitter.emit('update', search)
if (options.scroll === true) {
window.scrollTo({ top: 0 })
}
}

function useNuqsReactAdapter() {
Expand Down

0 comments on commit 223609e

Please sign in to comment.