Skip to content

Commit

Permalink
feat(useFreezeScroll): add new hook & test & doc
Browse files Browse the repository at this point in the history
  • Loading branch information
dev-aly3n committed Feb 11, 2024
1 parent 95409be commit 62d9a0e
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -156,4 +157,5 @@ export {
useRafTimeout,
useResetState,
useMutationObserver,
useFreezeScroll,
};
45 changes: 45 additions & 0 deletions packages/hooks/src/useFreezeScroll/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -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();
});
});
46 changes: 46 additions & 0 deletions packages/hooks/src/useFreezeScroll/index.en-US.md
Original file line number Diff line number Diff line change
@@ -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 (
<div style={{ height: '300px' }}>
<div style={{ marginTop: '20px' }}>
<p>Scroll should be frozen when enabled.</p>
<p>Try scrolling to test the effect.</p>
</div>
<button onClick={() => setIsActive(!isActive)} style={{ position: 'sticky', top: '100px' }}>
{isActive ? 'Disable Freeze Scroll' : 'Enable Freeze Scroll'}
</button>
</div>
);
};
```

## API

```javascript
useFreezeScroll(isActive)
```

### Params

| Property | Description | Type | Default |
| -------- | -------------- | ------- | ------- |
| isActive | state of modal | boolean | - |
17 changes: 17 additions & 0 deletions packages/hooks/src/useFreezeScroll/index.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 62d9a0e

Please sign in to comment.