From f8a9c1865046a3b09d072788d49d64cd4a6d7039 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Fri, 14 Jun 2024 19:36:36 -0400 Subject: [PATCH] fix: Scroll does not reset when navigating shared docs on mobile (#7037) closes #6968 --- app/components/MobileScrollWrapper.tsx | 28 ------------------ app/components/PageScroll.tsx | 39 ++++++++++++++++++++++++++ app/components/ScrollContext.ts | 15 ++++++++++ app/components/ScrollToTop.ts | 5 +++- app/index.tsx | 6 ++-- 5 files changed, 61 insertions(+), 32 deletions(-) delete mode 100644 app/components/MobileScrollWrapper.tsx create mode 100644 app/components/PageScroll.tsx create mode 100644 app/components/ScrollContext.ts diff --git a/app/components/MobileScrollWrapper.tsx b/app/components/MobileScrollWrapper.tsx deleted file mode 100644 index c4864470e..000000000 --- a/app/components/MobileScrollWrapper.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from "react"; -import styled from "styled-components"; -import useMediaQuery from "~/hooks/useMediaQuery"; -import useMobile from "~/hooks/useMobile"; - -type Props = { - children: React.ReactNode; -}; - -const MobileWrapper = styled.div` - width: 100vw; - height: 100vh; - overflow: auto; - -webkit-overflow-scrolling: touch; -`; - -const MobileScrollWrapper = ({ children }: Props) => { - const isMobile = useMobile(); - const isPrinting = useMediaQuery("print"); - - return isMobile && !isPrinting ? ( - {children} - ) : ( - <>{children} - ); -}; - -export default MobileScrollWrapper; diff --git a/app/components/PageScroll.tsx b/app/components/PageScroll.tsx new file mode 100644 index 000000000..731f3f62f --- /dev/null +++ b/app/components/PageScroll.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; +import styled from "styled-components"; +import useMediaQuery from "~/hooks/useMediaQuery"; +import useMobile from "~/hooks/useMobile"; +import ScrollContext from "./ScrollContext"; + +type Props = { + children: React.ReactNode; +}; + +const MobileWrapper = styled.div` + width: 100vw; + height: 100vh; + overflow: auto; + -webkit-overflow-scrolling: touch; +`; + +/** + * A component that wraps its children in a scrollable container on mobile devices. + * This allows us to place a fixed toolbar at the bottom of the page in the document + * editor, which would otherwise be obscured by the on-screen keyboard. + * + * On desktop devices, the children are rendered directly without any wrapping. + */ +const PageScroll = ({ children }: Props) => { + const isMobile = useMobile(); + const isPrinting = useMediaQuery("print"); + const ref = React.useRef(null); + + return isMobile && !isPrinting ? ( + + {children} + + ) : ( + <>{children} + ); +}; + +export default PageScroll; diff --git a/app/components/ScrollContext.ts b/app/components/ScrollContext.ts new file mode 100644 index 000000000..d01b9d5c4 --- /dev/null +++ b/app/components/ScrollContext.ts @@ -0,0 +1,15 @@ +import * as React from "react"; + +/** + * Context to provide a reference to the scrollable container + */ +const ScrollContext = React.createContext< + React.RefObject | undefined +>(undefined); + +/** + * Hook to get the scrollable container reference + */ +export const useScrollContext = () => React.useContext(ScrollContext); + +export default ScrollContext; diff --git a/app/components/ScrollToTop.ts b/app/components/ScrollToTop.ts index d9ffbe464..4d76d406c 100644 --- a/app/components/ScrollToTop.ts +++ b/app/components/ScrollToTop.ts @@ -2,6 +2,7 @@ import * as React from "react"; import { useLocation } from "react-router-dom"; import usePrevious from "~/hooks/usePrevious"; +import { useScrollContext } from "./ScrollContext"; type Props = { children: JSX.Element; @@ -10,6 +11,7 @@ type Props = { export default function ScrollToTop({ children }: Props) { const location = useLocation<{ retainScrollPosition?: boolean }>(); const previousLocationPathname = usePrevious(location.pathname); + const scrollContainerRef = useScrollContext(); React.useEffect(() => { if ( @@ -25,8 +27,9 @@ export default function ScrollToTop({ children }: Props) { ) { return; } - window.scrollTo(0, 0); + (scrollContainerRef?.current || window).scrollTo(0, 0); }, [ + scrollContainerRef, location.pathname, previousLocationPathname, location.state?.retainScrollPosition, diff --git a/app/index.tsx b/app/index.tsx index bfd845f8b..406b37d6b 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -20,7 +20,7 @@ import env from "~/env"; import { initI18n } from "~/utils/i18n"; import Desktop from "./components/DesktopEventHandler"; import LazyPolyfill from "./components/LazyPolyfills"; -import MobileScrollWrapper from "./components/MobileScrollWrapper"; +import PageScroll from "./components/PageScroll"; import Routes from "./routes"; import Logger from "./utils/Logger"; import history from "./utils/history"; @@ -61,7 +61,7 @@ if (element) { - + @@ -69,7 +69,7 @@ if (element) { - +