Skip to content

Commit 206ead1

Browse files
authored
feat: Warn when leaving data browser page with selected rows (#2887)
1 parent 1c94668 commit 206ead1

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/components/BrowserRow/BrowserRow.react.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export default class BrowserRow extends Component {
9292
>
9393
<input
9494
type="checkbox"
95-
checked={selection['*'] || selection[obj.id]}
95+
checked={!!selection['*'] || !!selection[obj.id]}
9696
onChange={e => selectRow(obj.id, e.target.checked)}
9797
onMouseDown={e => onMouseDownRowCheckBox(e.target.checked)}
9898
/>

src/dashboard/Data/Browser/Browser.react.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,86 @@ import subscribeTo from 'lib/subscribeTo';
3838
import * as ColumnPreferences from 'lib/ColumnPreferences';
3939
import * as ClassPreferences from 'lib/ClassPreferences';
4040
import { Helmet } from 'react-helmet';
41+
import { useBeforeUnload } from 'react-router-dom';
4142
import generatePath from 'lib/generatePath';
4243
import { withRouter } from 'lib/withRouter';
4344
import { get } from 'lib/AJAX';
4445
import BrowserFooter from './BrowserFooter.react';
4546

47+
const SELECTED_ROWS_MESSAGE =
48+
'There are selected rows. Are you sure you want to leave this page?';
49+
50+
function SelectedRowsNavigationPrompt({ when }) {
51+
const message = SELECTED_ROWS_MESSAGE;
52+
53+
React.useEffect(() => {
54+
if (!when) {
55+
return;
56+
}
57+
58+
const handleBeforeUnload = event => {
59+
event.preventDefault();
60+
event.returnValue = message;
61+
return message;
62+
};
63+
64+
const handleLinkClick = event => {
65+
if (event.defaultPrevented) {
66+
return;
67+
}
68+
if (event.button !== 0) {
69+
return;
70+
}
71+
if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
72+
return;
73+
}
74+
const anchor = event.target.closest('a[href]');
75+
if (!anchor || anchor.target === '_blank') {
76+
return;
77+
}
78+
const href = anchor.getAttribute('href');
79+
if (!href || href === '#') {
80+
return;
81+
}
82+
if (!window.confirm(message)) {
83+
event.preventDefault();
84+
event.stopPropagation();
85+
}
86+
};
87+
88+
const handlePopState = () => {
89+
if (!window.confirm(message)) {
90+
window.history.go(1);
91+
}
92+
};
93+
94+
window.addEventListener('beforeunload', handleBeforeUnload);
95+
document.addEventListener('click', handleLinkClick, true);
96+
window.addEventListener('popstate', handlePopState);
97+
98+
return () => {
99+
window.removeEventListener('beforeunload', handleBeforeUnload);
100+
document.removeEventListener('click', handleLinkClick, true);
101+
window.removeEventListener('popstate', handlePopState);
102+
};
103+
}, [when, message]);
104+
105+
useBeforeUnload(
106+
React.useCallback(
107+
event => {
108+
if (when) {
109+
event.preventDefault();
110+
event.returnValue = message;
111+
return message;
112+
}
113+
},
114+
[when, message]
115+
)
116+
);
117+
118+
return null;
119+
}
120+
46121
// The initial and max amount of rows fetched by lazy loading
47122
const BROWSER_LAST_LOCATION = 'brower_last_location';
48123

@@ -879,6 +954,11 @@ class Browser extends DashboardView {
879954
}
880955

881956
async refresh() {
957+
if (Object.keys(this.state.selection).length > 0) {
958+
if (!window.confirm(SELECTED_ROWS_MESSAGE)) {
959+
return;
960+
}
961+
}
882962
const relation = this.state.relation;
883963
const prevFilters = this.state.filters || new List();
884964
const initialState = {
@@ -2446,6 +2526,9 @@ class Browser extends DashboardView {
24462526
<Helmet>
24472527
<title>{pageTitle}</title>
24482528
</Helmet>
2529+
<SelectedRowsNavigationPrompt
2530+
when={Object.keys(this.state.selection).length > 0}
2531+
/>
24492532
{browser}
24502533
{notification}
24512534
{extras}

0 commit comments

Comments
 (0)