You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by GitBox <gi...@apache.org> on 2018/07/24 22:23:32 UTC

[GitHub] graceguo-supercat closed pull request #5418: Retire dashboard v1 (js and python)

graceguo-supercat closed pull request #5418: Retire dashboard v1 (js and python)
URL: https://github.com/apache/incubator-superset/pull/5418
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/superset/assets/package.json b/superset/assets/package.json
index da5405624f..c81a902e61 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 fd640d1f8b..d405ccf327 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 f8095cd875..7772f71015 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 80d4bdf1e1..2bb9b9ca20 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 3b1b6b1f36..0c1951b8d7 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 7b8a245074..dab11c382f 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 f5ad9d06db..8194f4662e 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 3740404da1..629b916456 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 a6216357b5..0000000000
--- 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 828651fbde..0000000000
--- 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 870688413a..0000000000
--- 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 b459f44182..0000000000
--- 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 b731412fc5..0000000000
--- 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 eda2054f92..0000000000
--- 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 254f1d60e4..0000000000
--- 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 8d11249598..0000000000
--- 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 a8701207cb..0000000000
--- 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 3f802c3471..0000000000
--- 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 01d5dcf2e4..0000000000
--- 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 ee11ff26d6..0000000000
--- 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 b9e6240956..0000000000
--- 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 a18a5d2990..0000000000
--- 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 d68b427d79..0000000000
--- 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 ef0ec24796..0000000000
--- 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 c801c0aa0d..0000000000
--- 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 3e43f9365e..0000000000
--- 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 7a44156857..0000000000
--- 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 3d3e46850e..0000000000
--- 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 a8c6aa7e4b..0000000000
--- 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 47d1e2416f..0000000000
--- 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 4c2d77fd32..0000000000
--- 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 69d4e69c7e..1479f88311 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 59695636ce..0c121d3071 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 92f1ff10f6..56f5d43774 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 bd0674b627..0000000000
--- 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 79880ea261..7ff106a1ba 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 30e1565306..711ef086ee 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 42f67f8101..afd33d8ea0 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 56aca88ee9..0291f1d844 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 8c77814a2c..65670772bf 100644
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -399,10 +399,10 @@ def params(self, value):
         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):
@@ -418,16 +418,7 @@ def import_obj(cls, dashboard_to_import, import_time=None):
         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": {
@@ -453,32 +444,17 @@ def alter_positions(dashboard, old_to_new_slc_id_dict):
             }
             """
             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 9dc903083f..dc3070a615 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -1561,7 +1561,6 @@ def copy_dash(self, dashboard_id):
         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 @@ def copy_dash(self, dashboard_id):
             # 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
@@ -1617,43 +1612,9 @@ def save_dash(self, dashboard_id):
         session.close()
         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 @@ def dashboard(self, dashboard_id):
         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 @@ def dashboard(**kwargs):  # noqa
             '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 3dda07bf17..1ab3727b1e 100644
--- a/tests/dashboard_tests.py
+++ b/tests/dashboard_tests.py
@@ -33,6 +33,25 @@ def setUp(self):
     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 @@ def test_save_dash(self, username='admin'):
         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 @@ def test_save_dash_with_filter(self, username='admin'):
         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 @@ def test_save_dash_with_invalid_filters(self, username='admin'):
             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 @@ def test_save_dash_with_dashboard_title(self, username='admin'):
             .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 @@ def test_copy_dash(self, username='admin'):
         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 @@ def test_remove_slices(self, username='admin'):
         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 dc9c4ade5b..4b2ba74d73 100644
--- a/tests/import_export_tests.py
+++ b/tests/import_export_tests.py
@@ -22,6 +22,8 @@
 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 @@ def assert_dash_equals(self, expected_dash, actual_dash,
         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 @@ def assert_datasource_equals(self, expected_ds, actual_ds):
             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 @@ def test_export_1_dashboard(self):
             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 @@ def test_import_dashboard_1_slice(self):
         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 @@ def test_import_dashboard_1_slice(self):
         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')


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org