From a4c7324d749ac049c9b7711d72612b85a7712aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:54:27 +0000 Subject: [PATCH] fix(island-ui): Filter Dialog - Scroll doesn't work on iOS (#15896) * Fix mobile scroll * Keep track of scroll position * Move hook into separate file * useRef instead of global variable --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/island-ui/core/src/lib/Filter/Filter.tsx | 10 +++-- .../src/lib/Filter/usePreventBodyScroll.ts | 37 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 libs/island-ui/core/src/lib/Filter/usePreventBodyScroll.ts diff --git a/libs/island-ui/core/src/lib/Filter/Filter.tsx b/libs/island-ui/core/src/lib/Filter/Filter.tsx index ac7db7fba7bd..692b369ebabd 100644 --- a/libs/island-ui/core/src/lib/Filter/Filter.tsx +++ b/libs/island-ui/core/src/lib/Filter/Filter.tsx @@ -6,6 +6,8 @@ import { Button } from '../Button/Button' import { Inline } from '../Inline/Inline' import { Stack } from '../Stack/Stack' import { Text } from '../Text/Text' +import { usePreventBodyScroll } from './usePreventBodyScroll' + import * as styles from './Filter.css' export interface FilterProps { @@ -52,7 +54,7 @@ export interface FilterProps { /** * Datatype to use for Filter context. * Provides the Filter's childs access to shared values, - * like the `isDialog` state with out bloating the childs props. + * like the `isDialog` state without bloating the childs props. */ interface FilterContextValue { variant?: FilterProps['variant'] @@ -78,7 +80,7 @@ export const Filter: FC> = ({ children, popoverFlip = true, }) => { - const dialog = useDialogState() + const dialog = useDialogState({ modal: true }) const popover = usePopoverState({ placement: 'bottom-start', unstable_flip: popoverFlip, @@ -87,6 +89,8 @@ export const Filter: FC> = ({ const hasFilterInput = !!filterInput + usePreventBodyScroll(dialog.visible && variant === 'dialog') + return ( {variant === 'popover' && ( @@ -171,7 +175,7 @@ export const Filter: FC> = ({ /> - + { + const initialBodyPosition = useRef(null) + const initialScrollPosition = useRef(null) + + useEffect(() => { + const isBrowser = typeof window !== 'undefined' + if (!isBrowser || !preventBodyScroll) { + return + } + + if (initialBodyPosition.current === null) { + initialBodyPosition.current = + window.document.body.style.position || 'static' + } + if (initialScrollPosition.current === null) { + initialScrollPosition.current = window.scrollY + } + + // Prevent scrolling on the body element + window.document.body.style.position = 'fixed' + + return () => { + if (initialBodyPosition.current !== null) { + window.document.body.style.position = initialBodyPosition.current + initialBodyPosition.current = null + } + if (initialScrollPosition.current !== null) { + // When setting the body position to fixed, the scroll position resets to 0 + // Here we are restoring the scroll position + window.scrollTo(0, initialScrollPosition.current) + initialScrollPosition.current = null + } + } + }, [preventBodyScroll]) +}