Skip to content

Commit b35d5c0

Browse files
authored
Properly restore original page scrollability for DialogContainer case (#3784)
* Properly restore original page scrollibility when opening a dialog container from a menu item * adding test * addressing review comments * address review comments store returned cleanup functions from preventScrollMobileSafari/preventScrollStandard in global var so that the final unmounting overlay usePreventScroll clean up call can properly revert any applied body scroll styles
1 parent 810579b commit b35d5c0

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

packages/@react-aria/overlays/src/usePreventScroll.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const nonTextInputTypes = new Set([
3333
'reset'
3434
]);
3535

36+
// The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position
37+
let preventScrollCount = 0;
38+
let restore;
39+
3640
/**
3741
* Prevents scrolling on the document body on mount, and
3842
* restores it on unmount. Also ensures that content does not
@@ -46,11 +50,21 @@ export function usePreventScroll(options: PreventScrollOptions = {}) {
4650
return;
4751
}
4852

49-
if (isIOS()) {
50-
return preventScrollMobileSafari();
51-
} else {
52-
return preventScrollStandard();
53+
preventScrollCount++;
54+
if (preventScrollCount === 1) {
55+
if (isIOS()) {
56+
restore = preventScrollMobileSafari();
57+
} else {
58+
restore = preventScrollStandard();
59+
}
5360
}
61+
62+
return () => {
63+
preventScrollCount--;
64+
if (preventScrollCount === 0) {
65+
restore();
66+
}
67+
};
5468
}, [isDisabled]);
5569
}
5670

@@ -183,6 +197,7 @@ function preventScrollMobileSafari() {
183197
// enable us to scroll the window to the top, which is required for the rest of this to work.
184198
let scrollX = window.pageXOffset;
185199
let scrollY = window.pageYOffset;
200+
186201
let restoreStyles = chain(
187202
setStyle(document.documentElement, 'paddingRight', `${window.innerWidth - document.documentElement.clientWidth}px`),
188203
setStyle(document.documentElement, 'overflow', 'hidden'),
@@ -212,6 +227,7 @@ function preventScrollMobileSafari() {
212227
function setStyle(element: HTMLElement, style: string, value: string) {
213228
let cur = element.style[style];
214229
element.style[style] = value;
230+
215231
return () => {
216232
element.style[style] = cur;
217233
};

packages/@react-aria/overlays/test/usePreventScroll.test.js

+16
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ describe('usePreventScroll', function () {
4848
expect(document.documentElement).not.toHaveStyle('overflow: hidden');
4949
});
5050

51+
it('should work with nested/multiple modals regardless of unmount order', function () {
52+
expect(document.documentElement).not.toHaveStyle('overflow: hidden');
53+
54+
let one = render(<Example />);
55+
expect(document.documentElement).toHaveStyle('overflow: hidden');
56+
57+
let two = render(<Example />);
58+
expect(document.documentElement).toHaveStyle('overflow: hidden');
59+
60+
one.unmount();
61+
expect(document.documentElement).toHaveStyle('overflow: hidden');
62+
63+
two.unmount();
64+
expect(document.documentElement).not.toHaveStyle('overflow: hidden');
65+
});
66+
5167
it('should remove overflow: hidden when isDisabled option is true', function () {
5268
expect(document.documentElement).not.toHaveStyle('overflow: hidden');
5369

0 commit comments

Comments
 (0)