Skip to content

Commit 47971e8

Browse files
authored
feat: preserve scroll position (#1424)
1 parent 28b6c8f commit 47971e8

File tree

2 files changed

+32
-2
lines changed

2 files changed

+32
-2
lines changed

src/Dialog/Dialog.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ class Dialog extends Component {
2727
// select body element to add Dialog component too
2828
// eslint-disable-next-line compat/compat
2929
bodyElm = document.querySelector('body');
30+
scrollX = 0
31+
scrollY = 0
32+
33+
// restore scroll position
34+
restoreOriginalScrollPosition = () => {
35+
if (this.props.focusElementOnClose?.focus) {
36+
this.props.focusElementOnClose.focus();
37+
} else {
38+
document.querySelector('html')?.scroll({
39+
top: this.scrollY,
40+
left: this.scrollX
41+
});
42+
}
43+
}
3044

3145
handleCloseClick = (e) => {
3246
this.props.onClose(e);
@@ -36,19 +50,32 @@ class Dialog extends Component {
3650
handleKeyPress = event => {
3751
if (event.key === 'Escape' || event.key === 'Esc') {
3852
this.handleCloseClick(event);
53+
this.restoreOriginalScrollPosition();
3954
}
4055
};
4156

42-
// add event listener for escape key
57+
// add event listener for escape key, save scroll position
4358
componentDidMount() {
4459
document.addEventListener('keydown', this.handleKeyPress, false);
60+
if (this.props.show) {
61+
this.scrollX = window.scrollX;
62+
this.scrollY = window.scrollY;
63+
}
4564
}
4665

4766
// remove event listener for escape key
4867
componentWillUnmount() {
4968
document.removeEventListener('keydown', this.handleKeyPress, false);
5069
}
5170

71+
componentDidUpdate(prevProps) {
72+
if (!prevProps.show && this.props.show ) {
73+
this.scrollX = window.scrollX;
74+
this.scrollY = window.scrollY;
75+
}
76+
}
77+
78+
5279
render() {
5380
const {
5481
actions,
@@ -149,7 +176,7 @@ class Dialog extends Component {
149176
<div className={classnames(`${cssNamespace}-bar__right`)}>
150177
{React.Children.toArray(actions).map((child, index) => (
151178
<div className={classnames(`${cssNamespace}-bar__element`)} key={index}>
152-
{React.cloneElement(child, { className: classnames(`${cssNamespace}-dialog__decisive-button`), onClick: disableAutoClose ? child.props?.onClick : chain(this.handleCloseClick, child.props?.onClick) })}
179+
{React.cloneElement(child, { className: classnames(`${cssNamespace}-dialog__decisive-button`), onClick: disableAutoClose ? chain(this.restoreOriginalScrollPosition, child.props?.onClick) : chain(this.handleCloseClick, this.restoreOriginalScrollPosition, child.props?.onClick) })}
153180
</div>
154181
))}
155182
</div>
@@ -184,6 +211,8 @@ Dialog.propTypes = {
184211
/** Additional props to disable auto closing dialog */
185212
disableAutoClose: PropTypes.bool,
186213
/** Additional props to be spread to the footer of the dialog */
214+
focusElementOnClose: PropTypes.object,
215+
/** Additional props containing an HTMLElement to be focused when the dialog closes. The element must be focusable. */
187216
footerProps: PropTypes.object,
188217
/** Text or Custom React node for the components header */
189218
header: PropTypes.oneOfType([

src/Dialog/Dialog.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Dialog from './Dialog';
33
import { mount } from 'enzyme';
44
import React from 'react';
55

6+
window.HTMLElement.prototype.scroll = () => {};
67
describe('<Dialog />', () => {
78
const mockOnClose = jest.fn();
89
const bShow = true;

0 commit comments

Comments
 (0)