diff --git a/react-responsive-modal/__tests__/index.test.tsx b/react-responsive-modal/__tests__/index.test.tsx index ffd8a4f3..6b794b2d 100644 --- a/react-responsive-modal/__tests__/index.test.tsx +++ b/react-responsive-modal/__tests__/index.test.tsx @@ -260,6 +260,26 @@ describe('modal', () => { ); expect(document.body.style.overflow).toBe(''); }); + it('should reserve scroll bar gap', () => { + const scrollBarWidth = 42; + const innerWidth = 500; + Object.defineProperty(window, 'innerWidth', { + writable: true, + configurable: true, + value: innerWidth, + }); + Object.defineProperty(document.documentElement, 'clientWidth', { + writable: true, + configurable: true, + value: innerWidth - scrollBarWidth, + }); + render( + null} reserveScrollBarGap={true}> +
modal content
+
+ ); + expect(document.body.style.paddingRight).toBe(`${scrollBarWidth}px`); + }); }); describe('closeIcon', () => { diff --git a/react-responsive-modal/package.json b/react-responsive-modal/package.json index 95addf30..de56e8f6 100644 --- a/react-responsive-modal/package.json +++ b/react-responsive-modal/package.json @@ -47,11 +47,11 @@ "size-limit": [ { "path": "dist/react-responsive-modal.cjs.production.min.js", - "limit": "4.0 KB" + "limit": "4.1 KB" }, { "path": "dist/react-responsive-modal.esm.js", - "limit": "4.0 KB" + "limit": "4.1 KB" } ], "dependencies": { diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index b06ddc41..cadd74ac 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -128,6 +128,10 @@ export interface ModalProps { * ARIA description for modal */ ariaDescribedby?: string; + /** + * Avoid unpleasant flickering effect when body overflow is hidden. For more information see https://www.npmjs.com/package/body-scroll-lock + */ + reserveScrollBarGap?: boolean; /** * id attribute for modal */ @@ -179,6 +183,7 @@ export const Modal = React.forwardRef( onOverlayClick, onAnimationEnd, children, + reserveScrollBarGap, }: ModalProps, ref: React.ForwardedRef ) => { @@ -200,7 +205,7 @@ export const Modal = React.forwardRef( useModalManager(refModal, open); // Hook used to manage the scroll - useScrollLock(refModal, open, showPortal, blockScroll); + useScrollLock(refModal, open, showPortal, blockScroll, reserveScrollBarGap); const handleOpen = () => { if ( diff --git a/react-responsive-modal/src/useScrollLock.ts b/react-responsive-modal/src/useScrollLock.ts index 810905c1..37e52f85 100644 --- a/react-responsive-modal/src/useScrollLock.ts +++ b/react-responsive-modal/src/useScrollLock.ts @@ -5,14 +5,15 @@ export const useScrollLock = ( refModal: React.RefObject, open: boolean, showPortal: boolean, - blockScroll: boolean + blockScroll: boolean, + reserveScrollBarGap?: boolean ) => { const oldRef = useRef(null); useEffect(() => { if (open && refModal.current && blockScroll) { oldRef.current = refModal.current; - disableBodyScroll(refModal.current); + disableBodyScroll(refModal.current, { reserveScrollBarGap }); } return () => { if (oldRef.current) { @@ -20,5 +21,5 @@ export const useScrollLock = ( oldRef.current = null; } }; - }, [open, showPortal, refModal]); + }, [open, showPortal, refModal, blockScroll, reserveScrollBarGap]); };