diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx
index cda9e1e44eb1a..08a1fe2b210bb 100644
--- a/superset/assets/javascripts/dashboard/Dashboard.jsx
+++ b/superset/assets/javascripts/dashboard/Dashboard.jsx
@@ -4,6 +4,7 @@ const px = require('../modules/superset');
const d3 = require('d3');
const urlLib = require('url');
const utils = require('../modules/utils');
+const { Alert } = require('react-bootstrap');
import React from 'react';
import { render } from 'react-dom';
@@ -33,6 +34,33 @@ export function getInitialState(dashboardData, context) {
return state;
}
+function unload() {
+ const message = 'You have unsaved changes.';
+ window.event.returnValue = message; // Gecko + IE
+ return message; // Gecko + Webkit, Safari, Chrome etc.
+}
+
+function onBeforeUnload(hasChanged) {
+ if (hasChanged) {
+ window.addEventListener('beforeunload', unload);
+ } else {
+ window.removeEventListener('beforeunload', unload);
+ }
+}
+
+function renderAlert() {
+ render(
+
+
+ You have unsaved changes. Click the
+
+ button on the top right to save your changes.
+
+
,
+ document.getElementById('alert-container')
+ );
+}
+
function initDashboardView(dashboard) {
render(
,
@@ -96,6 +124,14 @@ export function dashboardContainer(dashboard) {
this.startPeriodicRender(0);
this.bindResizeToWindowResize();
},
+ onChange() {
+ onBeforeUnload(true);
+ renderAlert();
+ },
+ onSave() {
+ onBeforeUnload(false);
+ $('#alert-container').html('');
+ },
loadPreSelectFilters() {
try {
const filters = JSON.parse(px.getParam('preselect_filters') || '{}');
diff --git a/superset/assets/javascripts/dashboard/components/Controls.jsx b/superset/assets/javascripts/dashboard/components/Controls.jsx
index b4cf96616c309..d158587e469dd 100644
--- a/superset/assets/javascripts/dashboard/components/Controls.jsx
+++ b/superset/assets/javascripts/dashboard/components/Controls.jsx
@@ -59,6 +59,7 @@ class Controls extends React.PureComponent {
data: JSON.stringify(data),
},
success() {
+ dashboard.onSave();
showModal({
title: 'Success',
body: 'This dashboard was saved successfully.',
@@ -75,6 +76,7 @@ class Controls extends React.PureComponent {
}
changeCss(css) {
this.setState({ css });
+ this.props.dashboard.onChange();
}
render() {
const dashboard = this.props.dashboard;
diff --git a/superset/assets/javascripts/dashboard/components/GridLayout.jsx b/superset/assets/javascripts/dashboard/components/GridLayout.jsx
index ac090978b0539..e2a118c6da1a0 100644
--- a/superset/assets/javascripts/dashboard/components/GridLayout.jsx
+++ b/superset/assets/javascripts/dashboard/components/GridLayout.jsx
@@ -48,10 +48,12 @@ class GridLayout extends React.Component {
if (oldItem.w !== newItem.w || oldItem.h !== newItem.h) {
this.setState({ layout }, () => newSlice.resize());
}
+ this.props.dashboard.onChange();
}
onDragStop(layout) {
this.setState({ layout });
+ this.props.dashboard.onChange();
}
removeSlice(sliceId) {
@@ -64,6 +66,7 @@ class GridLayout extends React.Component {
return slice.slice_id !== sliceId;
}),
});
+ this.props.dashboard.onChange();
}
serialize() {