From 8d0a9b9a08714c7cff1005bbda5e7796234e921a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Wed, 12 Feb 2025 23:41:24 +0100 Subject: [PATCH 1/3] test: Add scroll test --- .../e2e/next/cypress/e2e/shared/scroll.cy.ts | 11 ++++ .../next/src/app/app/(shared)/scroll/page.tsx | 10 ++++ packages/e2e/next/src/pages/pages/scroll.tsx | 3 + .../v6/cypress/e2e/shared/scroll.cy.ts | 3 + .../e2e/react-router/v6/src/react-router.tsx | 1 + .../e2e/react-router/v6/src/routes/scroll.tsx | 3 + packages/e2e/react-router/v7/app/routes.ts | 1 + .../e2e/react-router/v7/app/routes/scroll.tsx | 3 + .../v7/cypress/e2e/shared/scroll.cy.ts | 3 + .../e2e/react/cypress/e2e/shared/scroll.cy.ts | 3 + packages/e2e/react/src/routes.tsx | 1 + packages/e2e/react/src/routes/scroll.tsx | 3 + packages/e2e/remix/app/routes/scroll.tsx | 3 + .../e2e/remix/cypress/e2e/shared/scroll.cy.ts | 3 + packages/e2e/shared/specs/scroll.cy.ts | 18 ++++++ packages/e2e/shared/specs/scroll.tsx | 56 +++++++++++++++++++ 16 files changed, 125 insertions(+) create mode 100644 packages/e2e/next/cypress/e2e/shared/scroll.cy.ts create mode 100644 packages/e2e/next/src/app/app/(shared)/scroll/page.tsx create mode 100644 packages/e2e/next/src/pages/pages/scroll.tsx create mode 100644 packages/e2e/react-router/v6/cypress/e2e/shared/scroll.cy.ts create mode 100644 packages/e2e/react-router/v6/src/routes/scroll.tsx create mode 100644 packages/e2e/react-router/v7/app/routes/scroll.tsx create mode 100644 packages/e2e/react-router/v7/cypress/e2e/shared/scroll.cy.ts create mode 100644 packages/e2e/react/cypress/e2e/shared/scroll.cy.ts create mode 100644 packages/e2e/react/src/routes/scroll.tsx create mode 100644 packages/e2e/remix/app/routes/scroll.tsx create mode 100644 packages/e2e/remix/cypress/e2e/shared/scroll.cy.ts create mode 100644 packages/e2e/shared/specs/scroll.cy.ts create mode 100644 packages/e2e/shared/specs/scroll.tsx diff --git a/packages/e2e/next/cypress/e2e/shared/scroll.cy.ts b/packages/e2e/next/cypress/e2e/shared/scroll.cy.ts new file mode 100644 index 00000000..3f0af77e --- /dev/null +++ b/packages/e2e/next/cypress/e2e/shared/scroll.cy.ts @@ -0,0 +1,11 @@ +import { testScroll } from 'e2e-shared/specs/scroll.cy' + +testScroll({ + path: '/app/scroll', + nextJsRouter: 'app' +}) + +testScroll({ + path: '/pages/scroll', + nextJsRouter: 'pages' +}) diff --git a/packages/e2e/next/src/app/app/(shared)/scroll/page.tsx b/packages/e2e/next/src/app/app/(shared)/scroll/page.tsx new file mode 100644 index 00000000..3c874986 --- /dev/null +++ b/packages/e2e/next/src/app/app/(shared)/scroll/page.tsx @@ -0,0 +1,10 @@ +import { Scroll } from 'e2e-shared/specs/scroll' +import { Suspense } from 'react' + +export default function Page() { + return ( + + + + ) +} diff --git a/packages/e2e/next/src/pages/pages/scroll.tsx b/packages/e2e/next/src/pages/pages/scroll.tsx new file mode 100644 index 00000000..b857ba37 --- /dev/null +++ b/packages/e2e/next/src/pages/pages/scroll.tsx @@ -0,0 +1,3 @@ +import { Scroll } from 'e2e-shared/specs/scroll' + +export default Scroll diff --git a/packages/e2e/react-router/v6/cypress/e2e/shared/scroll.cy.ts b/packages/e2e/react-router/v6/cypress/e2e/shared/scroll.cy.ts new file mode 100644 index 00000000..e1801854 --- /dev/null +++ b/packages/e2e/react-router/v6/cypress/e2e/shared/scroll.cy.ts @@ -0,0 +1,3 @@ +import { testScroll } from 'e2e-shared/specs/scroll.cy' + +testScroll({ path: '/scroll' }) diff --git a/packages/e2e/react-router/v6/src/react-router.tsx b/packages/e2e/react-router/v6/src/react-router.tsx index 64fe6f4b..6d007b93 100644 --- a/packages/e2e/react-router/v6/src/react-router.tsx +++ b/packages/e2e/react-router/v6/src/react-router.tsx @@ -45,6 +45,7 @@ const router = createBrowserRouter( + diff --git a/packages/e2e/react-router/v6/src/routes/scroll.tsx b/packages/e2e/react-router/v6/src/routes/scroll.tsx new file mode 100644 index 00000000..b857ba37 --- /dev/null +++ b/packages/e2e/react-router/v6/src/routes/scroll.tsx @@ -0,0 +1,3 @@ +import { Scroll } from 'e2e-shared/specs/scroll' + +export default Scroll diff --git a/packages/e2e/react-router/v7/app/routes.ts b/packages/e2e/react-router/v7/app/routes.ts index fd9a0a24..0fc04bee 100644 --- a/packages/e2e/react-router/v7/app/routes.ts +++ b/packages/e2e/react-router/v7/app/routes.ts @@ -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'), diff --git a/packages/e2e/react-router/v7/app/routes/scroll.tsx b/packages/e2e/react-router/v7/app/routes/scroll.tsx new file mode 100644 index 00000000..b857ba37 --- /dev/null +++ b/packages/e2e/react-router/v7/app/routes/scroll.tsx @@ -0,0 +1,3 @@ +import { Scroll } from 'e2e-shared/specs/scroll' + +export default Scroll diff --git a/packages/e2e/react-router/v7/cypress/e2e/shared/scroll.cy.ts b/packages/e2e/react-router/v7/cypress/e2e/shared/scroll.cy.ts new file mode 100644 index 00000000..e1801854 --- /dev/null +++ b/packages/e2e/react-router/v7/cypress/e2e/shared/scroll.cy.ts @@ -0,0 +1,3 @@ +import { testScroll } from 'e2e-shared/specs/scroll.cy' + +testScroll({ path: '/scroll' }) diff --git a/packages/e2e/react/cypress/e2e/shared/scroll.cy.ts b/packages/e2e/react/cypress/e2e/shared/scroll.cy.ts new file mode 100644 index 00000000..e1801854 --- /dev/null +++ b/packages/e2e/react/cypress/e2e/shared/scroll.cy.ts @@ -0,0 +1,3 @@ +import { testScroll } from 'e2e-shared/specs/scroll.cy' + +testScroll({ path: '/scroll' }) diff --git a/packages/e2e/react/src/routes.tsx b/packages/e2e/react/src/routes.tsx index 953fdb5a..13e91c52 100644 --- a/packages/e2e/react/src/routes.tsx +++ b/packages/e2e/react/src/routes.tsx @@ -23,6 +23,7 @@ const routes: Record 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')), diff --git a/packages/e2e/react/src/routes/scroll.tsx b/packages/e2e/react/src/routes/scroll.tsx new file mode 100644 index 00000000..b857ba37 --- /dev/null +++ b/packages/e2e/react/src/routes/scroll.tsx @@ -0,0 +1,3 @@ +import { Scroll } from 'e2e-shared/specs/scroll' + +export default Scroll diff --git a/packages/e2e/remix/app/routes/scroll.tsx b/packages/e2e/remix/app/routes/scroll.tsx new file mode 100644 index 00000000..b857ba37 --- /dev/null +++ b/packages/e2e/remix/app/routes/scroll.tsx @@ -0,0 +1,3 @@ +import { Scroll } from 'e2e-shared/specs/scroll' + +export default Scroll diff --git a/packages/e2e/remix/cypress/e2e/shared/scroll.cy.ts b/packages/e2e/remix/cypress/e2e/shared/scroll.cy.ts new file mode 100644 index 00000000..e1801854 --- /dev/null +++ b/packages/e2e/remix/cypress/e2e/shared/scroll.cy.ts @@ -0,0 +1,3 @@ +import { testScroll } from 'e2e-shared/specs/scroll.cy' + +testScroll({ path: '/scroll' }) diff --git a/packages/e2e/shared/specs/scroll.cy.ts b/packages/e2e/shared/specs/scroll.cy.ts new file mode 100644 index 00000000..9cd47cb2 --- /dev/null +++ b/packages/e2e/shared/specs/scroll.cy.ts @@ -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') + }) +}) diff --git a/packages/e2e/shared/specs/scroll.tsx b/packages/e2e/shared/specs/scroll.tsx new file mode 100644 index 00000000..54d965e1 --- /dev/null +++ b/packages/e2e/shared/specs/scroll.tsx @@ -0,0 +1,56 @@ +'use client' + +import { parseAsBoolean, useQueryState } from 'nuqs' +import { useEffect, useState } from 'react' + +export function Scroll() { + return ( + <> + +
+ + + ) +} + +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 ( + + {atTheTop ? null : 'not '}at the top + + ) +} + +function ScrollAction() { + const [scroll] = useQueryState('scroll', parseAsBoolean.withDefault(false)) + const [, setState] = useQueryState('test', { + scroll + }) + + useEffect(() => { + document.getElementById('scroll-to-me')?.scrollIntoView() + }, []) + + return ( + + ) +} From 17dccbbe2be614b4f1100e521c01117198b10b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Wed, 12 Feb 2025 23:53:05 +0100 Subject: [PATCH 2/3] fix: Implement scroll for the React SPA adapter --- packages/nuqs/src/adapters/react.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nuqs/src/adapters/react.ts b/packages/nuqs/src/adapters/react.ts index 33351b49..5c3f7437 100644 --- a/packages/nuqs/src/adapters/react.ts +++ b/packages/nuqs/src/adapters/react.ts @@ -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() { From 152fb86e48f784f5eb9c6e54ed349df184b1c54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Thu, 13 Feb 2025 10:07:29 +0100 Subject: [PATCH 3/3] chore: Increase timeout --- packages/e2e/shared/cypress.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e/shared/cypress.config.ts b/packages/e2e/shared/cypress.config.ts index 27382fdd..e5fb55e7 100644 --- a/packages/e2e/shared/cypress.config.ts +++ b/packages/e2e/shared/cypress.config.ts @@ -12,7 +12,7 @@ export function defineConfig(config: Config) { video: false, fixturesFolder: false, testIsolation: true, - defaultCommandTimeout: 500, + defaultCommandTimeout: 1000, setupNodeEvents(on) { cypressTerminalReport(on) },