Skip to content

Commit 9cdaa28

Browse files
authored
Revert "feat(explore): Show confirmation modal if user exits Explore without saving changes (apache#19993)" (apache#20092)
This reverts commit ca9766c.
1 parent e69f629 commit 9cdaa28

File tree

5 files changed

+76
-119
lines changed

5 files changed

+76
-119
lines changed

superset-frontend/src/components/AlteredSliceTag/AlteredSliceTag.test.jsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,34 @@ describe('AlteredSliceTag', () => {
308308
).toBe(expected);
309309
});
310310
});
311+
describe('isEqualish', () => {
312+
it('considers null, undefined, {} and [] as equal', () => {
313+
const inst = wrapper.instance();
314+
expect(inst.isEqualish(null, undefined)).toBe(true);
315+
expect(inst.isEqualish(null, [])).toBe(true);
316+
expect(inst.isEqualish(null, {})).toBe(true);
317+
expect(inst.isEqualish(undefined, {})).toBe(true);
318+
});
319+
it('considers empty strings are the same as null', () => {
320+
const inst = wrapper.instance();
321+
expect(inst.isEqualish(undefined, '')).toBe(true);
322+
expect(inst.isEqualish(null, '')).toBe(true);
323+
});
324+
it('considers deeply equal objects as equal', () => {
325+
const inst = wrapper.instance();
326+
expect(inst.isEqualish('', '')).toBe(true);
327+
expect(inst.isEqualish({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(
328+
true,
329+
);
330+
// Out of order
331+
expect(inst.isEqualish({ a: 1, b: 2, c: 3 }, { b: 2, a: 1, c: 3 })).toBe(
332+
true,
333+
);
334+
335+
// Actually not equal
336+
expect(inst.isEqualish({ a: 1, b: 2, z: 9 }, { a: 1, b: 2, c: 3 })).toBe(
337+
false,
338+
);
339+
});
340+
});
311341
});

superset-frontend/src/components/AlteredSliceTag/index.jsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import React from 'react';
2020
import PropTypes from 'prop-types';
2121
import { isEqual, isEmpty } from 'lodash';
2222
import { styled, t } from '@superset-ui/core';
23-
import { getFormDataDiffs } from 'src/explore/exploreUtils/formData';
23+
import { sanitizeFormData } from 'src/explore/exploreUtils/formData';
2424
import getControlsForVizType from 'src/utils/getControlsForVizType';
2525
import { safeStringify } from 'src/utils/safeStringify';
2626
import { Tooltip } from 'src/components/Tooltip';
@@ -44,6 +44,24 @@ const StyledLabel = styled.span`
4444
`}
4545
`;
4646

47+
function alterForComparison(value) {
48+
// Considering `[]`, `{}`, `null` and `undefined` as identical
49+
// for this purpose
50+
if (value === undefined || value === null || value === '') {
51+
return null;
52+
}
53+
if (typeof value === 'object') {
54+
if (Array.isArray(value) && value.length === 0) {
55+
return null;
56+
}
57+
const keys = Object.keys(value);
58+
if (keys && keys.length === 0) {
59+
return null;
60+
}
61+
}
62+
return value;
63+
}
64+
4765
export default class AlteredSliceTag extends React.Component {
4866
constructor(props) {
4967
super(props);
@@ -77,7 +95,27 @@ export default class AlteredSliceTag extends React.Component {
7795
getDiffs(props) {
7896
// Returns all properties that differ in the
7997
// current form data and the saved form data
80-
return getFormDataDiffs(props.origFormData, props.currentFormData);
98+
const ofd = sanitizeFormData(props.origFormData);
99+
const cfd = sanitizeFormData(props.currentFormData);
100+
101+
const fdKeys = Object.keys(cfd);
102+
const diffs = {};
103+
fdKeys.forEach(fdKey => {
104+
if (!ofd[fdKey] && !cfd[fdKey]) {
105+
return;
106+
}
107+
if (['filters', 'having', 'having_filters', 'where'].includes(fdKey)) {
108+
return;
109+
}
110+
if (!this.isEqualish(ofd[fdKey], cfd[fdKey])) {
111+
diffs[fdKey] = { before: ofd[fdKey], after: cfd[fdKey] };
112+
}
113+
});
114+
return diffs;
115+
}
116+
117+
isEqualish(val1, val2) {
118+
return isEqual(alterForComparison(val1), alterForComparison(val2));
81119
}
82120

83121
formatValue(value, key, controlsMap) {

superset-frontend/src/explore/components/ExploreViewContainer/index.jsx

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,12 @@
1717
* under the License.
1818
*/
1919
/* eslint camelcase: 0 */
20-
import React, {
21-
useCallback,
22-
useEffect,
23-
useMemo,
24-
useRef,
25-
useState,
26-
} from 'react';
20+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
2721
import PropTypes from 'prop-types';
2822
import { bindActionCreators } from 'redux';
2923
import { connect } from 'react-redux';
3024
import { styled, t, css, useTheme, logging } from '@superset-ui/core';
31-
import { debounce, pick, isEmpty } from 'lodash';
25+
import { debounce, pick } from 'lodash';
3226
import { Resizable } from 're-resizable';
3327
import { useChangeEffect } from 'src/hooks/useChangeEffect';
3428
import { usePluginContext } from 'src/components/DynamicPlugins';
@@ -49,11 +43,7 @@ import * as chartActions from 'src/components/Chart/chartAction';
4943
import { fetchDatasourceMetadata } from 'src/dashboard/actions/datasources';
5044
import { chartPropShape } from 'src/dashboard/util/propShapes';
5145
import { mergeExtraFormData } from 'src/dashboard/components/nativeFilters/utils';
52-
import {
53-
getFormDataDiffs,
54-
postFormData,
55-
putFormData,
56-
} from 'src/explore/exploreUtils/formData';
46+
import { postFormData, putFormData } from 'src/explore/exploreUtils/formData';
5747
import { useTabId } from 'src/hooks/useTabId';
5848
import ExploreChartPanel from '../ExploreChartPanel';
5949
import ConnectedControlPanelsContainer from '../ControlPanelsContainer';
@@ -226,11 +216,6 @@ const updateHistory = debounce(
226216
1000,
227217
);
228218

229-
const handleUnloadEvent = e => {
230-
e.preventDefault();
231-
e.returnValue = 'Controls changed';
232-
};
233-
234219
function ExploreViewContainer(props) {
235220
const dynamicPluginContext = usePluginContext();
236221
const dynamicPlugin = dynamicPluginContext.dynamicPlugins[props.vizType];
@@ -251,9 +236,6 @@ function ExploreViewContainer(props) {
251236

252237
const theme = useTheme();
253238

254-
const isBeforeUnloadActive = useRef(false);
255-
const initialFormData = useRef(props.form_data);
256-
257239
const defaultSidebarsWidth = {
258240
controls_width: 320,
259241
datasource_width: 300,
@@ -383,27 +365,6 @@ function ExploreViewContainer(props) {
383365
};
384366
}, [handleKeydown, previousHandleKeyDown]);
385367

386-
useEffect(() => {
387-
const formDataChanged = !isEmpty(
388-
getFormDataDiffs(initialFormData.current, props.form_data),
389-
);
390-
if (formDataChanged && !isBeforeUnloadActive.current) {
391-
window.addEventListener('beforeunload', handleUnloadEvent);
392-
isBeforeUnloadActive.current = true;
393-
}
394-
if (!formDataChanged && isBeforeUnloadActive.current) {
395-
window.removeEventListener('beforeunload', handleUnloadEvent);
396-
isBeforeUnloadActive.current = false;
397-
}
398-
}, [props.form_data]);
399-
400-
// cleanup beforeunload event listener
401-
// we use separate useEffect to call it only on component unmount instead of on every form data change
402-
useEffect(
403-
() => () => window.removeEventListener('beforeunload', handleUnloadEvent),
404-
[],
405-
);
406-
407368
useEffect(() => {
408369
if (wasDynamicPluginLoading && !isDynamicPluginLoading) {
409370
// reload the controls now that we actually have the control config

superset-frontend/src/explore/exploreUtils/formData.test.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
import { sanitizeFormData, isEqualish } from './formData';
19+
import { sanitizeFormData } from './formData';
2020

2121
test('sanitizeFormData removes temporary control values', () => {
2222
expect(
@@ -26,25 +26,3 @@ test('sanitizeFormData removes temporary control values', () => {
2626
}),
2727
).toEqual({ metrics: ['foo', 'bar'] });
2828
});
29-
30-
test('isEqualish', () => {
31-
// considers null, undefined, {} and [] as equal
32-
expect(isEqualish(null, undefined)).toBe(true);
33-
expect(isEqualish(null, [])).toBe(true);
34-
expect(isEqualish(null, {})).toBe(true);
35-
expect(isEqualish(undefined, {})).toBe(true);
36-
37-
// considers empty strings are the same as null
38-
expect(isEqualish(undefined, '')).toBe(true);
39-
expect(isEqualish(null, '')).toBe(true);
40-
41-
// considers deeply equal objects as equal
42-
expect(isEqualish('', '')).toBe(true);
43-
expect(isEqualish({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true);
44-
45-
// Out of order
46-
expect(isEqualish({ a: 1, b: 2, c: 3 }, { b: 2, a: 1, c: 3 })).toBe(true);
47-
48-
// Actually not equal
49-
expect(isEqualish({ a: 1, b: 2, z: 9 }, { a: 1, b: 2, c: 3 })).toBe(false);
50-
});

superset-frontend/src/explore/exploreUtils/formData.ts

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
import { isEqual, omit } from 'lodash';
20-
import { SupersetClient, JsonObject, JsonValue } from '@superset-ui/core';
19+
import { omit } from 'lodash';
20+
import { SupersetClient, JsonObject } from '@superset-ui/core';
2121

2222
type Payload = {
2323
dataset_id: number;
@@ -30,56 +30,6 @@ const TEMPORARY_CONTROLS = ['url_params'];
3030
export const sanitizeFormData = (formData: JsonObject): JsonObject =>
3131
omit(formData, TEMPORARY_CONTROLS);
3232

33-
export const alterForComparison = (value: JsonValue | undefined) => {
34-
// Considering `[]`, `{}`, `null` and `undefined` as identical
35-
// for this purpose
36-
if (
37-
value === undefined ||
38-
value === null ||
39-
value === '' ||
40-
(Array.isArray(value) && value.length === 0) ||
41-
(typeof value === 'object' && Object.keys(value).length === 0)
42-
) {
43-
return null;
44-
}
45-
if (Array.isArray(value)) {
46-
// omit prototype for comparison of class instances with json objects
47-
return value.map(v => (typeof v === 'object' ? omit(v, ['__proto__']) : v));
48-
}
49-
if (typeof value === 'object') {
50-
return omit(value, ['__proto__']);
51-
}
52-
return value;
53-
};
54-
55-
export const isEqualish = (
56-
val1: JsonValue | undefined,
57-
val2: JsonValue | undefined,
58-
) => isEqual(alterForComparison(val1), alterForComparison(val2));
59-
60-
export const getFormDataDiffs = (
61-
formData1: JsonObject,
62-
formData2: JsonObject,
63-
) => {
64-
const ofd = sanitizeFormData(formData1);
65-
const cfd = sanitizeFormData(formData2);
66-
67-
const fdKeys = Object.keys(cfd);
68-
const diffs = {};
69-
fdKeys.forEach(fdKey => {
70-
if (!ofd[fdKey] && !cfd[fdKey]) {
71-
return;
72-
}
73-
if (['filters', 'having', 'having_filters', 'where'].includes(fdKey)) {
74-
return;
75-
}
76-
if (!isEqualish(ofd[fdKey], cfd[fdKey])) {
77-
diffs[fdKey] = { before: ofd[fdKey], after: cfd[fdKey] };
78-
}
79-
});
80-
return diffs;
81-
};
82-
8333
const assembleEndpoint = (key?: string, tabId?: string) => {
8434
let endpoint = 'api/v1/explore/form_data';
8535
if (key) {

0 commit comments

Comments
 (0)