diff --git a/.changeset/three-years-visit.md b/.changeset/three-years-visit.md new file mode 100644 index 00000000000..909567a3cf9 --- /dev/null +++ b/.changeset/three-years-visit.md @@ -0,0 +1,5 @@ +--- +"@salt-ds/core": minor +--- + +Added `disableScroll` prop to FloatingComponent. diff --git a/packages/core/src/__tests__/__e2e__/utils/useFloatingUI.cy.tsx b/packages/core/src/__tests__/__e2e__/utils/useFloatingUI.cy.tsx index e2d89260d73..0897a2684de 100644 --- a/packages/core/src/__tests__/__e2e__/utils/useFloatingUI.cy.tsx +++ b/packages/core/src/__tests__/__e2e__/utils/useFloatingUI.cy.tsx @@ -11,11 +11,13 @@ const TestComponent = ({ contentId = "test-1-content", focusManager, open = true, + disableScroll = false, }: { id?: string; contentId?: string; focusManager?: boolean; open?: boolean; + disableScroll?: boolean; }) => { const { Component: FloatingComponent } = useFloatingComponent(); const { context } = useFloatingUI({ @@ -26,6 +28,7 @@ const TestComponent = ({
@@ -84,4 +87,28 @@ describe("Use useFloatingComponent", () => { ); }); }); + describe("without disableScroll", () => { + it("the document body should not have hidden overflow", () => { + mount( + + + , + ); + + cy.document().its("documentElement.style.overflow").should("equal", ""); + }); + }); + describe("with disableScroll", () => { + it("the document body should have hidden overflow", () => { + mount( + + + , + ); + + cy.document() + .its("documentElement.style.overflow") + .should("equal", "hidden"); + }); + }); }); diff --git a/packages/core/src/dialog/Dialog.tsx b/packages/core/src/dialog/Dialog.tsx index 7e1b546146f..72b3a1e2101 100644 --- a/packages/core/src/dialog/Dialog.tsx +++ b/packages/core/src/dialog/Dialog.tsx @@ -148,6 +148,7 @@ export const Dialog = forwardRef( ref={floatingRef} width={elements.floating?.offsetWidth} height={elements.floating?.offsetHeight} + disableScroll={true} focusManagerProps={{ context: context, initialFocus, diff --git a/packages/core/src/utils/useFloatingUI/useFloatingUI.tsx b/packages/core/src/utils/useFloatingUI/useFloatingUI.tsx index 1f6a45e358d..ce7eb7e4244 100644 --- a/packages/core/src/utils/useFloatingUI/useFloatingUI.tsx +++ b/packages/core/src/utils/useFloatingUI/useFloatingUI.tsx @@ -13,12 +13,14 @@ import { shift, useFloating, } from "@floating-ui/react"; +import { useWindow } from "@salt-ds/window"; import { type ComponentPropsWithoutRef, type ReactNode, createContext, forwardRef, useContext, + useEffect, useMemo, } from "react"; import { SaltProvider, SaltProviderNext, useTheme } from "../../salt-provider"; @@ -45,6 +47,10 @@ export interface FloatingComponentProps width?: number; height?: number; position?: Strategy; + /** + * Disables scrolling the document body while the floating component is open. + */ + disableScroll?: boolean; } const DefaultFloatingComponent = forwardRef< @@ -59,6 +65,7 @@ const DefaultFloatingComponent = forwardRef< width, height, focusManagerProps, + disableScroll, ...rest } = props; const style = { @@ -69,8 +76,24 @@ const DefaultFloatingComponent = forwardRef< const { themeNext } = useTheme(); + const targetWindow = useWindow(); + const ChosenSaltProvider = themeNext ? SaltProviderNext : SaltProvider; + useEffect(() => { + if (!disableScroll || !targetWindow) { + return; + } + if (open) { + targetWindow.document.documentElement.style.overflow = "hidden"; + } else { + targetWindow.document.documentElement.style.removeProperty("overflow"); + } + return () => { + targetWindow.document.documentElement.style.removeProperty("overflow"); + }; + }, [disableScroll, open, targetWindow]); + if (focusManagerProps && open) { return (