diff --git a/src/App.jsx b/src/App.jsx index 6cb7586..bb1793c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -60,6 +60,12 @@ const App = () => { const reader = useBibleReader(currentPage, readerPlanProgress?.translation ?? null); const audio = useAudio(reader.readerBookAbbrev, reader.readerChapter, reader.readerTranslation); useEffect(() => { if (currentPage !== 'reader') setReaderPlan(null); }, [currentPage]); + // A new page starts at the top. Without this the scroll position of the previous page carries over, and on + // iPad content scrolled under the top edge makes the system blur the app bar. + useEffect(() => { + const scroller = document.scrollingElement ?? document.documentElement; + if (scroller) scroller.scrollTop = 0; + }, [currentPage]); // Keep the browser/OS chrome (status bar area, overscroll, scrollbars) in the chosen theme useEffect(() => { applySystemChrome(reader.readerTheme); }, [reader.readerTheme]); const { authUser, setAuthUser, authServerDown, signIn, logout } = useAuth({ diff --git a/src/App.test.jsx b/src/App.test.jsx index c4548ac..50fc767 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1420,3 +1420,19 @@ describe('System chrome follows the theme', () => { expect(document.documentElement.style.colorScheme).toBe('light'); }); }); + +describe('New pages start at the top', () => { + test('moving to another page resets the scroll position', async () => { + const user = userEvent.setup(); + await renderApp(); + const scroller = document.scrollingElement ?? document.documentElement; + scroller.scrollTop = 480; // scrolled down on the dashboard + await user.click(screen.getByRole('button', { name: /reading plans/i })); + await screen.findByRole('heading', { name: /^reading plans$/i }); + expect(scroller.scrollTop).toBe(0); + scroller.scrollTop = 300; + await user.click(screen.getByRole('button', { name: /back to home/i })); + await screen.findByText('My Studies'); + expect(scroller.scrollTop).toBe(0); + }); +});