Skip to content

Commit

Permalink
Allow the Modal component to block scrolling on any HTML element in…
Browse files Browse the repository at this point in the history
…stead of just the `<body>` element (#472)
  • Loading branch information
mbohal authored and adamkudrna committed Sep 11, 2023
1 parent d7792e2 commit 1503bd6
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 31 deletions.
10 changes: 5 additions & 5 deletions src/components/Modal/Modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Modal.defaultProps = {
closeButtonRef: null,
portalId: null,
position: 'center',
preventScrollUnderneath: 'default',
preventScrollUnderneath: window.document.body,
primaryButtonRef: null,
size: 'medium',
};
Expand Down Expand Up @@ -137,15 +137,15 @@ Modal.propTypes = {
position: PropTypes.oneOf(['top', 'center']),
/**
* Mode in which Modal prevents scroll of elements bellow:
* * `default` - Modal prevents scroll on the `body` element
* * `off` - Modal does not prevent any scroll
* * [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) - Modal prevents scroll on this HTML element
* * object
* * * `reset` - method called on Modal's unmount to reset scroll prevention
* * * `start` - method called on Modal's mount to custom scroll prevention
* * `reset` - method called on Modal's unmount to reset scroll prevention
* * `start` - method called on Modal's mount to custom scroll prevention
*/
preventScrollUnderneath: PropTypes.oneOfType([
PropTypes.oneOf([
'default',
HTMLElement,
'off',
]),
PropTypes.shape({
Expand Down
90 changes: 72 additions & 18 deletions src/components/Modal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch modal"
onClick={() => setModalOpen(true)}
Expand Down Expand Up @@ -66,7 +72,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand Down Expand Up @@ -116,7 +122,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch blocking modal without title"
onClick={() => {
Expand Down Expand Up @@ -235,7 +247,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand Down Expand Up @@ -265,7 +277,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch with close button"
onClick={() => {
Expand Down Expand Up @@ -356,7 +374,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand Down Expand Up @@ -393,7 +411,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch modal with footer variants"
onClick={() => setModalOpen(true)}
Expand Down Expand Up @@ -493,7 +517,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand All @@ -510,7 +534,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch small modal"
onClick={() => {
Expand Down Expand Up @@ -575,7 +605,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand All @@ -588,7 +618,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch auto-width modal"
onClick={() => setModalOpen(true)}
Expand Down Expand Up @@ -629,7 +665,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand All @@ -650,7 +686,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch auto-with modal with a form"
onClick={() => setModalOpen(true)}
Expand Down Expand Up @@ -692,7 +734,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand All @@ -708,7 +750,13 @@ React.createElement(() => {
const modalPrimaryButtonRef = React.useRef();
const modalCloseButtonRef = React.useRef();
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch modal at center"
onClick={() => {
Expand Down Expand Up @@ -759,7 +807,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand Down Expand Up @@ -884,7 +932,13 @@ React.createElement(() => {
</ModalContent>
)
return (
<>
{/*
The `preventScrollUnderneath` feature is necessary for Modals to work in
React UI docs. You may not need it in your application.
*/}
<RUIProvider globalProps={{
Modal: { preventScrollUnderneath: window.document.documentElement }
}}>
<Button
label="Launch modal with scrolling body"
onClick={() => {
Expand Down Expand Up @@ -945,7 +999,7 @@ React.createElement(() => {
</Modal>
)}
</div>
</>
</RUIProvider>
);
});
```
Expand Down
18 changes: 10 additions & 8 deletions src/components/Modal/_hooks/useModalScrollPrevention.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ export const useModalScrollPrevention = (preventScrollUnderneath) => {
return () => {};
}

if (preventScrollUnderneath === 'default') {
if (preventScrollUnderneath instanceof HTMLElement) {
const scrollableElement = preventScrollUnderneath;

const scrollbarWidth = Math.abs(window.innerWidth - window.document.documentElement.clientWidth);
const prevOverflow = window.document.body.style.overflow;
const prevPaddingRight = window.document.body.style.paddingRight;
const prevOverflow = scrollableElement.style.overflow;
const prevPaddingRight = scrollableElement.style.paddingRight;

window.document.body.style.overflow = 'hidden';
scrollableElement.style.overflow = 'hidden';

if (Number.isNaN(parseInt(prevPaddingRight, 10))) {
window.document.body.style.paddingRight = `${scrollbarWidth}px`;
scrollableElement.style.paddingRight = `${scrollbarWidth}px`;
} else {
window.document.body.style.paddingRight = `calc(${prevPaddingRight} + ${scrollbarWidth}px)`;
scrollableElement.style.paddingRight = `calc(${prevPaddingRight} + ${scrollbarWidth}px)`;
}

return () => {
window.document.body.style.overflow = prevOverflow;
window.document.body.style.paddingRight = prevPaddingRight;
scrollableElement.style.overflow = prevOverflow;
scrollableElement.style.paddingRight = prevPaddingRight;
};
}

Expand Down

0 comments on commit 1503bd6

Please sign in to comment.