Skip to content

Commit b0e9d5e

Browse files
committed
fix: unlock scrolling when radix select dropdown open
1 parent bf3d01c commit b0e9d5e

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

src/components/Layout/LanguageSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { useEffect, useMemo, useState } from 'react';
22
import { useLocation } from '@reach/router';
3-
import * as Select from '@radix-ui/react-select';
43
import Badge from '@ably/ui/core/Badge';
54
import Icon from '@ably/ui/core/Icon';
65
import { IconName } from '@ably/ui/core/Icon/types';
@@ -12,6 +11,7 @@ import { LanguageKey } from 'src/data/languages/types';
1211
import { useLayoutContext } from 'src/contexts/layout-context';
1312
import { navigate } from '../Link';
1413
import { LANGUAGE_SELECTOR_HEIGHT, INKEEP_ASK_BUTTON_HEIGHT } from './utils/heights';
14+
import * as Select from '../ui/Select';
1515
import { Skeleton } from '../ui/Skeleton';
1616

1717
type LanguageSelectorOptionData = {
@@ -92,7 +92,7 @@ export const LanguageSelector = () => {
9292

9393
<Select.Portal>
9494
<Select.Content
95-
className="overflow-hidden bg-neutral-000 shadow dark:bg-neutral-1300 border border-neutral-300 dark:border-neutral-1000 rounded-lg ui-shadow-sm-soft z-50"
95+
className="overflow-hidden bg-neutral-000 shadow dark:bg-neutral-1300 border border-neutral-300 dark:border-neutral-1000 rounded-lg ui-shadow-sm-soft z-40"
9696
position="popper"
9797
align="end"
9898
sideOffset={4}

src/components/ui/Select.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useLayoutEffect } from 'react';
2+
import * as RadixSelect from '@radix-ui/react-select';
3+
4+
// Radix Select insists on locking scrolling when the dropdown is open. This is a workaround to unlock scrolling.
5+
const useScrollUnlock = () => {
6+
useLayoutEffect(() => {
7+
const TYPE_LIST = ['wheel', 'scroll', 'touchmove'] as const;
8+
const stopper = (e: Event) => e.stopImmediatePropagation();
9+
10+
const unlockScroll = () => {
11+
TYPE_LIST.forEach((type) => {
12+
window.addEventListener(type, stopper, { capture: true, passive: false });
13+
});
14+
document.body.removeAttribute('data-scroll-locked');
15+
};
16+
17+
const cleanupListeners = () => {
18+
TYPE_LIST.forEach((type) => {
19+
window.removeEventListener(type, stopper, { capture: true });
20+
});
21+
};
22+
23+
const mo = new MutationObserver((mutations) => {
24+
// Only react if data-scroll-locked was added
25+
const wasLocked = mutations.some(
26+
(m) => m.type === 'attributes' && document.body.hasAttribute('data-scroll-locked'),
27+
);
28+
if (wasLocked) {
29+
cleanupListeners();
30+
unlockScroll();
31+
}
32+
});
33+
34+
mo.observe(document.body, { attributes: true, attributeFilter: ['data-scroll-locked'] });
35+
36+
return () => {
37+
mo.disconnect();
38+
cleanupListeners();
39+
};
40+
}, []);
41+
};
42+
43+
const Root = (props: RadixSelect.SelectProps) => {
44+
useScrollUnlock();
45+
return <RadixSelect.Root {...props} />;
46+
};
47+
48+
// Re-export everything from Radix Select, then override Root
49+
export * from '@radix-ui/react-select';
50+
export { Root };

0 commit comments

Comments
 (0)