You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2020/08/06 01:54:26 UTC

[incubator-superset] branch master updated: style: use tabs in dashboard edit pane (#10394)

This is an automated email from the ASF dual-hosted git repository.

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


The following commit(s) were added to refs/heads/master by this push:
     new ece9192  style: use tabs in dashboard edit pane (#10394)
ece9192 is described below

commit ece91928a9339190163c0bc72b96e51217a90d1e
Author: Maxime Beauchemin <ma...@gmail.com>
AuthorDate: Wed Aug 5 18:53:53 2020 -0700

    style: use tabs in dashboard edit pane (#10394)
    
    * style: use tabs in dashboard edit pane
    
    * fix tests
    
    * more hackin'
    
    * getting ready to rip cell measurer
    
    * working
    
    * pogress
    
    * Fix cards
    
    * done
    
    * fix jest
    
    * fix cy
---
 .../integration/dashboard/edit_mode.test.js        |  13 +-
 .../cypress/integration/dashboard/save.test.js     |   8 +-
 superset-frontend/images/icons/more.svg            |  21 +++
 .../dashboard/components/DashboardBuilder_spec.jsx |   4 -
 .../components/HeaderActionsDropdown_spec.jsx      |   2 +-
 .../dashboard/components/Header_spec.jsx           |  19 +--
 .../dashboard/fixtures/mockDashboardState.js       |   2 -
 .../dashboard/reducers/dashboardState_spec.js      |   3 -
 superset-frontend/src/components/Icon/index.tsx    |   3 +
 .../src/dashboard/actions/dashboardState.js        |   4 +-
 .../src/dashboard/components/AddSliceCard.jsx      |  16 +-
 .../dashboard/components/BuilderComponentPane.jsx  |  67 ++++-----
 .../dashboard/components/ColorComponentPane.jsx    | 107 -------------
 .../components/ColorSchemeControlWrapper.jsx       |  72 +++++++++
 .../src/dashboard/components/DashboardBuilder.jsx  |   6 +-
 .../src/dashboard/components/Header.jsx            | 165 ++++++++-------------
 .../dashboard/components/HeaderActionsDropdown.jsx | 125 +++++++---------
 .../dashboard/components/InsertComponentPane.jsx   | 118 ---------------
 .../src/dashboard/components/PropertiesModal.jsx   |  89 +++++++----
 .../dashboard/components/RefreshIntervalModal.jsx  |  11 +-
 .../src/dashboard/components/SliceAdder.jsx        |  45 ++----
 .../components/dnd/AddSliceDragPreview.jsx         |   5 +-
 .../components/filterscope/FilterScopeSelector.jsx |   6 +-
 .../src/dashboard/containers/DashboardBuilder.jsx  |   1 -
 .../src/dashboard/containers/DashboardHeader.jsx   |   5 +-
 .../src/dashboard/reducers/dashboardState.js       |   7 +-
 .../src/dashboard/reducers/getInitialState.js      |   5 -
 .../dashboard/stylesheets/builder-sidepane.less    |  21 +--
 .../src/dashboard/stylesheets/dashboard.less       |  13 --
 superset-frontend/src/dashboard/util/constants.ts  |   7 -
 .../src/dashboard/util/propShapes.jsx              |   1 -
 .../components/controls/ColorSchemeControl.jsx     |   5 +-
 .../components/controls/ColorSchemeControl.less}   |  26 ++--
 superset-frontend/src/explore/main.less            |  14 --
 .../src/views/CRUD/dashboard/DashboardList.tsx     |   3 +-
 .../stylesheets/less/cosmo/bootswatch.less         |   1 +
 superset/dashboards/dao.py                         |   3 +-
 superset/dashboards/schemas.py                     |   2 +-
 superset/views/chart/views.py                      |   1 +
 39 files changed, 398 insertions(+), 628 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
index b725354..1db86b5 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
@@ -23,7 +23,7 @@ describe('Dashboard edit mode', () => {
     cy.server();
     cy.login();
     cy.visit(WORLD_HEALTH_DASHBOARD);
-    cy.get('.dashboard-header').contains('Edit dashboard').click();
+    cy.get('.dashboard-header [data-test=pencil]').click();
   });
 
   it('remove, and add chart flow', () => {
@@ -38,11 +38,12 @@ describe('Dashboard edit mode', () => {
         cy.get('.grid-container .box_plot').should('not.exist');
       });
 
-    // open charts list
-    cy.get('.component-layer').contains('Your charts & filters').click();
+    cy.get('.tabs-components .nav-tabs li a').contains('Charts').click();
 
     // find box plot is available from list
-    cy.get('.slices-layer').find('.chart-card-container').contains('Box plot');
+    cy.get('.tabs-components')
+      .find('.chart-card-container')
+      .contains('Box plot');
 
     // drag-n-drop
     const dataTransfer = { data: {} };
@@ -62,14 +63,14 @@ describe('Dashboard edit mode', () => {
     cy.get('.grid-container .box_plot').should('be.exist');
 
     // should show Save changes button
-    cy.get('.dashboard-header .button-container').contains('Save changes');
+    cy.get('.dashboard-header .button-container').contains('Save');
 
     // undo 2 steps
     cy.get('.dashboard-header .undo-action').click().click();
 
     // no changes, can switch to view mode
     cy.get('.dashboard-header .button-container')
-      .contains('Switch to view mode')
+      .contains('Discard Changes')
       .click();
   });
 });
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js
index 650d372..028a236 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js
@@ -53,20 +53,18 @@ describe('Dashboard save action', () => {
     cy.get('.grid-container .box_plot', { timeout: 5000 }); // wait for 5 secs
 
     // remove box_plot chart from dashboard
-    cy.get('.dashboard-header')
-      .contains('Edit dashboard')
-      .trigger('click', { force: true });
+    cy.get('.dashboard-header [data-test=pencil]').click();
     cy.get('.fa.fa-trash').last().trigger('click', { force: true });
     cy.get('.grid-container .box_plot').should('not.exist');
 
     cy.route('POST', '/superset/save_dash/**/').as('saveRequest');
     cy.get('.dashboard-header')
-      .contains('Save changes')
+      .contains('Save')
       .trigger('click', { force: true });
 
     // go back to view mode
     cy.wait('@saveRequest');
-    cy.get('.dashboard-header').contains('Edit dashboard');
+    cy.get('.dashboard-header [data-test=pencil]').click();
     cy.get('.grid-container .box_plot').should('not.exist');
   });
 });
diff --git a/superset-frontend/images/icons/more.svg b/superset-frontend/images/icons/more.svg
new file mode 100644
index 0000000..5a66fe3
--- /dev/null
+++ b/superset-frontend/images/icons/more.svg
@@ -0,0 +1,21 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10ZM5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10ZM19 10C17.8954 10 17 10.8954 17 12C17 13.1046 17.8954 14 19 14C20.1046 14 21 13.1046 21 12C21 10.8954 20.1046 10 19 10Z" fill="currentColor"/>
+</svg>
diff --git a/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx
index fd71eb6..3d0f363 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx
@@ -32,7 +32,6 @@ import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
 import DashboardHeader from 'src/dashboard/containers/DashboardHeader';
 import DashboardGrid from 'src/dashboard/containers/DashboardGrid';
 import * as dashboardStateActions from 'src/dashboard/actions/dashboardState';
-import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
 
 import WithDragDropContext from '../helpers/WithDragDropContext';
 import {
@@ -64,7 +63,6 @@ describe('DashboardBuilder', () => {
     deleteTopLevelTabs() {},
     editMode: false,
     showBuilderPane() {},
-    builderPaneType: BUILDER_PANE_TYPE.NONE,
     setColorSchemeAndUnsavedChanges() {},
     colorScheme: undefined,
     handleComponentDrop() {},
@@ -155,7 +153,6 @@ describe('DashboardBuilder', () => {
     wrapper.setProps({
       ...props,
       editMode: true,
-      builderPaneType: BUILDER_PANE_TYPE.ADD_COMPONENTS,
     });
     expect(wrapper.find(BuilderComponentPane)).toExist();
   });
@@ -167,7 +164,6 @@ describe('DashboardBuilder', () => {
     wrapper.setProps({
       ...props,
       editMode: true,
-      builderPaneType: BUILDER_PANE_TYPE.COLORS,
     });
     expect(wrapper.find(BuilderComponentPane)).toExist();
   });
diff --git a/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx
index 815dd26..495e629 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx
@@ -68,7 +68,7 @@ describe('HeaderActionsDropdown', () => {
 
     it('should render two MenuItems', () => {
       const wrapper = setup(overrideProps);
-      expect(wrapper.find(MenuItem)).toHaveLength(2);
+      expect(wrapper.find(MenuItem)).toHaveLength(3);
     });
 
     it('should render the RefreshIntervalModal', () => {
diff --git a/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx
index a5425dc..bc05ddd 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx
@@ -25,7 +25,6 @@ import PublishedStatus from 'src/dashboard/components/PublishedStatus';
 import HeaderActionsDropdown from 'src/dashboard/components/HeaderActionsDropdown';
 import Button from 'src/components/Button';
 import UndoRedoKeylisteners from 'src/dashboard/components/UndoRedoKeylisteners';
-import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
 
 describe('Header', () => {
   const props = {
@@ -59,7 +58,6 @@ describe('Header', () => {
     editMode: false,
     setEditMode: () => {},
     showBuilderPane: () => {},
-    builderPaneType: BUILDER_PANE_TYPE.NONE,
     updateCss: () => {},
     hasUnsavedChanges: false,
     maxUndoHistoryExceeded: false,
@@ -111,11 +109,6 @@ describe('Header', () => {
       expect(wrapper.find(HeaderActionsDropdown)).toExist();
     });
 
-    it('should render one Button', () => {
-      const wrapper = setup(overrideProps);
-      expect(wrapper.find(Button)).toExist();
-    });
-
     it('should not set up undo/redo', () => {
       const wrapper = setup(overrideProps);
       expect(wrapper.find(UndoRedoKeylisteners)).not.toExist();
@@ -154,11 +147,6 @@ describe('Header', () => {
       expect(wrapper.find(HeaderActionsDropdown)).toExist();
     });
 
-    it('should render one Button', () => {
-      const wrapper = setup(overrideProps);
-      expect(wrapper.find(Button)).toExist();
-    });
-
     it('should not set up undo/redo', () => {
       const wrapper = setup(overrideProps);
       expect(wrapper.find(UndoRedoKeylisteners)).not.toExist();
@@ -199,7 +187,7 @@ describe('Header', () => {
 
     it('should render five Buttons', () => {
       const wrapper = setup(overrideProps);
-      expect(wrapper.find(Button)).toHaveLength(5);
+      expect(wrapper.find(Button)).toHaveLength(4);
     });
 
     it('should set up undo/redo', () => {
@@ -239,11 +227,6 @@ describe('Header', () => {
       expect(wrapper.find(HeaderActionsDropdown)).toExist();
     });
 
-    it('should render one Button', () => {
-      const wrapper = setup(overrideProps);
-      expect(wrapper.find(Button)).toExist();
-    });
-
     it('should not set up undo/redo', () => {
       const wrapper = setup(overrideProps);
       expect(wrapper.find(UndoRedoKeylisteners)).not.toExist();
diff --git a/superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js b/superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js
index 1727422..f6809a2 100644
--- a/superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js
+++ b/superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js
@@ -16,14 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
 import { sliceId } from './mockChartQueries';
 
 export default {
   sliceIds: [sliceId],
   expandedSlices: {},
   editMode: false,
-  builderPaneType: BUILDER_PANE_TYPE.NONE,
   hasUnsavedChanges: false,
   maxUndoHistoryExceeded: false,
   isStarred: true,
diff --git a/superset-frontend/spec/javascripts/dashboard/reducers/dashboardState_spec.js b/superset-frontend/spec/javascripts/dashboard/reducers/dashboardState_spec.js
index fe5d098..8f6de34 100644
--- a/superset-frontend/spec/javascripts/dashboard/reducers/dashboardState_spec.js
+++ b/superset-frontend/spec/javascripts/dashboard/reducers/dashboardState_spec.js
@@ -30,7 +30,6 @@ import {
 } from 'src/dashboard/actions/dashboardState';
 
 import dashboardStateReducer from 'src/dashboard/reducers/dashboardState';
-import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
 
 describe('dashboardState reducer', () => {
   it('should return initial state', () => {
@@ -72,7 +71,6 @@ describe('dashboardState reducer', () => {
       ),
     ).toEqual({
       editMode: true,
-      builderPaneType: BUILDER_PANE_TYPE.ADD_COMPONENTS,
     });
   });
 
@@ -128,7 +126,6 @@ describe('dashboardState reducer', () => {
       hasUnsavedChanges: false,
       maxUndoHistoryExceeded: false,
       editMode: false,
-      builderPaneType: BUILDER_PANE_TYPE.NONE,
       updatedColorScheme: false,
     });
   });
diff --git a/superset-frontend/src/components/Icon/index.tsx b/superset-frontend/src/components/Icon/index.tsx
index 7eb3916..a0d516d 100644
--- a/superset-frontend/src/components/Icon/index.tsx
+++ b/superset-frontend/src/components/Icon/index.tsx
@@ -32,6 +32,7 @@ import { ReactComponent as ErrorIcon } from 'images/icons/error.svg';
 import { ReactComponent as FavoriteSelectedIcon } from 'images/icons/favorite-selected.svg';
 import { ReactComponent as FavoriteUnselectedIcon } from 'images/icons/favorite-unselected.svg';
 import { ReactComponent as PencilIcon } from 'images/icons/pencil.svg';
+import { ReactComponent as MoreIcon } from 'images/icons/more.svg';
 import { ReactComponent as SearchIcon } from 'images/icons/search.svg';
 import { ReactComponent as SortAscIcon } from 'images/icons/sort-asc.svg';
 import { ReactComponent as SortDescIcon } from 'images/icons/sort-desc.svg';
@@ -55,6 +56,7 @@ type IconName =
   | 'error'
   | 'favorite-selected'
   | 'favorite-unselected'
+  | 'more'
   | 'pencil'
   | 'search'
   | 'sort'
@@ -84,6 +86,7 @@ export const iconsRegistry: Record<
   close: CloseIcon,
   compass: CompassIcon,
   error: ErrorIcon,
+  more: MoreIcon,
   pencil: PencilIcon,
   search: SearchIcon,
   sort: SortIcon,
diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js
index 33737f8..024c884 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -253,8 +253,8 @@ export function fetchCharts(
 }
 
 export const SHOW_BUILDER_PANE = 'SHOW_BUILDER_PANE';
-export function showBuilderPane(builderPaneType) {
-  return { type: SHOW_BUILDER_PANE, builderPaneType };
+export function showBuilderPane() {
+  return { type: SHOW_BUILDER_PANE };
 }
 
 export function addSliceToDashboard(id, component) {
diff --git a/superset-frontend/src/dashboard/components/AddSliceCard.jsx b/superset-frontend/src/dashboard/components/AddSliceCard.jsx
index 497cece..525aac5 100644
--- a/superset-frontend/src/dashboard/components/AddSliceCard.jsx
+++ b/superset-frontend/src/dashboard/components/AddSliceCard.jsx
@@ -22,24 +22,28 @@ import PropTypes from 'prop-types';
 import { t } from '@superset-ui/translation';
 
 const propTypes = {
-  datasourceLink: PropTypes.string,
+  datasourceUrl: PropTypes.string,
+  datasourceName: PropTypes.string,
   innerRef: PropTypes.func,
   isSelected: PropTypes.bool,
-  lastModified: PropTypes.string.isRequired,
+  lastModified: PropTypes.string,
   sliceName: PropTypes.string.isRequired,
   style: PropTypes.object,
   visType: PropTypes.string.isRequired,
 };
 
 const defaultProps = {
-  datasourceLink: '—',
+  datasourceUrl: null,
+  datasourceName: '-',
   innerRef: null,
   isSelected: false,
   style: null,
+  lastModified: null,
 };
 
 function AddSliceCard({
-  datasourceLink,
+  datasourceUrl,
+  datasourceName,
   innerRef,
   isSelected,
   lastModified,
@@ -62,9 +66,7 @@ function AddSliceCard({
           </div>
           <div className="item">
             <span>{t('Data source')} </span>
-            <span // eslint-disable-next-line react/no-danger
-              dangerouslySetInnerHTML={{ __html: datasourceLink }}
-            />
+            <a href={datasourceUrl}>{datasourceName}</a>
           </div>
         </div>
       </div>
diff --git a/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx b/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx
index 2d2ab08..336f399 100644
--- a/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx
+++ b/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx
@@ -19,37 +19,53 @@
 /* eslint-env browser */
 import PropTypes from 'prop-types';
 import React from 'react';
+import { Tabs, Tab } from 'react-bootstrap';
 import { StickyContainer, Sticky } from 'react-sticky';
 import { ParentSize } from '@vx/responsive';
 
-import InsertComponentPane, {
-  SUPERSET_HEADER_HEIGHT,
-} from './InsertComponentPane';
-import ColorComponentPane from './ColorComponentPane';
-import { BUILDER_PANE_TYPE } from '../util/constants';
+import { t } from '@superset-ui/translation';
+
+import NewColumn from './gridComponents/new/NewColumn';
+import NewDivider from './gridComponents/new/NewDivider';
+import NewHeader from './gridComponents/new/NewHeader';
+import NewRow from './gridComponents/new/NewRow';
+import NewTabs from './gridComponents/new/NewTabs';
+import NewMarkdown from './gridComponents/new/NewMarkdown';
+import SliceAdder from '../containers/SliceAdder';
 
 const propTypes = {
   topOffset: PropTypes.number,
-  showBuilderPane: PropTypes.func.isRequired,
-  builderPaneType: PropTypes.string.isRequired,
-  setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
-  colorScheme: PropTypes.string,
 };
 
 const defaultProps = {
   topOffset: 0,
-  colorScheme: undefined,
 };
 
+const SUPERSET_HEADER_HEIGHT = 59;
+
 class BuilderComponentPane extends React.PureComponent {
+  renderTabs(height) {
+    const { isSticky } = this.props;
+    return (
+      <Tabs className="m-t-10 tabs-components">
+        <Tab eventKey={1} title={t('Components')}>
+          <NewTabs />
+          <NewRow />
+          <NewColumn />
+          <NewHeader />
+          <NewMarkdown />
+          <NewDivider />
+        </Tab>
+        <Tab eventKey={2} title={t('Charts')} className="tab-charts">
+          <SliceAdder
+            height={height + (isSticky ? SUPERSET_HEADER_HEIGHT : 0)}
+          />
+        </Tab>
+      </Tabs>
+    );
+  }
   render() {
-    const {
-      topOffset,
-      builderPaneType,
-      showBuilderPane,
-      setColorSchemeAndUnsavedChanges,
-      colorScheme,
-    } = this.props;
+    const { topOffset } = this.props;
     return (
       <div
         className="dashboard-builder-sidepane"
@@ -66,22 +82,7 @@ class BuilderComponentPane extends React.PureComponent {
                     className="viewport"
                     style={isSticky ? { ...style, top: topOffset } : null}
                   >
-                    {builderPaneType === BUILDER_PANE_TYPE.ADD_COMPONENTS && (
-                      <InsertComponentPane
-                        height={height}
-                        isSticky={isSticky}
-                        showBuilderPane={showBuilderPane}
-                      />
-                    )}
-                    {builderPaneType === BUILDER_PANE_TYPE.COLORS && (
-                      <ColorComponentPane
-                        showBuilderPane={showBuilderPane}
-                        setColorSchemeAndUnsavedChanges={
-                          setColorSchemeAndUnsavedChanges
-                        }
-                        colorScheme={colorScheme}
-                      />
-                    )}
+                    {this.renderTabs(height)}
                   </div>
                 )}
               </Sticky>
diff --git a/superset-frontend/src/dashboard/components/ColorComponentPane.jsx b/superset-frontend/src/dashboard/components/ColorComponentPane.jsx
deleted file mode 100644
index ee6aec5..0000000
--- a/superset-frontend/src/dashboard/components/ColorComponentPane.jsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/* eslint-env browser */
-import PropTypes from 'prop-types';
-import React from 'react';
-import { getCategoricalSchemeRegistry } from '@superset-ui/color';
-import { t } from '@superset-ui/translation';
-
-import ColorSchemeControl from '../../explore/components/controls/ColorSchemeControl';
-import { BUILDER_PANE_TYPE } from '../util/constants';
-
-const propTypes = {
-  showBuilderPane: PropTypes.func.isRequired,
-  setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
-  colorScheme: PropTypes.string,
-};
-
-const defaultProps = {
-  colorScheme: undefined,
-};
-
-class ColorComponentPane extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = { hovered: false };
-    this.categoricalSchemeRegistry = getCategoricalSchemeRegistry();
-    this.getChoices = this.getChoices.bind(this);
-    this.getSchemes = this.getSchemes.bind(this);
-    this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
-    this.onMouseEnter = this.setHover.bind(this, true);
-    this.onMouseLeave = this.setHover.bind(this, false);
-  }
-
-  onCloseButtonClick() {
-    this.props.showBuilderPane(BUILDER_PANE_TYPE.NONE);
-  }
-
-  getChoices() {
-    return this.categoricalSchemeRegistry.keys().map(s => [s, s]);
-  }
-
-  getSchemes() {
-    return this.categoricalSchemeRegistry.getMap();
-  }
-
-  setHover(hovered) {
-    this.setState({ hovered });
-  }
-
-  render() {
-    const { setColorSchemeAndUnsavedChanges, colorScheme } = this.props;
-
-    return (
-      <div className="slider-container">
-        <div className="component-layer slide-content">
-          <div className="dashboard-builder-sidepane-header">
-            <span>{'Color Settings'}</span>
-            <i
-              className="fa fa-times trigger"
-              onClick={this.onCloseButtonClick}
-              role="none"
-            />
-          </div>
-          <div
-            className="panel-body"
-            onMouseEnter={this.onMouseEnter}
-            onMouseLeave={this.onMouseLeave}
-          >
-            <ColorSchemeControl
-              description={t(
-                "Any color palette selected here will override the colors applied to this dashboard's individual charts",
-              )}
-              label={t('Color Scheme')}
-              name="color_scheme"
-              onChange={setColorSchemeAndUnsavedChanges}
-              value={colorScheme}
-              choices={this.getChoices}
-              schemes={this.getSchemes}
-              hovered={this.state.hovered}
-            />
-          </div>
-        </div>
-      </div>
-    );
-  }
-}
-
-ColorComponentPane.propTypes = propTypes;
-ColorComponentPane.defaultProps = defaultProps;
-
-export default ColorComponentPane;
diff --git a/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx b/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx
new file mode 100644
index 0000000..edbfa2a
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/ColorSchemeControlWrapper.jsx
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/* eslint-env browser */
+import PropTypes from 'prop-types';
+import React from 'react';
+import { getCategoricalSchemeRegistry } from '@superset-ui/color';
+import { t } from '@superset-ui/translation';
+
+import ColorSchemeControl from 'src/explore/components/controls/ColorSchemeControl';
+
+const propTypes = {
+  onChange: PropTypes.func.isRequired,
+  colorScheme: PropTypes.string,
+};
+
+const defaultProps = {
+  colorScheme: undefined,
+  onChange: () => {},
+};
+
+class ColorSchemeControlWrapper extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = { hovered: false };
+    this.categoricalSchemeRegistry = getCategoricalSchemeRegistry();
+    this.choices = this.categoricalSchemeRegistry.keys().map(s => [s, s]);
+    this.schemes = this.categoricalSchemeRegistry.getMap();
+  }
+  setHover(hovered) {
+    this.setState({ hovered });
+  }
+
+  render() {
+    const { colorScheme } = this.props;
+    return (
+      <ColorSchemeControl
+        description={t(
+          "Any color palette selected here will override the colors applied to this dashboard's individual charts",
+        )}
+        label={t('Color Scheme')}
+        name="color_scheme"
+        onChange={this.props.onChange}
+        value={colorScheme}
+        choices={this.choices}
+        clearable
+        schemes={this.schemes}
+        hovered={this.state.hovered}
+      />
+    );
+  }
+}
+
+ColorSchemeControlWrapper.propTypes = propTypes;
+ColorSchemeControlWrapper.defaultProps = defaultProps;
+
+export default ColorSchemeControlWrapper;
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder.jsx b/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
index de9a94e..5ca6f2d 100644
--- a/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
@@ -39,7 +39,6 @@ import getDragDropManager from '../util/getDragDropManager';
 import findTabIndexByComponentId from '../util/findTabIndexByComponentId';
 
 import {
-  BUILDER_PANE_TYPE,
   DASHBOARD_GRID_ID,
   DASHBOARD_ROOT_ID,
   DASHBOARD_ROOT_DEPTH,
@@ -56,7 +55,6 @@ const propTypes = {
   deleteTopLevelTabs: PropTypes.func.isRequired,
   editMode: PropTypes.bool.isRequired,
   showBuilderPane: PropTypes.func.isRequired,
-  builderPaneType: PropTypes.string.isRequired,
   colorScheme: PropTypes.string,
   setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
   handleComponentDrop: PropTypes.func.isRequired,
@@ -161,7 +159,6 @@ class DashboardBuilder extends React.Component {
       dashboardLayout,
       editMode,
       showBuilderPane,
-      builderPaneType,
       setColorSchemeAndUnsavedChanges,
       colorScheme,
     } = this.props;
@@ -265,11 +262,10 @@ class DashboardBuilder extends React.Component {
               )}
             </ParentSize>
           </div>
-          {editMode && builderPaneType !== BUILDER_PANE_TYPE.NONE && (
+          {editMode && (
             <BuilderComponentPane
               topOffset={HEADER_HEIGHT + (topLevelTabs ? TABS_HEIGHT : 0)}
               showBuilderPane={showBuilderPane}
-              builderPaneType={builderPaneType}
               setColorSchemeAndUnsavedChanges={setColorSchemeAndUnsavedChanges}
               colorScheme={colorScheme}
             />
diff --git a/superset-frontend/src/dashboard/components/Header.jsx b/superset-frontend/src/dashboard/components/Header.jsx
index 3d04284..89f2084 100644
--- a/superset-frontend/src/dashboard/components/Header.jsx
+++ b/superset-frontend/src/dashboard/components/Header.jsx
@@ -21,20 +21,21 @@ import moment from 'moment';
 import React from 'react';
 import PropTypes from 'prop-types';
 import styled from '@superset-ui/style';
+import { ButtonGroup } from 'react-bootstrap';
 import { CategoricalColorNamespace } from '@superset-ui/color';
 import { t } from '@superset-ui/translation';
 
+import Icon from 'src/components/Icon';
+
 import HeaderActionsDropdown from './HeaderActionsDropdown';
 import EditableTitle from '../../components/EditableTitle';
 import Button from '../../components/Button';
 import FaveStar from '../../components/FaveStar';
-import FilterScopeModal from './filterscope/FilterScopeModal';
 import PublishedStatus from './PublishedStatus';
 import UndoRedoKeylisteners from './UndoRedoKeylisteners';
 
 import { chartPropShape } from '../util/propShapes';
 import {
-  BUILDER_PANE_TYPE,
   UNDO_LIMIT,
   SAVE_TYPE_OVERWRITE,
   DASHBOARD_POSITION_DATA_LIMIT,
@@ -62,6 +63,7 @@ const propTypes = {
   customCss: PropTypes.string.isRequired,
   colorNamespace: PropTypes.string,
   colorScheme: PropTypes.string,
+  setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
   isStarred: PropTypes.bool.isRequired,
   isPublished: PropTypes.bool.isRequired,
   isLoading: PropTypes.bool.isRequired,
@@ -75,7 +77,6 @@ const propTypes = {
   editMode: PropTypes.bool.isRequired,
   setEditMode: PropTypes.func.isRequired,
   showBuilderPane: PropTypes.func.isRequired,
-  builderPaneType: PropTypes.string.isRequired,
   updateCss: PropTypes.func.isRequired,
   logEvent: PropTypes.func.isRequired,
   hasUnsavedChanges: PropTypes.bool.isRequired,
@@ -124,10 +125,6 @@ class Header extends React.PureComponent {
     this.handleChangeText = this.handleChangeText.bind(this);
     this.handleCtrlZ = this.handleCtrlZ.bind(this);
     this.handleCtrlY = this.handleCtrlY.bind(this);
-    this.onInsertComponentsButtonClick = this.onInsertComponentsButtonClick.bind(
-      this,
-    );
-    this.onColorsButtonClick = this.onColorsButtonClick.bind(this);
     this.toggleEditMode = this.toggleEditMode.bind(this);
     this.forceRefresh = this.forceRefresh.bind(this);
     this.startPeriodicRender = this.startPeriodicRender.bind(this);
@@ -162,14 +159,6 @@ class Header extends React.PureComponent {
     clearTimeout(this.ctrlZTimeout);
   }
 
-  onInsertComponentsButtonClick() {
-    this.props.showBuilderPane(BUILDER_PANE_TYPE.ADD_COMPONENTS);
-  }
-
-  onColorsButtonClick() {
-    this.props.showBuilderPane(BUILDER_PANE_TYPE.COLORS);
-  }
-
   handleChangeText(nextText) {
     const { updateDashboardTitle, onChange } = this.props;
     if (nextText && this.props.dashboardTitle !== nextText) {
@@ -340,6 +329,7 @@ class Header extends React.PureComponent {
       expandedSlices,
       customCss,
       colorNamespace,
+      setColorSchemeAndUnsavedChanges,
       colorScheme,
       onUndo,
       onRedo,
@@ -350,7 +340,6 @@ class Header extends React.PureComponent {
       updateCss,
       editMode,
       isPublished,
-      builderPaneType,
       dashboardInfo,
       hasUnsavedChanges,
       isLoading,
@@ -366,7 +355,6 @@ class Header extends React.PureComponent {
     const refreshWarning =
       dashboardInfo.common.conf
         .SUPERSET_DASHBOARD_PERIODICAL_REFRESH_WARNING_MESSAGE;
-    const popButton = hasUnsavedChanges;
 
     return (
       <StyledDashboardHeader className="dashboard-header">
@@ -399,92 +387,63 @@ class Header extends React.PureComponent {
           {userCanSaveAs && (
             <div className="button-container">
               {editMode && (
-                <Button
-                  bsSize="small"
-                  onClick={onUndo}
-                  disabled={undoLength < 1}
-                  bsStyle={this.state.emphasizeUndo ? 'primary' : undefined}
-                >
-                  <div title="Undo" className="undo-action fa fa-reply" />
-                </Button>
-              )}
-
-              {editMode && (
-                <Button
-                  bsSize="small"
-                  onClick={onRedo}
-                  disabled={redoLength < 1}
-                  bsStyle={this.state.emphasizeRedo ? 'primary' : undefined}
-                >
-                  <div title="Redo" className="redo-action fa fa-share" />
-                </Button>
-              )}
-
-              {editMode && (
-                <Button
-                  active={builderPaneType === BUILDER_PANE_TYPE.ADD_COMPONENTS}
-                  bsSize="small"
-                  onClick={this.onInsertComponentsButtonClick}
-                >
-                  {t('Components')}
-                </Button>
-              )}
-
-              {editMode && (
-                <Button
-                  active={builderPaneType === BUILDER_PANE_TYPE.COLORS}
-                  bsSize="small"
-                  onClick={this.onColorsButtonClick}
-                >
-                  {t('Colors')}
-                </Button>
-              )}
-
-              {editMode && (
-                <FilterScopeModal
-                  triggerNode={<Button bsSize="small">{t('Filters')}</Button>}
-                />
-              )}
-
-              {editMode && hasUnsavedChanges && (
-                <Button
-                  bsSize="small"
-                  bsStyle={popButton ? 'primary' : undefined}
-                  onClick={this.overwriteDashboard}
-                >
-                  {t('Save changes')}
-                </Button>
-              )}
-
-              {editMode && !hasUnsavedChanges && (
-                <Button
-                  bsSize="small"
-                  onClick={this.toggleEditMode}
-                  bsStyle={undefined}
-                  disabled={!userCanEdit}
-                >
-                  {t('Switch to view mode')}
-                </Button>
-              )}
-
-              {editMode && (
-                <UndoRedoKeylisteners
-                  onUndo={this.handleCtrlZ}
-                  onRedo={this.handleCtrlY}
-                />
+                <>
+                  <ButtonGroup className="m-r-5">
+                    <Button
+                      bsSize="small"
+                      onClick={onUndo}
+                      disabled={undoLength < 1}
+                      bsStyle={this.state.emphasizeUndo ? 'primary' : undefined}
+                    >
+                      <i title="Undo" className="undo-action fa fa-reply" />
+                      &nbsp;
+                    </Button>
+                    <Button
+                      bsSize="small"
+                      onClick={onRedo}
+                      disabled={redoLength < 1}
+                      bsStyle={this.state.emphasizeRedo ? 'primary' : undefined}
+                    >
+                      &nbsp;
+                      <i title="Redo" className="redo-action fa fa-share" />
+                    </Button>
+                  </ButtonGroup>
+                  <Button
+                    bsSize="small"
+                    className="m-r-5"
+                    onClick={this.constructor.discardChanges}
+                    bsStyle="default"
+                  >
+                    {t('Discard Changes')}
+                  </Button>
+                  <Button
+                    bsSize="small"
+                    disabled={!hasUnsavedChanges}
+                    bsStyle="primary"
+                    onClick={this.overwriteDashboard}
+                  >
+                    {t('Save')}
+                  </Button>
+                </>
               )}
             </div>
           )}
+          {editMode && (
+            <UndoRedoKeylisteners
+              onUndo={this.handleCtrlZ}
+              onRedo={this.handleCtrlY}
+            />
+          )}
 
-          {!editMode && !hasUnsavedChanges && (
-            <Button
-              bsSize="small"
+          {!editMode && (
+            <span
+              role="button"
+              tabIndex={0}
+              className="action-button"
               onClick={this.toggleEditMode}
-              bsStyle={popButton ? 'primary' : undefined}
-              disabled={!userCanEdit}
             >
-              {t('Edit dashboard')}
-            </Button>
+              <Icon name="pencil" />
+            </span>
           )}
 
           {this.state.showingPropertiesModal && (
@@ -492,12 +451,18 @@ class Header extends React.PureComponent {
               dashboardId={dashboardInfo.id}
               show={this.state.showingPropertiesModal}
               onHide={this.hidePropertiesModal}
-              onDashboardSave={updates => {
-                this.props.dashboardInfoChanged({
+              colorScheme={this.props.colorScheme}
+              onSubmit={updates => {
+                const {
+                  dashboardInfoChanged,
+                  dashboardTitleChanged,
+                } = this.props;
+                dashboardInfoChanged({
                   slug: updates.slug,
                   metadata: JSON.parse(updates.jsonMetadata),
                 });
-                this.props.dashboardTitleChanged(updates.title);
+                setColorSchemeAndUnsavedChanges(updates.colorScheme);
+                dashboardTitleChanged(updates.title);
                 if (updates.slug) {
                   history.pushState(
                     { event: 'dashboard_properties_changed' },
diff --git a/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx b/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx
index da574e0..8a1c2e9 100644
--- a/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx
+++ b/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx
@@ -18,16 +18,20 @@
  */
 import React from 'react';
 import PropTypes from 'prop-types';
+
 import { SupersetClient } from '@superset-ui/connection';
 import { DropdownButton, MenuItem } from 'react-bootstrap';
 import { t } from '@superset-ui/translation';
 
+import Icon from 'src/components/Icon';
+
 import CssEditor from './CssEditor';
 import RefreshIntervalModal from './RefreshIntervalModal';
 import SaveModal from './SaveModal';
 import injectCustomCss from '../util/injectCustomCss';
 import { SAVE_TYPE_NEWDASHBOARD } from '../util/constants';
 import URLShortLinkModal from '../../components/URLShortLinkModal';
+import FilterScopeModal from './filterscope/FilterScopeModal';
 import downloadAsImage from '../../utils/downloadAsImage';
 import getDashboardUrl from '../util/getDashboardUrl';
 import { getActiveFilters } from '../util/activeDashboardFilters';
@@ -38,7 +42,6 @@ const propTypes = {
   dashboardInfo: PropTypes.object.isRequired,
   dashboardId: PropTypes.number.isRequired,
   dashboardTitle: PropTypes.string.isRequired,
-  hasUnsavedChanges: PropTypes.bool.isRequired,
   customCss: PropTypes.string.isRequired,
   colorNamespace: PropTypes.string,
   colorScheme: PropTypes.string,
@@ -128,7 +131,6 @@ class HeaderActionsDropdown extends React.PureComponent {
       customCss,
       colorNamespace,
       colorScheme,
-      hasUnsavedChanges,
       layout,
       expandedSlices,
       onSave,
@@ -145,72 +147,36 @@ class HeaderActionsDropdown extends React.PureComponent {
 
     return (
       <DropdownButton
-        title=""
+        title={<Icon name="more" />}
+        noCaret
         id="save-dash-split-button"
-        bsStyle={hasUnsavedChanges ? 'primary' : undefined}
         bsSize="small"
+        style={{ border: 'none', padding: 0, marginLeft: '4px' }}
         pullRight
       >
         {userCanSave && (
-          <SaveModal
-            addSuccessToast={this.props.addSuccessToast}
-            addDangerToast={this.props.addDangerToast}
-            dashboardId={dashboardId}
-            dashboardTitle={dashboardTitle}
-            dashboardInfo={dashboardInfo}
-            saveType={SAVE_TYPE_NEWDASHBOARD}
-            layout={layout}
-            expandedSlices={expandedSlices}
-            refreshFrequency={refreshFrequency}
-            shouldPersistRefreshFrequency={shouldPersistRefreshFrequency}
-            customCss={customCss}
-            colorNamespace={colorNamespace}
-            colorScheme={colorScheme}
-            onSave={onSave}
-            isMenuItem
-            triggerNode={<span>{t('Save as')}</span>}
-            canOverwrite={userCanEdit}
-          />
-        )}
-
-        {hasUnsavedChanges && userCanSave && (
-          <div>
-            <MenuItem
-              eventKey="discard"
-              onSelect={HeaderActionsDropdown.discardChanges}
-            >
-              {t('Discard changes')}
-            </MenuItem>
-          </div>
-        )}
-
-        {userCanSave && <MenuItem divider />}
-
-        <MenuItem onClick={forceRefreshAllCharts} disabled={isLoading}>
-          {t('Force refresh dashboard')}
-        </MenuItem>
-
-        <RefreshIntervalModal
-          refreshFrequency={refreshFrequency}
-          refreshLimit={refreshLimit}
-          refreshWarning={refreshWarning}
-          onChange={this.changeRefreshInterval}
-          editMode={editMode}
-          triggerNode={
-            <span>
-              {editMode
-                ? t('Set auto-refresh interval')
-                : t('Auto-refresh dashboard')}
-            </span>
-          }
-        />
-
-        {editMode && (
-          <MenuItem onClick={this.props.showPropertiesModal}>
-            {t('Edit dashboard properties')}
-          </MenuItem>
+          <>
+            <SaveModal
+              addSuccessToast={this.props.addSuccessToast}
+              addDangerToast={this.props.addDangerToast}
+              dashboardId={dashboardId}
+              dashboardTitle={dashboardTitle}
+              dashboardInfo={dashboardInfo}
+              saveType={SAVE_TYPE_NEWDASHBOARD}
+              layout={layout}
+              expandedSlices={expandedSlices}
+              refreshFrequency={refreshFrequency}
+              shouldPersistRefreshFrequency={shouldPersistRefreshFrequency}
+              customCss={customCss}
+              colorNamespace={colorNamespace}
+              colorScheme={colorScheme}
+              onSave={onSave}
+              isMenuItem
+              triggerNode={<span>{t('Save as')}</span>}
+              canOverwrite={userCanEdit}
+            />
+          </>
         )}
-
         <URLShortLinkModal
           url={getDashboardUrl(
             window.location.pathname,
@@ -223,14 +189,37 @@ class HeaderActionsDropdown extends React.PureComponent {
           isMenuItem
           triggerNode={<span>{t('Share dashboard')}</span>}
         />
+        <MenuItem onClick={forceRefreshAllCharts} disabled={isLoading}>
+          {t('Refresh dashboard')}
+        </MenuItem>
+        <MenuItem divider />
+        <RefreshIntervalModal
+          refreshFrequency={refreshFrequency}
+          refreshLimit={refreshLimit}
+          refreshWarning={refreshWarning}
+          onChange={this.changeRefreshInterval}
+          editMode={editMode}
+          triggerNode={<span>{t('Set auto-refresh interval')}</span>}
+        />
 
         {editMode && (
-          <CssEditor
-            triggerNode={<span>{t('Edit CSS')}</span>}
-            initialCss={this.state.css}
-            templates={this.state.cssTemplates}
-            onChange={this.changeCss}
-          />
+          <>
+            <FilterScopeModal
+              className="m-r-5"
+              triggerNode={
+                <MenuItem bsSize="small">{t('Set filter mapping')}</MenuItem>
+              }
+            />
+            <MenuItem onClick={this.props.showPropertiesModal}>
+              {t('Edit dashboard properties')}
+            </MenuItem>
+            <CssEditor
+              triggerNode={<span>{t('Edit CSS')}</span>}
+              initialCss={this.state.css}
+              templates={this.state.cssTemplates}
+              onChange={this.changeCss}
+            />
+          </>
         )}
 
         {!editMode && (
diff --git a/superset-frontend/src/dashboard/components/InsertComponentPane.jsx b/superset-frontend/src/dashboard/components/InsertComponentPane.jsx
deleted file mode 100644
index 3141347..0000000
--- a/superset-frontend/src/dashboard/components/InsertComponentPane.jsx
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/* eslint-env browser */
-import PropTypes from 'prop-types';
-import React from 'react';
-import cx from 'classnames';
-import { t } from '@superset-ui/translation';
-
-import NewColumn from './gridComponents/new/NewColumn';
-import NewDivider from './gridComponents/new/NewDivider';
-import NewHeader from './gridComponents/new/NewHeader';
-import NewRow from './gridComponents/new/NewRow';
-import NewTabs from './gridComponents/new/NewTabs';
-import NewMarkdown from './gridComponents/new/NewMarkdown';
-import SliceAdder from '../containers/SliceAdder';
-import { BUILDER_PANE_TYPE } from '../util/constants';
-
-export const SUPERSET_HEADER_HEIGHT = 59;
-
-const propTypes = {
-  height: PropTypes.number.isRequired,
-  isSticky: PropTypes.bool.isRequired,
-  showBuilderPane: PropTypes.func.isRequired,
-};
-
-class InsertComponentPane extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      slideDirection: 'slide-out',
-    };
-
-    this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
-    this.openSlicesPane = this.slide.bind(this, 'slide-in');
-    this.closeSlicesPane = this.slide.bind(this, 'slide-out');
-  }
-
-  onCloseButtonClick() {
-    this.props.showBuilderPane(BUILDER_PANE_TYPE.NONE);
-  }
-
-  slide(direction) {
-    this.setState({
-      slideDirection: direction,
-    });
-  }
-
-  render() {
-    return (
-      <div className={cx('slider-container', this.state.slideDirection)}>
-        <div className="component-layer slide-content">
-          <div className="dashboard-builder-sidepane-header">
-            <span>{t('Insert components')}</span>
-            <i
-              className="fa fa-times trigger"
-              onClick={this.onCloseButtonClick}
-              role="none"
-            />
-          </div>
-          <div
-            className="new-component static"
-            role="none"
-            onClick={this.openSlicesPane}
-          >
-            <div className="new-component-placeholder fa fa-area-chart" />
-            <div className="new-component-label">
-              {t('Your charts & filters')}
-            </div>
-
-            <i className="fa fa-arrow-right trigger" />
-          </div>
-          <NewTabs />
-          <NewRow />
-          <NewColumn />
-          <NewHeader />
-          <NewMarkdown />
-          <NewDivider />
-        </div>
-        <div className="slices-layer slide-content">
-          <div
-            className="dashboard-builder-sidepane-header"
-            onClick={this.closeSlicesPane}
-            role="none"
-          >
-            <i className="fa fa-arrow-left trigger" />
-            <span>{t('Your charts and filters')}</span>
-          </div>
-          <SliceAdder
-            height={
-              this.props.height +
-              (this.props.isSticky ? SUPERSET_HEADER_HEIGHT : 0)
-            }
-          />
-        </div>
-      </div>
-    );
-  }
-}
-
-InsertComponentPane.propTypes = propTypes;
-
-export default InsertComponentPane;
diff --git a/superset-frontend/src/dashboard/components/PropertiesModal.jsx b/superset-frontend/src/dashboard/components/PropertiesModal.jsx
index 77c2995..aac3be2 100644
--- a/superset-frontend/src/dashboard/components/PropertiesModal.jsx
+++ b/superset-frontend/src/dashboard/components/PropertiesModal.jsx
@@ -27,6 +27,7 @@ import { t } from '@superset-ui/translation';
 import { SupersetClient } from '@superset-ui/connection';
 
 import FormLabel from 'src/components/FormLabel';
+import ColorSchemeControlWrapper from 'src/dashboard/components/ColorSchemeControlWrapper';
 import getClientErrorObject from '../../utils/getClientErrorObject';
 import withToasts from '../../messageToasts/enhancers/withToasts';
 import '../stylesheets/buttons.less';
@@ -35,14 +36,20 @@ const propTypes = {
   dashboardId: PropTypes.number.isRequired,
   show: PropTypes.bool.isRequired,
   onHide: PropTypes.func,
-  onDashboardSave: PropTypes.func,
+  colorScheme: PropTypes.object,
+  setColorSchemeAndUnsavedChanges: PropTypes.func,
+  onSubmit: PropTypes.func,
   addSuccessToast: PropTypes.func.isRequired,
+  onlyApply: PropTypes.bool,
 };
 
 const defaultProps = {
   onHide: () => {},
-  onDashboardSave: () => {},
+  setColorSchemeAndUnsavedChanges: () => {},
+  onSubmit: () => {},
   show: false,
+  colorScheme: undefined,
+  onlyApply: false,
 };
 
 class PropertiesModal extends React.PureComponent {
@@ -55,6 +62,7 @@ class PropertiesModal extends React.PureComponent {
         slug: '',
         owners: [],
         json_metadata: '',
+        colorScheme: props.colorScheme,
       },
       isDashboardLoaded: false,
       isAdvancedOpen: false,
@@ -62,15 +70,19 @@ class PropertiesModal extends React.PureComponent {
     this.onChange = this.onChange.bind(this);
     this.onMetadataChange = this.onMetadataChange.bind(this);
     this.onOwnersChange = this.onOwnersChange.bind(this);
-    this.save = this.save.bind(this);
+    this.submit = this.submit.bind(this);
     this.toggleAdvanced = this.toggleAdvanced.bind(this);
     this.loadOwnerOptions = this.loadOwnerOptions.bind(this);
     this.handleErrorResponse = this.handleErrorResponse.bind(this);
+    this.onColorSchemeChange = this.onColorSchemeChange.bind(this);
   }
 
   componentDidMount() {
     this.fetchDashboardDetails();
   }
+  onColorSchemeChange(value) {
+    this.updateFormState('colorScheme', value);
+  }
 
   onOwnersChange(value) {
     this.updateFormState('owners', value);
@@ -155,39 +167,55 @@ class PropertiesModal extends React.PureComponent {
     });
   }
 
-  save(e) {
+  submit(e) {
     e.preventDefault();
     e.stopPropagation();
     const { values } = this.state;
+    const { onlyApply } = this.props;
     const owners = values.owners.map(o => o.value);
-
-    SupersetClient.put({
-      endpoint: `/api/v1/dashboard/${this.props.dashboardId}`,
-      headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({
-        dashboard_title: values.dashboard_title,
-        slug: values.slug || null,
-        json_metadata: values.json_metadata || null,
-        owners,
-      }),
-    }).then(({ json }) => {
-      this.props.addSuccessToast(t('The dashboard has been saved'));
-      this.props.onDashboardSave({
+    if (onlyApply) {
+      this.props.onSubmit({
         id: this.props.dashboardId,
-        title: json.result.dashboard_title,
-        slug: json.result.slug,
-        jsonMetadata: json.result.json_metadata,
-        ownerIds: json.result.owners,
+        title: values.dashboard_title,
+        slug: values.slug,
+        jsonMetadata: values.json_metadata,
+        ownerIds: owners,
+        colorScheme: values.colorScheme,
       });
       this.props.onHide();
-    }, this.handleErrorResponse);
+    } else {
+      SupersetClient.put({
+        endpoint: `/api/v1/dashboard/${this.props.dashboardId}`,
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({
+          dashboard_title: values.dashboard_title,
+          slug: values.slug || null,
+          json_metadata: values.json_metadata || null,
+          owners,
+        }),
+      }).then(({ json }) => {
+        this.props.addSuccessToast(t('The dashboard has been saved'));
+        this.props.onSubmit({
+          id: this.props.dashboardId,
+          title: json.result.dashboard_title,
+          slug: json.result.slug,
+          jsonMetadata: json.result.json_metadata,
+          ownerIds: json.result.owners,
+          colorScheme: values.colorScheme,
+        });
+        this.props.onHide();
+      }, this.handleErrorResponse);
+    }
   }
 
   render() {
-    const { values, isDashboardLoaded, isAdvancedOpen } = this.state;
+    const { values, isDashboardLoaded, isAdvancedOpen, errors } = this.state;
+    const { onHide, onlyApply } = this.props;
+
+    const saveLabel = onlyApply ? t('Apply') : t('Save');
     return (
       <Modal show={this.props.show} onHide={this.props.onHide} bsSize="lg">
-        <form onSubmit={this.save}>
+        <form onSubmit={this.submit}>
           <Modal.Header closeButton>
             <Modal.Title>
               <div>
@@ -249,6 +277,13 @@ class PropertiesModal extends React.PureComponent {
                   )}
                 </p>
               </Col>
+              <Col md={6}>
+                <h3 style={{ marginTop: '1em' }}>{t('Colors')}</h3>
+                <ColorSchemeControlWrapper
+                  onChange={this.onColorSchemeChange}
+                  colorScheme={values.colorScheme}
+                />
+              </Col>
             </Row>
             <Row>
               <Col md={12}>
@@ -300,11 +335,11 @@ class PropertiesModal extends React.PureComponent {
                 bsSize="sm"
                 bsStyle="primary"
                 className="m-r-5"
-                disabled={this.state.errors.length > 0}
+                disabled={errors.length > 0}
               >
-                {t('Save')}
+                {saveLabel}
               </Button>
-              <Button type="button" bsSize="sm" onClick={this.props.onHide}>
+              <Button type="button" bsSize="sm" onClick={onHide}>
                 {t('Cancel')}
               </Button>
               <Dialog
diff --git a/superset-frontend/src/dashboard/components/RefreshIntervalModal.jsx b/superset-frontend/src/dashboard/components/RefreshIntervalModal.jsx
index 592c322..c6957a3 100644
--- a/superset-frontend/src/dashboard/components/RefreshIntervalModal.jsx
+++ b/superset-frontend/src/dashboard/components/RefreshIntervalModal.jsx
@@ -22,7 +22,8 @@ import Select from 'src/components/Select';
 import { t } from '@superset-ui/translation';
 import { Alert, Button } from 'react-bootstrap';
 
-import ModalTrigger from '../../components/ModalTrigger';
+import ModalTrigger from 'src/components/ModalTrigger';
+import FormLabel from 'src/components/FormLabel';
 
 const propTypes = {
   triggerNode: PropTypes.node.isRequired,
@@ -96,7 +97,7 @@ class RefreshIntervalModal extends React.PureComponent {
         modalTitle={t('Refresh Interval')}
         modalBody={
           <div>
-            {t('Choose the refresh frequency for this dashboard')}
+            <FormLabel>{t('Refresh frequency')}</FormLabel>
             <Select
               options={options}
               value={this.state.refreshFrequency}
@@ -115,10 +116,12 @@ class RefreshIntervalModal extends React.PureComponent {
         }
         modalFooter={
           <>
-            <Button bsStyle="primary" onClick={this.onSave}>
+            <Button bsStyle="primary" bsSize="sm" onClick={this.onSave}>
               {editMode ? t('Save') : t('Save for this session')}
             </Button>
-            <Button onClick={this.onCancel}>{t('Cancel')}</Button>
+            <Button onClick={this.onCancel} bsSize="sm">
+              {t('Cancel')}
+            </Button>
           </>
         }
       />
diff --git a/superset-frontend/src/dashboard/components/SliceAdder.jsx b/superset-frontend/src/dashboard/components/SliceAdder.jsx
index f9c256f..76ebe52 100644
--- a/superset-frontend/src/dashboard/components/SliceAdder.jsx
+++ b/superset-frontend/src/dashboard/components/SliceAdder.jsx
@@ -20,7 +20,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { DropdownButton, MenuItem } from 'react-bootstrap';
-import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
+import { List } from 'react-virtualized';
 import SearchInput, { createFilter } from 'react-search-input';
 import { t } from '@superset-ui/translation';
 
@@ -60,14 +60,9 @@ const KEYS_TO_SORT = [
 ];
 
 const MARGIN_BOTTOM = 16;
-const SIDEPANE_HEADER_HEIGHT = 55;
+const SIDEPANE_HEADER_HEIGHT = 30;
 const SLICE_ADDER_CONTROL_HEIGHT = 64;
-const DEFAULT_CELL_HEIGHT = 136;
-
-const cache = new CellMeasurerCache({
-  defaultHeight: DEFAULT_CELL_HEIGHT,
-  fixedWidth: true,
-});
+const DEFAULT_CELL_HEIGHT = 112;
 
 class SliceAdder extends React.Component {
   static sortByComparator(attr) {
@@ -91,7 +86,6 @@ class SliceAdder extends React.Component {
       sortBy: KEYS_TO_SORT.findIndex(item => item.key === 'changed_on'),
       selectedSliceIdsSet: new Set(props.selectedSliceIds),
     };
-
     this.rowRenderer = this.rowRenderer.bind(this);
     this.searchUpdated = this.searchUpdated.bind(this);
     this.handleKeyPress = this.handleKeyPress.bind(this);
@@ -159,7 +153,7 @@ class SliceAdder extends React.Component {
     });
   }
 
-  rowRenderer({ key, index, style, parent }) {
+  rowRenderer({ key, index, style }) {
     const { filteredSlices, selectedSliceIdsSet } = this.state;
     const cellData = filteredSlices[index];
     const isSelected = selectedSliceIdsSet.has(cellData.slice_id);
@@ -190,23 +184,16 @@ class SliceAdder extends React.Component {
         style={{}}
       >
         {({ dragSourceRef }) => (
-          <CellMeasurer
-            cache={cache}
-            columnIndex={0}
-            key={key}
-            parent={parent}
-            rowIndex={index}
-          >
-            <AddSliceCard
-              innerRef={dragSourceRef}
-              style={style}
-              sliceName={cellData.slice_name}
-              lastModified={cellData.changed_on_humanized}
-              visType={cellData.viz_type}
-              datasourceLink={cellData.datasource_link}
-              isSelected={isSelected}
-            />
-          </CellMeasurer>
+          <AddSliceCard
+            innerRef={dragSourceRef}
+            style={style}
+            sliceName={cellData.slice_name}
+            lastModified={cellData.changed_on_humanized}
+            visType={cellData.viz_type}
+            datasourceUrl={cellData.datasource_url}
+            datasourceName={cellData.datasource_name}
+            isSelected={isSelected}
+          />
         )}
       </DragDroppable>
     );
@@ -227,7 +214,6 @@ class SliceAdder extends React.Component {
             onChange={this.searchUpdated}
             onKeyPress={this.handleKeyPress}
           />
-
           <DropdownButton
             title={`Sort by ${KEYS_TO_SORT[this.state.sortBy].label}`}
             onSelect={this.handleSelect}
@@ -246,8 +232,7 @@ class SliceAdder extends React.Component {
             width={376}
             height={slicesListHeight}
             rowCount={this.state.filteredSlices.length}
-            deferredMeasurementCache={cache}
-            rowHeight={cache.rowHeight}
+            rowHeight={DEFAULT_CELL_HEIGHT}
             rowRenderer={this.rowRenderer}
             searchTerm={this.state.searchTerm}
             sortBy={this.state.sortBy}
diff --git a/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx b/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx
index 494f4b5..404561e 100644
--- a/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx
+++ b/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx
@@ -72,9 +72,10 @@ function AddSliceDragPreview({ dragItem, slices, isDragging, currentOffset }) {
         transform: `translate(${currentOffset.x}px, ${currentOffset.y}px)`,
       }}
       sliceName={slice.slice_name}
-      lastModified={slice.modified}
+      lastModified={slice.changed_on_humanized}
       visType={slice.viz_type}
-      datasourceLink={slice.datasource_link}
+      datasourceUrl={slice.datasource_url}
+      datasourceName={slice.datasource_name}
     />
   );
 }
diff --git a/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx b/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx
index d4dad08..e3edd2b 100644
--- a/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx
+++ b/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx
@@ -513,9 +513,11 @@ export default class FilterScopeSelector extends React.PureComponent {
         </div>
 
         <div className="dashboard-modal-actions-container">
-          <Button onClick={this.onClose}>{t('Close')}</Button>
+          <Button bsSize="sm" onClick={this.onClose}>
+            {t('Close')}
+          </Button>
           {showSelector && (
-            <Button bsStyle="primary" onClick={this.onSave}>
+            <Button bsSize="sm" bsStyle="primary" onClick={this.onSave}>
               {t('Save')}
             </Button>
           )}
diff --git a/superset-frontend/src/dashboard/containers/DashboardBuilder.jsx b/superset-frontend/src/dashboard/containers/DashboardBuilder.jsx
index 9887295..5f31d47 100644
--- a/superset-frontend/src/dashboard/containers/DashboardBuilder.jsx
+++ b/superset-frontend/src/dashboard/containers/DashboardBuilder.jsx
@@ -36,7 +36,6 @@ function mapStateToProps({ dashboardLayout: undoableLayout, dashboardState }) {
     editMode: dashboardState.editMode,
     showBuilderPane: dashboardState.showBuilderPane,
     directPathToChild: dashboardState.directPathToChild,
-    builderPaneType: dashboardState.builderPaneType,
     colorScheme: dashboardState.colorScheme,
   };
 }
diff --git a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
index 962fc9a..4bdc249 100644
--- a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
+++ b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
@@ -30,6 +30,7 @@ import {
   fetchFaveStar,
   saveFaveStar,
   savePublished,
+  setColorSchemeAndUnsavedChanges,
   fetchCharts,
   updateCss,
   onChange,
@@ -83,7 +84,8 @@ function mapStateToProps({
     hasUnsavedChanges: !!dashboardState.hasUnsavedChanges,
     maxUndoHistoryExceeded: !!dashboardState.maxUndoHistoryExceeded,
     editMode: !!dashboardState.editMode,
-    builderPaneType: dashboardState.builderPaneType,
+    slug: dashboardInfo.slug,
+    metadata: dashboardInfo.metadata,
   };
 }
 
@@ -97,6 +99,7 @@ function mapDispatchToProps(dispatch) {
       onRedo: redoLayoutAction,
       setEditMode,
       showBuilderPane,
+      setColorSchemeAndUnsavedChanges,
       fetchFaveStar,
       saveFaveStar,
       savePublished,
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.js b/superset-frontend/src/dashboard/reducers/dashboardState.js
index fc7e079..afb8521 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardState.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.js
@@ -35,7 +35,6 @@ import {
   SET_DIRECT_PATH,
   SET_FOCUSED_FILTER_FIELD,
 } from '../actions/dashboardState';
-import { BUILDER_PANE_TYPE } from '../util/constants';
 
 export default function dashboardStateReducer(state = {}, action) {
   const actionHandlers = {
@@ -70,9 +69,6 @@ export default function dashboardStateReducer(state = {}, action) {
       return {
         ...state,
         editMode: action.editMode,
-        builderPaneType: action.editMode
-          ? BUILDER_PANE_TYPE.ADD_COMPONENTS
-          : BUILDER_PANE_TYPE.NONE,
       };
     },
     [SET_MAX_UNDO_HISTORY_EXCEEDED]() {
@@ -80,7 +76,7 @@ export default function dashboardStateReducer(state = {}, action) {
       return { ...state, maxUndoHistoryExceeded };
     },
     [SHOW_BUILDER_PANE]() {
-      return { ...state, builderPaneType: action.builderPaneType };
+      return { ...state };
     },
     [SET_COLOR_SCHEME]() {
       return {
@@ -108,7 +104,6 @@ export default function dashboardStateReducer(state = {}, action) {
         hasUnsavedChanges: false,
         maxUndoHistoryExceeded: false,
         editMode: false,
-        builderPaneType: BUILDER_PANE_TYPE.NONE,
         updatedColorScheme: false,
       };
     },
diff --git a/superset-frontend/src/dashboard/reducers/getInitialState.js b/superset-frontend/src/dashboard/reducers/getInitialState.js
index 4f77b07..770e249 100644
--- a/superset-frontend/src/dashboard/reducers/getInitialState.js
+++ b/superset-frontend/src/dashboard/reducers/getInitialState.js
@@ -31,7 +31,6 @@ import { getParam } from '../../modules/utils';
 import { applyDefaultFormData } from '../../explore/store';
 import { buildActiveFilters } from '../util/activeDashboardFilters';
 import {
-  BUILDER_PANE_TYPE,
   DASHBOARD_HEADER_ID,
   GRID_DEFAULT_CHART_WIDTH,
   GRID_COLUMN_COUNT,
@@ -301,10 +300,6 @@ export default function (bootstrapData) {
       colorScheme: dashboard.metadata.color_scheme,
       editMode: dashboard.dash_edit_perm && editMode,
       isPublished: dashboard.published,
-      builderPaneType:
-        dashboard.dash_edit_perm && editMode
-          ? BUILDER_PANE_TYPE.ADD_COMPONENTS
-          : BUILDER_PANE_TYPE.NONE,
       hasUnsavedChanges: false,
       maxUndoHistoryExceeded: false,
     },
diff --git a/superset-frontend/src/dashboard/stylesheets/builder-sidepane.less b/superset-frontend/src/dashboard/stylesheets/builder-sidepane.less
index fd56f6f..7d9c35f 100644
--- a/superset-frontend/src/dashboard/stylesheets/builder-sidepane.less
+++ b/superset-frontend/src/dashboard/stylesheets/builder-sidepane.less
@@ -94,11 +94,13 @@
     .chart-card {
       border: 1px solid @gray-light;
       font-weight: @font-weight-light;
-      padding: 16px;
-      margin: 0 16px 16px 16px;
+      padding: 8px;
+      margin: 0 8px 8px 8px;
       position: relative;
       cursor: move;
       background: fade(@lightest, @opacity-medium-light);
+      white-space: nowrap;
+      overflow: hidden;
 
       &:hover {
         background: @gray-bg;
@@ -147,6 +149,7 @@
 
   .slice-adder-container {
     position: relative;
+    background-color: white;
     min-height: 200px; /* for loader positioning */
 
     .error-message {
@@ -185,18 +188,4 @@
       outline: none;
     }
   }
-
-  .color-scheme-container {
-    list-style: none;
-    margin: 0;
-    padding: 0;
-    display: flex;
-    align-items: center;
-  }
-
-  .color-scheme-container li {
-    flex-basis: 9px;
-    height: 10px;
-    margin: 9px 1px;
-  }
 }
diff --git a/superset-frontend/src/dashboard/stylesheets/dashboard.less b/superset-frontend/src/dashboard/stylesheets/dashboard.less
index b28b8ef..97f2f26 100644
--- a/superset-frontend/src/dashboard/stylesheets/dashboard.less
+++ b/superset-frontend/src/dashboard/stylesheets/dashboard.less
@@ -115,19 +115,6 @@ body {
     display: flex;
     flex-direction: row;
     flex-wrap: nowrap;
-
-    & > :nth-child(3) {
-      border-radius: @border-radius-normal 0px 0px @border-radius-normal;
-      border-right: none;
-    }
-
-    & > :nth-child(4) {
-      border-radius: 0px @border-radius-normal @border-radius-normal 0px;
-    }
-
-    & > :not(:nth-child(3)):not(:last-child) {
-      margin-right: 8px;
-    }
   }
 }
 
diff --git a/superset-frontend/src/dashboard/util/constants.ts b/superset-frontend/src/dashboard/util/constants.ts
index 5eae2a8..dbfd61b 100644
--- a/superset-frontend/src/dashboard/util/constants.ts
+++ b/superset-frontend/src/dashboard/util/constants.ts
@@ -63,13 +63,6 @@ export const SAVE_TYPE_NEWDASHBOARD = 'newDashboard';
 // could be overwritten by server-side config
 export const DASHBOARD_POSITION_DATA_LIMIT = 65535;
 
-// Dashboard pane types
-export const BUILDER_PANE_TYPE = {
-  NONE: 'NONE',
-  ADD_COMPONENTS: 'ADD_COMPONENTS',
-  COLORS: 'COLORS',
-};
-
 // filter indicators display length
 export const FILTER_INDICATORS_DISPLAY_LENGTH = 3;
 
diff --git a/superset-frontend/src/dashboard/util/propShapes.jsx b/superset-frontend/src/dashboard/util/propShapes.jsx
index e1eb01c..3052ee5 100644
--- a/superset-frontend/src/dashboard/util/propShapes.jsx
+++ b/superset-frontend/src/dashboard/util/propShapes.jsx
@@ -102,7 +102,6 @@ export const dashboardStatePropShape = PropTypes.shape({
   expandedSlices: PropTypes.object,
   editMode: PropTypes.bool,
   isPublished: PropTypes.bool.isRequired,
-  builderPaneType: PropTypes.string.isRequired,
   colorNamespace: PropTypes.string,
   colorScheme: PropTypes.string,
   updatedColorScheme: PropTypes.bool,
diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx
index a9e285a..428cfe7 100644
--- a/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx
+++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl.jsx
@@ -22,6 +22,7 @@ import { isFunction } from 'lodash';
 import { CreatableSelect } from 'src/components/Select';
 import ControlHeader from '../ControlHeader';
 import TooltipWrapper from '../../../components/TooltipWrapper';
+import './ColorSchemeControl.less';
 
 const propTypes = {
   description: PropTypes.string,
@@ -29,6 +30,7 @@ const propTypes = {
   name: PropTypes.string.isRequired,
   onChange: PropTypes.func,
   value: PropTypes.string,
+  clearable: PropTypes.bool,
   default: PropTypes.string,
   choices: PropTypes.oneOfType([
     PropTypes.arrayOf(PropTypes.array),
@@ -41,6 +43,7 @@ const propTypes = {
 const defaultProps = {
   choices: [],
   schemes: {},
+  clearable: false,
   onChange: () => {},
 };
 
@@ -111,7 +114,7 @@ export default class ColorSchemeControl extends React.PureComponent {
       options,
       value: this.props.value,
       autosize: false,
-      clearable: false,
+      clearable: this.props.clearable,
       onChange: this.onChange,
       optionRenderer: this.renderOption,
       valueRenderer: this.renderOption,
diff --git a/superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js b/superset-frontend/src/explore/components/controls/ColorSchemeControl.less
similarity index 68%
copy from superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js
copy to superset-frontend/src/explore/components/controls/ColorSchemeControl.less
index 1727422..94213b7 100644
--- a/superset-frontend/spec/javascripts/dashboard/fixtures/mockDashboardState.js
+++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl.less
@@ -16,18 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
-import { sliceId } from './mockChartQueries';
+.color-scheme-container {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+  display: flex;
+  align-items: center;
 
-export default {
-  sliceIds: [sliceId],
-  expandedSlices: {},
-  editMode: false,
-  builderPaneType: BUILDER_PANE_TYPE.NONE,
-  hasUnsavedChanges: false,
-  maxUndoHistoryExceeded: false,
-  isStarred: true,
-  isPublished: true,
-  css: '',
-  focusedFilterField: [],
-};
+  li {
+    flex-basis: 9px;
+    height: 10px;
+    margin: 9px 1px;
+  }
+}
diff --git a/superset-frontend/src/explore/main.less b/superset-frontend/src/explore/main.less
index 856258e..4ca4a76 100644
--- a/superset-frontend/src/explore/main.less
+++ b/superset-frontend/src/explore/main.less
@@ -61,20 +61,6 @@
   background-color: transparent !important;
 }
 
-.color-scheme-container {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-  display: flex;
-  align-items: center;
-
-  li {
-    flex-basis: 9px;
-    height: 10px;
-    margin: 9px 1px;
-  }
-}
-
 .control-panel-section {
   .panel-body {
     margin-left: 15px;
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
index 273b01b..f91fb05 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
@@ -495,9 +495,8 @@ class DashboardList extends React.PureComponent<Props, State> {
                 {dashboardToEdit && (
                   <PropertiesModal
                     dashboardId={dashboardToEdit.id}
-                    onDashboardSave={this.handleDashboardEdit}
                     onHide={() => this.setState({ dashboardToEdit: null })}
-                    show
+                    onSubmit={this.handleDashboardEdit}
                   />
                 )}
                 <ListView
diff --git a/superset-frontend/stylesheets/less/cosmo/bootswatch.less b/superset-frontend/stylesheets/less/cosmo/bootswatch.less
index f65e95a..6daabb4 100644
--- a/superset-frontend/stylesheets/less/cosmo/bootswatch.less
+++ b/superset-frontend/stylesheets/less/cosmo/bootswatch.less
@@ -234,6 +234,7 @@ table,
   & > li > a:hover,
   & > li > a:focus {
     background-image: none;
+    text-decoration: none;
   }
 }
 
diff --git a/superset/dashboards/dao.py b/superset/dashboards/dao.py
index 5859640..6345bb7 100644
--- a/superset/dashboards/dao.py
+++ b/superset/dashboards/dao.py
@@ -152,10 +152,9 @@ class DashboardDAO(BaseDAO):
             key: v for key, v in default_filters_data.items() if int(key) in slice_ids
         }
         md["default_filters"] = json.dumps(applicable_filters)
+        md["color_scheme"] = data.get("color_scheme")
         if data.get("color_namespace"):
             md["color_namespace"] = data.get("color_namespace")
-        if data.get("color_scheme"):
-            md["color_scheme"] = data.get("color_scheme")
         if data.get("label_colors"):
             md["label_colors"] = data.get("label_colors")
         dashboard.json_metadata = json.dumps(md)
diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py
index ca9c25c..848a80c 100644
--- a/superset/dashboards/schemas.py
+++ b/superset/dashboards/schemas.py
@@ -104,7 +104,7 @@ class DashboardJSONMetadataSchema(Schema):
     default_filters = fields.Str()
     stagger_refresh = fields.Boolean()
     stagger_time = fields.Integer()
-    color_scheme = fields.Str()
+    color_scheme = fields.Str(allow_none=True)
     label_colors = fields.Dict()
 
 
diff --git a/superset/views/chart/views.py b/superset/views/chart/views.py
index 896948e..db100a7 100644
--- a/superset/views/chart/views.py
+++ b/superset/views/chart/views.py
@@ -84,6 +84,7 @@ class SliceAsync(SliceModelView):  # pylint: disable=too-many-ancestors
         "creator",
         "datasource_id",
         "datasource_link",
+        "datasource_url",
         "datasource_name_text",
         "datasource_type",
         "description",