From ab5a4102cd8921ca2df234bfa6133973ba83a425 Mon Sep 17 00:00:00 2001 From: Alanna Scott Date: Fri, 18 Nov 2016 09:37:01 -0800 Subject: [PATCH] [dashboard] give user feedback when there are unsaved changes (#1633) * show alert and use dialog window if there are unsaved changes. * add container class to alert --- .../javascripts/dashboard/Dashboard.jsx | 36 +++++++++++++++++++ .../dashboard/components/Controls.jsx | 2 ++ .../dashboard/components/GridLayout.jsx | 3 ++ 3 files changed, 41 insertions(+) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index cda9e1e44e..08a1fe2b21 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 b4cf96616c..d158587e46 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 ac090978b0..e2a118c6da 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() {