Skip to content

Commit d3a85a9

Browse files
authored
Fix scroll-lock caused by modal (#2403)
1 parent d6511aa commit d3a85a9

File tree

2 files changed

+29
-8
lines changed

2 files changed

+29
-8
lines changed

.changeset/loud-penguins-type.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sumup/circuit-ui': patch
3+
---
4+
5+
Fixed a bug where users were unable to scroll after a modal was mounted and immediately unmounted.

packages/circuit-ui/components/ModalContext/ModalContext.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,30 @@ import { warn } from '../../util/logger.js';
2828
import { BaseModalProps, ModalComponent } from './types.js';
2929
import './Modal.css';
3030

31+
const PORTAL_CLASS_NAME = 'cui-modal-portal';
32+
const HTML_OPEN_CLASS_NAME = 'cui-modal-open';
33+
// These are the default app element ids in Next.js, Docusaurus, CRA and Storybook.
34+
const APP_ELEMENT_IDS = ['root', '__next', '__docusaurus', 'storybook-root'];
35+
36+
function getAppElement(): HTMLElement | null {
37+
// eslint-disable-next-line no-restricted-syntax
38+
for (const id of APP_ELEMENT_IDS) {
39+
const element = document.getElementById(id);
40+
41+
if (element) {
42+
return element;
43+
}
44+
}
45+
return null;
46+
}
47+
3148
// It is important for users of screen readers that other page content be hidden
3249
// (via the `aria-hidden` attribute) while the modal is open.
3350
// To allow react-modal to do this, Circuit UI calls `Modal.setAppElement`
3451
// with a query selector identifying the root of the app.
3552
// http://reactcommunity.org/react-modal/accessibility/#app-element
3653
if (typeof window !== 'undefined') {
37-
// These are the default app elements in Next.js, Docusaurus, CRA and Storybook.
38-
const appElement =
39-
document.getElementById('__next') ||
40-
document.getElementById('__docusaurus') ||
41-
document.getElementById('root') ||
42-
document.getElementById('storybook-root');
54+
const appElement = getAppElement();
4355

4456
if (appElement) {
4557
ReactModal.setAppElement(appElement);
@@ -119,6 +131,10 @@ export function ModalProvider<TProps extends BaseModalProps>({
119131

120132
useEffect(() => {
121133
if (!activeModal) {
134+
// Clean up after react-modal in case it fails to do so itself
135+
// https://github.com/reactjs/react-modal/issues/888#issuecomment-1158061329
136+
document.documentElement.classList.remove(HTML_OPEN_CLASS_NAME);
137+
getAppElement()?.removeAttribute('aria-hidden');
122138
return undefined;
123139
}
124140

@@ -159,8 +175,8 @@ export function ModalProvider<TProps extends BaseModalProps>({
159175
key={id}
160176
isOpen={!transition}
161177
onClose={() => removeModal(modal)}
162-
portalClassName="cui-modal-portal"
163-
htmlOpenClassName="cui-modal-open"
178+
portalClassName={PORTAL_CLASS_NAME}
179+
htmlOpenClassName={HTML_OPEN_CLASS_NAME}
164180
bodyOpenClassName=""
165181
/>
166182
);

0 commit comments

Comments
 (0)