diff --git a/src/components/dialog/Dialog.stories.mdx b/src/components/dialog/Dialog.stories.mdx index deb957646..92765961d 100644 --- a/src/components/dialog/Dialog.stories.mdx +++ b/src/components/dialog/Dialog.stories.mdx @@ -517,6 +517,10 @@ import {fireEvent, within} from '@testing-library/react'; const handleDismissAnother = () => { setAnotherOpen(false); }; + const handleCloseAll = () => { + setAnotherOpen(false); + setOpen(false); + }; const headerId = 'dialog-header'; const anotherHeaderId = 'another-dialog-header'; return ( @@ -583,7 +587,9 @@ import {fireEvent, within} from '@testing-library/react'; - + diff --git a/src/components/dialog/Dialog.tsx b/src/components/dialog/Dialog.tsx index b0f4df001..35136708b 100644 --- a/src/components/dialog/Dialog.tsx +++ b/src/components/dialog/Dialog.tsx @@ -106,7 +106,7 @@ function BaseDialog({ const [isDialogHigherThanOverlay, setIsDialogHigherThanOverlay] = React.useState(false); const hasAnimations = supportsTransitions() && motionPreset !== 'none'; - const cleanupBodyNoScroll = useBodyNoScroll(); + const cleanupBodyNoScroll = useBodyNoScroll(overlayRef); const fireTransitionEndCallbacks = React.useCallback(() => { setHasFinishedTransition(true); diff --git a/src/components/dialog/useBodyNoScroll.ts b/src/components/dialog/useBodyNoScroll.ts index 15ef088f5..8ce076d98 100644 --- a/src/components/dialog/useBodyNoScroll.ts +++ b/src/components/dialog/useBodyNoScroll.ts @@ -3,13 +3,24 @@ import * as React from 'react'; const NO_SCROLL_CLASS = 'sg-dialog-no-scroll'; const DIALOG_SELECTOR = '.js-dialog'; -export function useBodyNoScroll() { +export function useBodyNoScroll(overlayRef: {current: HTMLDivElement | null}) { const cleanupRef = React.useRef(null); const forceCleanup = React.useCallback(() => { if (cleanupRef.current) cleanupRef.current(); }, []); React.useEffect(() => { + // @todo Use React Context API for detecting nested components + // https://github.com/brainly/style-guide/issues/2795 + const isNestedDialog = + overlayRef.current?.parentElement?.closest(DIALOG_SELECTOR); + + if (isNestedDialog) { + // if dialog is nested, this logic was already fired by parent dialog + // it prevents an issue with no cleanup when nested dialogs + return; + } + const body = document.body; const scrollY = window.scrollY; @@ -20,8 +31,18 @@ export function useBodyNoScroll() { const cleanup = () => { // it can only be forced once cleanupRef.current = null; - const manyDialogsOpened = - document.querySelectorAll(DIALOG_SELECTOR).length > 1; + + const dialogsOpenCount = + document.querySelectorAll(DIALOG_SELECTOR).length; + const nestedOpenDialogsCount = + overlayRef.current?.querySelectorAll(DIALOG_SELECTOR).length | 0; + + // nested dialogs shouldn't be counted + // as a parent for nested dialogs, this particular one should perfrom the cleanup + const notNestedDialogsOpenCount = + dialogsOpenCount - nestedOpenDialogsCount; + + const manyDialogsOpened = notNestedDialogsOpenCount > 1; if (manyDialogsOpened) { return; @@ -34,6 +55,6 @@ export function useBodyNoScroll() { cleanupRef.current = cleanup; return cleanup; - }, []); + }, [overlayRef]); return forceCleanup; }