From 62d9a0eebf2ffa5e926845cd8549d767a99d41cb Mon Sep 17 00:00:00 2001 From: dev-aly3n Date: Mon, 12 Feb 2024 00:02:04 +0330 Subject: [PATCH] feat(useFreezeScroll): add new hook & test & doc --- packages/hooks/src/index.ts | 2 + .../useFreezeScroll/__tests__/index.test.ts | 45 ++++++++++++++++++ .../hooks/src/useFreezeScroll/index.en-US.md | 46 +++++++++++++++++++ packages/hooks/src/useFreezeScroll/index.ts | 17 +++++++ 4 files changed, 110 insertions(+) create mode 100644 packages/hooks/src/useFreezeScroll/__tests__/index.test.ts create mode 100644 packages/hooks/src/useFreezeScroll/index.en-US.md create mode 100644 packages/hooks/src/useFreezeScroll/index.ts diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 58cefd9c79..98a58657b8 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -75,6 +75,7 @@ import useVirtualList from './useVirtualList'; import useWebSocket from './useWebSocket'; import useWhyDidYouUpdate from './useWhyDidYouUpdate'; import useMutationObserver from './useMutationObserver'; +import useFreezeScroll from './useFreezeScroll'; export { useRequest, @@ -156,4 +157,5 @@ export { useRafTimeout, useResetState, useMutationObserver, + useFreezeScroll, }; diff --git a/packages/hooks/src/useFreezeScroll/__tests__/index.test.ts b/packages/hooks/src/useFreezeScroll/__tests__/index.test.ts new file mode 100644 index 0000000000..b647eee0a0 --- /dev/null +++ b/packages/hooks/src/useFreezeScroll/__tests__/index.test.ts @@ -0,0 +1,45 @@ +import { renderHook } from '@testing-library/react'; +import { useFreezeScroll } from '../index'; + +// Mock window.innerWidth and document.documentElement properties +const originalWindowInnerWidth = window.innerWidth; +const originalDocumentElementClientWidth = document.documentElement.clientWidth; + +beforeAll(() => { + Object.defineProperty(window, 'innerWidth', { writable: true, value: 805 }); // Set initial innerWidth + Object.defineProperty(document.documentElement, 'clientWidth', { writable: true, value: 800 }); // Set initial clientWidth +}); + +afterAll(() => { + Object.defineProperty(window, 'innerWidth', { writable: true, value: originalWindowInnerWidth }); + Object.defineProperty(document.documentElement, 'clientWidth', { + writable: true, + value: originalDocumentElementClientWidth, + }); +}); + +describe('useFreezeScroll', () => { + it('should remove scrollbar and freeze scroll when isActive is true', () => { + const { unmount } = renderHook(() => useFreezeScroll(true)); + + expect(document.documentElement.style.overflow).toBe('hidden'); + expect(document.documentElement.style.marginRight).toBe('5px'); // Since we mocked innerWidth and clientWidth 805 - 800 = 5px + unmount(); + }); + + it('should restore scrollbar and scroll when isActive becomes false', () => { + const { rerender, unmount } = renderHook(({ isActive }) => useFreezeScroll(isActive), { + initialProps: { isActive: true }, + }); + + expect(document.documentElement.style.overflow).toBe('hidden'); + expect(document.documentElement.style.marginRight).toBe('5px'); + + rerender({ isActive: false }); + + expect(document.documentElement.style.overflow).toBe('visible'); + expect(document.documentElement.style.marginRight).toBe('0px'); + + unmount(); + }); +}); diff --git a/packages/hooks/src/useFreezeScroll/index.en-US.md b/packages/hooks/src/useFreezeScroll/index.en-US.md new file mode 100644 index 0000000000..12b9021054 --- /dev/null +++ b/packages/hooks/src/useFreezeScroll/index.en-US.md @@ -0,0 +1,46 @@ +--- +nav: + path: /hooks +--- + +# useFreezeScroll + +React hook that freezes the scroll and removes the scrollbar from the browser window when it is active without any layout shift, for example, when a modal is opened. It doesn't work on fixed position elements. + +## Examples + +### Default usage + +```jsx +import { useFreezeScroll } from 'ahooks'; +import React, { useState } from 'react'; + +export default () => { + const [isActive, setIsActive] = useState(false); + useFreezeScroll(isActive); + + return ( +
+
+

Scroll should be frozen when enabled.

+

Try scrolling to test the effect.

+
+ +
+ ); +}; +``` + +## API + +```javascript +useFreezeScroll(isActive) +``` + +### Params + +| Property | Description | Type | Default | +| -------- | -------------- | ------- | ------- | +| isActive | state of modal | boolean | - | diff --git a/packages/hooks/src/useFreezeScroll/index.ts b/packages/hooks/src/useFreezeScroll/index.ts new file mode 100644 index 0000000000..4b3ec3009a --- /dev/null +++ b/packages/hooks/src/useFreezeScroll/index.ts @@ -0,0 +1,17 @@ +import { useEffect } from 'react'; + +const useFreezeScroll = (isActive: boolean) => { + useEffect(() => { + if (!isActive) return; + const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; + document.documentElement.style.marginRight = scrollbarWidth + 'px'; + document.documentElement.style.overflow = 'hidden'; + + return () => { + document.documentElement.style.overflow = 'visible'; + document.documentElement.style.marginRight = '0'; + }; + }, [isActive]); +}; + +export default useFreezeScroll;