superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From grace...@apache.org
Subject [incubator-superset] branch master updated: retire dashboard v1 (js and python) (#5418)
Date Tue, 24 Jul 2018 22:23:33 GMT
This is an automated email from the ASF dual-hosted git repository.

graceguo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 3f2fc8f  retire dashboard v1 (js and python) (#5418)
3f2fc8f is described below

commit 3f2fc8f413605a388f91593e38b2bbb46b3227a1
Author: Grace Guo <grace.guo@airbnb.com>
AuthorDate: Tue Jul 24 15:23:30 2018 -0700

    retire dashboard v1 (js and python) (#5418)
---
 superset/assets/package.json                       |   1 -
 .../dashboard/fixtures/mockDashboardState.js       |   1 -
 .../dashboard/reducers/dashboardState_spec.js      |   1 -
 .../assets/src/dashboard/components/Dashboard.jsx  |   7 +-
 .../assets/src/dashboard/components/Header.jsx     |  46 +-
 .../dashboard/components/HeaderActionsDropdown.jsx |   7 +-
 .../assets/src/dashboard/components/SaveModal.jsx  |   9 +-
 .../src/dashboard/containers/DashboardHeader.jsx   |   1 -
 .../deprecated/PromptV2ConversionModal.jsx         | 102 ----
 .../src/dashboard/deprecated/V2PreviewModal.jsx    | 148 ------
 .../src/dashboard/deprecated/chart/Chart.jsx       | 294 ------------
 .../src/dashboard/deprecated/chart/ChartBody.jsx   |  55 ---
 .../dashboard/deprecated/chart/ChartContainer.jsx  |  29 --
 .../src/dashboard/deprecated/chart/chart.css       |   4 -
 .../src/dashboard/deprecated/chart/chartAction.js  | 195 --------
 .../src/dashboard/deprecated/chart/chartReducer.js | 158 -------
 .../assets/src/dashboard/deprecated/v1/actions.js  | 128 -----
 .../deprecated/v1/components/CodeModal.jsx         |  48 --
 .../deprecated/v1/components/Controls.jsx          | 215 ---------
 .../deprecated/v1/components/CssEditor.jsx         |  91 ----
 .../deprecated/v1/components/Dashboard.jsx         | 441 ------------------
 .../v1/components/DashboardContainer.jsx           |  31 --
 .../deprecated/v1/components/GridCell.jsx          | 157 -------
 .../deprecated/v1/components/GridLayout.jsx        | 198 --------
 .../dashboard/deprecated/v1/components/Header.jsx  | 169 -------
 .../v1/components/RefreshIntervalModal.jsx         |  64 ---
 .../deprecated/v1/components/SaveModal.jsx         | 168 -------
 .../deprecated/v1/components/SliceAdder.jsx        | 216 ---------
 .../deprecated/v1/components/SliceHeader.jsx       | 194 --------
 .../assets/src/dashboard/deprecated/v1/index.jsx   |  34 --
 .../assets/src/dashboard/deprecated/v1/reducers.js | 274 -----------
 .../src/dashboard/reducers/dashboardState.js       |   1 -
 .../src/dashboard/reducers/getInitialState.js      |  24 +-
 .../src/dashboard/stylesheets/dashboard.less       |  22 -
 .../src/dashboard/util/dashboardLayoutConverter.js | 513 ---------------------
 .../assets/src/explore/components/SaveModal.jsx    |   9 +-
 superset/assets/src/explore/constants.js           |   2 +
 superset/assets/src/logger.js                      |  13 -
 superset/assets/webpack.config.js                  |   1 -
 superset/models/core.py                            |  50 +-
 superset/views/core.py                             | 116 +----
 tests/dashboard_tests.py                           |  43 +-
 tests/import_export_tests.py                       |  37 +-
 43 files changed, 106 insertions(+), 4211 deletions(-)

diff --git a/superset/assets/package.json b/superset/assets/package.json
index da54056..c81a902 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -102,7 +102,6 @@
     "react-dnd-html5-backend": "^2.5.4",
     "react-dom": "^15.6.2",
     "react-gravatar": "^2.6.1",
-    "react-grid-layout": "0.16.6",
     "react-map-gl": "^3.0.4",
     "react-markdown": "^3.3.0",
     "react-redux": "^5.0.2",
diff --git a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js
index fd640d1..d405ccf 100644
--- a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js
+++ b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js
@@ -11,5 +11,4 @@ export default {
   maxUndoHistoryExceeded: false,
   isStarred: true,
   css: '',
-  isV2Preview: false, // @TODO remove upon v1 deprecation
 };
diff --git a/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js b/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js
index f8095cd..7772f71 100644
--- a/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js
+++ b/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js
@@ -135,7 +135,6 @@ describe('dashboardState reducer', () => {
       hasUnsavedChanges: false,
       maxUndoHistoryExceeded: false,
       editMode: false,
-      isV2Preview: false, // @TODO remove upon v1 deprecation
     });
   });
 
diff --git a/superset/assets/src/dashboard/components/Dashboard.jsx b/superset/assets/src/dashboard/components/Dashboard.jsx
index 80d4bdf..2bb9b9c 100644
--- a/superset/assets/src/dashboard/components/Dashboard.jsx
+++ b/superset/assets/src/dashboard/components/Dashboard.jsx
@@ -86,9 +86,6 @@ class Dashboard extends React.PureComponent {
 
   componentWillReceiveProps(nextProps) {
     if (!nextProps.dashboardState.editMode) {
-      const version = nextProps.dashboardState.isV2Preview
-        ? 'v2-preview'
-        : 'v2';
       // log pane loads
       const loadedPaneIds = [];
       let minQueryStartTime = Infinity;
@@ -107,7 +104,7 @@ class Dashboard extends React.PureComponent {
             Logger.append(LOG_ACTIONS_LOAD_DASHBOARD_PANE, {
               ...restStats,
               duration: new Date().getTime() - paneMinQueryStart,
-              version,
+              version: 'v2',
             });
 
             if (!this.isFirstLoad) {
@@ -128,7 +125,7 @@ class Dashboard extends React.PureComponent {
         Logger.append(LOG_ACTIONS_FIRST_DASHBOARD_LOAD, {
           pane_ids: loadedPaneIds,
           duration: new Date().getTime() - minQueryStartTime,
-          version,
+          version: 'v2',
         });
         Logger.send(this.actionLog);
         this.isFirstLoad = false;
diff --git a/superset/assets/src/dashboard/components/Header.jsx b/superset/assets/src/dashboard/components/Header.jsx
index 3b1b6b1..0c1951b 100644
--- a/superset/assets/src/dashboard/components/Header.jsx
+++ b/superset/assets/src/dashboard/components/Header.jsx
@@ -7,7 +7,6 @@ import EditableTitle from '../../components/EditableTitle';
 import Button from '../../components/Button';
 import FaveStar from '../../components/FaveStar';
 import UndoRedoKeylisteners from './UndoRedoKeylisteners';
-import V2PreviewModal from '../deprecated/V2PreviewModal';
 
 import { chartPropShape } from '../util/propShapes';
 import { t } from '../../locales';
@@ -32,7 +31,6 @@ const propTypes = {
   startPeriodicRender: PropTypes.func.isRequired,
   updateDashboardTitle: PropTypes.func.isRequired,
   editMode: PropTypes.bool.isRequired,
-  isV2Preview: PropTypes.bool.isRequired,
   setEditMode: PropTypes.func.isRequired,
   showBuilderPane: PropTypes.bool.isRequired,
   toggleBuilderPane: PropTypes.func.isRequired,
@@ -60,7 +58,6 @@ class Header extends React.PureComponent {
       didNotifyMaxUndoHistoryToast: false,
       emphasizeUndo: false,
       hightlightRedo: false,
-      showV2PreviewModal: props.isV2Preview,
     };
 
     this.handleChangeText = this.handleChangeText.bind(this);
@@ -69,7 +66,6 @@ class Header extends React.PureComponent {
     this.toggleEditMode = this.toggleEditMode.bind(this);
     this.forceRefresh = this.forceRefresh.bind(this);
     this.overwriteDashboard = this.overwriteDashboard.bind(this);
-    this.toggleShowV2PreviewModal = this.toggleShowV2PreviewModal.bind(this);
   }
 
   componentWillReceiveProps(nextProps) {
@@ -129,10 +125,6 @@ class Header extends React.PureComponent {
     this.props.setEditMode(!this.props.editMode);
   }
 
-  toggleShowV2PreviewModal() {
-    this.setState({ showV2PreviewModal: !this.state.showV2PreviewModal });
-  }
-
   overwriteDashboard() {
     const {
       dashboardTitle,
@@ -161,7 +153,6 @@ class Header extends React.PureComponent {
       filters,
       expandedSlices,
       css,
-      isV2Preview,
       onUndo,
       onRedo,
       undoLength,
@@ -177,7 +168,7 @@ class Header extends React.PureComponent {
 
     const userCanEdit = dashboardInfo.dash_edit_perm;
     const userCanSaveAs = dashboardInfo.dash_save_perm;
-    const popButton = hasUnsavedChanges || isV2Preview;
+    const popButton = hasUnsavedChanges;
 
     return (
       <div className="dashboard-header">
@@ -196,20 +187,6 @@ class Header extends React.PureComponent {
               isStarred={this.props.isStarred}
             />
           </span>
-          {isV2Preview && (
-            <div
-              role="none"
-              className="v2-preview-badge"
-              onClick={this.toggleShowV2PreviewModal}
-            >
-              {t('v2 Preview')}
-              <span className="fa fa-info-circle m-l-5" />
-            </div>
-          )}
-          {isV2Preview &&
-            this.state.showV2PreviewModal && (
-              <V2PreviewModal onClose={this.toggleShowV2PreviewModal} />
-            )}
         </div>
 
         {userCanSaveAs && (
@@ -245,32 +222,17 @@ class Header extends React.PureComponent {
             )}
 
             {editMode &&
-              (hasUnsavedChanges || isV2Preview) && (
+              hasUnsavedChanges && (
                 <Button
                   bsSize="small"
                   bsStyle={popButton ? 'primary' : undefined}
                   onClick={this.overwriteDashboard}
                 >
-                  {isV2Preview
-                    ? t('Persist as Dashboard v2')
-                    : t('Save changes')}
-                </Button>
-              )}
-
-            {!editMode &&
-              isV2Preview && (
-                <Button
-                  bsSize="small"
-                  onClick={this.toggleEditMode}
-                  bsStyle={popButton ? 'primary' : undefined}
-                  disabled={!userCanEdit}
-                >
-                  {t('Edit to persist Dashboard v2')}
+                  {t('Save changes')}
                 </Button>
               )}
 
             {!editMode &&
-              !isV2Preview &&
               !hasUnsavedChanges && (
                 <Button
                   bsSize="small"
@@ -283,7 +245,6 @@ class Header extends React.PureComponent {
               )}
 
             {editMode &&
-              !isV2Preview &&
               !hasUnsavedChanges && (
                 <Button
                   bsSize="small"
@@ -312,7 +273,6 @@ class Header extends React.PureComponent {
               editMode={editMode}
               hasUnsavedChanges={hasUnsavedChanges}
               userCanEdit={userCanEdit}
-              isV2Preview={isV2Preview}
             />
 
             {editMode && (
diff --git a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx
index 7b8a245..dab11c3 100644
--- a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx
+++ b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx
@@ -28,7 +28,6 @@ const propTypes = {
   filters: PropTypes.object.isRequired,
   expandedSlices: PropTypes.object.isRequired,
   onSave: PropTypes.func.isRequired,
-  isV2Preview: PropTypes.bool.isRequired,
 };
 
 const defaultProps = {};
@@ -83,7 +82,6 @@ class HeaderActionsDropdown extends React.PureComponent {
       expandedSlices,
       onSave,
       userCanEdit,
-      isV2Preview,
     } = this.props;
 
     const emailBody = t('Check out this dashboard: %s', window.location.href);
@@ -93,7 +91,7 @@ class HeaderActionsDropdown extends React.PureComponent {
       <DropdownButton
         title=""
         id="save-dash-split-button"
-        bsStyle={hasUnsavedChanges || isV2Preview ? 'primary' : undefined}
+        bsStyle={hasUnsavedChanges ? 'primary' : undefined}
         bsSize="small"
         pullRight
       >
@@ -111,9 +109,8 @@ class HeaderActionsDropdown extends React.PureComponent {
           isMenuItem
           triggerNode={<span>{t('Save as')}</span>}
           canOverwrite={userCanEdit}
-          isV2Preview={isV2Preview}
         />
-        {(isV2Preview || hasUnsavedChanges) && (
+        {hasUnsavedChanges && (
           <MenuItem
             eventKey="discard"
             onSelect={HeaderActionsDropdown.discardChanges}
diff --git a/superset/assets/src/dashboard/components/SaveModal.jsx b/superset/assets/src/dashboard/components/SaveModal.jsx
index f5ad9d0..8194f46 100644
--- a/superset/assets/src/dashboard/components/SaveModal.jsx
+++ b/superset/assets/src/dashboard/components/SaveModal.jsx
@@ -22,7 +22,6 @@ const propTypes = {
   onSave: PropTypes.func.isRequired,
   isMenuItem: PropTypes.bool,
   canOverwrite: PropTypes.bool.isRequired,
-  isV2Preview: PropTypes.bool.isRequired,
 };
 
 const defaultProps = {
@@ -104,16 +103,12 @@ class SaveModal extends React.PureComponent {
   }
 
   render() {
-    const { isV2Preview } = this.props;
     return (
       <ModalTrigger
         ref={this.setModalRef}
         isMenuItem={this.props.isMenuItem}
         triggerNode={this.props.triggerNode}
-        modalTitle={t(
-          'Save Dashboard%s',
-          isV2Preview ? ' (⚠️ all saved dashboards will be V2)' : '',
-        )}
+        modalTitle={t('Save Dashboard')}
         modalBody={
           <FormGroup>
             <Radio
@@ -144,7 +139,7 @@ class SaveModal extends React.PureComponent {
                 checked={this.state.duplicateSlices}
                 onChange={this.toggleDuplicateSlices}
               />
-              <span className="m-l-5">also copy (duplicate) charts</span>
+              <span className="m-l-5">{t('also copy (duplicate) charts')}</span>
             </div>
           </FormGroup>
         }
diff --git a/superset/assets/src/dashboard/containers/DashboardHeader.jsx b/superset/assets/src/dashboard/containers/DashboardHeader.jsx
index 3740404..629b916 100644
--- a/superset/assets/src/dashboard/containers/DashboardHeader.jsx
+++ b/superset/assets/src/dashboard/containers/DashboardHeader.jsx
@@ -51,7 +51,6 @@ function mapStateToProps({
     maxUndoHistoryExceeded: !!dashboardState.maxUndoHistoryExceeded,
     editMode: !!dashboardState.editMode,
     showBuilderPane: !!dashboardState.showBuilderPane,
-    isV2Preview: dashboardState.isV2Preview,
   };
 }
 
diff --git a/superset/assets/src/dashboard/deprecated/PromptV2ConversionModal.jsx b/superset/assets/src/dashboard/deprecated/PromptV2ConversionModal.jsx
deleted file mode 100644
index a621635..0000000
--- a/superset/assets/src/dashboard/deprecated/PromptV2ConversionModal.jsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import moment from 'moment';
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Modal, Button } from 'react-bootstrap';
-import { Logger, LOG_ACTIONS_READ_ABOUT_V2_CHANGES } from '../../logger';
-import { t } from '../../locales';
-
-const propTypes = {
-  v2FeedbackUrl: PropTypes.string,
-  v2AutoConvertDate: PropTypes.string,
-  onClose: PropTypes.func.isRequired,
-  handleConvertToV2: PropTypes.func.isRequired,
-  forceV2Edit: PropTypes.bool.isRequired,
-};
-
-const defaultProps = {
-  v2FeedbackUrl: null,
-  v2AutoConvertDate: null,
-};
-
-function logReadAboutV2Changes() {
-  Logger.append(LOG_ACTIONS_READ_ABOUT_V2_CHANGES, { version: 'v1' }, true);
-}
-
-function PromptV2ConversionModal({
-  v2FeedbackUrl,
-  v2AutoConvertDate,
-  onClose,
-  handleConvertToV2,
-  forceV2Edit,
-}) {
-  const timeUntilAutoConversion = v2AutoConvertDate
-    ? `approximately ${moment(v2AutoConvertDate).toNow(
-        true,
-      )} (${v2AutoConvertDate})` // eg 2 weeks (MM-DD-YYYY)
-    : 'a limited amount of time';
-
-  return (
-    <Modal onHide={onClose} onExit={onClose} animation show>
-      <Modal.Header closeButton>
-        <div style={{ fontSize: 20, fontWeight: 200, margin: '0px 4px -4px' }}>
-          {t('Convert to Dashboard v2 🎉')}
-        </div>
-      </Modal.Header>
-      <Modal.Body>
-        <h4>{t('Who')}</h4>
-        <p>
-          {t(
-            "As this dashboard's owner or a Superset Admin, we're soliciting your help to ensure a successful transition to the new dashboard experience.",
-          )}
-        </p>
-        <br />
-        <h4>{t('What and When')}</h4>
-        <p>
-          {t('You have ')}
-          <strong>
-            {timeUntilAutoConversion}
-            {t(' to convert this v1 dashboard to the new v2 format')}
-          </strong>
-          {t(' before it is auto-converted. ')}
-          {forceV2Edit && (
-            <em>
-              {t(
-                'Note that you may only edit dashboards using the v2 experience.',
-              )}
-            </em>
-          )}
-          {t('You may read more about these changes ')}
-          <a
-            target="_blank"
-            rel="noopener noreferrer"
-            href="http://bit.ly/superset-dash-v2"
-            onClick={logReadAboutV2Changes}
-          >
-            here
-          </a>
-          {v2FeedbackUrl ? t(' or ') : ''}
-          {v2FeedbackUrl ? (
-            <a target="_blank" rel="noopener noreferrer" href={v2FeedbackUrl}>
-              {t('provide feedback')}
-            </a>
-          ) : (
-            ''
-          )}.
-        </p>
-      </Modal.Body>
-      <Modal.Footer>
-        <Button onClick={onClose}>
-          {t(`${forceV2Edit ? 'View in' : 'Continue with'}  v1`)}
-        </Button>
-        <Button bsStyle="primary" onClick={handleConvertToV2}>
-          {t('Preview v2')}
-        </Button>
-      </Modal.Footer>
-    </Modal>
-  );
-}
-
-PromptV2ConversionModal.propTypes = propTypes;
-PromptV2ConversionModal.defaultProps = defaultProps;
-
-export default PromptV2ConversionModal;
diff --git a/superset/assets/src/dashboard/deprecated/V2PreviewModal.jsx b/superset/assets/src/dashboard/deprecated/V2PreviewModal.jsx
deleted file mode 100644
index 828651f..0000000
--- a/superset/assets/src/dashboard/deprecated/V2PreviewModal.jsx
+++ /dev/null
@@ -1,148 +0,0 @@
-/* eslint-env browser */
-import moment from 'moment';
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Modal, Button } from 'react-bootstrap';
-import { connect } from 'react-redux';
-import {
-  Logger,
-  LOG_ACTIONS_READ_ABOUT_V2_CHANGES,
-  LOG_ACTIONS_FALLBACK_TO_V1,
-} from '../../logger';
-
-import { t } from '../../locales';
-
-const propTypes = {
-  v2FeedbackUrl: PropTypes.string,
-  v2AutoConvertDate: PropTypes.string,
-  forceV2Edit: PropTypes.bool.isRequired,
-  onClose: PropTypes.func.isRequired,
-};
-
-const defaultProps = {
-  v2FeedbackUrl: null,
-  v2AutoConvertDate: null,
-  handleFallbackToV1: null,
-};
-
-// This is a gross component but it is temporary!
-class V2PreviewModal extends React.Component {
-  static logReadAboutV2Changes() {
-    Logger.append(
-      LOG_ACTIONS_READ_ABOUT_V2_CHANGES,
-      { version: 'v2-preview' },
-      true,
-    );
-  }
-
-  constructor(props) {
-    super(props);
-    this.handleFallbackToV1 = this.handleFallbackToV1.bind(this);
-  }
-
-  handleFallbackToV1() {
-    Logger.append(
-      LOG_ACTIONS_FALLBACK_TO_V1,
-      {
-        force_v2_edit: this.props.forceV2Edit,
-      },
-      true,
-    );
-    const url = new URL(window.location); // eslint-disable-line
-    url.searchParams.set('version', 'v1');
-    url.searchParams.delete('edit'); // remove JIC they were editing and v1 editing is not allowed
-    window.location = url;
-  }
-
-  render() {
-    const { v2FeedbackUrl, v2AutoConvertDate, onClose } = this.props;
-
-    const timeUntilAutoConversion = v2AutoConvertDate
-      ? `approximately ${moment(v2AutoConvertDate).toNow(
-          true,
-        )} (${v2AutoConvertDate})` // eg 2 weeks (MM-DD-YYYY)
-      : 'a limited amount of time';
-
-    return (
-      <Modal onHide={onClose} onExit={onClose} animation show>
-        <Modal.Header closeButton>
-          <div
-            style={{ fontSize: 20, fontWeight: 200, margin: '0px 4px -4px' }}
-          >
-            {t('Welcome to the new Dashboard v2 experience! 🎉')}
-          </div>
-        </Modal.Header>
-        <Modal.Body>
-          <h3>{t('Who')}</h3>
-          <p>
-            {t(
-              "As this dashboard's owner or a Superset Admin, we're soliciting your help to ensure a successful transition to the new dashboard experience. You can learn more about these changes ",
-            )}
-            <a
-              target="_blank"
-              rel="noopener noreferrer"
-              href="http://bit.ly/superset-dash-v2"
-              onClick={V2PreviewModal.logReadAboutV2Changes}
-            >
-              here
-            </a>
-            {v2FeedbackUrl ? t(' or ') : ''}
-            {v2FeedbackUrl ? (
-              <a target="_blank" rel="noopener noreferrer" href={v2FeedbackUrl}>
-                {t('provide feedback')}
-              </a>
-            ) : (
-              ''
-            )}.
-          </p>
-          <br />
-          <h3>{t('What')}</h3>
-          <p>
-            {t('You are ')}
-            <strong>{t('previewing')}</strong>
-            {t(
-              ' an auto-converted v2 version of your v1 dashboard. This conversion may have introduced regressions, such as minor layout variation or incompatible custom CSS. ',
-            )}
-            <strong>
-              {t(
-                'To persist your dashboard as v2, please make any necessary changes and save the dashboard',
-              )}
-            </strong>
-            {t(
-              '. Note that non-owners/-admins will continue to see the original version until you take this action.',
-            )}
-          </p>
-          <br />
-          <h3>{t('When')}</h3>
-          <p>
-            {t('You have ')}
-            <strong>
-              {timeUntilAutoConversion}
-              {t(' to edit and save this version ')}
-            </strong>
-            {t(
-              ' before it is auto-persisted to this preview. Upon save you will no longer be able to use the v1 experience.',
-            )}
-          </p>
-        </Modal.Body>
-        <Modal.Footer>
-          <Button onClick={this.handleFallbackToV1}>
-            {t('Fallback to v1')}
-          </Button>
-          <Button bsStyle="primary" onClick={onClose}>
-            {t('Preview v2')}
-          </Button>
-        </Modal.Footer>
-      </Modal>
-    );
-  }
-}
-
-V2PreviewModal.propTypes = propTypes;
-V2PreviewModal.defaultProps = defaultProps;
-
-export default connect(({ dashboardInfo }) => ({
-  v2FeedbackUrl: dashboardInfo.v2FeedbackUrl,
-  v2AutoConvertDate: dashboardInfo.v2AutoConvertDate,
-  forceV2Edit: dashboardInfo.forceV2Edit,
-}))(V2PreviewModal);
diff --git a/superset/assets/src/dashboard/deprecated/chart/Chart.jsx b/superset/assets/src/dashboard/deprecated/chart/Chart.jsx
deleted file mode 100644
index 8706884..0000000
--- a/superset/assets/src/dashboard/deprecated/chart/Chart.jsx
+++ /dev/null
@@ -1,294 +0,0 @@
-/* eslint camelcase: 0 */
-import React from 'react';
-import PropTypes from 'prop-types';
-import Mustache from 'mustache';
-import { Tooltip } from 'react-bootstrap';
-
-import { d3format } from '../../../modules/utils';
-import ChartBody from './ChartBody';
-import Loading from '../../../components/Loading';
-import { Logger, LOG_ACTIONS_RENDER_CHART } from '../../../logger';
-import StackTraceMessage from '../../../components/StackTraceMessage';
-import RefreshChartOverlay from '../../../components/RefreshChartOverlay';
-import visPromiseLookup from '../../../visualizations';
-import sandboxedEval from '../../../modules/sandbox';
-import './chart.css';
-
-const propTypes = {
-  annotationData: PropTypes.object,
-  actions: PropTypes.object,
-  chartKey: PropTypes.string.isRequired,
-  containerId: PropTypes.string.isRequired,
-  datasource: PropTypes.object.isRequired,
-  formData: PropTypes.object.isRequired,
-  headerHeight: PropTypes.number,
-  height: PropTypes.number,
-  width: PropTypes.number,
-  setControlValue: PropTypes.func,
-  timeout: PropTypes.number,
-  vizType: PropTypes.string.isRequired,
-  // state
-  chartAlert: PropTypes.string,
-  chartStatus: PropTypes.string,
-  chartUpdateEndTime: PropTypes.number,
-  chartUpdateStartTime: PropTypes.number,
-  latestQueryFormData: PropTypes.object,
-  queryRequest: PropTypes.object,
-  queryResponse: PropTypes.object,
-  lastRendered: PropTypes.number,
-  triggerQuery: PropTypes.bool,
-  refreshOverlayVisible: PropTypes.bool,
-  errorMessage: PropTypes.node,
-  // dashboard callbacks
-  addFilter: PropTypes.func,
-  getFilters: PropTypes.func,
-  clearFilter: PropTypes.func,
-  removeFilter: PropTypes.func,
-  onQuery: PropTypes.func,
-  onDismissRefreshOverlay: PropTypes.func,
-};
-
-const defaultProps = {
-  addFilter: () => ({}),
-  getFilters: () => ({}),
-  clearFilter: () => ({}),
-  removeFilter: () => ({}),
-};
-
-class Chart extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    // visualizations are lazy-loaded with promises that resolve to a renderVis function
-    this.state = {
-      renderVis: null,
-    };
-    // these properties are used by visualizations
-    this.annotationData = props.annotationData;
-    this.containerId = props.containerId;
-    this.selector = `#${this.containerId}`;
-    this.formData = props.formData;
-    this.datasource = props.datasource;
-    this.addFilter = this.addFilter.bind(this);
-    this.getFilters = this.getFilters.bind(this);
-    this.clearFilter = this.clearFilter.bind(this);
-    this.removeFilter = this.removeFilter.bind(this);
-    this.headerHeight = this.headerHeight.bind(this);
-    this.height = this.height.bind(this);
-    this.width = this.width.bind(this);
-    this.visPromise = null;
-  }
-
-  componentDidMount() {
-    if (this.props.triggerQuery) {
-      this.props.actions.runQuery(this.props.formData, false,
-        this.props.timeout,
-        this.props.chartKey,
-      );
-    }
-    this.loadAsyncVis(this.props.vizType);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    this.annotationData = nextProps.annotationData;
-    this.containerId = nextProps.containerId;
-    this.selector = `#${this.containerId}`;
-    this.formData = nextProps.formData;
-    this.datasource = nextProps.datasource;
-    if (nextProps.vizType !== this.props.vizType) {
-      this.setState(() => ({ renderVis: null }));
-      this.loadAsyncVis(nextProps.vizType);
-    }
-  }
-
-  componentDidUpdate(prevProps) {
-    if (
-        this.props.queryResponse &&
-        ['success', 'rendered'].indexOf(this.props.chartStatus) > -1 &&
-        !this.props.queryResponse.error && (
-        prevProps.annotationData !== this.props.annotationData ||
-        prevProps.queryResponse !== this.props.queryResponse ||
-        prevProps.height !== this.props.height ||
-        prevProps.width !== this.props.width ||
-        prevProps.lastRendered !== this.props.lastRendered)
-    ) {
-      this.renderViz();
-    }
-  }
-
-  componentWillUnmount() {
-    this.visPromise = null;
-  }
-
-  getFilters() {
-    return this.props.getFilters();
-  }
-
-  setTooltip(tooltip) {
-    this.setState({ tooltip });
-  }
-
-  loadAsyncVis(visType) {
-    this.visPromise = visPromiseLookup[visType];
-
-    this.visPromise()
-      .then((renderVis) => {
-        // ensure Component is still mounted
-        if (this.visPromise) {
-          this.setState({ renderVis }, this.renderViz);
-        }
-      })
-      .catch((error) => {
-        console.error(error); // eslint-disable-line
-        this.props.actions.chartRenderingFailed(error, this.props.chartKey);
-      });
-  }
-
-  addFilter(col, vals, merge = true, refresh = true) {
-    this.props.addFilter(col, vals, merge, refresh);
-  }
-
-  clearFilter() {
-    this.props.clearFilter();
-  }
-
-  removeFilter(col, vals, refresh = true) {
-    this.props.removeFilter(col, vals, refresh);
-  }
-
-  clearError() {
-    this.setState({ errorMsg: null });
-  }
-
-  width() {
-    return this.props.width || this.container.el.offsetWidth;
-  }
-
-  headerHeight() {
-    return this.props.headerHeight || 0;
-  }
-
-  height() {
-    return this.props.height || this.container.el.offsetHeight;
-  }
-
-  d3format(col, number) {
-    const { datasource } = this.props;
-    const format = (datasource.column_formats && datasource.column_formats[col]) || '0.3s';
-
-    return d3format(format, number);
-  }
-
-  error(e) {
-    this.props.actions.chartRenderingFailed(e, this.props.chartKey);
-  }
-
-  verboseMetricName(metric) {
-    return this.props.datasource.verbose_map[metric] || metric;
-  }
-
-  render_template(s) {
-    const context = {
-      width: this.width(),
-      height: this.height(),
-    };
-    return Mustache.render(s, context);
-  }
-
-  renderTooltip() {
-    if (this.state.tooltip) {
-      /* eslint-disable react/no-danger */
-      return (
-        <Tooltip
-          className="chart-tooltip"
-          id="chart-tooltip"
-          placement="right"
-          positionTop={this.state.tooltip.y - 10}
-          positionLeft={this.state.tooltip.x + 30}
-          arrowOffsetTop={10}
-        >
-          <div dangerouslySetInnerHTML={{ __html: this.state.tooltip.content }} />
-        </Tooltip>
-      );
-      /* eslint-enable react/no-danger */
-    }
-    return null;
-  }
-
-  renderViz() {
-    const hasVisPromise = !!this.state.renderVis;
-
-    if (hasVisPromise && ['success', 'rendered'].indexOf(this.props.chartStatus) > -1) {
-      const fd = this.props.formData;
-      const qr = this.props.queryResponse;
-      const renderStart = Logger.getTimestamp();
-      try {
-        // Executing user-defined data mutator function
-        if (fd.js_data) {
-          qr.data = sandboxedEval(fd.js_data)(qr.data);
-        }
-        // [re]rendering the visualization
-        this.state.renderVis(this, qr, this.props.setControlValue);
-        Logger.append(LOG_ACTIONS_RENDER_CHART, {
-          slice_id: this.props.chartKey,
-          viz_type: this.props.vizType,
-          start_offset: renderStart,
-          duration: Logger.getTimestamp() - renderStart,
-        });
-        if (this.props.chartStatus !== 'rendered') {
-          this.props.actions.chartRenderingSucceeded(this.props.chartKey);
-        }
-      } catch (e) {
-        this.props.actions.chartRenderingFailed(e, this.props.chartKey);
-      }
-    }
-  }
-
-  render() {
-    const isLoading = this.props.chartStatus === 'loading' || !this.state.renderVis;
-
-    return (
-      <div className={`token col-md-12 ${isLoading ? 'is-loading' : ''}`}>
-        {this.renderTooltip()}
-        {isLoading &&
-          <Loading size={25} />
-        }
-        {this.props.chartAlert &&
-        <StackTraceMessage
-          message={this.props.chartAlert}
-          queryResponse={this.props.queryResponse}
-        />
-        }
-
-        {!isLoading &&
-          !this.props.chartAlert &&
-          this.props.refreshOverlayVisible &&
-          !this.props.errorMessage &&
-          this.container &&
-          <RefreshChartOverlay
-            height={this.height()}
-            width={this.width()}
-            onQuery={this.props.onQuery}
-            onDismiss={this.props.onDismissRefreshOverlay}
-          />
-        }
-        {!isLoading && !this.props.chartAlert &&
-          <ChartBody
-            containerId={this.containerId}
-            vizType={this.props.vizType}
-            height={this.height}
-            width={this.width}
-            faded={this.props.refreshOverlayVisible && !this.props.errorMessage}
-            ref={(inner) => {
-              this.container = inner;
-            }}
-          />
-        }
-      </div>
-    );
-  }
-}
-
-Chart.propTypes = propTypes;
-Chart.defaultProps = defaultProps;
-
-export default Chart;
diff --git a/superset/assets/src/dashboard/deprecated/chart/ChartBody.jsx b/superset/assets/src/dashboard/deprecated/chart/ChartBody.jsx
deleted file mode 100644
index b459f44..0000000
--- a/superset/assets/src/dashboard/deprecated/chart/ChartBody.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import $ from 'jquery';
-
-const propTypes = {
-  containerId: PropTypes.string.isRequired,
-  vizType: PropTypes.string.isRequired,
-  height: PropTypes.func.isRequired,
-  width: PropTypes.func.isRequired,
-  faded: PropTypes.bool,
-};
-
-class ChartBody extends React.PureComponent {
-  html(data) {
-    this.el.innerHTML = data;
-  }
-
-  css(property, value) {
-    this.el.style[property] = value;
-  }
-
-  get(n) {
-    return $(this.el).get(n);
-  }
-
-  find(classname) {
-    return $(this.el).find(classname);
-  }
-
-  show() {
-    return $(this.el).show();
-  }
-
-  height() {
-    return this.props.height();
-  }
-
-  width() {
-    return this.props.width();
-  }
-
-  render() {
-    return (
-      <div
-        id={this.props.containerId}
-        className={`slice_container ${this.props.vizType}${this.props.faded ? ' faded' : ''}`}
-        ref={(el) => { this.el = el; }}
-      />
-    );
-  }
-}
-
-ChartBody.propTypes = propTypes;
-
-export default ChartBody;
diff --git a/superset/assets/src/dashboard/deprecated/chart/ChartContainer.jsx b/superset/assets/src/dashboard/deprecated/chart/ChartContainer.jsx
deleted file mode 100644
index b731412..0000000
--- a/superset/assets/src/dashboard/deprecated/chart/ChartContainer.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { connect } from 'react-redux';
-import { bindActionCreators } from 'redux';
-
-import * as Actions from './chartAction';
-import Chart from './Chart';
-
-function mapStateToProps({ charts }, ownProps) {
-  const chart = charts[ownProps.chartKey];
-  return {
-    annotationData: chart.annotationData,
-    chartAlert: chart.chartAlert,
-    chartStatus: chart.chartStatus,
-    chartUpdateEndTime: chart.chartUpdateEndTime,
-    chartUpdateStartTime: chart.chartUpdateStartTime,
-    latestQueryFormData: chart.latestQueryFormData,
-    lastRendered: chart.lastRendered,
-    queryResponse: chart.queryResponse,
-    queryRequest: chart.queryRequest,
-    triggerQuery: chart.triggerQuery,
-  };
-}
-
-function mapDispatchToProps(dispatch) {
-  return {
-    actions: bindActionCreators(Actions, dispatch),
-  };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(Chart);
diff --git a/superset/assets/src/dashboard/deprecated/chart/chart.css b/superset/assets/src/dashboard/deprecated/chart/chart.css
deleted file mode 100644
index eda2054..0000000
--- a/superset/assets/src/dashboard/deprecated/chart/chart.css
+++ /dev/null
@@ -1,4 +0,0 @@
-.chart-tooltip {
-  opacity: 0.75;
-  font-size: 12px;
-}
diff --git a/superset/assets/src/dashboard/deprecated/chart/chartAction.js b/superset/assets/src/dashboard/deprecated/chart/chartAction.js
deleted file mode 100644
index 254f1d6..0000000
--- a/superset/assets/src/dashboard/deprecated/chart/chartAction.js
+++ /dev/null
@@ -1,195 +0,0 @@
-import { getExploreUrlAndPayload, getAnnotationJsonUrl } from '../../../explore/exploreUtils';
-import { requiresQuery, ANNOTATION_SOURCE_TYPES } from '../../../modules/AnnotationTypes';
-import { Logger, LOG_ACTIONS_LOAD_CHART } from '../../../logger';
-import { COMMON_ERR_MESSAGES } from '../../../common';
-import { t } from '../../../locales';
-
-const $ = window.$ = require('jquery');
-
-export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED';
-export function chartUpdateStarted(queryRequest, latestQueryFormData, key) {
-  return { type: CHART_UPDATE_STARTED, queryRequest, latestQueryFormData, key };
-}
-
-export const CHART_UPDATE_SUCCEEDED = 'CHART_UPDATE_SUCCEEDED';
-export function chartUpdateSucceeded(queryResponse, key) {
-  return { type: CHART_UPDATE_SUCCEEDED, queryResponse, key };
-}
-
-export const CHART_UPDATE_STOPPED = 'CHART_UPDATE_STOPPED';
-export function chartUpdateStopped(key) {
-  return { type: CHART_UPDATE_STOPPED, key };
-}
-
-export const CHART_UPDATE_TIMEOUT = 'CHART_UPDATE_TIMEOUT';
-export function chartUpdateTimeout(statusText, timeout, key) {
-  return { type: CHART_UPDATE_TIMEOUT, statusText, timeout, key };
-}
-
-export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED';
-export function chartUpdateFailed(queryResponse, key) {
-  return { type: CHART_UPDATE_FAILED, queryResponse, key };
-}
-
-export const CHART_RENDERING_FAILED = 'CHART_RENDERING_FAILED';
-export function chartRenderingFailed(error, key) {
-  return { type: CHART_RENDERING_FAILED, error, key };
-}
-
-export const CHART_RENDERING_SUCCEEDED = 'CHART_RENDERING_SUCCEEDED';
-export function chartRenderingSucceeded(key) {
-  return { type: CHART_RENDERING_SUCCEEDED, key };
-}
-
-export const REMOVE_CHART = 'REMOVE_CHART';
-export function removeChart(key) {
-  return { type: REMOVE_CHART, key };
-}
-
-export const ANNOTATION_QUERY_SUCCESS = 'ANNOTATION_QUERY_SUCCESS';
-export function annotationQuerySuccess(annotation, queryResponse, key) {
-  return { type: ANNOTATION_QUERY_SUCCESS, annotation, queryResponse, key };
-}
-
-export const ANNOTATION_QUERY_STARTED = 'ANNOTATION_QUERY_STARTED';
-export function annotationQueryStarted(annotation, queryRequest, key) {
-  return { type: ANNOTATION_QUERY_STARTED, annotation, queryRequest, key };
-}
-
-export const ANNOTATION_QUERY_FAILED = 'ANNOTATION_QUERY_FAILED';
-export function annotationQueryFailed(annotation, queryResponse, key) {
-  return { type: ANNOTATION_QUERY_FAILED, annotation, queryResponse, key };
-}
-
-export function runAnnotationQuery(annotation, timeout = 60, formData = null, key) {
-  return function (dispatch, getState) {
-    const sliceKey = key || Object.keys(getState().charts)[0];
-    const fd = formData || getState().charts[sliceKey].latestQueryFormData;
-
-    if (!requiresQuery(annotation.sourceType)) {
-      return Promise.resolve();
-    }
-
-    const granularity = fd.time_grain_sqla || fd.granularity;
-    fd.time_grain_sqla = granularity;
-    fd.granularity = granularity;
-
-    const sliceFormData = Object.keys(annotation.overrides)
-      .reduce((d, k) => ({
-        ...d,
-        [k]: annotation.overrides[k] || fd[k],
-      }), {});
-    const isNative = annotation.sourceType === ANNOTATION_SOURCE_TYPES.NATIVE;
-    const url = getAnnotationJsonUrl(annotation.value, sliceFormData, isNative);
-    const queryRequest = $.ajax({
-      url,
-      dataType: 'json',
-      timeout: timeout * 1000,
-    });
-    dispatch(annotationQueryStarted(annotation, queryRequest, sliceKey));
-    return queryRequest
-      .then(queryResponse => dispatch(annotationQuerySuccess(annotation, queryResponse, sliceKey)))
-      .catch((err) => {
-        if (err.statusText === 'timeout') {
-          dispatch(annotationQueryFailed(annotation, { error: 'Query Timeout' }, sliceKey));
-        } else if ((err.responseJSON.error || '').toLowerCase().startsWith('no data')) {
-          dispatch(annotationQuerySuccess(annotation, err, sliceKey));
-        } else if (err.statusText !== 'abort') {
-          dispatch(annotationQueryFailed(annotation, err.responseJSON, sliceKey));
-        }
-      });
-  };
-}
-
-export const TRIGGER_QUERY = 'TRIGGER_QUERY';
-export function triggerQuery(value = true, key) {
-  return { type: TRIGGER_QUERY, value, key };
-}
-
-// this action is used for forced re-render without fetch data
-export const RENDER_TRIGGERED = 'RENDER_TRIGGERED';
-export function renderTriggered(value, key) {
-  return { type: RENDER_TRIGGERED, value, key };
-}
-
-export const UPDATE_QUERY_FORM_DATA = 'UPDATE_QUERY_FORM_DATA';
-export function updateQueryFormData(value, key) {
-  return { type: UPDATE_QUERY_FORM_DATA, value, key };
-}
-
-export const RUN_QUERY = 'RUN_QUERY';
-export function runQuery(formData, force = false, timeout = 60, key) {
-  return (dispatch) => {
-    const { url, payload } = getExploreUrlAndPayload({
-      formData,
-      endpointType: 'json',
-      force,
-    });
-    const logStart = Logger.getTimestamp();
-    const queryRequest = $.ajax({
-      type: 'POST',
-      url,
-      dataType: 'json',
-      data: {
-        form_data: JSON.stringify(payload),
-      },
-      timeout: timeout * 1000,
-    });
-    const queryPromise = Promise.resolve(dispatch(chartUpdateStarted(queryRequest, payload, key)))
-      .then(() => queryRequest)
-      .then((queryResponse) => {
-        Logger.append(LOG_ACTIONS_LOAD_CHART, {
-          slice_id: key,
-          is_cached: queryResponse.is_cached,
-          force_refresh: force,
-          row_count: queryResponse.rowcount,
-          datasource: formData.datasource,
-          start_offset: logStart,
-          duration: Logger.getTimestamp() - logStart,
-          has_extra_filters: formData.extra_filters && formData.extra_filters.length > 0,
-          viz_type: formData.viz_type,
-        });
-        return dispatch(chartUpdateSucceeded(queryResponse, key));
-      })
-      .catch((err) => {
-        Logger.append(LOG_ACTIONS_LOAD_CHART, {
-          slice_id: key,
-          has_err: true,
-          datasource: formData.datasource,
-          start_offset: logStart,
-          duration: Logger.getTimestamp() - logStart,
-        });
-        if (err.statusText === 'timeout') {
-          dispatch(chartUpdateTimeout(err.statusText, timeout, key));
-        } else if (err.statusText === 'abort') {
-          dispatch(chartUpdateStopped(key));
-        } else {
-          let errObject;
-          if (err.responseJSON) {
-            errObject = err.responseJSON;
-          } else if (err.stack) {
-            errObject = {
-              error: t('Unexpected error: ') + err.description,
-              stacktrace: err.stack,
-            };
-          } else if (err.responseText && err.responseText.indexOf('CSRF') >= 0) {
-            errObject = {
-              error: COMMON_ERR_MESSAGES.SESSION_TIMED_OUT,
-            };
-          } else {
-            errObject = {
-              error: t('Unexpected error.'),
-            };
-          }
-          dispatch(chartUpdateFailed(errObject, key));
-        }
-      });
-    const annotationLayers = formData.annotation_layers || [];
-    return Promise.all([
-      queryPromise,
-      dispatch(triggerQuery(false, key)),
-      dispatch(updateQueryFormData(payload, key)),
-      ...annotationLayers.map(x => dispatch(runAnnotationQuery(x, timeout, formData, key))),
-    ]);
-  };
-}
diff --git a/superset/assets/src/dashboard/deprecated/chart/chartReducer.js b/superset/assets/src/dashboard/deprecated/chart/chartReducer.js
deleted file mode 100644
index 8d11249..0000000
--- a/superset/assets/src/dashboard/deprecated/chart/chartReducer.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/* eslint camelcase: 0 */
-import PropTypes from 'prop-types';
-
-import { now } from '../../../modules/dates';
-import * as actions from './chartAction';
-import { t } from '../../../locales';
-
-export const chartPropType = {
-  chartKey: PropTypes.string.isRequired,
-  chartAlert: PropTypes.string,
-  chartStatus: PropTypes.string,
-  chartUpdateEndTime: PropTypes.number,
-  chartUpdateStartTime: PropTypes.number,
-  latestQueryFormData: PropTypes.object,
-  queryRequest: PropTypes.object,
-  queryResponse: PropTypes.object,
-  triggerQuery: PropTypes.bool,
-  lastRendered: PropTypes.number,
-};
-
-export const chart = {
-  chartKey: '',
-  chartAlert: null,
-  chartStatus: 'loading',
-  chartUpdateEndTime: null,
-  chartUpdateStartTime: now(),
-  latestQueryFormData: {},
-  queryRequest: null,
-  queryResponse: null,
-  triggerQuery: true,
-  lastRendered: 0,
-};
-
-export default function chartReducer(charts = {}, action) {
-  const actionHandlers = {
-    [actions.CHART_UPDATE_SUCCEEDED](state) {
-      return { ...state,
-        chartStatus: 'success',
-        queryResponse: action.queryResponse,
-        chartUpdateEndTime: now(),
-      };
-    },
-    [actions.CHART_UPDATE_STARTED](state) {
-      return { ...state,
-        chartStatus: 'loading',
-        chartAlert: null,
-        chartUpdateEndTime: null,
-        chartUpdateStartTime: now(),
-        queryRequest: action.queryRequest,
-      };
-    },
-    [actions.CHART_UPDATE_STOPPED](state) {
-      return { ...state,
-        chartStatus: 'stopped',
-        chartAlert: t('Updating chart was stopped'),
-      };
-    },
-    [actions.CHART_RENDERING_SUCCEEDED](state) {
-      return { ...state,
-        chartStatus: 'rendered',
-      };
-    },
-    [actions.CHART_RENDERING_FAILED](state) {
-      return { ...state,
-        chartStatus: 'failed',
-        chartAlert: t('An error occurred while rendering the visualization: %s', action.error),
-      };
-    },
-    [actions.CHART_UPDATE_TIMEOUT](state) {
-      return { ...state,
-        chartStatus: 'failed',
-        chartAlert: (
-            `${t('Query timeout')} - ` +
-            t(`visualization queries are set to timeout at ${action.timeout} seconds. `) +
-            t('Perhaps your data has grown, your database is under unusual load, ' +
-                'or you are simply querying a data source that is too large ' +
-                'to be processed within the timeout range. ' +
-                'If that is the case, we recommend that you summarize your data further.')),
-      };
-    },
-    [actions.CHART_UPDATE_FAILED](state) {
-      return { ...state,
-        chartStatus: 'failed',
-        chartAlert: action.queryResponse ? action.queryResponse.error : t('Network error.'),
-        chartUpdateEndTime: now(),
-        queryResponse: action.queryResponse,
-      };
-    },
-    [actions.TRIGGER_QUERY](state) {
-      return { ...state, triggerQuery: action.value };
-    },
-    [actions.RENDER_TRIGGERED](state) {
-      return { ...state, lastRendered: action.value };
-    },
-    [actions.UPDATE_QUERY_FORM_DATA](state) {
-      return { ...state, latestQueryFormData: action.value };
-    },
-    [actions.ANNOTATION_QUERY_STARTED](state) {
-      if (state.annotationQuery &&
-        state.annotationQuery[action.annotation.name]) {
-        state.annotationQuery[action.annotation.name].abort();
-      }
-      const annotationQuery = {
-        ...state.annotationQuery,
-        [action.annotation.name]: action.queryRequest,
-      };
-      return {
-        ...state,
-        annotationQuery,
-      };
-    },
-    [actions.ANNOTATION_QUERY_SUCCESS](state) {
-      const annotationData = {
-        ...state.annotationData,
-        [action.annotation.name]: action.queryResponse.data,
-      };
-      const annotationError = { ...state.annotationError };
-      delete annotationError[action.annotation.name];
-      const annotationQuery = { ...state.annotationQuery };
-      delete annotationQuery[action.annotation.name];
-      return {
-        ...state,
-        annotationData,
-        annotationError,
-        annotationQuery,
-      };
-    },
-    [actions.ANNOTATION_QUERY_FAILED](state) {
-      const annotationData = { ...state.annotationData };
-      delete annotationData[action.annotation.name];
-      const annotationError = {
-        ...state.annotationError,
-        [action.annotation.name]: action.queryResponse ?
-          action.queryResponse.error : t('Network error.'),
-      };
-      const annotationQuery = { ...state.annotationQuery };
-      delete annotationQuery[action.annotation.name];
-      return {
-        ...state,
-        annotationData,
-        annotationError,
-        annotationQuery,
-      };
-    },
-  };
-
-  /* eslint-disable no-param-reassign */
-  if (action.type === actions.REMOVE_CHART) {
-    delete charts[action.key];
-    return charts;
-  }
-
-  if (action.type in actionHandlers) {
-    return { ...charts, [action.key]: actionHandlers[action.type](charts[action.key], action) };
-  }
-
-  return charts;
-}
diff --git a/superset/assets/src/dashboard/deprecated/v1/actions.js b/superset/assets/src/dashboard/deprecated/v1/actions.js
deleted file mode 100644
index a870120..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/actions.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/* global window */
-import $ from 'jquery';
-import { getExploreUrlAndPayload } from '../../../explore/exploreUtils';
-import { addSuccessToast, addDangerToast } from '../../../messageToasts/actions';
-
-export const ADD_FILTER = 'ADD_FILTER';
-export function addFilter(sliceId, col, vals, merge = true, refresh = true) {
-  return { type: ADD_FILTER, sliceId, col, vals, merge, refresh };
-}
-
-export const CLEAR_FILTER = 'CLEAR_FILTER';
-export function clearFilter(sliceId) {
-  return { type: CLEAR_FILTER, sliceId };
-}
-
-export const REMOVE_FILTER = 'REMOVE_FILTER';
-export function removeFilter(sliceId, col, vals, refresh = true) {
-  return { type: REMOVE_FILTER, sliceId, col, vals, refresh };
-}
-
-export const UPDATE_DASHBOARD_LAYOUT = 'UPDATE_DASHBOARD_LAYOUT';
-export function updateDashboardLayout(layout) {
-  return { type: UPDATE_DASHBOARD_LAYOUT, layout };
-}
-
-export const UPDATE_DASHBOARD_TITLE = 'UPDATE_DASHBOARD_TITLE';
-export function updateDashboardTitle(title) {
-  return { type: UPDATE_DASHBOARD_TITLE, title };
-}
-
-export function addSlicesToDashboard(dashboardId, sliceIds) {
-  return () => (
-    $.ajax({
-      type: 'POST',
-      url: `/superset/add_slices/${dashboardId}/`,
-      data: {
-        data: JSON.stringify({ slice_ids: sliceIds }),
-      },
-    })
-    .done(() => {
-      // Refresh page to allow for slices to re-render
-      window.location.reload();
-    })
-  );
-}
-
-export const REMOVE_SLICE = 'REMOVE_SLICE';
-export function removeSlice(slice) {
-  return { type: REMOVE_SLICE, slice };
-}
-
-export const UPDATE_SLICE_NAME = 'UPDATE_SLICE_NAME';
-export function updateSliceName(slice, sliceName) {
-  return { type: UPDATE_SLICE_NAME, slice, sliceName };
-}
-export function saveSlice(slice, sliceName) {
-  const oldName = slice.slice_name;
-  return (dispatch) => {
-    const sliceParams = {};
-    sliceParams.slice_id = slice.slice_id;
-    sliceParams.action = 'overwrite';
-    sliceParams.slice_name = sliceName;
-
-    const { url, payload } = getExploreUrlAndPayload({
-      formData: slice.form_data,
-      endpointType: 'base',
-      force: false,
-      curUrl: null,
-      requestParams: sliceParams,
-    });
-    return $.ajax({
-      url,
-      type: 'POST',
-      data: {
-        form_data: JSON.stringify(payload),
-      },
-      success: () => {
-        dispatch(updateSliceName(slice, sliceName));
-        dispatch(addSuccessToast('This slice name was saved successfully.'));
-      },
-      error: () => {
-        // if server-side reject the overwrite action,
-        // revert to old state
-        dispatch(updateSliceName(slice, oldName));
-        dispatch(addDangerToast("You don't have the rights to alter this slice"));
-      },
-    });
-  };
-}
-
-const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard';
-export const TOGGLE_FAVE_STAR = 'TOGGLE_FAVE_STAR';
-export function toggleFaveStar(isStarred) {
-  return { type: TOGGLE_FAVE_STAR, isStarred };
-}
-
-export const FETCH_FAVE_STAR = 'FETCH_FAVE_STAR';
-export function fetchFaveStar(id) {
-  return function (dispatch) {
-    const url = `${FAVESTAR_BASE_URL}/${id}/count`;
-    return $.get(url)
-      .done((data) => {
-        if (data.count > 0) {
-          dispatch(toggleFaveStar(true));
-        }
-      });
-  };
-}
-
-export const SAVE_FAVE_STAR = 'SAVE_FAVE_STAR';
-export function saveFaveStar(id, isStarred) {
-  return function (dispatch) {
-    const urlSuffix = isStarred ? 'unselect' : 'select';
-    const url = `${FAVESTAR_BASE_URL}/${id}/${urlSuffix}/`;
-    $.get(url);
-    dispatch(toggleFaveStar(!isStarred));
-  };
-}
-
-export const TOGGLE_EXPAND_SLICE = 'TOGGLE_EXPAND_SLICE';
-export function toggleExpandSlice(slice, isExpanded) {
-  return { type: TOGGLE_EXPAND_SLICE, slice, isExpanded };
-}
-
-export const SET_EDIT_MODE = 'SET_EDIT_MODE';
-export function setEditMode(editMode) {
-  return { type: SET_EDIT_MODE, editMode };
-}
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/CodeModal.jsx b/superset/assets/src/dashboard/deprecated/v1/components/CodeModal.jsx
deleted file mode 100644
index 3f802c3..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/CodeModal.jsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import ModalTrigger from '../../../../components/ModalTrigger';
-import { t } from '../../../../locales';
-
-const propTypes = {
-  triggerNode: PropTypes.node.isRequired,
-  code: PropTypes.string,
-  codeCallback: PropTypes.func,
-};
-
-const defaultProps = {
-  codeCallback: () => {},
-};
-
-export default class CodeModal extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = { code: props.code };
-  }
-  beforeOpen() {
-    let code = this.props.code;
-    if (!code && this.props.codeCallback) {
-      code = this.props.codeCallback();
-    }
-    this.setState({ code });
-  }
-  render() {
-    return (
-      <ModalTrigger
-        triggerNode={this.props.triggerNode}
-        isButton
-        beforeOpen={this.beforeOpen.bind(this)}
-        modalTitle={t('Active Dashboard Filters')}
-        modalBody={
-          <div className="CodeModal">
-            <pre>
-              {this.state.code}
-            </pre>
-          </div>
-        }
-      />
-    );
-  }
-}
-CodeModal.propTypes = propTypes;
-CodeModal.defaultProps = defaultProps;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/Controls.jsx b/superset/assets/src/dashboard/deprecated/v1/components/Controls.jsx
deleted file mode 100644
index 01d5dcf..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/Controls.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { DropdownButton, MenuItem } from 'react-bootstrap';
-
-import CssEditor from './CssEditor';
-import RefreshIntervalModal from './RefreshIntervalModal';
-import SaveModal from './SaveModal';
-import SliceAdder from './SliceAdder';
-import { t } from '../../../../locales';
-import InfoTooltipWithTrigger from '../../../../components/InfoTooltipWithTrigger';
-
-const $ = window.$ = require('jquery');
-
-const propTypes = {
-  dashboard: PropTypes.object.isRequired,
-  filters: PropTypes.object.isRequired,
-  slices: PropTypes.array,
-  userId: PropTypes.string.isRequired,
-  addSlicesToDashboard: PropTypes.func,
-  onSave: PropTypes.func,
-  onChange: PropTypes.func,
-  renderSlices: PropTypes.func,
-  serialize: PropTypes.func,
-  startPeriodicRender: PropTypes.func,
-  editMode: PropTypes.bool,
-};
-
-function MenuItemContent({ faIcon, text, tooltip, children }) {
-  return (
-    <span>
-      <i className={`fa fa-${faIcon}`} /> {text} {''}
-      <InfoTooltipWithTrigger
-        tooltip={tooltip}
-        label={`dash-${faIcon}`}
-        placement="top"
-      />
-      {children}
-    </span>
-  );
-}
-MenuItemContent.propTypes = {
-  faIcon: PropTypes.string.isRequired,
-  text: PropTypes.string,
-  tooltip: PropTypes.string,
-  children: PropTypes.node,
-};
-
-function ActionMenuItem(props) {
-  return (
-    <MenuItem onClick={props.onClick}>
-      <MenuItemContent {...props} />
-    </MenuItem>
-  );
-}
-ActionMenuItem.propTypes = {
-  onClick: PropTypes.func,
-};
-
-class Controls extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      css: props.dashboard.css || '',
-      cssTemplates: [],
-    };
-    this.refresh = this.refresh.bind(this);
-    this.toggleModal = this.toggleModal.bind(this);
-    this.updateDom = this.updateDom.bind(this);
-  }
-  componentWillMount() {
-    this.updateDom(this.state.css);
-
-    $.get('/csstemplateasyncmodelview/api/read', (data) => {
-      const cssTemplates = data.result.map(row => ({
-        value: row.template_name,
-        css: row.css,
-        label: row.template_name,
-      }));
-      this.setState({ cssTemplates });
-    });
-  }
-  refresh() {
-    // Force refresh all slices
-    this.props.renderSlices(true);
-  }
-  toggleModal(modal) {
-    let currentModal;
-    if (modal !== this.state.currentModal) {
-      currentModal = modal;
-    }
-    this.setState({ currentModal });
-  }
-  changeCss(css) {
-    this.setState({ css }, () => {
-      this.updateDom(css);
-    });
-    this.props.onChange();
-  }
-  updateDom(css) {
-    const className = 'CssEditor-css';
-    const head = document.head || document.getElementsByTagName('head')[0];
-    let style = document.querySelector('.' + className);
-
-    if (!style) {
-      style = document.createElement('style');
-      style.className = className;
-      style.type = 'text/css';
-      head.appendChild(style);
-    }
-    if (style.styleSheet) {
-      style.styleSheet.cssText = css;
-    } else {
-      style.innerHTML = css;
-    }
-  }
-  render() {
-    const { dashboard, userId, filters,
-      addSlicesToDashboard, startPeriodicRender,
-      serialize, onSave, editMode } = this.props;
-    const emailBody = t('Checkout this dashboard: %s', window.location.href);
-    const emailLink = 'mailto:?Subject=Superset%20Dashboard%20'
-      + `${dashboard.dashboard_title}&Body=${emailBody}`;
-    let saveText = t('Save as');
-    if (editMode) {
-      saveText = t('Save');
-    }
-    return (
-      <span>
-        <DropdownButton title="Actions" bsSize="small" id="bg-nested-dropdown" pullRight>
-          <ActionMenuItem
-            text={t('Force Refresh')}
-            tooltip={t('Force refresh the whole dashboard')}
-            faIcon="refresh"
-            onClick={this.refresh}
-          />
-          <RefreshIntervalModal
-            onChange={refreshInterval => startPeriodicRender(refreshInterval * 1000)}
-            triggerNode={
-              <MenuItemContent
-                text={t('Set autorefresh')}
-                tooltip={t('Set the auto-refresh interval for this session')}
-                faIcon="clock-o"
-              />
-            }
-          />
-          {dashboard.dash_save_perm &&
-            !dashboard.forceV2Edit &&
-            <SaveModal
-              dashboard={dashboard}
-              filters={filters}
-              serialize={serialize}
-              onSave={onSave}
-              css={this.state.css}
-              triggerNode={
-                <MenuItemContent
-                  text={saveText}
-                  tooltip={t('Save the dashboard')}
-                  faIcon="save"
-                />
-              }
-            />
-          }
-          {editMode &&
-            <ActionMenuItem
-              text={t('Edit properties')}
-              tooltip={t("Edit the dashboards's properties")}
-              faIcon="edit"
-              onClick={() => { window.location = `/dashboardmodelview/edit/${dashboard.id}`; }}
-            />
-          }
-          {editMode &&
-            <ActionMenuItem
-              text={t('Email')}
-              tooltip={t('Email a link to this dashboard')}
-              onClick={() => { window.location = emailLink; }}
-              faIcon="envelope"
-            />
-          }
-          {editMode &&
-            <SliceAdder
-              dashboard={dashboard}
-              addSlicesToDashboard={addSlicesToDashboard}
-              userId={userId}
-              triggerNode={
-                <MenuItemContent
-                  text={t('Add Charts')}
-                  tooltip={t('Add some charts to this dashboard')}
-                  faIcon="plus"
-                />
-              }
-            />
-          }
-          {editMode &&
-            <CssEditor
-              dashboard={dashboard}
-              triggerNode={
-                <MenuItemContent
-                  text={t('Edit CSS')}
-                  tooltip={t('Change the style of the dashboard using CSS code')}
-                  faIcon="css3"
-                />
-              }
-              initialCss={this.state.css}
-              templates={this.state.cssTemplates}
-              onChange={this.changeCss.bind(this)}
-            />
-          }
-        </DropdownButton>
-      </span>
-    );
-  }
-}
-Controls.propTypes = propTypes;
-
-export default Controls;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/CssEditor.jsx b/superset/assets/src/dashboard/deprecated/v1/components/CssEditor.jsx
deleted file mode 100644
index ee11ff2..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/CssEditor.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Select from 'react-select';
-
-import AceEditor from 'react-ace';
-import 'brace/mode/css';
-import 'brace/theme/github';
-
-import ModalTrigger from '../../../../components/ModalTrigger';
-import { t } from '../../../../locales';
-
-const propTypes = {
-  initialCss: PropTypes.string,
-  triggerNode: PropTypes.node.isRequired,
-  onChange: PropTypes.func,
-  templates: PropTypes.array,
-};
-
-const defaultProps = {
-  initialCss: '',
-  onChange: () => {},
-  templates: [],
-};
-
-class CssEditor extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      css: props.initialCss,
-      cssTemplateOptions: [],
-    };
-  }
-  changeCss(css) {
-    this.setState({ css }, () => {
-      this.props.onChange(css);
-    });
-  }
-  changeCssTemplate(opt) {
-    this.changeCss(opt.css);
-  }
-  renderTemplateSelector() {
-    if (this.props.templates) {
-      return (
-        <div style={{ zIndex: 10 }}>
-          <h5>{t('Load a template')}</h5>
-          <Select
-            options={this.props.templates}
-            placeholder={t('Load a CSS template')}
-            onChange={this.changeCssTemplate.bind(this)}
-          />
-        </div>
-      );
-    }
-    return null;
-  }
-  render() {
-    return (
-      <ModalTrigger
-        triggerNode={this.props.triggerNode}
-        modalTitle={t('CSS')}
-        isMenuItem
-        modalBody={
-          <div>
-            {this.renderTemplateSelector()}
-            <div style={{ zIndex: 1 }}>
-              <h5>{t('Live CSS Editor')}</h5>
-              <div style={{ border: 'solid 1px grey' }}>
-                <AceEditor
-                  mode="css"
-                  theme="github"
-                  minLines={8}
-                  maxLines={30}
-                  onChange={this.changeCss.bind(this)}
-                  height="200px"
-                  width="100%"
-                  editorProps={{ $blockScrolling: true }}
-                  enableLiveAutocompletion
-                  value={this.state.css || ''}
-                />
-              </div>
-            </div>
-          </div>
-        }
-      />
-    );
-  }
-}
-CssEditor.propTypes = propTypes;
-CssEditor.defaultProps = defaultProps;
-
-export default CssEditor;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/Dashboard.jsx b/superset/assets/src/dashboard/deprecated/v1/components/Dashboard.jsx
deleted file mode 100644
index b9e6240..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/Dashboard.jsx
+++ /dev/null
@@ -1,441 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import ToastsPresenter from '../../../../messageToasts/containers/ToastPresenter';
-import GridLayout from './GridLayout';
-import Header from './Header';
-import { exportChart } from '../../../../explore/exploreUtils';
-import { areObjectsEqual } from '../../../../reduxUtils';
-import {
-  Logger,
-  ActionLog,
-  DASHBOARD_EVENT_NAMES,
-  LOG_ACTIONS_PREVIEW_V2,
-  LOG_ACTIONS_MOUNT_DASHBOARD,
-  LOG_ACTIONS_EXPLORE_DASHBOARD_CHART,
-  LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART,
-  LOG_ACTIONS_FIRST_DASHBOARD_LOAD,
-  LOG_ACTIONS_REFRESH_CHART,
-  LOG_ACTIONS_REFRESH_DASHBOARD,
-} from '../../../../logger';
-
-import { t } from '../../../../locales';
-
-import '../../../../../stylesheets/dashboard_deprecated.css';
-
-const propTypes = {
-  actions: PropTypes.object,
-  initMessages: PropTypes.array,
-  dashboard: PropTypes.object.isRequired,
-  slices: PropTypes.object,
-  datasources: PropTypes.object,
-  filters: PropTypes.object,
-  refresh: PropTypes.bool,
-  timeout: PropTypes.number,
-  userId: PropTypes.string,
-  isStarred: PropTypes.bool,
-  editMode: PropTypes.bool,
-  impressionId: PropTypes.string,
-};
-
-const defaultProps = {
-  initMessages: [],
-  dashboard: {},
-  slices: {},
-  datasources: {},
-  filters: {},
-  refresh: false,
-  timeout: 60,
-  userId: '',
-  isStarred: false,
-  editMode: false,
-};
-
-class Dashboard extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.refreshTimer = null;
-    this.firstLoad = true;
-    this.loadingLog = new ActionLog({
-      impressionId: props.impressionId,
-      source: 'dashboard',
-      sourceId: props.dashboard.id,
-      eventNames: DASHBOARD_EVENT_NAMES,
-    });
-    Logger.start(this.loadingLog);
-
-    // alert for unsaved changes
-    this.state = {
-      unsavedChanges: false,
-    };
-    this.handleSetEditMode = this.handleSetEditMode.bind(this);
-    this.handleConvertToV2 = this.handleConvertToV2.bind(this);
-
-    this.rerenderCharts = this.rerenderCharts.bind(this);
-    this.updateDashboardTitle = this.updateDashboardTitle.bind(this);
-    this.onSave = this.onSave.bind(this);
-    this.onChange = this.onChange.bind(this);
-    this.serialize = this.serialize.bind(this);
-    this.fetchAllSlices = this.fetchSlices.bind(this, this.getAllSlices());
-    this.startPeriodicRender = this.startPeriodicRender.bind(this);
-    this.addSlicesToDashboard = this.addSlicesToDashboard.bind(this);
-    this.fetchSlice = this.fetchSlice.bind(this);
-    this.getFormDataExtra = this.getFormDataExtra.bind(this);
-    this.exploreChart = this.exploreChart.bind(this);
-    this.exportCSV = this.exportCSV.bind(this);
-    this.props.actions.fetchFaveStar = this.props.actions.fetchFaveStar.bind(this);
-    this.props.actions.saveFaveStar = this.props.actions.saveFaveStar.bind(this);
-    this.props.actions.saveSlice = this.props.actions.saveSlice.bind(this);
-    this.props.actions.removeSlice = this.props.actions.removeSlice.bind(this);
-    this.props.actions.removeChart = this.props.actions.removeChart.bind(this);
-    this.props.actions.updateDashboardLayout = this.props.actions.updateDashboardLayout.bind(this);
-    this.props.actions.toggleExpandSlice = this.props.actions.toggleExpandSlice.bind(this);
-    this.props.actions.addFilter = this.props.actions.addFilter.bind(this);
-    this.props.actions.clearFilter = this.props.actions.clearFilter.bind(this);
-    this.props.actions.removeFilter = this.props.actions.removeFilter.bind(this);
-  }
-
-  componentDidMount() {
-    window.addEventListener('resize', this.rerenderCharts);
-    this.ts_mount = new Date().getTime();
-    Logger.append(LOG_ACTIONS_MOUNT_DASHBOARD, { version: 'v1' });
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (this.firstLoad &&
-      Object.values(nextProps.slices)
-        .every(slice => (['rendered', 'failed', 'stopped'].indexOf(slice.chartStatus) > -1))
-    ) {
-      Logger.append(LOG_ACTIONS_FIRST_DASHBOARD_LOAD, {
-        duration: new Date().getTime() - this.ts_mount,
-        version: 'v1',
-      });
-      Logger.send(this.loadingLog);
-      this.firstLoad = false;
-    }
-  }
-
-  componentDidUpdate(prevProps) {
-    if (this.props.refresh) {
-      let changedFilterKey;
-      const prevFiltersKeySet = new Set(Object.keys(prevProps.filters));
-      Object.keys(this.props.filters).some((key) => {
-        prevFiltersKeySet.delete(key);
-        if (prevProps.filters[key] === undefined ||
-          !areObjectsEqual(prevProps.filters[key], this.props.filters[key])) {
-          changedFilterKey = key;
-          return true;
-        }
-        return false;
-      });
-      // has changed filter or removed a filter?
-      if (!!changedFilterKey || prevFiltersKeySet.size) {
-        this.refreshExcept(changedFilterKey);
-      }
-    }
-  }
-
-  componentWillUnmount() {
-    window.removeEventListener('resize', this.rerenderCharts);
-  }
-
-  onBeforeUnload(hasChanged) {
-    if (hasChanged) {
-      window.addEventListener('beforeunload', this.unload);
-    } else {
-      window.removeEventListener('beforeunload', this.unload);
-    }
-  }
-
-  onChange() {
-    this.onBeforeUnload(true);
-    this.setState({ unsavedChanges: true });
-  }
-
-  onSave() {
-    this.onBeforeUnload(false);
-    this.setState({ unsavedChanges: false });
-  }
-
-  // return charts in array
-  getAllSlices() {
-    return Object.values(this.props.slices);
-  }
-
-  getFormDataExtra(slice) {
-    const formDataExtra = Object.assign({}, slice.formData);
-    formDataExtra.extra_filters = this.effectiveExtraFilters(slice.slice_id);
-    return formDataExtra;
-  }
-
-  getFilters(sliceId) {
-    return this.props.filters[sliceId];
-  }
-
-  unload() {
-    const message = t('You have unsaved changes.');
-    window.event.returnValue = message; // Gecko + IE
-    return message; // Gecko + Webkit, Safari, Chrome etc.
-  }
-
-  effectiveExtraFilters(sliceId) {
-    const metadata = this.props.dashboard.metadata;
-    const filters = this.props.filters;
-    const f = [];
-    const immuneSlices = metadata.filter_immune_slices || [];
-    if (sliceId && immuneSlices.includes(sliceId)) {
-      // The slice is immune to dashboard filters
-      return f;
-    }
-
-    // Building a list of fields the slice is immune to filters on
-    let immuneToFields = [];
-    if (
-      sliceId &&
-      metadata.filter_immune_slice_fields &&
-      metadata.filter_immune_slice_fields[sliceId]) {
-      immuneToFields = metadata.filter_immune_slice_fields[sliceId];
-    }
-    for (const filteringSliceId in filters) {
-      if (filteringSliceId === sliceId.toString()) {
-        // Filters applied by the slice don't apply to itself
-        continue;
-      }
-      for (const field in filters[filteringSliceId]) {
-        if (!immuneToFields.includes(field)) {
-          f.push({
-            col: field,
-            op: 'in',
-            val: filters[filteringSliceId][field],
-          });
-        }
-      }
-    }
-    return f;
-  }
-
-  refreshExcept(filterKey) {
-    const immune = this.props.dashboard.metadata.filter_immune_slices || [];
-    let slices = this.getAllSlices();
-    if (filterKey) {
-      slices = slices.filter(slice => (
-        String(slice.slice_id) !== filterKey &&
-        immune.indexOf(slice.slice_id) === -1
-      ));
-    }
-    this.fetchSlices(slices);
-  }
-
-  stopPeriodicRender() {
-    if (this.refreshTimer) {
-      clearTimeout(this.refreshTimer);
-      this.refreshTimer = null;
-    }
-  }
-
-  startPeriodicRender(interval) {
-    this.stopPeriodicRender();
-    const immune = this.props.dashboard.metadata.timed_refresh_immune_slices || [];
-    const refreshAll = () => {
-      const affectedSlices = this.getAllSlices()
-        .filter(slice => immune.indexOf(slice.slice_id) === -1);
-      this.fetchSlices(affectedSlices, true, interval * 0.2);
-    };
-    const fetchAndRender = () => {
-      refreshAll();
-      if (interval > 0) {
-        this.refreshTimer = setTimeout(fetchAndRender, interval);
-      }
-    };
-
-    fetchAndRender();
-  }
-
-  updateDashboardTitle(title) {
-    this.props.actions.updateDashboardTitle(title);
-    this.onChange();
-  }
-
-  serialize() {
-    return this.props.dashboard.layout.map(reactPos => ({
-      slice_id: reactPos.i,
-      col: reactPos.x + 1,
-      row: reactPos.y,
-      size_x: reactPos.w,
-      size_y: reactPos.h,
-    }));
-  }
-
-  addSlicesToDashboard(sliceIds) {
-    return this.props.actions.addSlicesToDashboard(this.props.dashboard.id, sliceIds);
-  }
-
-  fetchSlice(slice, force = false, fetchingAllSlices = false) {
-    if (force && !fetchingAllSlices) {
-      const chartQuery = (this.props.slices[slice.chartKey] || {}).queryResponse;
-      Logger.append(
-        LOG_ACTIONS_REFRESH_CHART,
-        {
-          slice_id: slice.slice_id,
-          is_cached: chartQuery.is_cached,
-          version: 'v1',
-        },
-        true,
-      );
-    }
-    return this.props.actions.runQuery(
-      this.getFormDataExtra(slice), force, this.props.timeout, slice.chartKey,
-    );
-  }
-
-  // fetch and render an list of slices
-  fetchSlices(slc, force = false, interval = 0) {
-    const slices = slc || this.getAllSlices();
-    Logger.append(
-      LOG_ACTIONS_REFRESH_DASHBOARD,
-      {
-        force,
-        interval,
-        chartCount: slices.length,
-        version: 'v1',
-      },
-      true,
-    );
-    if (!interval) {
-      slices.forEach((slice) => { this.fetchSlice(slice, force, true); });
-      return;
-    }
-
-    const meta = this.props.dashboard.metadata;
-    const refreshTime = Math.max(interval, meta.stagger_time || 5000); // default 5 seconds
-    if (typeof meta.stagger_refresh !== 'boolean') {
-      meta.stagger_refresh = meta.stagger_refresh === undefined ?
-        true : meta.stagger_refresh === 'true';
-    }
-    const delay = meta.stagger_refresh ? refreshTime / (slices.length - 1) : 0;
-    slices.forEach((slice, i) => {
-      setTimeout(() => { this.fetchSlice(slice, force, true); }, delay * i);
-    });
-  }
-
-  exploreChart(slice) {
-    const chartQuery = (this.props.slices[slice.chartKey] || {}).queryResponse;
-    Logger.append(
-      LOG_ACTIONS_EXPLORE_DASHBOARD_CHART,
-      {
-        slice_id: slice.slice_id,
-        is_cached: chartQuery && chartQuery.is_cached,
-        version: 'v1',
-      },
-      true,
-    );
-    const formData = this.getFormDataExtra(slice);
-    exportChart(formData);
-  }
-
-  exportCSV(slice) {
-    const chartQuery = (this.props.slices[slice.chartKey] || {}).queryResponse;
-    Logger.append(
-      LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART,
-      {
-        slice_id: slice.slice_id,
-        is_cached: chartQuery && chartQuery.is_cached,
-        version: 'v1',
-      },
-      true,
-    );
-    const formData = this.getFormDataExtra(slice);
-    exportChart(formData, 'csv');
-  }
-
-  handleConvertToV2(editMode) {
-    Logger.append(
-      LOG_ACTIONS_PREVIEW_V2,
-      {
-        force_v2_edit: this.props.dashboard.forceV2Edit,
-        edit_mode: editMode === true,
-      },
-      true,
-    );
-    const url = new URL(window.location); // eslint-disable-line
-    url.searchParams.set('version', 'v2');
-    if (editMode === true) url.searchParams.set('edit', true);
-    window.location = url; // eslint-disable-line
-  }
-
-  handleSetEditMode(nextEditMode) {
-    if (this.props.dashboard.forceV2Edit) {
-      this.handleConvertToV2(true);
-    } else {
-      this.props.actions.setEditMode(nextEditMode);
-    }
-  }
-
-  // re-render chart without fetch
-  rerenderCharts() {
-    this.getAllSlices().forEach((slice) => {
-      setTimeout(() => {
-        this.props.actions.renderTriggered(new Date().getTime(), slice.chartKey);
-      }, 50);
-    });
-  }
-
-  render() {
-    const { dashboard, editMode } = this.props;
-    return (
-      <div id="dashboard-container">
-        <div id="dashboard-header">
-          <ToastsPresenter />
-          <Header
-            dashboard={this.props.dashboard}
-            unsavedChanges={this.state.unsavedChanges}
-            filters={this.props.filters}
-            userId={this.props.userId}
-            isStarred={this.props.isStarred}
-            updateDashboardTitle={this.updateDashboardTitle}
-            onSave={this.onSave}
-            onChange={this.onChange}
-            serialize={this.serialize}
-            fetchFaveStar={this.props.actions.fetchFaveStar}
-            saveFaveStar={this.props.actions.saveFaveStar}
-            renderSlices={this.fetchAllSlices}
-            startPeriodicRender={this.startPeriodicRender}
-            addSlicesToDashboard={this.addSlicesToDashboard}
-            editMode={this.props.editMode}
-            setEditMode={this.handleSetEditMode}
-            handleConvertToV2={this.handleConvertToV2}
-          />
-        </div>
-        <div id="grid-container" className="slice-grid gridster">
-          <GridLayout
-            dashboard={this.props.dashboard}
-            datasources={this.props.datasources}
-            filters={this.props.filters}
-            charts={this.props.slices}
-            timeout={this.props.timeout}
-            onChange={this.onChange}
-            getFormDataExtra={this.getFormDataExtra}
-            exploreChart={this.exploreChart}
-            exportCSV={this.exportCSV}
-            fetchSlice={this.fetchSlice}
-            saveSlice={this.props.actions.saveSlice}
-            removeSlice={this.props.actions.removeSlice}
-            removeChart={this.props.actions.removeChart}
-            updateDashboardLayout={this.props.actions.updateDashboardLayout}
-            toggleExpandSlice={this.props.actions.toggleExpandSlice}
-            addFilter={this.props.actions.addFilter}
-            getFilters={this.getFilters}
-            clearFilter={this.props.actions.clearFilter}
-            removeFilter={this.props.actions.removeFilter}
-            editMode={this.props.editMode}
-          />
-        </div>
-      </div>
-    );
-  }
-}
-
-Dashboard.propTypes = propTypes;
-Dashboard.defaultProps = defaultProps;
-
-export default Dashboard;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/DashboardContainer.jsx b/superset/assets/src/dashboard/deprecated/v1/components/DashboardContainer.jsx
deleted file mode 100644
index a18a5d2..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/DashboardContainer.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { bindActionCreators } from 'redux';
-import { connect } from 'react-redux';
-
-import * as dashboardActions from '../actions';
-import * as chartActions from '../../chart/chartAction';
-import Dashboard from './Dashboard';
-
-function mapStateToProps({ charts, dashboard, impressionId }) {
-  return {
-    initMessages: dashboard.common.flash_messages,
-    timeout: dashboard.common.conf.SUPERSET_WEBSERVER_TIMEOUT,
-    dashboard: dashboard.dashboard,
-    slices: charts,
-    datasources: dashboard.datasources,
-    filters: dashboard.filters,
-    refresh: !!dashboard.refresh,
-    userId: dashboard.userId,
-    isStarred: !!dashboard.isStarred,
-    editMode: dashboard.editMode,
-    impressionId,
-  };
-}
-
-function mapDispatchToProps(dispatch) {
-  const actions = { ...chartActions, ...dashboardActions };
-  return {
-    actions: bindActionCreators(actions, dispatch),
-  };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/GridCell.jsx b/superset/assets/src/dashboard/deprecated/v1/components/GridCell.jsx
deleted file mode 100644
index d68b427..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/GridCell.jsx
+++ /dev/null
@@ -1,157 +0,0 @@
-/* eslint-disable react/no-danger */
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import SliceHeader from './SliceHeader';
-import ChartContainer from '../../chart/ChartContainer';
-
-import '../../../../../stylesheets/dashboard_deprecated.css';
-
-const propTypes = {
-  timeout: PropTypes.number,
-  datasource: PropTypes.object,
-  isLoading: PropTypes.bool,
-  isCached: PropTypes.bool,
-  cachedDttm: PropTypes.string,
-  isExpanded: PropTypes.bool,
-  widgetHeight: PropTypes.number,
-  widgetWidth: PropTypes.number,
-  slice: PropTypes.object,
-  chartKey: PropTypes.string,
-  formData: PropTypes.object,
-  filters: PropTypes.object,
-  forceRefresh: PropTypes.func,
-  removeSlice: PropTypes.func,
-  updateSliceName: PropTypes.func,
-  toggleExpandSlice: PropTypes.func,
-  exploreChart: PropTypes.func,
-  exportCSV: PropTypes.func,
-  addFilter: PropTypes.func,
-  getFilters: PropTypes.func,
-  clearFilter: PropTypes.func,
-  removeFilter: PropTypes.func,
-  editMode: PropTypes.bool,
-  annotationQuery: PropTypes.object,
-};
-
-const defaultProps = {
-  forceRefresh: () => ({}),
-  removeSlice: () => ({}),
-  updateSliceName: () => ({}),
-  toggleExpandSlice: () => ({}),
-  exploreChart: () => ({}),
-  exportCSV: () => ({}),
-  addFilter: () => ({}),
-  getFilters: () => ({}),
-  clearFilter: () => ({}),
-  removeFilter: () => ({}),
-  editMode: false,
-};
-
-class GridCell extends React.PureComponent {
-  constructor(props) {
-    super(props);
-
-    const sliceId = this.props.slice.slice_id;
-    this.addFilter = this.props.addFilter.bind(this, sliceId);
-    this.getFilters = this.props.getFilters.bind(this, sliceId);
-    this.clearFilter = this.props.clearFilter.bind(this, sliceId);
-    this.removeFilter = this.props.removeFilter.bind(this, sliceId);
-  }
-
-  getDescriptionId(slice) {
-    return 'description_' + slice.slice_id;
-  }
-
-  getHeaderId(slice) {
-    return 'header_' + slice.slice_id;
-  }
-
-  width() {
-    return this.props.widgetWidth - 10;
-  }
-
-  height(slice) {
-    const widgetHeight = this.props.widgetHeight;
-    const headerHeight = this.headerHeight(slice);
-    const descriptionId = this.getDescriptionId(slice);
-    let descriptionHeight = 0;
-    if (this.props.isExpanded && this.refs[descriptionId]) {
-      descriptionHeight = this.refs[descriptionId].offsetHeight + 10;
-    }
-
-    return widgetHeight - headerHeight - descriptionHeight;
-  }
-
-  headerHeight(slice) {
-    const headerId = this.getHeaderId(slice);
-    return this.refs[headerId] ? this.refs[headerId].offsetHeight : 30;
-  }
-
-  render() {
-    const {
-      isExpanded, isLoading, isCached, cachedDttm,
-      removeSlice, updateSliceName, toggleExpandSlice, forceRefresh,
-      chartKey, slice, datasource, formData, timeout, annotationQuery,
-      exploreChart, exportCSV,
-    } = this.props;
-    return (
-      <div
-        className={isLoading ? 'slice-cell-highlight' : 'slice-cell'}
-        id={`${slice.slice_id}-cell`}
-      >
-        <div ref={this.getHeaderId(slice)}>
-          <SliceHeader
-            slice={slice}
-            isExpanded={isExpanded}
-            isCached={isCached}
-            cachedDttm={cachedDttm}
-            removeSlice={removeSlice}
-            updateSliceName={updateSliceName}
-            toggleExpandSlice={toggleExpandSlice}
-            forceRefresh={forceRefresh}
-            editMode={this.props.editMode}
-            annotationQuery={annotationQuery}
-            exploreChart={exploreChart}
-            exportCSV={exportCSV}
-          />
-        </div>
-        {
-        /* This usage of dangerouslySetInnerHTML is safe since it is being used to render
-           markdown that is sanitized with bleach. See:
-             https://github.com/apache/incubator-superset/pull/4390
-           and
-             https://github.com/apache/incubator-superset/commit/b6fcc22d5a2cb7a5e92599ed5795a0169385a825 */}
-        <div
-          className="slice_description bs-callout bs-callout-default"
-          style={isExpanded ? {} : { display: 'none' }}
-          ref={this.getDescriptionId(slice)}
-          dangerouslySetInnerHTML={{ __html: slice.description_markeddown }}
-        />
-        <div className="row chart-container">
-          <input type="hidden" value="false" />
-          <ChartContainer
-            containerId={`slice-container-${slice.slice_id}`}
-            chartKey={chartKey}
-            datasource={datasource}
-            formData={formData}
-            headerHeight={this.headerHeight(slice)}
-            height={this.height(slice)}
-            width={this.width()}
-            timeout={timeout}
-            vizType={slice.formData.viz_type}
-            addFilter={this.addFilter}
-            getFilters={this.getFilters}
-            clearFilter={this.clearFilter}
-            removeFilter={this.removeFilter}
-          />
-        </div>
-      </div>
-    );
-  }
-}
-
-GridCell.propTypes = propTypes;
-GridCell.defaultProps = defaultProps;
-
-export default GridCell;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/GridLayout.jsx b/superset/assets/src/dashboard/deprecated/v1/components/GridLayout.jsx
deleted file mode 100644
index ef0ec24..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/GridLayout.jsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Responsive, WidthProvider } from 'react-grid-layout';
-
-import GridCell from './GridCell';
-
-require('react-grid-layout/css/styles.css');
-require('react-resizable/css/styles.css');
-
-const ResponsiveReactGridLayout = WidthProvider(Responsive);
-
-const propTypes = {
-  dashboard: PropTypes.object.isRequired,
-  datasources: PropTypes.object,
-  charts: PropTypes.object.isRequired,
-  filters: PropTypes.object,
-  timeout: PropTypes.number,
-  onChange: PropTypes.func,
-  getFormDataExtra: PropTypes.func,
-  exploreChart: PropTypes.func,
-  exportCSV: PropTypes.func,
-  fetchSlice: PropTypes.func,
-  saveSlice: PropTypes.func,
-  removeSlice: PropTypes.func,
-  removeChart: PropTypes.func,
-  updateDashboardLayout: PropTypes.func,
-  toggleExpandSlice: PropTypes.func,
-  addFilter: PropTypes.func,
-  getFilters: PropTypes.func,
-  clearFilter: PropTypes.func,
-  removeFilter: PropTypes.func,
-  editMode: PropTypes.bool.isRequired,
-};
-
-const defaultProps = {
-  onChange: () => ({}),
-  getFormDataExtra: () => ({}),
-  exploreChart: () => ({}),
-  exportCSV: () => ({}),
-  fetchSlice: () => ({}),
-  saveSlice: () => ({}),
-  removeSlice: () => ({}),
-  removeChart: () => ({}),
-  updateDashboardLayout: () => ({}),
-  toggleExpandSlice: () => ({}),
-  addFilter: () => ({}),
-  getFilters: () => ({}),
-  clearFilter: () => ({}),
-  removeFilter: () => ({}),
-};
-
-class GridLayout extends React.Component {
-  constructor(props) {
-    super(props);
-
-    this.onResizeStop = this.onResizeStop.bind(this);
-    this.onDragStop = this.onDragStop.bind(this);
-    this.forceRefresh = this.forceRefresh.bind(this);
-    this.removeSlice = this.removeSlice.bind(this);
-    this.updateSliceName = this.props.dashboard.dash_edit_perm ?
-      this.updateSliceName.bind(this) : null;
-  }
-
-  onResizeStop(layout) {
-    this.props.updateDashboardLayout(layout);
-    this.props.onChange();
-  }
-
-  onDragStop(layout) {
-    this.props.updateDashboardLayout(layout);
-    this.props.onChange();
-  }
-
-  getWidgetId(slice) {
-    return 'widget_' + slice.slice_id;
-  }
-
-  getWidgetHeight(slice) {
-    const widgetId = this.getWidgetId(slice);
-    if (!widgetId || !this.refs[widgetId]) {
-      return 400;
-    }
-    return this.refs[widgetId].offsetHeight;
-  }
-
-  getWidgetWidth(slice) {
-    const widgetId = this.getWidgetId(slice);
-    if (!widgetId || !this.refs[widgetId]) {
-      return 400;
-    }
-    return this.refs[widgetId].offsetWidth;
-  }
-
-  findSliceIndexById(sliceId) {
-    return this.props.dashboard.slices
-      .map(slice => (slice.slice_id)).indexOf(sliceId);
-  }
-
-  forceRefresh(sliceId) {
-    return this.props.fetchSlice(this.props.charts['slice_' + sliceId], true);
-  }
-
-  removeSlice(slice) {
-    if (!slice) {
-      return;
-    }
-
-    // remove slice dashboard and charts
-    this.props.removeSlice(slice);
-    this.props.removeChart(this.props.charts['slice_' + slice.slice_id].chartKey);
-    this.props.onChange();
-  }
-
-  updateSliceName(sliceId, sliceName) {
-    const index = this.findSliceIndexById(sliceId);
-    if (index === -1) {
-      return;
-    }
-
-    const currentSlice = this.props.dashboard.slices[index];
-    if (currentSlice.slice_name === sliceName) {
-      return;
-    }
-
-    this.props.saveSlice(currentSlice, sliceName);
-  }
-
-  isExpanded(slice) {
-    return this.props.dashboard.metadata.expanded_slices &&
-      this.props.dashboard.metadata.expanded_slices[slice.slice_id];
-  }
-
-  render() {
-    const cells = this.props.dashboard.slices.map((slice) => {
-      const chartKey = `slice_${slice.slice_id}`;
-      const currentChart = this.props.charts[chartKey];
-      const queryResponse = currentChart.queryResponse || {};
-      return (
-        <div
-          id={'slice_' + slice.slice_id}
-          key={slice.slice_id}
-          data-slice-id={slice.slice_id}
-          className={`widget ${slice.form_data.viz_type}`}
-          ref={this.getWidgetId(slice)}
-        >
-          <GridCell
-            slice={slice}
-            chartKey={chartKey}
-            datasource={this.props.datasources[slice.form_data.datasource]}
-            filters={this.props.filters}
-            formData={this.props.getFormDataExtra(slice)}
-            timeout={this.props.timeout}
-            widgetHeight={this.getWidgetHeight(slice)}
-            widgetWidth={this.getWidgetWidth(slice)}
-            exploreChart={this.props.exploreChart}
-            exportCSV={this.props.exportCSV}
-            isExpanded={!!this.isExpanded(slice)}
-            isLoading={currentChart.chartStatus === 'loading'}
-            isCached={queryResponse.is_cached}
-            cachedDttm={queryResponse.cached_dttm}
-            toggleExpandSlice={this.props.toggleExpandSlice}
-            forceRefresh={this.forceRefresh}
-            removeSlice={this.removeSlice}
-            updateSliceName={this.updateSliceName}
-            addFilter={this.props.addFilter}
-            getFilters={this.props.getFilters}
-            clearFilter={this.props.clearFilter}
-            removeFilter={this.props.removeFilter}
-            editMode={this.props.editMode}
-            annotationQuery={currentChart.annotationQuery}
-            annotationError={currentChart.annotationError}
-          />
-        </div>);
-    });
-
-    return (
-      <ResponsiveReactGridLayout
-        className="layout"
-        layouts={{ lg: this.props.dashboard.layout }}
-        onResizeStop={this.onResizeStop}
-        onDragStop={this.onDragStop}
-        cols={{ lg: 48, md: 48, sm: 40, xs: 32, xxs: 24 }}
-        rowHeight={10}
-        autoSize
-        margin={[20, 20]}
-        useCSSTransforms
-        draggableHandle=".drag"
-      >
-        {cells}
-      </ResponsiveReactGridLayout>
-    );
-  }
-}
-
-GridLayout.propTypes = propTypes;
-GridLayout.defaultProps = defaultProps;
-
-export default GridLayout;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/Header.jsx b/superset/assets/src/dashboard/deprecated/v1/components/Header.jsx
deleted file mode 100644
index c801c0a..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/Header.jsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Controls from './Controls';
-import EditableTitle from '../../../../components/EditableTitle';
-import Button from '../../../../components/Button';
-import FaveStar from '../../../../components/FaveStar';
-import InfoTooltipWithTrigger from '../../../../components/InfoTooltipWithTrigger';
-import PromptV2ConversionModal from '../../PromptV2ConversionModal';
-import {
-  Logger,
-  LOG_ACTIONS_DISMISS_V2_PROMPT,
-  LOG_ACTIONS_SHOW_V2_INFO_PROMPT,
-} from '../../../../logger';
-import { t } from '../../../../locales';
-
-const propTypes = {
-  dashboard: PropTypes.object.isRequired,
-  filters: PropTypes.object.isRequired,
-  userId: PropTypes.string.isRequired,
-  isStarred: PropTypes.bool,
-  addSlicesToDashboard: PropTypes.func,
-  onSave: PropTypes.func,
-  onChange: PropTypes.func,
-  fetchFaveStar: PropTypes.func,
-  renderSlices: PropTypes.func,
-  saveFaveStar: PropTypes.func,
-  serialize: PropTypes.func,
-  startPeriodicRender: PropTypes.func,
-  updateDashboardTitle: PropTypes.func,
-  editMode: PropTypes.bool.isRequired,
-  setEditMode: PropTypes.func.isRequired,
-  handleConvertToV2: PropTypes.func.isRequired,
-  unsavedChanges: PropTypes.bool.isRequired,
-};
-
-class Header extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.handleSaveTitle = this.handleSaveTitle.bind(this);
-    this.toggleEditMode = this.toggleEditMode.bind(this);
-    this.state = {
-      showV2PromptModal: props.dashboard.promptV2Conversion,
-    };
-    this.toggleShowV2PromptModal = this.toggleShowV2PromptModal.bind(this);
-  }
-  handleSaveTitle(title) {
-    this.props.updateDashboardTitle(title);
-  }
-  toggleEditMode() {
-    this.props.setEditMode(!this.props.editMode);
-  }
-  toggleShowV2PromptModal() {
-    const nextShowModal = !this.state.showV2PromptModal;
-    this.setState({ showV2PromptModal: nextShowModal });
-    if (nextShowModal) {
-      Logger.append(
-        LOG_ACTIONS_SHOW_V2_INFO_PROMPT,
-        {
-          force_v2_edit: this.props.dashboard.forceV2Edit,
-        },
-        true,
-      );
-    } else {
-      Logger.append(
-        LOG_ACTIONS_DISMISS_V2_PROMPT,
-        {
-          force_v2_edit: this.props.dashboard.forceV2Edit,
-        },
-        true,
-      );
-    }
-  }
-  renderUnsaved() {
-    if (!this.props.unsavedChanges) {
-      return null;
-    }
-    return (
-      <InfoTooltipWithTrigger
-        label="unsaved"
-        tooltip={t('Unsaved changes')}
-        icon="exclamation-triangle"
-        className="text-danger m-r-5"
-        placement="top"
-      />
-    );
-  }
-  renderEditButton() {
-    if (!this.props.dashboard.dash_save_perm) {
-      return null;
-    }
-    const btnText = this.props.editMode ? 'Switch to View Mode' : 'Edit Dashboard';
-    return (
-      <Button
-        bsStyle="default"
-        className="m-r-5"
-        style={{ width: '150px' }}
-        onClick={this.toggleEditMode}
-      >
-        {btnText}
-      </Button>);
-  }
-  render() {
-    const dashboard = this.props.dashboard;
-    return (
-      <div className="title">
-        <div className="pull-left">
-          <h1 className="outer-container pull-left">
-            <EditableTitle
-              title={dashboard.dashboard_title}
-              canEdit={dashboard.dash_save_perm && this.props.editMode}
-              onSaveTitle={this.handleSaveTitle}
-              showTooltip={this.props.editMode}
-            />
-            <span className="favstar m-l-5">
-              <FaveStar
-                itemId={dashboard.id}
-                fetchFaveStar={this.props.fetchFaveStar}
-                saveFaveStar={this.props.saveFaveStar}
-                isStarred={this.props.isStarred}
-              />
-            </span>
-            {dashboard.promptV2Conversion && (
-              <span
-                role="none"
-                className="v2-preview-badge"
-                onClick={this.toggleShowV2PromptModal}
-              >
-                {t('Convert to v2')}
-                <span className="fa fa-info-circle m-l-5" />
-              </span>
-            )}
-            {this.renderUnsaved()}
-          </h1>
-        </div>
-        <div className="pull-right" style={{ marginTop: '35px' }}>
-          {this.renderEditButton()}
-          <Controls
-            dashboard={dashboard}
-            filters={this.props.filters}
-            userId={this.props.userId}
-            addSlicesToDashboard={this.props.addSlicesToDashboard}
-            onSave={this.props.onSave}
-            onChange={this.props.onChange}
-            renderSlices={this.props.renderSlices}
-            serialize={this.props.serialize}
-            startPeriodicRender={this.props.startPeriodicRender}
-            editMode={this.props.editMode}
-          />
-        </div>
-        <div className="clearfix" />
-        {this.state.showV2PromptModal &&
-          dashboard.promptV2Conversion &&
-          !this.props.editMode && (
-            <PromptV2ConversionModal
-              onClose={this.toggleShowV2PromptModal}
-              handleConvertToV2={this.props.handleConvertToV2}
-              forceV2Edit={dashboard.forceV2Edit}
-              v2AutoConvertDate={dashboard.v2AutoConvertDate}
-              v2FeedbackUrl={dashboard.v2FeedbackUrl}
-            />
-          )}
-      </div>
-    );
-  }
-}
-Header.propTypes = propTypes;
-
-export default Header;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/RefreshIntervalModal.jsx b/superset/assets/src/dashboard/deprecated/v1/components/RefreshIntervalModal.jsx
deleted file mode 100644
index 3e43f93..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/RefreshIntervalModal.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Select from 'react-select';
-import ModalTrigger from '../../../../components/ModalTrigger';
-import { t } from '../../../../locales';
-
-const propTypes = {
-  triggerNode: PropTypes.node.isRequired,
-  initialRefreshFrequency: PropTypes.number,
-  onChange: PropTypes.func,
-};
-
-const defaultProps = {
-  initialRefreshFrequency: 0,
-  onChange: () => {},
-};
-
-const options = [
-  [0, t('Don\'t refresh')],
-  [10, t('10 seconds')],
-  [30, t('30 seconds')],
-  [60, t('1 minute')],
-  [300, t('5 minutes')],
-  [1800, t('30 minutes')],
-  [3600, t('1 hour')],
-  [21600, t('6 hours')],
-  [43200, t('12 hours')],
-  [86400, t('24 hours')],
-].map(o => ({ value: o[0], label: o[1] }));
-
-class RefreshIntervalModal extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      refreshFrequency: props.initialRefreshFrequency,
-    };
-  }
-  render() {
-    return (
-      <ModalTrigger
-        triggerNode={this.props.triggerNode}
-        isMenuItem
-        modalTitle={t('Refresh Interval')}
-        modalBody={
-          <div>
-            {t('Choose the refresh frequency for this dashboard')}
-            <Select
-              options={options}
-              value={this.state.refreshFrequency}
-              onChange={(opt) => {
-                this.setState({ refreshFrequency: opt.value });
-                this.props.onChange(opt.value);
-              }}
-            />
-          </div>
-        }
-      />
-    );
-  }
-}
-RefreshIntervalModal.propTypes = propTypes;
-RefreshIntervalModal.defaultProps = defaultProps;
-
-export default RefreshIntervalModal;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/SaveModal.jsx b/superset/assets/src/dashboard/deprecated/v1/components/SaveModal.jsx
deleted file mode 100644
index 7a44156..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/SaveModal.jsx
+++ /dev/null
@@ -1,168 +0,0 @@
-/* global window */
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Button, FormControl, FormGroup, Radio } from 'react-bootstrap';
-import $ from 'jquery';
-
-import { getAjaxErrorMsg } from '../../../../modules/utils';
-import ModalTrigger from '../../../../components/ModalTrigger';
-import { t } from '../../../../locales';
-import Checkbox from '../../../../components/Checkbox';
-import withToasts from '../../../../messageToasts/enhancers/withToasts';
-
-const propTypes = {
-  css: PropTypes.string,
-  dashboard: PropTypes.object.isRequired,
-  triggerNode: PropTypes.node.isRequired,
-  filters: PropTypes.object.isRequired,
-  serialize: PropTypes.func,
-  onSave: PropTypes.func,
-  addSuccessToast: PropTypes.func.isRequired,
-  addDangerToast: PropTypes.func.isRequired,
-};
-
-class SaveModal extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      dashboard: props.dashboard,
-      css: props.css,
-      saveType: 'overwrite',
-      newDashName: props.dashboard.dashboard_title + ' [copy]',
-      duplicateSlices: false,
-    };
-    this.modal = null;
-    this.handleSaveTypeChange = this.handleSaveTypeChange.bind(this);
-    this.handleNameChange = this.handleNameChange.bind(this);
-    this.saveDashboard = this.saveDashboard.bind(this);
-  }
-  toggleDuplicateSlices() {
-    this.setState({ duplicateSlices: !this.state.duplicateSlices });
-  }
-  handleSaveTypeChange(event) {
-    this.setState({
-      saveType: event.target.value,
-    });
-  }
-  handleNameChange(event) {
-    this.setState({
-      newDashName: event.target.value,
-      saveType: 'newDashboard',
-    });
-  }
-  saveDashboardRequest(data, url, saveType) {
-    const saveModal = this.modal;
-    const onSaveDashboard = this.props.onSave;
-    Object.assign(data, { css: this.props.css });
-    $.ajax({
-      type: 'POST',
-      url,
-      data: {
-        data: JSON.stringify(data),
-      },
-      success: (resp) => {
-        saveModal.close();
-        onSaveDashboard();
-        if (saveType === 'newDashboard') {
-          window.location = `/superset/dashboard/${resp.id}/`;
-        } else {
-          this.props.addSuccessToast(
-            t('This dashboard was saved successfully.'),
-          );
-        }
-      },
-      error: (error) => {
-        saveModal.close();
-        const errorMsg = getAjaxErrorMsg(error);
-        this.props.addDangerToast(
-          t('Sorry, there was an error saving this dashboard: ') + errorMsg,
-        );
-      },
-    });
-  }
-  saveDashboard(saveType, newDashboardTitle) {
-    const dashboard = this.props.dashboard;
-    const positions = this.props.serialize();
-    const data = {
-      positions,
-      css: this.state.css,
-      expanded_slices: dashboard.metadata.expanded_slices || {},
-      dashboard_title: dashboard.dashboard_title,
-      default_filters: JSON.stringify(this.props.filters),
-      duplicate_slices: this.state.duplicateSlices,
-    };
-    let url = null;
-    if (saveType === 'overwrite') {
-      url = `/superset/save_dash/${dashboard.id}/`;
-      this.saveDashboardRequest(data, url, saveType);
-    } else if (saveType === 'newDashboard') {
-      if (!newDashboardTitle) {
-        this.modal.close();
-        this.props.addDangerToast(
-          t('You must pick a name for the new dashboard'),
-        );
-      } else {
-        data.dashboard_title = newDashboardTitle;
-        url = `/superset/copy_dash/${dashboard.id}/`;
-        this.saveDashboardRequest(data, url, saveType);
-      }
-    }
-  }
-  render() {
-    return (
-      <ModalTrigger
-        ref={(modal) => { this.modal = modal; }}
-        isMenuItem
-        triggerNode={this.props.triggerNode}
-        modalTitle={t('Save Dashboard')}
-        modalBody={
-          <FormGroup>
-            <Radio
-              value="overwrite"
-              onChange={this.handleSaveTypeChange}
-              checked={this.state.saveType === 'overwrite'}
-            >
-              {t('Overwrite Dashboard [%s]', this.props.dashboard.dashboard_title)}
-            </Radio>
-            <hr />
-            <Radio
-              value="newDashboard"
-              onChange={this.handleSaveTypeChange}
-              checked={this.state.saveType === 'newDashboard'}
-            >
-              {t('Save as:')}
-            </Radio>
-            <FormControl
-              type="text"
-              placeholder={t('[dashboard name]')}
-              value={this.state.newDashName}
-              onFocus={this.handleNameChange}
-              onChange={this.handleNameChange}
-            />
-            <div className="m-l-25 m-t-5">
-              <Checkbox
-                checked={this.state.duplicateSlices}
-                onChange={this.toggleDuplicateSlices.bind(this)}
-              />
-              <span className="m-l-5">also copy (duplicate) charts</span>
-            </div>
-          </FormGroup>
-        }
-        modalFooter={
-          <div>
-            <Button
-              bsStyle="primary"
-              onClick={() => { this.saveDashboard(this.state.saveType, this.state.newDashName); }}
-            >
-              {t('Save')}
-            </Button>
-          </div>
-        }
-      />
-    );
-  }
-}
-
-SaveModal.propTypes = propTypes;
-
-export default withToasts(SaveModal);
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/SliceAdder.jsx b/superset/assets/src/dashboard/deprecated/v1/components/SliceAdder.jsx
deleted file mode 100644
index 3d3e468..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/SliceAdder.jsx
+++ /dev/null
@@ -1,216 +0,0 @@
-import React from 'react';
-import $ from 'jquery';
-import PropTypes from 'prop-types';
-import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
-
-import Loading from '../../../../components/Loading';
-import ModalTrigger from '../../../../components/ModalTrigger';
-import { t } from '../../../../locales';
-
-require('react-bootstrap-table/css/react-bootstrap-table.css');
-
-const propTypes = {
-  dashboard: PropTypes.object.isRequired,
-  triggerNode: PropTypes.node.isRequired,
-  userId: PropTypes.string.isRequired,
-  addSlicesToDashboard: PropTypes.func,
-};
-
-class SliceAdder extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      slices: [],
-      slicesLoaded: false,
-      selectionMap: {},
-    };
-
-    this.options = {
-      defaultSortOrder: 'desc',
-      defaultSortName: 'modified',
-      sizePerPage: 10,
-    };
-
-    this.addSlices = this.addSlices.bind(this);
-    this.toggleSlice = this.toggleSlice.bind(this);
-
-    this.selectRowProp = {
-      mode: 'checkbox',
-      clickToSelect: true,
-      onSelect: this.toggleSlice,
-    };
-  }
-
-  componentWillUnmount() {
-    if (this.slicesRequest) {
-      this.slicesRequest.abort();
-    }
-  }
-
-  onEnterModal() {
-    const uri = `/sliceaddview/api/read?_flt_0_created_by=${this.props.userId}`;
-    this.slicesRequest = $.ajax({
-      url: uri,
-      type: 'GET',
-      success: (response) => {
-        // Prepare slice data for table
-        const slices = response.result.map(slice => ({
-          id: slice.id,
-          sliceName: slice.slice_name,
-          vizType: slice.viz_type,
-          datasourceLink: slice.datasource_link,
-          modified: slice.modified,
-        }));
-
-        this.setState({
-          slices,
-          selectionMap: {},
-          slicesLoaded: true,
-        });
-      },
-      error: (error) => {
-        this.errored = true;
-        this.setState({
-          errorMsg: t('Sorry, there was an error fetching charts to this dashboard: ') +
-          this.getAjaxErrorMsg(error),
-        });
-      },
-    });
-  }
-
-  getAjaxErrorMsg(error) {
-    const respJSON = error.responseJSON;
-    return (respJSON && respJSON.message) ? respJSON.message :
-      error.responseText;
-  }
-
-  addSlices() {
-    const adder = this;
-    this.props.addSlicesToDashboard(Object.keys(this.state.selectionMap))
-      // if successful, page will be reloaded.
-      .fail((error) => {
-        adder.errored = true;
-        adder.setState({
-          errorMsg: t('Sorry, there was an error adding charts to this dashboard: ') +
-          this.getAjaxErrorMsg(error),
-        });
-      });
-  }
-
-  toggleSlice(slice) {
-    const selectionMap = Object.assign({}, this.state.selectionMap);
-    selectionMap[slice.id] = !selectionMap[slice.id];
-    this.setState({ selectionMap });
-  }
-
-  modifiedDateComparator(a, b, order) {
-    if (order === 'desc') {
-      if (a.changed_on > b.changed_on) {
-        return -1;
-      } else if (a.changed_on < b.changed_on) {
-        return 1;
-      }
-      return 0;
-    }
-
-    if (a.changed_on < b.changed_on) {
-      return -1;
-    } else if (a.changed_on > b.changed_on) {
-      return 1;
-    }
-    return 0;
-  }
-
-  render() {
-    const hideLoad = this.state.slicesLoaded || this.errored;
-    let enableAddSlice = this.state.selectionMap && Object.keys(this.state.selectionMap);
-    if (enableAddSlice) {
-      enableAddSlice = enableAddSlice.some(function (key) {
-        return this.state.selectionMap[key];
-      }, this);
-    }
-    const modalContent = (
-      <div>
-        {!hideLoad && <Loading />}
-        <div className={this.errored ? '' : 'hidden'}>
-          {this.state.errorMsg}
-        </div>
-        <div className={this.state.slicesLoaded ? '' : 'hidden'}>
-          <BootstrapTable
-            ref="table"
-            data={this.state.slices}
-            selectRow={this.selectRowProp}
-            options={this.options}
-            hover
-            search
-            pagination
-            condensed
-            height="auto"
-          >
-            <TableHeaderColumn
-              dataField="id"
-              isKey
-              dataSort
-              hidden
-            />
-            <TableHeaderColumn
-              dataField="sliceName"
-              dataSort
-            >
-              {t('Name')}
-            </TableHeaderColumn>
-            <TableHeaderColumn
-              dataField="vizType"
-              dataSort
-            >
-              {t('Viz')}
-            </TableHeaderColumn>
-            <TableHeaderColumn
-              dataField="datasourceLink"
-              dataSort
-              // Will cause react-bootstrap-table to interpret the HTML returned
-              dataFormat={datasourceLink => datasourceLink}
-            >
-              {t('Datasource')}
-            </TableHeaderColumn>
-            <TableHeaderColumn
-              dataField="modified"
-              dataSort
-              sortFunc={this.modifiedDateComparator}
-              // Will cause react-bootstrap-table to interpret the HTML returned
-              dataFormat={modified => modified}
-            >
-              {t('Modified')}
-            </TableHeaderColumn>
-          </BootstrapTable>
-          <button
-            type="button"
-            className="btn btn-default"
-            data-dismiss="modal"
-            onClick={this.addSlices}
-            disabled={!enableAddSlice}
-          >
-            {t('Add Charts')}
-          </button>
-        </div>
-      </div>
-    );
-
-    return (
-      <ModalTrigger
-        triggerNode={this.props.triggerNode}
-        tooltip={t('Add a new chart to the dashboard')}
-        beforeOpen={this.onEnterModal.bind(this)}
-        isMenuItem
-        modalBody={modalContent}
-        bsSize="large"
-        setModalAsTriggerChildren
-        modalTitle={t('Add Charts to Dashboard')}
-      />
-    );
-  }
-}
-
-SliceAdder.propTypes = propTypes;
-
-export default SliceAdder;
diff --git a/superset/assets/src/dashboard/deprecated/v1/components/SliceHeader.jsx b/superset/assets/src/dashboard/deprecated/v1/components/SliceHeader.jsx
deleted file mode 100644
index a8c6aa7..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/components/SliceHeader.jsx
+++ /dev/null
@@ -1,194 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import moment from 'moment';
-import { connect } from 'react-redux';
-
-import { t } from '../../../../locales';
-import EditableTitle from '../../../../components/EditableTitle';
-import TooltipWrapper from '../../../../components/TooltipWrapper';
-
-const propTypes = {
-  slice: PropTypes.object.isRequired,
-  supersetCanExplore: PropTypes.bool,
-  sliceCanEdit: PropTypes.bool,
-  isExpanded: PropTypes.bool,
-  isCached: PropTypes.bool,
-  cachedDttm: PropTypes.string,
-  removeSlice: PropTypes.func,
-  updateSliceName: PropTypes.func,
-  toggleExpandSlice: PropTypes.func,
-  forceRefresh: PropTypes.func,
-  exploreChart: PropTypes.func,
-  exportCSV: PropTypes.func,
-  editMode: PropTypes.bool,
-  annotationQuery: PropTypes.object,
-  annotationError: PropTypes.object,
-};
-
-const defaultProps = {
-  forceRefresh: () => ({}),
-  removeSlice: () => ({}),
-  updateSliceName: () => ({}),
-  toggleExpandSlice: () => ({}),
-  exploreChart: () => ({}),
-  exportCSV: () => ({}),
-  editMode: false,
-};
-
-class SliceHeader extends React.PureComponent {
-  constructor(props) {
-    super(props);
-
-    this.onSaveTitle = this.onSaveTitle.bind(this);
-    this.onToggleExpandSlice = this.onToggleExpandSlice.bind(this);
-    this.exportCSV = this.props.exportCSV.bind(this, this.props.slice);
-    this.exploreChart = this.props.exploreChart.bind(this, this.props.slice);
-    this.forceRefresh = this.props.forceRefresh.bind(this, this.props.slice.slice_id);
-    this.removeSlice = this.props.removeSlice.bind(this, this.props.slice);
-  }
-
-  onSaveTitle(newTitle) {
-    if (this.props.updateSliceName) {
-      this.props.updateSliceName(this.props.slice.slice_id, newTitle);
-    }
-  }
-
-  onToggleExpandSlice() {
-    this.props.toggleExpandSlice(this.props.slice, !this.props.isExpanded);
-  }
-
-  render() {
-    const slice = this.props.slice;
-    const isCached = this.props.isCached;
-    const cachedWhen = moment.utc(this.props.cachedDttm).fromNow();
-    const refreshTooltip = isCached ?
-      t('Served from data cached %s . Click to force refresh.', cachedWhen) :
-      t('Force refresh data');
-    const annoationsLoading = t('Annotation layers are still loading.');
-    const annoationsError = t('One ore more annotation layers failed loading.');
-
-    return (
-      <div className="row chart-header">
-        <div className="col-md-12">
-          <div className="header">
-            <EditableTitle
-              title={slice.slice_name}
-              canEdit={!!this.props.updateSliceName && this.props.editMode}
-              onSaveTitle={this.onSaveTitle}
-              showTooltip={this.props.editMode}
-              noPermitTooltip={'You don\'t have the rights to alter this dashboard.'}
-            />
-            {!!Object.values(this.props.annotationQuery || {}).length &&
-              <TooltipWrapper
-                label="annotations-loading"
-                placement="top"
-                tooltip={annoationsLoading}
-              >
-                <i className="fa fa-refresh warning" />
-              </TooltipWrapper>
-            }
-            {!!Object.values(this.props.annotationError || {}).length &&
-              <TooltipWrapper
-                label="annoation-errors"
-                placement="top"
-                tooltip={annoationsError}
-              >
-                <i className="fa fa-exclamation-circle danger" />
-              </TooltipWrapper>
-            }
-          </div>
-          <div className="chart-controls">
-            <div id={'controls_' + slice.slice_id} className="pull-right">
-              {this.props.editMode &&
-                <a>
-                  <TooltipWrapper
-                    placement="top"
-                    label="move"
-                    tooltip={t('Move chart')}
-                  >
-                    <i className="fa fa-arrows drag" />
-                  </TooltipWrapper>
-                </a>
-              }
-              <a className={`refresh ${isCached ? 'danger' : ''}`} onClick={this.forceRefresh}>
-                <TooltipWrapper
-                  placement="top"
-                  label="refresh"
-                  tooltip={refreshTooltip}
-                >
-                  <i className="fa fa-repeat" />
-                </TooltipWrapper>
-              </a>
-              {slice.description &&
-              <a onClick={this.onToggleExpandSlice}>
-                <TooltipWrapper
-                  placement="top"
-                  label="description"
-                  tooltip={t('Toggle chart description')}
-                >
-                  <i className="fa fa-info-circle slice_info" />
-                </TooltipWrapper>
-              </a>
-              }
-              {this.props.sliceCanEdit &&
-                <a href={slice.edit_url} target="_blank">
-                  <TooltipWrapper
-                    placement="top"
-                    label="edit"
-                    tooltip={t('Edit chart')}
-                  >
-                    <i className="fa fa-pencil" />
-                  </TooltipWrapper>
-                </a>
-              }
-              <a className="exportCSV" onClick={this.exportCSV}>
-                <TooltipWrapper
-                  placement="top"
-                  label="exportCSV"
-                  tooltip={t('Export CSV')}
-                >
-                  <i className="fa fa-table" />
-                </TooltipWrapper>
-              </a>
-              {this.props.supersetCanExplore &&
-                <a className="exploreChart" onClick={this.exploreChart}>
-                  <TooltipWrapper
-                    placement="top"
-                    label="exploreChart"
-                    tooltip={t('Explore chart')}
-                  >
-                    <i className="fa fa-share" />
-                  </TooltipWrapper>
-                </a>
-              }
-              {this.props.editMode &&
-                <a className="remove-chart" onClick={this.removeSlice}>
-                  <TooltipWrapper
-                    placement="top"
-                    label="close"
-                    tooltip={t('Remove chart from dashboard')}
-                  >
-                    <i className="fa fa-close" />
-                  </TooltipWrapper>
-                </a>
-              }
-            </div>
-          </div>
-        </div>
-      </div>
-    );
-  }
-}
-
-SliceHeader.propTypes = propTypes;
-SliceHeader.defaultProps = defaultProps;
-
-function mapStateToProps({ dashboard }) {
-  return {
-    supersetCanExplore: dashboard.dashboard.superset_can_explore,
-    sliceCanEdit: dashboard.dashboard.slice_can_edit,
-  };
-}
-
-export { SliceHeader };
-export default connect(mapStateToProps, () => ({}))(SliceHeader);
diff --git a/superset/assets/src/dashboard/deprecated/v1/index.jsx b/superset/assets/src/dashboard/deprecated/v1/index.jsx
deleted file mode 100644
index 47d1e24..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/index.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { createStore, applyMiddleware, compose } from 'redux';
-import { Provider } from 'react-redux';
-import thunk from 'redux-thunk';
-
-import { initEnhancer } from '../../../reduxUtils';
-import { appSetup } from '../../../common';
-import { initJQueryAjax } from '../../../modules/utils';
-import DashboardContainer from './components/DashboardContainer';
-import rootReducer, { getInitialState } from './reducers';
-
-appSetup();
-initJQueryAjax();
-
-const appContainer = document.getElementById('app');
-const bootstrapData = JSON.parse(appContainer.getAttribute('data-bootstrap'));
-const initState = getInitialState(bootstrapData);
-
-const store = createStore(
-  rootReducer,
-  initState,
-  compose(
-    applyMiddleware(thunk),
-    initEnhancer(false),
-  ),
-);
-
-ReactDOM.render(
-  <Provider store={store}>
-    <DashboardContainer />
-  </Provider>,
-  appContainer,
-);
diff --git a/superset/assets/src/dashboard/deprecated/v1/reducers.js b/superset/assets/src/dashboard/deprecated/v1/reducers.js
deleted file mode 100644
index 4c2d77f..0000000
--- a/superset/assets/src/dashboard/deprecated/v1/reducers.js
+++ /dev/null
@@ -1,274 +0,0 @@
-/* eslint-disable camelcase */
-import { combineReducers } from 'redux';
-import d3 from 'd3';
-import shortid from 'shortid';
-
-import charts, { chart } from '../chart/chartReducer';
-import * as actions from './actions';
-import { getParam } from '../../../modules/utils';
-import { alterInArr, removeFromArr } from '../../../reduxUtils';
-import { applyDefaultFormData } from '../../../explore/store';
-import { getColorFromScheme } from '../../../modules/colors';
-import messageToasts from '../../../messageToasts/reducers';
-
-export function getInitialState(bootstrapData) {
-  const {
-    user_id,
-    datasources,
-    common,
-    editMode,
-    prompt_v2_conversion,
-    force_v2_edit,
-    v2_auto_convert_date,
-    v2_feedback_url,
-  } = bootstrapData;
-  delete common.locale;
-  delete common.language_pack;
-
-  const dashboard = {
-    ...bootstrapData.dashboard_data,
-    promptV2Conversion: prompt_v2_conversion,
-    forceV2Edit: force_v2_edit,
-    v2AutoConvertDate: v2_auto_convert_date,
-    v2FeedbackUrl: v2_feedback_url,
-  };
-  let filters = {};
-  try {
-    // allow request parameter overwrite dashboard metadata
-    filters = JSON.parse(
-      getParam('preselect_filters') || dashboard.metadata.default_filters,
-    );
-  } catch (e) {
-    //
-  }
-
-  // Priming the color palette with user's label-color mapping provided in
-  // the dashboard's JSON metadata
-  if (dashboard.metadata && dashboard.metadata.label_colors) {
-    const colorMap = dashboard.metadata.label_colors;
-    for (const label in colorMap) {
-      getColorFromScheme(label, null, colorMap[label]);
-    }
-  }
-
-  dashboard.posDict = {};
-  dashboard.layout = [];
-  if (Array.isArray(dashboard.position_json)) {
-    dashboard.position_json.forEach(position => {
-      dashboard.posDict[position.slice_id] = position;
-    });
-  } else {
-    dashboard.position_json = [];
-  }
-
-  const lastRowId = Math.max(
-    0,
-    Math.max.apply(
-      null,
-      dashboard.position_json.map(pos => pos.row + pos.size_y),
-    ),
-  );
-  let newSliceCounter = 0;
-  dashboard.slices.forEach(slice => {
-    const sliceId = slice.slice_id;
-    let pos = dashboard.posDict[sliceId];
-    if (!pos) {
-      // append new slices to dashboard bottom, 3 slices per row
-      pos = {
-        col: (newSliceCounter % 3) * 16 + 1,
-        row: lastRowId + Math.floor(newSliceCounter / 3) * 16,
-        size_x: 16,
-        size_y: 16,
-      };
-      newSliceCounter++;
-    }
-
-    dashboard.layout.push({
-      i: String(sliceId),
-      x: pos.col - 1,
-      y: pos.row,
-      w: pos.size_x,
-      minW: 2,
-      h: pos.size_y,
-    });
-  });
-
-  // will use charts action/reducers to handle chart render
-  const initCharts = {};
-  dashboard.slices.forEach(slice => {
-    const chartKey = `slice_${slice.slice_id}`;
-    initCharts[chartKey] = {
-      ...chart,
-      chartKey,
-      slice_id: slice.slice_id,
-      form_data: slice.form_data,
-      formData: applyDefaultFormData(slice.form_data),
-    };
-  });
-
-  // also need to add formData for dashboard.slices
-  dashboard.slices = dashboard.slices.map(slice => ({
-    ...slice,
-    formData: applyDefaultFormData(slice.form_data),
-  }));
-
-  return {
-    charts: initCharts,
-    dashboard: {
-      filters,
-      dashboard,
-      userId: user_id,
-      datasources,
-      common,
-      editMode,
-    },
-    messageToasts: [],
-  };
-}
-
-export const dashboard = function(state = {}, action) {
-  const actionHandlers = {
-    [actions.UPDATE_DASHBOARD_TITLE]() {
-      const newDashboard = {
-        ...state.dashboard,
-        dashboard_title: action.title,
-      };
-      return { ...state, dashboard: newDashboard };
-    },
-    [actions.UPDATE_DASHBOARD_LAYOUT]() {
-      const newDashboard = { ...state.dashboard, layout: action.layout };
-      return { ...state, dashboard: newDashboard };
-    },
-    [actions.REMOVE_SLICE]() {
-      const key = String(action.slice.slice_id);
-      const newLayout = state.dashboard.layout.filter(
-        reactPos => reactPos.i !== key,
-      );
-      const newDashboard = removeFromArr(
-        state.dashboard,
-        'slices',
-        action.slice,
-        'slice_id',
-      );
-      // if this slice is a filter
-      const newFilter = { ...state.filters };
-      let refresh = false;
-      if (state.filters[key]) {
-        delete newFilter[key];
-        refresh = true;
-      }
-      return {
-        ...state,
-        dashboard: { ...newDashboard, layout: newLayout },
-        filters: newFilter,
-        refresh,
-      };
-    },
-    [actions.TOGGLE_FAVE_STAR]() {
-      return { ...state, isStarred: action.isStarred };
-    },
-    [actions.SET_EDIT_MODE]() {
-      return { ...state, editMode: action.editMode };
-    },
-    [actions.TOGGLE_EXPAND_SLICE]() {
-      const updatedExpandedSlices = {
-        ...state.dashboard.metadata.expanded_slices,
-      };
-      const sliceId = action.slice.slice_id;
-      if (action.isExpanded) {
-        updatedExpandedSlices[sliceId] = true;
-      } else {
-        delete updatedExpandedSlices[sliceId];
-      }
-      const metadata = {
-        ...state.dashboard.metadata,
-        expanded_slices: updatedExpandedSlices,
-      };
-      const newDashboard = { ...state.dashboard, metadata };
-      return { ...state, dashboard: newDashboard };
-    },
-
-    // filters
-    [actions.ADD_FILTER]() {
-      const selectedSlice = state.dashboard.slices.find(
-        slice => slice.slice_id === action.sliceId,
-      );
-      if (!selectedSlice) {
-        return state;
-      }
-
-      let filters = state.filters;
-      const { sliceId, col, vals, merge, refresh } = action;
-      const filterKeys = [
-        '__time_range',
-        '__time_col',
-        '__time_grain',
-        '__time_origin',
-        '__granularity',
-      ];
-      if (
-        filterKeys.indexOf(col) >= 0 ||
-        selectedSlice.formData.groupby.indexOf(col) !== -1
-      ) {
-        let newFilter = {};
-        if (!(sliceId in filters)) {
-          // Straight up set the filters if none existed for the slice
-          newFilter = { [col]: vals };
-        } else if ((filters[sliceId] && !(col in filters[sliceId])) || !merge) {
-          newFilter = { ...filters[sliceId], [col]: vals };
-          // d3.merge pass in array of arrays while some value form filter components
-          // from and to filter box require string to be process and return
-        } else if (filters[sliceId][col] instanceof Array) {
-          newFilter[col] = d3.merge([filters[sliceId][col], vals]);
-        } else {
-          newFilter[col] = d3.merge([[filters[sliceId][col]], vals])[0] || '';
-        }
-        filters = { ...filters, [sliceId]: newFilter };
-      }
-      return { ...state, filters, refresh };
-    },
-    [actions.CLEAR_FILTER]() {
-      const newFilters = { ...state.filters };
-      delete newFilters[action.sliceId];
-      return { ...state, filter: newFilters, refresh: true };
-    },
-    [actions.REMOVE_FILTER]() {
-      const { sliceId, col, vals, refresh } = action;
-      const excluded = new Set(vals);
-      const valFilter = val => !excluded.has(val);
-
-      let filters = state.filters;
-      // Have to be careful not to modify the dashboard state so that
-      // the render actually triggers
-      if (sliceId in state.filters && col in state.filters[sliceId]) {
-        const newFilter = filters[sliceId][col].filter(valFilter);
-        filters = { ...filters, [sliceId]: newFilter };
-      }
-      return { ...state, filters, refresh };
-    },
-
-    // slice reducer
-    [actions.UPDATE_SLICE_NAME]() {
-      const newDashboard = alterInArr(
-        state.dashboard,
-        'slices',
-        action.slice,
-        { slice_name: action.sliceName },
-        'slice_id',
-      );
-      return { ...state, dashboard: newDashboard };
-    },
-  };
-
-  if (action.type in actionHandlers) {
-    return actionHandlers[action.type]();
-  }
-  return state;
-};
-
-export default combineReducers({
-  charts,
-  dashboard,
-  impressionId: () => shortid.generate(),
-  messageToasts,
-});
diff --git a/superset/assets/src/dashboard/reducers/dashboardState.js b/superset/assets/src/dashboard/reducers/dashboardState.js
index 69d4e69..1479f88 100644
--- a/superset/assets/src/dashboard/reducers/dashboardState.js
+++ b/superset/assets/src/dashboard/reducers/dashboardState.js
@@ -83,7 +83,6 @@ export default function dashboardStateReducer(state = {}, action) {
         hasUnsavedChanges: false,
         maxUndoHistoryExceeded: false,
         editMode: false,
-        isV2Preview: false, // @TODO remove upon v1 deprecation
       };
     },
 
diff --git a/superset/assets/src/dashboard/reducers/getInitialState.js b/superset/assets/src/dashboard/reducers/getInitialState.js
index 5969563..0c121d3 100644
--- a/superset/assets/src/dashboard/reducers/getInitialState.js
+++ b/superset/assets/src/dashboard/reducers/getInitialState.js
@@ -7,10 +7,9 @@ import { getParam } from '../../modules/utils';
 import { applyDefaultFormData } from '../../explore/store';
 import { getColorFromScheme } from '../../modules/colors';
 import findFirstParentContainerId from '../util/findFirstParentContainer';
-import layoutConverter from '../util/dashboardLayoutConverter';
 import getEmptyLayout from '../util/getEmptyLayout';
 import newComponentFactory from '../util/newComponentFactory';
-import { DASHBOARD_VERSION_KEY, DASHBOARD_HEADER_ID } from '../util/constants';
+import { DASHBOARD_HEADER_ID } from '../util/constants';
 import {
   DASHBOARD_HEADER_TYPE,
   CHART_TYPE,
@@ -18,15 +17,7 @@ import {
 } from '../util/componentTypes';
 
 export default function(bootstrapData) {
-  const {
-    user_id,
-    datasources,
-    common,
-    editMode,
-    force_v2_edit: forceV2Edit,
-    v2_auto_convert_date: v2AutoConvertDate,
-    v2_feedback_url: v2FeedbackUrl,
-  } = bootstrapData;
+  const { user_id, datasources, common, editMode } = bootstrapData;
   delete common.locale;
   delete common.language_pack;
 
@@ -52,12 +43,7 @@ export default function(bootstrapData) {
 
   // dashboard layout
   const { position_json: positionJson } = dashboard;
-  const shouldConvertToV2 =
-    positionJson && positionJson[DASHBOARD_VERSION_KEY] !== 'v2';
-
-  const layout = shouldConvertToV2
-    ? layoutConverter(dashboard)
-    : positionJson || getEmptyLayout();
+  const layout = positionJson || getEmptyLayout();
 
   // create a lookup to sync layout names with slice names
   const chartIdToLayoutId = {};
@@ -160,9 +146,6 @@ export default function(bootstrapData) {
       superset_can_explore: dashboard.superset_can_explore,
       slice_can_edit: dashboard.slice_can_edit,
       common,
-      v2AutoConvertDate,
-      v2FeedbackUrl,
-      forceV2Edit,
     },
     dashboardState: {
       sliceIds: Array.from(sliceIds),
@@ -174,7 +157,6 @@ export default function(bootstrapData) {
       showBuilderPane: dashboard.dash_edit_perm && editMode,
       hasUnsavedChanges: false,
       maxUndoHistoryExceeded: false,
-      isV2Preview: shouldConvertToV2,
     },
     dashboardLayout,
     messageToasts: [],
diff --git a/superset/assets/src/dashboard/stylesheets/dashboard.less b/superset/assets/src/dashboard/stylesheets/dashboard.less
index 92f1ff1..56f5d43 100644
--- a/superset/assets/src/dashboard/stylesheets/dashboard.less
+++ b/superset/assets/src/dashboard/stylesheets/dashboard.less
@@ -167,28 +167,6 @@ body {
     position: relative;
     margin-left: 8px;
   }
-
-  /* @TODO remove upon v1 deprecation */
-  .v2-preview-badge {
-    margin-left: 8px;
-    text-transform: uppercase;
-    font-size: 12px;
-    font-weight: 400;
-    background: linear-gradient(to bottom right, @pink, @purple);
-    color: white;
-    padding: 4px 8px;
-    line-height: 1em;
-    cursor: pointer;
-    opacity: 0.9;
-    flex-wrap: nowrap;
-    display: flex;
-    align-items: center;
-    white-space: nowrap;
-
-    &:hover {
-      opacity: 1;
-    }
-  }
 }
 
 .ace_gutter {
diff --git a/superset/assets/src/dashboard/util/dashboardLayoutConverter.js b/superset/assets/src/dashboard/util/dashboardLayoutConverter.js
deleted file mode 100644
index bd0674b..0000000
--- a/superset/assets/src/dashboard/util/dashboardLayoutConverter.js
+++ /dev/null
@@ -1,513 +0,0 @@
-/* eslint-disable no-param-reassign */
-/* eslint-disable camelcase */
-/* eslint-disable no-loop-func */
-import shortid from 'shortid';
-
-import getEmptyLayout from './getEmptyLayout';
-
-import {
-  ROW_TYPE,
-  COLUMN_TYPE,
-  CHART_TYPE,
-  MARKDOWN_TYPE,
-  DASHBOARD_GRID_TYPE,
-} from './componentTypes';
-
-import {
-  DASHBOARD_GRID_ID,
-  GRID_MIN_COLUMN_COUNT,
-  GRID_MIN_ROW_UNITS,
-  GRID_COLUMN_COUNT,
-} from './constants';
-
-const MAX_RECURSIVE_LEVEL = 6;
-const GRID_RATIO = 4;
-const ROW_HEIGHT = 8;
-
-/**
- *
- * @param positions: single array of slices
- * @returns boundary object {top: number, bottom: number, left: number, right: number}
- */
-function getBoundary(positions) {
-  let top = Number.MAX_VALUE;
-  let bottom = 0;
-  let left = Number.MAX_VALUE;
-  let right = 1;
-  positions.forEach(item => {
-    const { row, col, size_x, size_y } = item;
-    if (row <= top) top = row;
-    if (col <= left) left = col;
-    if (bottom <= row + size_y) bottom = row + size_y;
-    if (right <= col + size_x) right = col + size_x;
-  });
-
-  return {
-    top,
-    bottom,
-    left,
-    right,
-  };
-}
-
-function generateId() {
-  return shortid.generate();
-}
-
-function getRowContainer() {
-  return {
-    type: ROW_TYPE,
-    id: `DASHBOARD_ROW_TYPE-${generateId()}`,
-    children: [],
-    meta: {
-      background: 'BACKGROUND_TRANSPARENT',
-    },
-  };
-}
-
-function getColContainer() {
-  return {
-    type: COLUMN_TYPE,
-    id: `DASHBOARD_COLUMN_TYPE-${generateId()}`,
-    children: [],
-    meta: {
-      background: 'BACKGROUND_TRANSPARENT',
-    },
-  };
-}
-
-function getChartHolder(item) {
-  const { size_x, size_y, slice_id, code, slice_name } = item;
-
-  const width = Math.max(
-    GRID_MIN_COLUMN_COUNT,
-    Math.round(size_x / GRID_RATIO),
-  );
-  const height = Math.max(
-    GRID_MIN_ROW_UNITS,
-    Math.round(((size_y / GRID_RATIO) * 100) / ROW_HEIGHT),
-  );
-  if (code !== undefined) {
-    let markdownContent = ' '; // white-space markdown
-    if (code) {
-      markdownContent = code;
-    } else if (slice_name.trim()) {
-      markdownContent = `##### ${slice_name}`;
-    }
-
-    return {
-      type: MARKDOWN_TYPE,
-      id: `DASHBOARD_MARKDOWN_TYPE-${generateId()}`,
-      children: [],
-      meta: {
-        width,
-        height,
-        code: markdownContent,
-      },
-    };
-  }
-  return {
-    type: CHART_TYPE,
-    id: `DASHBOARD_CHART_TYPE-${generateId()}`,
-    children: [],
-    meta: {
-      width,
-      height,
-      chartId: parseInt(slice_id, 10),
-    },
-  };
-}
-
-function getChildrenMax(items, attr, layout) {
-  return Math.max.apply(null, items.map(child => layout[child].meta[attr]));
-}
-
-function getChildrenSum(items, attr, layout) {
-  return items.reduce(
-    (preValue, child) => preValue + layout[child].meta[attr],
-    0,
-  );
-}
-
-function sortByRowId(item1, item2) {
-  return item1.row - item2.row;
-}
-
-function sortByColId(item1, item2) {
-  return item1.col - item2.col;
-}
-
-function hasOverlap(positions, xAxis = true) {
-  return positions
-    .slice()
-    .sort(xAxis ? sortByColId : sortByRowId)
-    .some((item, index, arr) => {
-      if (index === arr.length - 1) {
-        return false;
-      }
-
-      if (xAxis) {
-        return item.col + item.size_x > arr[index + 1].col;
-      }
-      return item.row + item.size_y > arr[index + 1].row;
-    });
-}
-
-function isWideLeafComponent(component) {
-  return (
-    [CHART_TYPE, MARKDOWN_TYPE].indexOf(component.type) > -1 &&
-    component.meta.width > GRID_MIN_COLUMN_COUNT
-  );
-}
-
-function canReduceColumnWidth(columnComponent, root) {
-  return (
-    columnComponent.type === COLUMN_TYPE &&
-    columnComponent.meta.width > GRID_MIN_COLUMN_COUNT &&
-    columnComponent.children.every(
-      childId =>
-        isWideLeafComponent(root[childId]) ||
-        (root[childId].type === ROW_TYPE &&
-          root[childId].children.every(id => isWideLeafComponent(root[id]))),
-    )
-  );
-}
-
-function reduceRowWidth(rowComponent, root) {
-  // find widest free chart and reduce width
-  const widestChartId = rowComponent.children
-    .filter(childId => isWideLeafComponent(root[childId]))
-    .reduce((prev, current) => {
-      if (root[prev].meta.width >= root[current].meta.width) {
-        return prev;
-      }
-      return current;
-    });
-
-  if (widestChartId) {
-    root[widestChartId].meta.width -= 1;
-  }
-  return getChildrenSum(rowComponent.children, 'width', root);
-}
-
-function reduceComponentWidth(component) {
-  if (isWideLeafComponent(component)) {
-    component.meta.width -= 1;
-  }
-  return component.meta.width;
-}
-
-function doConvert(positions, level, parent, root) {
-  if (positions.length === 0) {
-    return;
-  }
-
-  if (positions.length === 1 || level >= MAX_RECURSIVE_LEVEL) {
-    // special treatment for single chart dash, always wrap chart inside a row
-    if (parent.type === DASHBOARD_GRID_TYPE) {
-      const rowContainer = getRowContainer();
-      root[rowContainer.id] = rowContainer;
-      parent.children.push(rowContainer.id);
-      parent = rowContainer;
-    }
-
-    const chartHolder = getChartHolder(positions[0]);
-    root[chartHolder.id] = chartHolder;
-    parent.children.push(chartHolder.id);
-    return;
-  }
-
-  let currentItems = positions.slice();
-  const { top, bottom, left, right } = getBoundary(positions);
-  // find row dividers
-  const layers = [];
-  let currentRow = top + 1;
-  while (currentItems.length && currentRow <= bottom) {
-    const upper = [];
-    const lower = [];
-
-    const isRowDivider = currentItems.every(item => {
-      const { row, size_y } = item;
-      if (row + size_y <= currentRow) {
-        lower.push(item);
-        return true;
-      } else if (row >= currentRow) {
-        upper.push(item);
-        return true;
-      }
-      return false;
-    });
-
-    if (isRowDivider) {
-      currentItems = upper.slice();
-      layers.push(lower);
-    }
-    currentRow += 1;
-  }
-
-  layers.forEach(layer => {
-    if (layer.length === 0) {
-      return;
-    }
-
-    if (layer.length === 1 && parent.type === COLUMN_TYPE) {
-      const chartHolder = getChartHolder(layer[0]);
-      root[chartHolder.id] = chartHolder;
-      parent.children.push(chartHolder.id);
-      return;
-    }
-
-    // create a new row
-    const rowContainer = getRowContainer();
-    root[rowContainer.id] = rowContainer;
-    parent.children.push(rowContainer.id);
-
-    currentItems = layer.slice();
-    if (!hasOverlap(currentItems)) {
-      currentItems.sort(sortByColId).forEach(item => {
-        const chartHolder = getChartHolder(item);
-        root[chartHolder.id] = chartHolder;
-        rowContainer.children.push(chartHolder.id);
-      });
-    } else {
-      // find col dividers for each layer
-      let currentCol = left + 1;
-      while (currentItems.length && currentCol <= right) {
-        const upper = [];
-        const lower = [];
-
-        const isColDivider = currentItems.every(item => {
-          const { col, size_x } = item;
-          if (col + size_x <= currentCol) {
-            lower.push(item);
-            return true;
-          } else if (col >= currentCol) {
-            upper.push(item);
-            return true;
-          }
-          return false;
-        });
-
-        if (isColDivider) {
-          if (lower.length === 1) {
-            const chartHolder = getChartHolder(lower[0]);
-            root[chartHolder.id] = chartHolder;
-            rowContainer.children.push(chartHolder.id);
-          } else {
-            // create a new column
-            const colContainer = getColContainer();
-            root[colContainer.id] = colContainer;
-
-            if (!hasOverlap(lower, false)) {
-              lower.sort(sortByRowId).forEach(item => {
-                const chartHolder = getChartHolder(item);
-                root[chartHolder.id] = chartHolder;
-                colContainer.children.push(chartHolder.id);
-              });
-            } else {
-              doConvert(lower, level + 2, colContainer, root);
-            }
-
-            // add col meta
-            if (colContainer.children.length) {
-              rowContainer.children.push(colContainer.id);
-              // add col meta
-              colContainer.meta.width = getChildrenMax(
-                colContainer.children,
-                'width',
-                root,
-              );
-            }
-          }
-
-          currentItems = upper.slice();
-        }
-        currentCol += 1;
-      }
-    }
-
-    rowContainer.meta.width = getChildrenSum(
-      rowContainer.children,
-      'width',
-      root,
-    );
-  });
-}
-
-export function convertToLayout(positions) {
-  const root = getEmptyLayout();
-
-  doConvert(positions, 0, root[DASHBOARD_GRID_ID], root);
-
-  // remove row's width/height and col's height
-  Object.values(root).forEach(item => {
-    if (ROW_TYPE === item.type) {
-      const meta = item.meta;
-      if (meta.width > GRID_COLUMN_COUNT) {
-        let currentWidth = meta.width;
-        while (
-          currentWidth > GRID_COLUMN_COUNT &&
-          item.children.filter(id => isWideLeafComponent(root[id])).length
-        ) {
-          currentWidth = reduceRowWidth(item, root);
-        }
-
-        // reduce column width
-        if (currentWidth > GRID_COLUMN_COUNT) {
-          // find column that: width > 2 and each row has at least 1 chart can reduce
-          // 2 loops: same column may reduce multiple times
-          let colIds;
-          do {
-            colIds = item.children.filter(colId =>
-              canReduceColumnWidth(root[colId], root),
-            );
-            let idx = 0;
-            while (idx < colIds.length && currentWidth > GRID_COLUMN_COUNT) {
-              const currentColumn = colIds[idx];
-              root[currentColumn].children.forEach(childId => {
-                if (root[childId].type === ROW_TYPE) {
-                  root[childId].meta.width = reduceRowWidth(
-                    root[childId],
-                    root,
-                  );
-                } else {
-                  root[childId].meta.width = reduceComponentWidth(
-                    root[childId],
-                  );
-                }
-              });
-              root[currentColumn].meta.width = getChildrenMax(
-                root[currentColumn].children,
-                'width',
-                root,
-              );
-              currentWidth = getChildrenSum(item.children, 'width', root);
-              idx += 1;
-            }
-          } while (colIds.length && currentWidth > GRID_COLUMN_COUNT);
-        }
-      }
-      delete meta.width;
-    }
-  });
-
-  // console.log(JSON.stringify(root));
-  return root;
-}
-
-function mergePosition(position, bottomLine, maxColumn) {
-  const { col, size_x, size_y } = position;
-  const endColumn = col + size_x > maxColumn ? bottomLine.length : col + size_x;
-  const sectionLength =
-    bottomLine.slice(col).findIndex(value => value > bottomLine[col]) + 1;
-
-  const currentBottom =
-    sectionLength > 0 && sectionLength < size_x
-      ? Math.max.apply(null, bottomLine.slice(col, col + size_x))
-      : bottomLine[col];
-  bottomLine.fill(currentBottom + size_y, col, endColumn);
-}
-
-// In original position data, a lot of position's row attribute are not correct, and same positions are
-// assigned to more than 1 chart. The convert function depends on row id, col id to split
-// the whole dashboard into nested rows and columns. Bad row id will lead to many empty spaces, or a few
-// charts are overlapped in the same row.
-// This function read positions by row first. Then based on previous col id, width and height attribute,
-// re-calculate next position's row id.
-function scanDashboardPositionsData(positions) {
-  const bottomLine = new Array(49).fill(0);
-  bottomLine[0] = Number.MAX_VALUE;
-  const maxColumn = Math.max.apply(
-    null,
-    positions.slice().map(position => position.col),
-  );
-
-  const positionsByRowId = {};
-  positions
-    .slice()
-    .sort(sortByRowId)
-    .forEach(position => {
-      const { row } = position;
-      if (positionsByRowId[row] === undefined) {
-        positionsByRowId[row] = [];
-      }
-      positionsByRowId[row].push(position);
-    });
-  const rawPositions = Object.values(positionsByRowId);
-  const updatedPositions = [];
-
-  while (rawPositions.length) {
-    const nextRow = rawPositions.shift();
-    let nextCol = 1;
-    while (nextRow.length) {
-      // special treatment for duplicated positions: display wider one first
-      const availableIndexByColumn = nextRow
-        .filter(position => position.col === nextCol)
-        .map((position, index) => index);
-      if (availableIndexByColumn.length) {
-        const idx =
-          availableIndexByColumn.length > 1
-            ? availableIndexByColumn.sort(
-                (idx1, idx2) => nextRow[idx2].size_x - nextRow[idx1].size_x,
-              )[0]
-            : availableIndexByColumn[0];
-
-        const nextPosition = nextRow.splice(idx, 1)[0];
-        mergePosition(nextPosition, bottomLine, maxColumn + 1);
-        nextPosition.row = bottomLine[nextPosition.col] - nextPosition.size_y;
-        updatedPositions.push(nextPosition);
-        nextCol += nextPosition.size_x;
-      } else {
-        nextCol = nextRow[0].col;
-      }
-    }
-  }
-
-  return updatedPositions;
-}
-
-export default function(dashboard) {
-  const positions = [];
-  let { position_json } = dashboard;
-  const positionDict = {};
-  if (Array.isArray(position_json)) {
-    // scan and fix positions data: extra spaces, dup rows, .etc
-    position_json = scanDashboardPositionsData(position_json);
-    position_json.forEach(position => {
-      positionDict[position.slice_id] = position;
-    });
-  } else {
-    position_json = [];
-  }
-
-  // position data clean up. some dashboard didn't have position_json
-  const lastRowId = Math.max(
-    0,
-    Math.max.apply(null, position_json.map(pos => pos.row + pos.size_y)),
-  );
-  let newSliceCounter = 0;
-  dashboard.slices.forEach(({ slice_id, form_data, slice_name }) => {
-    let position = positionDict[slice_id];
-    if (!position) {
-      // append new slices to dashboard bottom, 3 slices per row
-      position = {
-        col: (newSliceCounter % 3) * 16 + 1,
-        row: lastRowId + Math.floor(newSliceCounter / 3) * 16,
-        size_x: 16,
-        size_y: 16,
-        slice_id,
-      };
-      newSliceCounter += 1;
-    }
-    if (form_data && ['markup', 'separator'].indexOf(form_data.viz_type) > -1) {
-      position = {
-        ...position,
-        code: form_data.code,
-        slice_name,
-      };
-    }
-    positions.push(position);
-  });
-
-  return convertToLayout(positions);
-}
diff --git a/superset/assets/src/explore/components/SaveModal.jsx b/superset/assets/src/explore/components/SaveModal.jsx
index 79880ea..7ff106a 100644
--- a/superset/assets/src/explore/components/SaveModal.jsx
+++ b/superset/assets/src/explore/components/SaveModal.jsx
@@ -7,6 +7,7 @@ import { Modal, Alert, Button, Radio } from 'react-bootstrap';
 import Select from 'react-select';
 import { t } from '../../locales';
 import { supersetURL } from '../../utils/common';
+import { EXPLORE_ONLY_VIZ_TYPE } from '../constants';
 
 const propTypes = {
   can_overwrite: PropTypes.bool,
@@ -31,6 +32,7 @@ class SaveModal extends React.Component {
       alert: null,
       action: props.can_overwrite ? 'overwrite' : 'saveas',
       addToDash: 'noSave',
+      vizType: props.form_data.viz_type,
     };
   }
   componentDidMount() {
@@ -122,6 +124,7 @@ class SaveModal extends React.Component {
     this.setState({ alert: null });
   }
   render() {
+    const canNotSaveToDash = EXPLORE_ONLY_VIZ_TYPE.indexOf(this.state.vizType) > -1;
     return (
       <Modal
         show
@@ -182,6 +185,7 @@ class SaveModal extends React.Component {
 
           <Radio
             inline
+            disabled={canNotSaveToDash}
             checked={this.state.addToDash === 'existing'}
             onChange={this.changeDash.bind(this, 'existing')}
           >
@@ -189,6 +193,7 @@ class SaveModal extends React.Component {
           </Radio>
           <Select
             className="save-modal-selector"
+            disabled={canNotSaveToDash}
             options={this.props.dashboards}
             onChange={this.onChange.bind(this, 'saveToDashboardId')}
             autoSize={false}
@@ -200,11 +205,13 @@ class SaveModal extends React.Component {
             inline
             checked={this.state.addToDash === 'new'}
             onChange={this.changeDash.bind(this, 'new')}
+            disabled={canNotSaveToDash}
           >
             {t('Add to new dashboard')} &nbsp;
           </Radio>
           <input
             onChange={this.onChange.bind(this, 'newDashboardName')}
+            disabled={canNotSaveToDash}
             onFocus={this.changeDash.bind(this, 'new')}
             placeholder={t('[dashboard name]')}
           />
@@ -224,7 +231,7 @@ class SaveModal extends React.Component {
             type="button"
             id="btn_modal_save_goto_dash"
             className="btn btn-primary pull-left gotodash"
-            disabled={this.state.addToDash === 'noSave'}
+            disabled={this.state.addToDash === 'noSave' || canNotSaveToDash}
             onClick={this.saveOrOverwrite.bind(this, true)}
           >
             {t('Save & go to dashboard')}
diff --git a/superset/assets/src/explore/constants.js b/superset/assets/src/explore/constants.js
index 30e1565..711ef08 100644
--- a/superset/assets/src/explore/constants.js
+++ b/superset/assets/src/explore/constants.js
@@ -37,3 +37,5 @@ export const MULTI_OPERATORS = [OPERATORS.in, OPERATORS['not in']];
 export const sqlaAutoGeneratedMetricNameRegex = /^(sum|min|max|avg|count|count_distinct)__.*$/i;
 export const sqlaAutoGeneratedMetricRegex = /^(LONG|DOUBLE|FLOAT)?(SUM|AVG|MAX|MIN|COUNT)\([A-Z0-9_."]*\)$/i;
 export const druidAutoGeneratedMetricRegex = /^(LONG|DOUBLE|FLOAT)?(SUM|MAX|MIN|COUNT)\([A-Z0-9_."]*\)$/i;
+
+export const EXPLORE_ONLY_VIZ_TYPE = ['separator', 'markup'];
diff --git a/superset/assets/src/logger.js b/superset/assets/src/logger.js
index 42f67f8..afd33d8 100644
--- a/superset/assets/src/logger.js
+++ b/superset/assets/src/logger.js
@@ -145,13 +145,6 @@ export const LOG_ACTIONS_EXPLORE_DASHBOARD_CHART = 'explore_dashboard_chart';
 export const LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART = 'export_csv_dashboard_chart';
 export const LOG_ACTIONS_CHANGE_DASHBOARD_FILTER = 'change_dashboard_filter';
 
-// @TODO remove upon v1 deprecation
-export const LOG_ACTIONS_PREVIEW_V2 = 'preview_dashboard_v2';
-export const LOG_ACTIONS_FALLBACK_TO_V1 = 'fallback_to_dashboard_v1';
-export const LOG_ACTIONS_READ_ABOUT_V2_CHANGES = 'read_about_v2_changes';
-export const LOG_ACTIONS_DISMISS_V2_PROMPT = 'dismiss_v2_conversion_prompt';
-export const LOG_ACTIONS_SHOW_V2_INFO_PROMPT = 'show_v2_conversion_prompt';
-
 export const DASHBOARD_EVENT_NAMES = [
   LOG_ACTIONS_MOUNT_DASHBOARD,
   LOG_ACTIONS_FIRST_DASHBOARD_LOAD,
@@ -163,12 +156,6 @@ export const DASHBOARD_EVENT_NAMES = [
   LOG_ACTIONS_EXPORT_CSV_DASHBOARD_CHART,
   LOG_ACTIONS_CHANGE_DASHBOARD_FILTER,
   LOG_ACTIONS_REFRESH_DASHBOARD,
-
-  LOG_ACTIONS_PREVIEW_V2,
-  LOG_ACTIONS_FALLBACK_TO_V1,
-  LOG_ACTIONS_READ_ABOUT_V2_CHANGES,
-  LOG_ACTIONS_DISMISS_V2_PROMPT,
-  LOG_ACTIONS_SHOW_V2_INFO_PROMPT,
 ];
 
 export const EXPLORE_EVENT_NAMES = [
diff --git a/superset/assets/webpack.config.js b/superset/assets/webpack.config.js
index 56aca88..0291f1d 100644
--- a/superset/assets/webpack.config.js
+++ b/superset/assets/webpack.config.js
@@ -21,7 +21,6 @@ const config = {
     addSlice: ['babel-polyfill', APP_DIR + '/src/addSlice/index.jsx'],
     explore: ['babel-polyfill', APP_DIR + '/src/explore/index.jsx'],
     dashboard: ['babel-polyfill', APP_DIR + '/src/dashboard/index.jsx'],
-    dashboard_deprecated: ['babel-polyfill', APP_DIR + '/src/dashboard/deprecated/v1/index.jsx'],
     sqllab: ['babel-polyfill', APP_DIR + '/src/SqlLab/index.jsx'],
     welcome: ['babel-polyfill', APP_DIR + '/src/welcome/index.jsx'],
     profile: ['babel-polyfill', APP_DIR + '/src/profile/index.jsx'],
diff --git a/superset/models/core.py b/superset/models/core.py
index d50cf4f..7ae9297 100644
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -401,10 +401,10 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
         self.json_metadata = value
 
     @property
-    def position_array(self):
+    def position(self):
         if self.position_json:
             return json.loads(self.position_json)
-        return []
+        return {}
 
     @classmethod
     def import_obj(cls, dashboard_to_import, import_time=None):
@@ -420,16 +420,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
         def alter_positions(dashboard, old_to_new_slc_id_dict):
             """ Updates slice_ids in the position json.
 
-            Sample position json v1:
-            [{
-                "col": 5,
-                "row": 10,
-                "size_x": 4,
-                "size_y": 2,
-                "slice_id": "3610"
-            }]
-
-            Sample position json v2:
+            Sample position_json data:
             {
                 "DASHBOARD_VERSION_KEY": "v2",
                 "DASHBOARD_ROOT_ID": {
@@ -455,32 +446,17 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
             }
             """
             position_data = json.loads(dashboard.position_json)
-            is_v2_dash = (
-                isinstance(position_data, dict) and
-                position_data.get('DASHBOARD_VERSION_KEY') == 'v2'
-            )
-            if is_v2_dash:
-                position_json = position_data.values()
-                for value in position_json:
-                    if (isinstance(value, dict) and value.get('meta') and
-                            value.get('meta').get('chartId')):
-                        old_slice_id = value.get('meta').get('chartId')
-
-                        if old_slice_id in old_to_new_slc_id_dict:
-                            value['meta']['chartId'] = (
-                                old_to_new_slc_id_dict[old_slice_id]
-                            )
-                dashboard.position_json = json.dumps(position_data)
-            else:
-                position_array = dashboard.position_array
-                for position in position_array:
-                    if 'slice_id' not in position:
-                        continue
-                    old_slice_id = int(position['slice_id'])
+            position_json = position_data.values()
+            for value in position_json:
+                if (isinstance(value, dict) and value.get('meta') and
+                        value.get('meta').get('chartId')):
+                    old_slice_id = value.get('meta').get('chartId')
+
                     if old_slice_id in old_to_new_slc_id_dict:
-                        position['slice_id'] = '{}'.format(
-                            old_to_new_slc_id_dict[old_slice_id])
-                dashboard.position_json = json.dumps(position_array)
+                        value['meta']['chartId'] = (
+                            old_to_new_slc_id_dict[old_slice_id]
+                        )
+            dashboard.position_json = json.dumps(position_data)
 
         logging.info('Started import of the dashboard: {}'
                      .format(dashboard_to_import.to_json()))
diff --git a/superset/views/core.py b/superset/views/core.py
index 07a4079..dc99345 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -1561,7 +1561,6 @@ class Superset(BaseSupersetView):
         dash.owners = [g.user] if g.user else []
         dash.dashboard_title = data['dashboard_title']
 
-        is_v2_dash = Superset._is_v2_dash(data['positions'])
         if data['duplicate_slices']:
             # Duplicating slices as well, mapping old ids to new ones
             old_to_new_sliceids = {}
@@ -1577,18 +1576,14 @@ class Superset(BaseSupersetView):
             # update chartId of layout entities
             # in v2_dash positions json data, chartId should be integer,
             # while in older version slice_id is string type
-            if is_v2_dash:
-                for value in data['positions'].values():
-                    if (
-                        isinstance(value, dict) and value.get('meta') and
-                        value.get('meta').get('chartId')
-                    ):
-                        old_id = '{}'.format(value.get('meta').get('chartId'))
-                        new_id = int(old_to_new_sliceids[old_id])
-                        value['meta']['chartId'] = new_id
-            else:
-                for d in data['positions']:
-                    d['slice_id'] = old_to_new_sliceids[d['slice_id']]
+            for value in data['positions'].values():
+                if (
+                    isinstance(value, dict) and value.get('meta') and
+                    value.get('meta').get('chartId')
+                ):
+                    old_id = '{}'.format(value.get('meta').get('chartId'))
+                    new_id = int(old_to_new_sliceids[old_id])
+                    value['meta']['chartId'] = new_id
         else:
             dash.slices = original_dash.slices
         dash.params = original_dash.params
@@ -1618,42 +1613,8 @@ class Superset(BaseSupersetView):
         return 'SUCCESS'
 
     @staticmethod
-    def _is_v2_dash(positions):
-        return (
-            isinstance(positions, dict) and
-            positions.get('DASHBOARD_VERSION_KEY') == 'v2'
-        )
-
-    @staticmethod
     def _set_dash_metadata(dashboard, data):
         positions = data['positions']
-        is_v2_dash = Superset._is_v2_dash(positions)
-
-        # @TODO remove upon v1 deprecation
-        if not is_v2_dash:
-            positions = data['positions']
-            slice_ids = [int(d['slice_id']) for d in positions]
-            dashboard.slices = [o for o in dashboard.slices if o.id in slice_ids]
-            positions = sorted(data['positions'], key=lambda x: int(x['slice_id']))
-            dashboard.position_json = json.dumps(positions, indent=4, sort_keys=True)
-            md = dashboard.params_dict
-            dashboard.css = data['css']
-            dashboard.dashboard_title = data['dashboard_title']
-
-            if 'filter_immune_slices' not in md:
-                md['filter_immune_slices'] = []
-            if 'timed_refresh_immune_slices' not in md:
-                md['timed_refresh_immune_slices'] = []
-            if 'filter_immune_slice_fields' not in md:
-                md['filter_immune_slice_fields'] = {}
-            md['expanded_slices'] = data['expanded_slices']
-            default_filters_data = json.loads(data.get('default_filters', '{}'))
-            applicable_filters =\
-                {key: v for key, v in default_filters_data.items()
-                 if int(key) in slice_ids}
-            md['default_filters'] = json.dumps(applicable_filters)
-            dashboard.json_metadata = json.dumps(md, indent=4)
-            return
 
         # find slices in the position data
         slice_ids = []
@@ -2140,57 +2101,14 @@ class Superset(BaseSupersetView):
         standalone_mode = request.args.get('standalone') == 'true'
         edit_mode = request.args.get('edit') == 'true'
 
-        # TODO remove switch upon v1 deprecation 🎉
-        # during v2 rollout, multiple factors determine whether we show v1 or v2
-        # if layout == v1
-        #   view = v1 for non-editors
-        #   view = v1 or v2 for editors depending on config + request (force)
-        #   edit = v1 or v2 for editors depending on config + request (force)
-        #
-        # if layout == v2 (not backwards compatible)
-        #   view = v2
-        #   edit = v2
-        dashboard_layout = dash.data.get('position_json', {})
-        is_v2_dash = (
-            isinstance(dashboard_layout, dict) and
-            dashboard_layout.get('DASHBOARD_VERSION_KEY') == 'v2'
-        )
-
-        force_v1 = request.args.get('version') == 'v1' and not is_v2_dash
-        force_v2 = request.args.get('version') == 'v2'
-        force_v2_edit = (
-            is_v2_dash or
-            not app.config.get('CAN_FALLBACK_TO_DASH_V1_EDIT_MODE')
-        )
-        v2_is_default_view = app.config.get('DASH_V2_IS_DEFAULT_VIEW_FOR_EDITORS')
-        prompt_v2_conversion = False
-        if is_v2_dash:
-            dashboard_view = 'v2'
-        elif not dash_edit_perm:
-            dashboard_view = 'v1'
-        else:
-            if force_v2 or (v2_is_default_view and not force_v1):
-                dashboard_view = 'v2'
-            else:
-                dashboard_view = 'v1'
-                prompt_v2_conversion = not force_v1
-                if force_v2_edit:
-                    dash_edit_perm = False
-
         # Hack to log the dashboard_id properly, even when getting a slug
         @log_this
         def dashboard(**kwargs):  # noqa
             pass
-
-        # TODO remove extra logging upon v1 deprecation 🎉
         dashboard(
             dashboard_id=dash.id,
-            dashboard_version='v2' if is_v2_dash else 'v1',
-            dashboard_view=dashboard_view,
+            dashboard_version='v2',
             dash_edit_perm=dash_edit_perm,
-            force_v1=force_v1,
-            force_v2=force_v2,
-            force_v2_edit=force_v2_edit,
             edit_mode=edit_mode)
 
         dashboard_data = dash.data
@@ -2208,26 +2126,14 @@ class Superset(BaseSupersetView):
             'datasources': {ds.uid: ds.data for ds in datasources},
             'common': self.common_bootsrap_payload(),
             'editMode': edit_mode,
-            # TODO remove the following upon v1 deprecation 🎉
-            'force_v2_edit': force_v2_edit,
-            'prompt_v2_conversion': prompt_v2_conversion,
-            'v2_auto_convert_date': app.config.get('PLANNED_V2_AUTO_CONVERT_DATE'),
-            'v2_feedback_url': app.config.get('V2_FEEDBACK_URL'),
         }
 
         if request.args.get('json') == 'true':
             return json_success(json.dumps(bootstrap_data))
 
-        if dashboard_view == 'v2':
-            entry = 'dashboard'
-            template = 'superset/dashboard.html'
-        else:
-            entry = 'dashboard_deprecated'
-            template = 'superset/dashboard_v1_deprecated.html'
-
         return self.render_template(
-            template,
-            entry=entry,
+            'superset/dashboard.html',
+            entry='dashboard',
             standalone_mode=standalone_mode,
             title=dash.dashboard_title,
             bootstrap_data=json.dumps(bootstrap_data),
diff --git a/tests/dashboard_tests.py b/tests/dashboard_tests.py
index 3dda07b..1ab3727 100644
--- a/tests/dashboard_tests.py
+++ b/tests/dashboard_tests.py
@@ -33,6 +33,25 @@ class DashboardTests(SupersetTestCase):
     def tearDown(self):
         pass
 
+    def get_mock_positions(self, dash):
+        positions = {
+            'DASHBOARD_VERSION_KEY': 'v2',
+        }
+        for i, slc in enumerate(dash.slices):
+            id = 'DASHBOARD_CHART_TYPE-{}'.format(i)
+            d = {
+                'type': 'DASHBOARD_CHART_TYPE',
+                'id': id,
+                'children': [],
+                'meta': {
+                    'width': 4,
+                    'height': 50,
+                    'chartId': slc.id,
+                },
+            }
+            positions[id] = d
+        return positions
+
     def test_dashboard(self):
         self.login(username='admin')
         urls = {}
@@ -61,10 +80,11 @@ class DashboardTests(SupersetTestCase):
         self.login(username=username)
         dash = db.session.query(models.Dashboard).filter_by(
             slug='births').first()
+        positions = self.get_mock_positions(dash)
         data = {
             'css': '',
             'expanded_slices': {},
-            'positions': dash.position_array,
+            'positions': positions,
             'dashboard_title': dash.dashboard_title,
         }
         url = '/superset/save_dash/{}/'.format(dash.id)
@@ -76,12 +96,13 @@ class DashboardTests(SupersetTestCase):
         dash = db.session.query(models.Dashboard).filter_by(
             slug='world_health').first()
 
+        positions = self.get_mock_positions(dash)
         filters = {str(dash.slices[0].id): {'region': ['North America']}}
         default_filters = json.dumps(filters)
         data = {
             'css': '',
             'expanded_slices': {},
-            'positions': dash.position_array,
+            'positions': positions,
             'dashboard_title': dash.dashboard_title,
             'default_filters': default_filters,
         }
@@ -104,12 +125,13 @@ class DashboardTests(SupersetTestCase):
             slug='world_health').first()
 
         # add an invalid filter slice
+        positions = self.get_mock_positions(dash)
         filters = {str(99999): {'region': ['North America']}}
         default_filters = json.dumps(filters)
         data = {
             'css': '',
             'expanded_slices': {},
-            'positions': dash.position_array,
+            'positions': positions,
             'dashboard_title': dash.dashboard_title,
             'default_filters': default_filters,
         }
@@ -131,10 +153,11 @@ class DashboardTests(SupersetTestCase):
             .first()
         )
         origin_title = dash.dashboard_title
+        positions = self.get_mock_positions(dash)
         data = {
             'css': '',
             'expanded_slices': {},
-            'positions': dash.position_array,
+            'positions': positions,
             'dashboard_title': 'new title',
         }
         url = '/superset/save_dash/{}/'.format(dash.id)
@@ -153,11 +176,12 @@ class DashboardTests(SupersetTestCase):
         self.login(username=username)
         dash = db.session.query(models.Dashboard).filter_by(
             slug='births').first()
+        positions = self.get_mock_positions(dash)
         data = {
             'css': '',
             'duplicate_slices': False,
             'expanded_slices': {},
-            'positions': dash.position_array,
+            'positions': positions,
             'dashboard_title': 'Copy Of Births',
         }
 
@@ -216,9 +240,16 @@ class DashboardTests(SupersetTestCase):
         self.login(username=username)
         dash = db.session.query(models.Dashboard).filter_by(
             slug='births').first()
-        positions = dash.position_array[:-1]
         origin_slices_length = len(dash.slices)
 
+        positions = self.get_mock_positions(dash)
+        # remove one chart
+        chart_keys = []
+        for key in positions.keys():
+            if key.startswith('DASHBOARD_CHART_TYPE'):
+                chart_keys.append(key)
+        positions.pop(chart_keys[0])
+
         data = {
             'css': '',
             'expanded_slices': {},
diff --git a/tests/import_export_tests.py b/tests/import_export_tests.py
index dc9c4ad..4b2ba74 100644
--- a/tests/import_export_tests.py
+++ b/tests/import_export_tests.py
@@ -22,6 +22,8 @@ from .base_tests import SupersetTestCase
 class ImportExportTests(SupersetTestCase):
     """Testing export import functionality for dashboards"""
 
+    requires_examples = True
+
     def __init__(self, *args, **kwargs):
         super(ImportExportTests, self).__init__(*args, **kwargs)
 
@@ -155,9 +157,9 @@ class ImportExportTests(SupersetTestCase):
         self.assertEquals(
             len(expected_dash.slices), len(actual_dash.slices))
         expected_slices = sorted(
-            expected_dash.slices, key=lambda s: s.slice_name)
+            expected_dash.slices, key=lambda s: s.slice_name or '')
         actual_slices = sorted(
-            actual_dash.slices, key=lambda s: s.slice_name)
+            actual_dash.slices, key=lambda s: s.slice_name or '')
         for e_slc, a_slc in zip(expected_slices, actual_slices):
             self.assert_slice_equals(e_slc, a_slc)
         if check_position:
@@ -191,7 +193,10 @@ class ImportExportTests(SupersetTestCase):
             set([m.metric_name for m in actual_ds.metrics]))
 
     def assert_slice_equals(self, expected_slc, actual_slc):
-        self.assertEquals(expected_slc.slice_name, actual_slc.slice_name)
+        # to avoid bad slice data (no slice_name)
+        expected_slc_name = expected_slc.slice_name or ''
+        actual_slc_name = actual_slc.slice_name or ''
+        self.assertEquals(expected_slc_name, actual_slc_name)
         self.assertEquals(
             expected_slc.datasource_type, actual_slc.datasource_type)
         self.assertEquals(expected_slc.viz_type, actual_slc.viz_type)
@@ -209,6 +214,7 @@ class ImportExportTests(SupersetTestCase):
             resp.data.decode('utf-8'),
             object_hook=utils.decode_dashboards,
         )['dashboards']
+
         self.assert_dash_equals(birth_dash, exported_dashboards[0])
         self.assertEquals(
             birth_dash.id,
@@ -320,13 +326,18 @@ class ImportExportTests(SupersetTestCase):
         dash_with_1_slice = self.create_dashboard(
             'dash_with_1_slice', slcs=[slc], id=10002)
         dash_with_1_slice.position_json = """
-            [{{
-                "col": 5,
-                "row": 10,
-                "size_x": 4,
-                "size_y": 2,
-                "slice_id": "{}"
-            }}]
+            {{"DASHBOARD_VERSION_KEY": "v2",
+              "DASHBOARD_CHART_TYPE-{0}": {{
+                "type": "DASHBOARD_CHART_TYPE",
+                "id": {0},
+                "children": [],
+                "meta": {{
+                  "width": 4,
+                  "height": 50,
+                  "chartId": {0}
+                }}
+              }}
+            }}
         """.format(slc.id)
         imported_dash_id = models.Dashboard.import_obj(
             dash_with_1_slice, import_time=1990)
@@ -340,10 +351,8 @@ class ImportExportTests(SupersetTestCase):
         self.assertEquals({'remote_id': 10002, 'import_time': 1990},
                           json.loads(imported_dash.json_metadata))
 
-        expected_position = dash_with_1_slice.position_array
-        expected_position[0]['slice_id'] = '{}'.format(
-            imported_dash.slices[0].id)
-        self.assertEquals(expected_position, imported_dash.position_array)
+        expected_position = dash_with_1_slice.position
+        self.assertEquals(expected_position, imported_dash.position)
 
     def test_import_dashboard_2_slices(self):
         e_slc = self.create_slice('e_slc', id=10007, table_name='energy_usage')


Mime
View raw message