You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2020/04/17 18:41:17 UTC

[incubator-superset] branch master updated: Migrating shared NVD3 controls to new module (#9525)

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

rusackas 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 ea27e68  Migrating shared NVD3 controls to new module (#9525)
ea27e68 is described below

commit ea27e68ee1c746b0daa831765bbadb81a6b2ed0d
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Fri Apr 17 11:40:50 2020 -0700

    Migrating shared NVD3 controls to new module (#9525)
    
    * proto module
    
    * caught a missed 'freq' unique control
    
    * line_interpolation
    
    * linting
    
    * showLegend
    
    * show_controls
    
    * xAxisLabel
    
    * bottomMargin
    
    * x_ticks_layout
    
    * missed one
    
    * x_axis_format
    
    * yLogScale
    
    * y_axis_bounds
    
    * linting
    
    * nixing yarn lock
    
    * x_axis_showminmax
    
    * xAxisShowminmax control
    
    * richTooltip
    
    * linting, syntax fix
    
    * show_bar_value, bar_stacked
    
    * reduceXticks, yaxislabel
    
    * left_margin, max_bubble_size, y_axis_showminmax
    
    * show_labels
    
    * send_time_range, y_axis_2_format, show_markers, order_bars
    
    * nixing commented imports
    
    * fake controls
    
    * looking up actual controls for comparison.
    
    * adding key to test setup
    
    * controls inventory
    
    * apache junk
    
    * lint :sparkles:
    
    * ignore null controls
    
    * fixing goofed up spread operation for xAxisFormat config
    
    * lint :sparkles:
    
    * fixes for errors caused by <hr> element in filterbox controls
    
    * fixing filter controls for 'druid_time_origin', 'granularity', 'granularity_sqla', 'time_grain_sqla'
    
    * getControlsInventory -> getControlsForVizType
    
    * further renaming of chartControlsInventory - > getControlsForVizType
    
    Co-authored-by: David Aaron Suddjian <aa...@gmail.com>
---
 CONTRIBUTING.md                                    |  30 --
 .../components/AlteredSliceTag_spec.jsx            |  45 ++-
 .../utils/getControlsForVizType_spec.js            | 104 +++++++
 .../src/components/AlteredSliceTag.jsx             |  30 +-
 .../src/explore/components/Control.jsx             |   1 +
 .../src/explore/controlPanels/Area.js              |  34 ++-
 superset-frontend/src/explore/controlPanels/Bar.js |  46 +--
 .../src/explore/controlPanels/BoxPlot.js           |  18 +-
 .../src/explore/controlPanels/Bubble.js            |  57 +++-
 .../src/explore/controlPanels/CalHeatmap.js        |  14 +-
 .../src/explore/controlPanels/Compare.js           |  30 +-
 .../src/explore/controlPanels/DistBar.js           |  35 ++-
 .../src/explore/controlPanels/DualLine.js          |   9 +-
 .../src/explore/controlPanels/Heatmap.js           |  90 +++++-
 .../src/explore/controlPanels/Histogram.js         |  21 +-
 .../src/explore/controlPanels/Line.js              |  52 +++-
 .../src/explore/controlPanels/LineMulti.js         |  23 +-
 .../src/explore/controlPanels/Mapbox.js            |   7 -
 .../src/explore/controlPanels/Partition.jsx        |  15 +-
 superset-frontend/src/explore/controlPanels/Pie.js |  17 +-
 .../src/explore/controlPanels/Rose.js              |  13 +-
 .../src/explore/controlPanels/Shared_NVD3.js       | 309 +++++++++++++++++++++
 .../src/explore/controlPanels/TimePivot.js         |  76 ++++-
 .../src/explore/controlPanels/WorldMap.js          |  22 +-
 superset-frontend/src/explore/controls.jsx         | 265 ------------------
 .../src/utils/getControlsForVizType.js             |  46 +++
 .../src/visualizations/FilterBox/FilterBox.jsx     |   2 +-
 27 files changed, 991 insertions(+), 420 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5513d03..d11d72d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1007,7 +1007,6 @@ Note not all fields are correctly catagorized. The fields vary based on visualiz
 | Field             | Type     | Notes                                               |
 | ----------------- | -------- | --------------------------------------------------- |
 | `metric_2`        | -        | The **Right Axis Metric** widget. See Query section |
-| `y_axis_2_format` | _string_ | The **Right Axis Format** widget                    |
 
 ### Query
 
@@ -1019,7 +1018,6 @@ Note not all fields are correctly catagorized. The fields vary based on visualiz
 | `contribution`                                                                                         | _boolean_                                         | The **Contribution** widget                       |
 | `groupby`                                                                                              | _array(string)_                                   | The **Group by** or **Series** widget             |
 | `limit`                                                                                                | _number_                                          | The **Series Limit** widget                       |
-| `max_bubble_size`                                                                                      | _number_                                          | The **Max Bubble Size** widget                    |
 | `metric`<br>`metric_2`<br>`metrics`<br>`percent_mertics`<br>`secondary_metric`<br>`size`<br>`x`<br>`y` | _string_,_object_,_array(string)_,_array(object)_ | The metric(s) depending on the visualization type |
 | `order_asc`                                                                                            | _boolean_                                         | The **Sort Descending** widget                    |
 | `row_limit`                                                                                            | _number_                                          | The **Row limit** widget                          |
@@ -1042,38 +1040,17 @@ The filter-box configuration references column names (via the `column` key) and
 | `color_picker`        | _object_  | The **Fixed Color** widget                       |
 | `global_opacity`      | _number_  | The **Opacity** widget                           |
 | `label_colors`        | _object_  | The **Color Scheme** widget                      |
-| `line_interpolation`  | _string_  | The **Line Style** widget                        |
 | `link_length`         | _number_  | The **No of Bins** widget                        |
 | `normalized`          | _boolean_ | The **Normalized** widget                        |
 | `number_format`       | _string_  | The **Number format** widget                     |
-| `rich_tooltip`        | _boolean_ | The **Rich Tooltip** widget                      |
-| `send_time_range`     | _boolean_ | The **Show Markers** widget                      |
-| `show_brush`          | _string_  | The **Show Range Filter** widget                 |
-| `show_legend`         | _boolean_ | The **Legend** widget                            |
-| `show_markers`        | _string_  | The **Show Markers** widget                      |
-
-### X Axis
-
-| Field                | Type      | Notes                        |
-| -------------------- | --------- | ---------------------------- |
-| `bottom_margin`      | _string_  | The **Bottom Margin** widget |
-| `x_axis_format`      | _string_  | The **X Axis Format** widget |
-| `x_axis_label`       | _string_  | The **X Axis Label** widget  |
-| `x_axis_showminmax`  | _boolean_ | The **X bounds** widget      |
-| `x_ticks_layout`     | _string_  | The **X Tick Layout** widget |
 
 ### Y Axis
 
 | Field               | Type            | Notes                        |
 | ------------------- | --------------- | ---------------------------- |
-| `left_margin`       | _number_        | The **Left Margin** widget   |
 | `y_axis_2_label`    | _N/A_           | _Deprecated?_                |
-| `y_axis_bounds`     | _array(string)_ | The **Y Axis Bounds** widget |
 | `y_axis_format`     | _string_        | The **Y Axis Format** widget |
-| `y_axis_label`      | _string_        | The **Y Axis Label** widget  |
-| `y_axis_showminmax` | _boolean_       | The **Y bounds** widget      |
 | `y_axis_zero`       | _N/A_           | _Deprecated?_                |
-| `y_log_scale`       | _boolean_       | The **Y Log Scale** widget   |
 
 Note the `y_axis_format` is defined under various section for some charts.
 
@@ -1092,7 +1069,6 @@ Note the `y_axis_format` is defined under various section for some charts.
 | `add_to_dash`                   | _N/A_ |       |
 | `all_columns_y`                 | _N/A_ |       |
 | `annotation_layers`             | _N/A_ |       |
-| `bar_stacked`                   | _N/A_ |       |
 | `cache_timeout`                 | _N/A_ |       |
 | `code`                          | _N/A_ |       |
 | `collapsed_fieldsets`           | _N/A_ |       |
@@ -1125,13 +1101,11 @@ Note the `y_axis_format` is defined under various section for some charts.
 | `new_slice_name`                | _N/A_ |       |
 | `normalize_across`              | _N/A_ |       |
 | `num_period_compare`            | _N/A_ |       |
-| `order_bars`                    | _N/A_ |       |
 | `order_desc`                    | _N/A_ |       |
 | `pandas_aggfunc`                | _N/A_ |       |
 | `period_ratio_type`             | _N/A_ |       |
 | `perm`                          | _N/A_ |       |
 | `rdo_save`                      | _N/A_ |       |
-| `reduce_x_ticks`                | _N/A_ |       |
 | `refresh_frequency`             | _N/A_ |       |
 | `remote_id`                     | _N/A_ |       |
 | `resample_fillmethod`           | _N/A_ |       |
@@ -1143,11 +1117,7 @@ Note the `y_axis_format` is defined under various section for some charts.
 | `schema`                        | _N/A_ |       |
 | `select_country`                | _N/A_ |       |
 | `series`                        | _N/A_ |       |
-| `show_bar_value`                | _N/A_ |       |
-| `show_brush`                    | _N/A_ |       |
 | `show_bubbles`                  | _N/A_ |       |
-| `show_controls`                 | _N/A_ |       |
-| `show_labels`                   | _N/A_ |       |
 | `show_values`                   | _N/A_ |       |
 | `slice_name`                    | _N/A_ |       |
 | `table_filter`                  | _N/A_ |       |
diff --git a/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx b/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx
index f3ea8a2..6adf397 100644
--- a/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx
+++ b/superset-frontend/spec/javascripts/components/AlteredSliceTag_spec.jsx
@@ -18,8 +18,8 @@
  */
 import React from 'react';
 import { shallow } from 'enzyme';
-
 import { Table, Thead, Td, Th, Tr } from 'reactable-arc';
+import { getChartControlPanelRegistry } from '@superset-ui/chart';
 
 import AlteredSliceTag from '../../../src/components/AlteredSliceTag';
 import ModalTrigger from '../../../src/components/ModalTrigger';
@@ -27,6 +27,7 @@ import TooltipWrapper from '../../../src/components/TooltipWrapper';
 
 const defaultProps = {
   origFormData: {
+    viz_type: 'altered_slice_tag_spec',
     adhoc_filters: [
       {
         clause: 'WHERE',
@@ -111,11 +112,52 @@ const expectedDiffs = {
   },
 };
 
+const fakePluginControls = {
+  controlPanelSections: [
+    {
+      label: 'Fake Control Panel Sections',
+      expanded: true,
+      controlSetRows: [
+        [
+          {
+            name: 'y_axis_bounds',
+            config: {
+              type: 'BoundsControl',
+              label: 'Value bounds',
+              default: [null, null],
+              description: 'Value bounds for the y axis',
+            },
+          },
+          {
+            name: 'column_collection',
+            config: {
+              type: 'CollectionControl',
+              label: 'Fake Collection Control',
+            },
+          },
+          {
+            name: 'adhoc_filters',
+            config: {
+              type: 'AdhocFilterControl',
+              label: 'Fake Filters',
+              default: null,
+            },
+          },
+        ],
+      ],
+    },
+  ],
+};
+
 describe('AlteredSliceTag', () => {
   let wrapper;
   let props;
 
   beforeEach(() => {
+    getChartControlPanelRegistry().registerValue(
+      'altered_slice_tag_spec',
+      fakePluginControls,
+    );
     props = { ...defaultProps };
     wrapper = shallow(<AlteredSliceTag {...props} />);
   });
@@ -237,6 +279,7 @@ describe('AlteredSliceTag', () => {
     });
 
     it('returns "Max" and "Min" for BoundsControl', () => {
+      // need to pass the viz type to the wrapper
       expect(wrapper.instance().formatValue([5, 6], 'y_axis_bounds')).toBe(
         'Min: 5, Max: 6',
       );
diff --git a/superset-frontend/spec/javascripts/utils/getControlsForVizType_spec.js b/superset-frontend/spec/javascripts/utils/getControlsForVizType_spec.js
new file mode 100644
index 0000000..167863a
--- /dev/null
+++ b/superset-frontend/spec/javascripts/utils/getControlsForVizType_spec.js
@@ -0,0 +1,104 @@
+/**
+ * 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.
+ */
+
+import { getChartControlPanelRegistry } from '@superset-ui/chart';
+import getControlsForVizType from 'src/utils/getControlsForVizType';
+
+const fakePluginControls = {
+  controlPanelSections: [
+    {
+      label: 'Fake Control Panel Sections',
+      expanded: true,
+      controlSetRows: [
+        ['url_params'],
+        [
+          {
+            name: 'y_axis_bounds',
+            config: {
+              type: 'BoundsControl',
+              label: 'Value bounds',
+              default: [null, null],
+              description: 'Value bounds for the y axis',
+            },
+          },
+        ],
+        [
+          {
+            name: 'adhoc_filters',
+            config: {
+              type: 'AdhocFilterControl',
+              label: 'Fake Filters',
+              default: null,
+            },
+          },
+        ],
+      ],
+    },
+    {
+      label: 'Fake Control Panel Sections 2',
+      expanded: true,
+      controlSetRows: [
+        [
+          {
+            name: 'column_collection',
+            config: {
+              type: 'CollectionControl',
+              label: 'Fake Collection Control',
+            },
+          },
+        ],
+      ],
+    },
+  ],
+};
+
+describe('getControlsForVizType', () => {
+  beforeEach(() => {
+    getChartControlPanelRegistry().registerValue(
+      'chart_controls_inventory_fake',
+      fakePluginControls,
+    );
+  });
+
+  it('returns a map of the controls', () => {
+    expect(getControlsForVizType('chart_controls_inventory_fake')).toEqual({
+      url_params: {
+        type: 'HiddenControl',
+        label: 'URL Parameters',
+        hidden: true,
+        description: 'Extra parameters for use in jinja templated queries',
+      },
+      y_axis_bounds: {
+        type: 'BoundsControl',
+        label: 'Value bounds',
+        default: [null, null],
+        description: 'Value bounds for the y axis',
+      },
+      adhoc_filters: {
+        type: 'AdhocFilterControl',
+        label: 'Fake Filters',
+        default: null,
+      },
+      column_collection: {
+        type: 'CollectionControl',
+        label: 'Fake Collection Control',
+      },
+    });
+  });
+});
diff --git a/superset-frontend/src/components/AlteredSliceTag.jsx b/superset-frontend/src/components/AlteredSliceTag.jsx
index 5f27404..dbba032 100644
--- a/superset-frontend/src/components/AlteredSliceTag.jsx
+++ b/superset-frontend/src/components/AlteredSliceTag.jsx
@@ -20,9 +20,10 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { Table, Tr, Td, Thead, Th } from 'reactable-arc';
 import { isEqual, isEmpty } from 'lodash';
+import { getChartControlPanelRegistry } from '@superset-ui/chart';
+import getControlsForVizType from 'src/utils/getControlsForVizType';
 import { t } from '@superset-ui/translation';
 import TooltipWrapper from './TooltipWrapper';
-import { controls } from '../explore/controls';
 import ModalTrigger from './ModalTrigger';
 import { safeStringify } from '../utils/safeStringify';
 
@@ -52,7 +53,10 @@ export default class AlteredSliceTag extends React.Component {
   constructor(props) {
     super(props);
     const diffs = this.getDiffs(props);
-    this.state = { diffs, hasDiffs: !isEmpty(diffs) };
+
+    const controlsMap = getControlsForVizType(this.props.origFormData.viz_type);
+
+    this.state = { diffs, hasDiffs: !isEmpty(diffs), controlsMap };
   }
 
   UNSAFE_componentWillReceiveProps(newProps) {
@@ -69,6 +73,7 @@ export default class AlteredSliceTag extends React.Component {
     // current form data and the saved form data
     const ofd = props.origFormData;
     const cfd = props.currentFormData;
+
     const fdKeys = Object.keys(cfd);
     const diffs = {};
     for (const fdKey of fdKeys) {
@@ -98,7 +103,10 @@ export default class AlteredSliceTag extends React.Component {
       return 'N/A';
     } else if (value === null) {
       return 'null';
-    } else if (controls[key] && controls[key].type === 'AdhocFilterControl') {
+    } else if (
+      this.state.controlsMap[key] &&
+      this.state.controlsMap[key].type === 'AdhocFilterControl'
+    ) {
       if (!value.length) {
         return '[]';
       }
@@ -111,9 +119,15 @@ export default class AlteredSliceTag extends React.Component {
           return `${v.subject} ${v.operator} ${filterVal}`;
         })
         .join(', ');
-    } else if (controls[key] && controls[key].type === 'BoundsControl') {
+    } else if (
+      this.state.controlsMap[key] &&
+      this.state.controlsMap[key].type === 'BoundsControl'
+    ) {
       return `Min: ${value[0]}, Max: ${value[1]}`;
-    } else if (controls[key] && controls[key].type === 'CollectionControl') {
+    } else if (
+      this.state.controlsMap[key] &&
+      this.state.controlsMap[key].type === 'CollectionControl'
+    ) {
       return value.map(v => safeStringify(v)).join(', ');
     } else if (typeof value === 'boolean') {
       return value ? 'true' : 'false';
@@ -133,7 +147,11 @@ export default class AlteredSliceTag extends React.Component {
         <Tr key={key}>
           <Td
             column="control"
-            data={(controls[key] && controls[key].label) || key}
+            data={
+              (this.state.controlsMap[key] &&
+                this.state.controlsMap[key].label) ||
+              key
+            }
           />
           <Td column="before">{this.formatValue(diffs[key].before, key)}</Td>
           <Td column="after">{this.formatValue(diffs[key].after, key)}</Td>
diff --git a/superset-frontend/src/explore/components/Control.jsx b/superset-frontend/src/explore/components/Control.jsx
index fbb74a1..d9b44a0 100644
--- a/superset-frontend/src/explore/components/Control.jsx
+++ b/superset-frontend/src/explore/components/Control.jsx
@@ -70,6 +70,7 @@ export default class Control extends React.PureComponent {
     this.setState({ hovered });
   }
   render() {
+    if (!this.props.type) return null; // this catches things like <hr/> elements (not a control!) shoved into the control panel configs.
     const ControlType = controlMap[this.props.type];
     const divStyle = this.props.hidden ? { display: 'none' } : null;
     return (
diff --git a/superset-frontend/src/explore/controlPanels/Area.js b/superset-frontend/src/explore/controlPanels/Area.js
index 2eda633..e471b16 100644
--- a/superset-frontend/src/explore/controlPanels/Area.js
+++ b/superset-frontend/src/explore/controlPanels/Area.js
@@ -19,6 +19,20 @@
 import { t } from '@superset-ui/translation';
 import { NVD3TimeSeries, annotations } from './sections';
 import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import {
+  lineInterpolation,
+  showBrush,
+  showLegend,
+  showControls,
+  xAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  xAxisFormat,
+  yLogScale,
+  yAxisBounds,
+  xAxisShowMinmax,
+  richTooltip,
+} from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -28,9 +42,9 @@ export default {
       label: t('Chart Options'),
       expanded: true,
       controlSetRows: [
-        ['show_brush', 'show_legend'],
+        [showBrush, showLegend],
         [
-          'line_interpolation',
+          lineInterpolation,
           {
             name: 'stacked_style',
             config: {
@@ -48,34 +62,30 @@ export default {
           },
         ],
         ['color_scheme', 'label_colors'],
-        ['rich_tooltip', 'show_controls'],
+        [richTooltip, showControls],
       ],
     },
     {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_ticks_layout', 'x_axis_format'],
-        ['x_axis_showminmax', null],
+        [xAxisLabel, bottomMargin],
+        [xTicksLayout, xAxisFormat],
+        [xAxisShowMinmax, null],
       ],
     },
     {
       label: t('Y Axis'),
       expanded: true,
       controlSetRows: [
-        ['y_axis_format', 'y_axis_bounds'],
-        ['y_log_scale', null],
+        ['y_axis_format', yAxisBounds],
+        [yLogScale, null],
       ],
     },
     NVD3TimeSeries[1],
     annotations,
   ],
   controlOverrides: {
-    x_axis_format: {
-      default: 'smart_date',
-      choices: D3_TIME_FORMAT_OPTIONS,
-    },
     color_scheme: {
       renderTrigger: false,
     },
diff --git a/superset-frontend/src/explore/controlPanels/Bar.js b/superset-frontend/src/explore/controlPanels/Bar.js
index c7ba0c9..6df2569 100644
--- a/superset-frontend/src/explore/controlPanels/Bar.js
+++ b/superset-frontend/src/explore/controlPanels/Bar.js
@@ -19,6 +19,26 @@
 import { t } from '@superset-ui/translation';
 import { NVD3TimeSeries, annotations } from './sections';
 import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import {
+  lineInterpolation,
+  showBrush,
+  showLegend,
+  showControls,
+  xAxisLabel,
+  yAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  xAxisFormat,
+  yLogScale,
+  yAxisBounds,
+  xAxisShowMinmax,
+  yAxisShowMinmax,
+  richTooltip,
+  showBarValue,
+  barStacked,
+  reduceXTicks,
+  leftMargin,
+} from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -29,39 +49,33 @@ export default {
       expanded: true,
       controlSetRows: [
         ['color_scheme', 'label_colors'],
-        ['show_brush', 'show_legend', 'show_bar_value'],
-        ['rich_tooltip', 'bar_stacked'],
-        ['line_interpolation', 'show_controls'],
-        ['bottom_margin'],
+        [showBrush, showLegend, showBarValue],
+        [richTooltip, barStacked],
+        [lineInterpolation, showControls],
+        [bottomMargin],
       ],
     },
     {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_ticks_layout', 'x_axis_format'],
-        ['x_axis_showminmax', 'reduce_x_ticks'],
+        [xAxisLabel, bottomMargin],
+        [xTicksLayout, xAxisFormat],
+        [xAxisShowMinmax, reduceXTicks],
       ],
     },
     {
       label: t('Y Axis'),
       expanded: true,
       controlSetRows: [
-        ['y_axis_label', 'left_margin'],
-        ['y_axis_showminmax', 'y_log_scale'],
-        ['y_axis_format', 'y_axis_bounds'],
+        [yAxisLabel, leftMargin],
+        [yAxisShowMinmax, yLogScale],
+        ['y_axis_format', yAxisBounds],
       ],
     },
     NVD3TimeSeries[1],
     annotations,
   ],
-  controlOverrides: {
-    x_axis_format: {
-      choices: D3_TIME_FORMAT_OPTIONS,
-      default: 'smart_date',
-    },
-  },
   sectionOverrides: {
     druidTimeSeries: {
       controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
diff --git a/superset-frontend/src/explore/controlPanels/BoxPlot.js b/superset-frontend/src/explore/controlPanels/BoxPlot.js
index 6d76e46..ecf58e2 100644
--- a/superset-frontend/src/explore/controlPanels/BoxPlot.js
+++ b/superset-frontend/src/explore/controlPanels/BoxPlot.js
@@ -50,7 +50,23 @@ export default {
               ]),
             },
           },
-          'x_ticks_layout',
+          {
+            name: 'x_ticks_layout',
+            config: {
+              type: 'SelectControl',
+              label: t('X Tick Layout'),
+              choices: formatSelectOptions([
+                'auto',
+                'flat',
+                '45°',
+                'staggered',
+              ]),
+              default: 'auto',
+              clearable: false,
+              renderTrigger: true,
+              description: t('The way the ticks are laid out on the X-axis'),
+            },
+          },
         ],
       ],
     },
diff --git a/superset-frontend/src/explore/controlPanels/Bubble.js b/superset-frontend/src/explore/controlPanels/Bubble.js
index 3406061..169cb20 100644
--- a/superset-frontend/src/explore/controlPanels/Bubble.js
+++ b/superset-frontend/src/explore/controlPanels/Bubble.js
@@ -17,6 +17,20 @@
  * under the License.
  */
 import { t } from '@superset-ui/translation';
+import { D3_FORMAT_OPTIONS } from '../controls';
+import { formatSelectOptions } from '../../modules/utils';
+import {
+  showLegend,
+  xAxisLabel,
+  yAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  xAxisFormat,
+  yLogScale,
+  xAxisShowMinmax,
+  yAxisShowMinmax,
+  leftMargin,
+} from './Shared_NVD3';
 
 export default {
   label: t('Bubble Chart'),
@@ -30,7 +44,26 @@ export default {
         ['y'],
         ['adhoc_filters'],
         ['size'],
-        ['max_bubble_size'],
+        [
+          {
+            name: 'max_bubble_size',
+            config: {
+              type: 'SelectControl',
+              freeForm: true,
+              label: t('Max Bubble Size'),
+              default: '25',
+              choices: formatSelectOptions([
+                '5',
+                '10',
+                '15',
+                '25',
+                '50',
+                '75',
+                '100',
+              ]),
+            },
+          },
+        ],
         ['limit', null],
       ],
     },
@@ -39,15 +72,25 @@ export default {
       expanded: true,
       controlSetRows: [
         ['color_scheme', 'label_colors'],
-        ['show_legend', null],
+        [showLegend, null],
       ],
     },
     {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'left_margin'],
-        ['x_axis_format', 'x_ticks_layout'],
+        [xAxisLabel, leftMargin],
+        [
+          {
+            name: xAxisFormat.name,
+            config: {
+              ...xAxisFormat.config,
+              default: 'SMART_NUMBER',
+              choices: D3_FORMAT_OPTIONS,
+            },
+          },
+          xTicksLayout,
+        ],
         [
           {
             name: 'x_log_scale',
@@ -59,7 +102,7 @@ export default {
               description: t('Use a log scale for the X-axis'),
             },
           },
-          'x_axis_showminmax',
+          xAxisShowMinmax,
         ],
       ],
     },
@@ -67,9 +110,9 @@ export default {
       label: t('Y Axis'),
       expanded: true,
       controlSetRows: [
-        ['y_axis_label', 'bottom_margin'],
+        [yAxisLabel, bottomMargin],
         ['y_axis_format', null],
-        ['y_log_scale', 'y_axis_showminmax'],
+        [yLogScale, yAxisShowMinmax],
       ],
     },
   ],
diff --git a/superset-frontend/src/explore/controlPanels/CalHeatmap.js b/superset-frontend/src/explore/controlPanels/CalHeatmap.js
index a7ded55..c915065 100644
--- a/superset-frontend/src/explore/controlPanels/CalHeatmap.js
+++ b/superset-frontend/src/explore/controlPanels/CalHeatmap.js
@@ -145,7 +145,19 @@ export default {
             },
           },
         ],
-        ['show_legend', 'show_values'],
+        [
+          {
+            name: 'show_legend',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Legend'),
+              renderTrigger: true,
+              default: true,
+              description: t('Whether to display the legend (toggles)'),
+            },
+          },
+          'show_values',
+        ],
         [
           {
             name: 'show_metric_name',
diff --git a/superset-frontend/src/explore/controlPanels/Compare.js b/superset-frontend/src/explore/controlPanels/Compare.js
index 99ed300..82f4308 100644
--- a/superset-frontend/src/explore/controlPanels/Compare.js
+++ b/superset-frontend/src/explore/controlPanels/Compare.js
@@ -19,6 +19,18 @@
 import { t } from '@superset-ui/translation';
 import { NVD3TimeSeries, annotations } from './sections';
 import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import {
+  xAxisLabel,
+  yAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  xAxisFormat,
+  yLogScale,
+  yAxisBounds,
+  xAxisShowMinmax,
+  yAxisShowMinmax,
+  leftMargin,
+} from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -33,29 +45,23 @@ export default {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_ticks_layout', 'x_axis_format'],
-        ['x_axis_showminmax', null],
+        [xAxisLabel, bottomMargin],
+        [xTicksLayout, xAxisFormat],
+        [xAxisShowMinmax, null],
       ],
     },
     {
       label: t('Y Axis'),
       expanded: true,
       controlSetRows: [
-        ['y_axis_label', 'left_margin'],
-        ['y_axis_showminmax', 'y_log_scale'],
-        ['y_axis_format', 'y_axis_bounds'],
+        [yAxisLabel, leftMargin],
+        [yAxisShowMinmax, yLogScale],
+        ['y_axis_format', yAxisBounds],
       ],
     },
     NVD3TimeSeries[1],
     annotations,
   ],
-  controlOverrides: {
-    x_axis_format: {
-      choices: D3_TIME_FORMAT_OPTIONS,
-      default: 'smart_date',
-    },
-  },
   sectionOverrides: {
     druidTimeSeries: {
       controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
diff --git a/superset-frontend/src/explore/controlPanels/DistBar.js b/superset-frontend/src/explore/controlPanels/DistBar.js
index 891dcfb..5c1ee4a 100644
--- a/superset-frontend/src/explore/controlPanels/DistBar.js
+++ b/superset-frontend/src/explore/controlPanels/DistBar.js
@@ -18,6 +18,17 @@
  */
 import { t } from '@superset-ui/translation';
 import { validateNonEmpty } from '@superset-ui/validator';
+import {
+  showLegend,
+  showControls,
+  xAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  showBarValue,
+  barStacked,
+  reduceXTicks,
+  yAxisLabel,
+} from './Shared_NVD3';
 
 export default {
   controlPanelSections: [
@@ -38,18 +49,30 @@ export default {
       expanded: true,
       controlSetRows: [
         ['color_scheme', 'label_colors'],
-        ['show_legend', 'show_bar_value'],
-        ['bar_stacked', 'order_bars'],
-        ['y_axis_format', 'y_axis_label'],
-        ['show_controls', null],
+        [showLegend, showBarValue],
+        [
+          barStacked,
+          {
+            name: 'order_bars',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Sort Bars'),
+              default: false,
+              renderTrigger: true,
+              description: t('Sort bars by x labels.'),
+            },
+          },
+        ],
+        ['y_axis_format', yAxisLabel],
+        [showControls, null],
       ],
     },
     {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_ticks_layout', 'reduce_x_ticks'],
+        [xAxisLabel, bottomMargin],
+        [xTicksLayout, reduceXTicks],
       ],
     },
   ],
diff --git a/superset-frontend/src/explore/controlPanels/DualLine.js b/superset-frontend/src/explore/controlPanels/DualLine.js
index f4b854f..b385970 100644
--- a/superset-frontend/src/explore/controlPanels/DualLine.js
+++ b/superset-frontend/src/explore/controlPanels/DualLine.js
@@ -19,6 +19,7 @@
 import { t } from '@superset-ui/translation';
 import { annotations } from './sections';
 import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import { xAxisFormat, yAxis2Format } from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -26,7 +27,7 @@ export default {
     {
       label: t('Chart Options'),
       expanded: true,
-      controlSetRows: [['color_scheme', 'label_colors'], ['x_axis_format']],
+      controlSetRows: [['color_scheme', 'label_colors'], [xAxisFormat]],
     },
     {
       label: t('Y Axis 1'),
@@ -36,7 +37,7 @@ export default {
     {
       label: t('Y Axis 2'),
       expanded: true,
-      controlSetRows: [['metric_2', 'y_axis_2_format']],
+      controlSetRows: [['metric_2', yAxis2Format]],
     },
     {
       label: t('Query'),
@@ -53,10 +54,6 @@ export default {
     y_axis_format: {
       label: t('Left Axis Format'),
     },
-    x_axis_format: {
-      choices: D3_TIME_FORMAT_OPTIONS,
-      default: 'smart_date',
-    },
   },
   sectionOverrides: {
     druidTimeSeries: {
diff --git a/superset-frontend/src/explore/controlPanels/Heatmap.js b/superset-frontend/src/explore/controlPanels/Heatmap.js
index d56c96e..fd379a1 100644
--- a/superset-frontend/src/explore/controlPanels/Heatmap.js
+++ b/superset-frontend/src/explore/controlPanels/Heatmap.js
@@ -18,7 +18,10 @@
  */
 import { t } from '@superset-ui/translation';
 import { validateNonEmpty } from '@superset-ui/validator';
-import { formatSelectOptionsForRange } from '../../modules/utils';
+import {
+  formatSelectOptionsForRange,
+  formatSelectOptions,
+} from '../../modules/utils';
 
 const sortAxisChoices = [
   ['alpha_asc', t('Axis ascending')],
@@ -94,10 +97,81 @@ export default {
           },
           'normalize_across',
         ],
-        ['left_margin', 'bottom_margin'],
-        ['y_axis_bounds', 'y_axis_format'],
         [
-          'show_legend',
+          {
+            name: 'left_margin',
+            config: {
+              type: 'SelectControl',
+              freeForm: true,
+              clearable: false,
+              label: t('Left Margin'),
+              choices: formatSelectOptions([
+                'auto',
+                50,
+                75,
+                100,
+                125,
+                150,
+                200,
+              ]),
+              default: 'auto',
+              renderTrigger: true,
+              description: t(
+                'Left margin, in pixels, allowing for more room for axis labels',
+              ),
+            },
+          },
+          {
+            name: 'bottom_margin',
+            config: {
+              type: 'SelectControl',
+              clearable: false,
+              freeForm: true,
+              label: t('Bottom Margin'),
+              choices: formatSelectOptions([
+                'auto',
+                50,
+                75,
+                100,
+                125,
+                150,
+                200,
+              ]),
+              default: 'auto',
+              renderTrigger: true,
+              description: t(
+                'Bottom margin, in pixels, allowing for more room for axis labels',
+              ),
+            },
+          },
+        ],
+        [
+          {
+            name: 'y_axis_bounds',
+            config: {
+              type: 'BoundsControl',
+              label: t('Value bounds'),
+              renderTrigger: true,
+              default: [null, null],
+              description: t(
+                'Hard value bounds applied for color coding. Is only relevant ' +
+                  'and applied when the normalization is applied against the whole heatmap.',
+              ),
+            },
+          },
+          'y_axis_format',
+        ],
+        [
+          {
+            name: 'show_legend',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Legend'),
+              renderTrigger: true,
+              default: true,
+              description: t('Whether to display the legend (toggles)'),
+            },
+          },
           {
             name: 'show_perc',
             config: {
@@ -147,14 +221,6 @@ export default {
     normalized: t(
       'Whether to apply a normal distribution based on rank on the color scale',
     ),
-    y_axis_bounds: {
-      label: t('Value bounds'),
-      renderTrigger: true,
-      description: t(
-        'Hard value bounds applied for color coding. Is only relevant ' +
-          'and applied when the normalization is applied against the whole heatmap.',
-      ),
-    },
     y_axis_format: {
       label: t('Value Format'),
     },
diff --git a/superset-frontend/src/explore/controlPanels/Histogram.js b/superset-frontend/src/explore/controlPanels/Histogram.js
index 041570e..3f5810e 100644
--- a/superset-frontend/src/explore/controlPanels/Histogram.js
+++ b/superset-frontend/src/explore/controlPanels/Histogram.js
@@ -37,7 +37,26 @@ export default {
       controlSetRows: [
         ['color_scheme', 'label_colors'],
         ['link_length'],
-        ['x_axis_label', 'y_axis_label'],
+        [
+          {
+            name: 'x_axis_label',
+            config: {
+              type: 'TextControl',
+              label: t('X Axis Label'),
+              renderTrigger: true,
+              default: '',
+            },
+          },
+          {
+            name: 'y_axis_label',
+            config: {
+              type: 'TextControl',
+              label: t('Y Axis Label'),
+              renderTrigger: true,
+              default: '',
+            },
+          },
+        ],
         ['global_opacity'],
         ['normalized'],
       ],
diff --git a/superset-frontend/src/explore/controlPanels/Line.js b/superset-frontend/src/explore/controlPanels/Line.js
index 201cf08..4da6e45 100644
--- a/superset-frontend/src/explore/controlPanels/Line.js
+++ b/superset-frontend/src/explore/controlPanels/Line.js
@@ -19,6 +19,23 @@
 import { t } from '@superset-ui/translation';
 import { NVD3TimeSeries, annotations } from './sections';
 import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import {
+  lineInterpolation,
+  showBrush,
+  showLegend,
+  xAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  xAxisFormat,
+  yLogScale,
+  yAxisBounds,
+  yAxisLabel,
+  xAxisShowMinmax,
+  yAxisShowMinmax,
+  richTooltip,
+  leftMargin,
+  showMarkers,
+} from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -29,37 +46,46 @@ export default {
       expanded: true,
       controlSetRows: [
         ['color_scheme', 'label_colors'],
-        ['show_brush', 'send_time_range', 'show_legend'],
-        ['rich_tooltip', 'show_markers'],
-        ['line_interpolation'],
+        [
+          showBrush,
+          {
+            name: 'send_time_range',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Propagate'),
+              renderTrigger: true,
+              default: false,
+              description: t('Send range filter events to other charts'),
+            },
+          },
+          showLegend,
+        ],
+        [richTooltip, showMarkers],
+        [lineInterpolation],
       ],
     },
     {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_ticks_layout', 'x_axis_format'],
-        ['x_axis_showminmax', null],
+        [xAxisLabel, bottomMargin],
+        [xTicksLayout, xAxisFormat],
+        [xAxisShowMinmax, null],
       ],
     },
     {
       label: t('Y Axis'),
       expanded: true,
       controlSetRows: [
-        ['y_axis_label', 'left_margin'],
-        ['y_axis_showminmax', 'y_log_scale'],
-        ['y_axis_format', 'y_axis_bounds'],
+        [yAxisLabel, leftMargin],
+        [yAxisShowMinmax, yLogScale],
+        ['y_axis_format', yAxisBounds],
       ],
     },
     NVD3TimeSeries[1],
     annotations,
   ],
   controlOverrides: {
-    x_axis_format: {
-      choices: D3_TIME_FORMAT_OPTIONS,
-      default: 'smart_date',
-    },
     row_limit: {
       default: 50000,
     },
diff --git a/superset-frontend/src/explore/controlPanels/LineMulti.js b/superset-frontend/src/explore/controlPanels/LineMulti.js
index f0c1e4d..b9b6fa3 100644
--- a/superset-frontend/src/explore/controlPanels/LineMulti.js
+++ b/superset-frontend/src/explore/controlPanels/LineMulti.js
@@ -20,6 +20,17 @@ import { t } from '@superset-ui/translation';
 import { validateNonEmpty } from '@superset-ui/validator';
 import { annotations } from './sections';
 import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import {
+  lineInterpolation,
+  showLegend,
+  xAxisLabel,
+  bottomMargin,
+  xTicksLayout,
+  xAxisFormat,
+  xAxisShowMinmax,
+  showMarkers,
+  yAxis2Format,
+} from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -41,17 +52,17 @@ export default {
           },
           null,
         ],
-        ['show_legend', 'show_markers'],
-        ['line_interpolation', null],
+        [showLegend, showMarkers],
+        [lineInterpolation, null],
       ],
     },
     {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_ticks_layout', 'x_axis_format'],
-        ['x_axis_showminmax', null],
+        [xAxisLabel, bottomMargin],
+        [xTicksLayout, xAxisFormat],
+        [xAxisShowMinmax, null],
       ],
     },
     {
@@ -116,7 +127,7 @@ export default {
               },
             },
           },
-          'y_axis_2_format',
+          yAxis2Format,
         ],
       ],
     },
diff --git a/superset-frontend/src/explore/controlPanels/Mapbox.js b/superset-frontend/src/explore/controlPanels/Mapbox.js
index 46b6385..15f87b8 100644
--- a/superset-frontend/src/explore/controlPanels/Mapbox.js
+++ b/superset-frontend/src/explore/controlPanels/Mapbox.js
@@ -212,13 +212,6 @@ export default {
           'in each cluster to produce the cluster label.',
       ),
     },
-    rich_tooltip: {
-      label: t('Tooltip'),
-      description: t(
-        'Show a tooltip when hovering over points and clusters ' +
-          'describing the label',
-      ),
-    },
     groupby: {
       description: t(
         'One or many controls to group by. If grouping, latitude ' +
diff --git a/superset-frontend/src/explore/controlPanels/Partition.jsx b/superset-frontend/src/explore/controlPanels/Partition.jsx
index 466a32f..86089d8 100644
--- a/superset-frontend/src/explore/controlPanels/Partition.jsx
+++ b/superset-frontend/src/explore/controlPanels/Partition.jsx
@@ -143,7 +143,20 @@ export default {
             },
           },
         ],
-        ['rich_tooltip'],
+        [
+          {
+            name: 'rich_tooltip',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Rich Tooltip'),
+              renderTrigger: true,
+              default: true,
+              description: t(
+                'The rich tooltip shows a list of all series for that point in time',
+              ),
+            },
+          },
+        ],
       ],
     },
     NVD3TimeSeries[1],
diff --git a/superset-frontend/src/explore/controlPanels/Pie.js b/superset-frontend/src/explore/controlPanels/Pie.js
index 7ce475f..fed9e01 100644
--- a/superset-frontend/src/explore/controlPanels/Pie.js
+++ b/superset-frontend/src/explore/controlPanels/Pie.js
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { t } from '@superset-ui/translation';
+import { showLegend } from './Shared_NVD3';
 
 export default {
   controlPanelSections: [
@@ -65,10 +66,22 @@ export default {
               description: t('Do you want a donut or a pie?'),
             },
           },
-          'show_legend',
+          showLegend,
         ],
         [
-          'show_labels',
+          {
+            name: 'show_labels',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Show Labels'),
+              renderTrigger: true,
+              default: true,
+              description: t(
+                'Whether to display the labels. Note that the label only displays when the the 5% ' +
+                  'threshold.',
+              ),
+            },
+          },
           {
             name: 'labels_outside',
             config: {
diff --git a/superset-frontend/src/explore/controlPanels/Rose.js b/superset-frontend/src/explore/controlPanels/Rose.js
index 854985a..147bd24 100644
--- a/superset-frontend/src/explore/controlPanels/Rose.js
+++ b/superset-frontend/src/explore/controlPanels/Rose.js
@@ -30,7 +30,18 @@ export default {
         ['color_scheme', 'label_colors'],
         ['number_format', 'date_time_format'],
         [
-          'rich_tooltip',
+          {
+            name: 'rich_tooltip',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Rich Tooltip'),
+              renderTrigger: true,
+              default: true,
+              description: t(
+                'The rich tooltip shows a list of all series for that point in time',
+              ),
+            },
+          },
           {
             name: 'rose_area_proportion',
             config: {
diff --git a/superset-frontend/src/explore/controlPanels/Shared_NVD3.js b/superset-frontend/src/explore/controlPanels/Shared_NVD3.js
new file mode 100644
index 0000000..ea4398b
--- /dev/null
+++ b/superset-frontend/src/explore/controlPanels/Shared_NVD3.js
@@ -0,0 +1,309 @@
+/**
+ * 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.
+ */
+
+// These are control configurations that are shared ONLY within the DeckGL viz plugin repo.
+
+import React from 'react';
+import { t } from '@superset-ui/translation';
+import { formatSelectOptions } from '../../modules/utils';
+import {
+  D3_TIME_FORMAT_OPTIONS,
+  D3_FORMAT_DOCS,
+  D3_FORMAT_OPTIONS,
+} from '../controls';
+
+/*
+  Plugins in question:
+  
+  AreaChartPlugin,
+  BarChartPlugin,
+  BubbleChartPlugin,
+  BulletChartPlugin,
+  CompareChartPlugin,
+  DistBarChartPlugin,
+  DualLineChartPlugin,
+  LineChartPlugin,
+  LineMultiChartPlugin,
+  PieChartPlugin,
+  TimePivotChartPlugin,
+*/
+
+export const yAxis2Format = {
+  name: 'y_axis_2_format',
+  config: {
+    type: 'SelectControl',
+    freeForm: true,
+    label: t('Right Axis Format'),
+    default: 'SMART_NUMBER',
+    choices: D3_FORMAT_OPTIONS,
+    description: D3_FORMAT_DOCS,
+  },
+};
+
+export const showMarkers = {
+  name: 'show_markers',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Show Markers'),
+    renderTrigger: true,
+    default: false,
+    description: t('Show data points as circle markers on the lines'),
+  },
+};
+
+export const leftMargin = {
+  name: 'left_margin',
+  config: {
+    type: 'SelectControl',
+    freeForm: true,
+    clearable: false,
+    label: t('Left Margin'),
+    choices: formatSelectOptions(['auto', 50, 75, 100, 125, 150, 200]),
+    default: 'auto',
+    renderTrigger: true,
+    description: t(
+      'Left margin, in pixels, allowing for more room for axis labels',
+    ),
+  },
+};
+
+export const yAxisShowMinmax = {
+  name: 'y_axis_showminmax',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Y bounds'),
+    renderTrigger: true,
+    default: false,
+    description: t('Whether to display the min and max values of the Y-axis'),
+  },
+};
+
+export const lineInterpolation = {
+  name: 'line_interpolation',
+  config: {
+    type: 'SelectControl',
+    label: t('Line Style'),
+    renderTrigger: true,
+    choices: formatSelectOptions([
+      'linear',
+      'basis',
+      'cardinal',
+      'monotone',
+      'step-before',
+      'step-after',
+    ]),
+    default: 'linear',
+    description: t('Line interpolation as defined by d3.js'),
+  },
+};
+
+export const showBrush = {
+  name: 'show_brush',
+  config: {
+    type: 'SelectControl',
+    label: t('Show Range Filter'),
+    renderTrigger: true,
+    clearable: false,
+    default: 'auto',
+    choices: [
+      ['yes', 'Yes'],
+      ['no', 'No'],
+      ['auto', 'Auto'],
+    ],
+    description: t('Whether to display the time range interactive selector'),
+  },
+};
+
+export const showLegend = {
+  name: 'show_legend',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Legend'),
+    renderTrigger: true,
+    default: true,
+    description: t('Whether to display the legend (toggles)'),
+  },
+};
+
+export const showControls = {
+  name: 'show_controls',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Extra Controls'),
+    renderTrigger: true,
+    default: false,
+    description: t(
+      'Whether to show extra controls or not. Extra controls ' +
+        'include things like making mulitBar charts stacked ' +
+        'or side by side.',
+    ),
+  },
+};
+
+export const xAxisLabel = {
+  name: 'x_axis_label',
+  config: {
+    type: 'TextControl',
+    label: t('X Axis Label'),
+    renderTrigger: true,
+    default: '',
+  },
+};
+
+export const bottomMargin = {
+  name: 'bottom_margin',
+  config: {
+    type: 'SelectControl',
+    clearable: false,
+    freeForm: true,
+    label: t('Bottom Margin'),
+    choices: formatSelectOptions(['auto', 50, 75, 100, 125, 150, 200]),
+    default: 'auto',
+    renderTrigger: true,
+    description: t(
+      'Bottom margin, in pixels, allowing for more room for axis labels',
+    ),
+  },
+};
+
+export const xTicksLayout = {
+  name: 'x_ticks_layout',
+  config: {
+    type: 'SelectControl',
+    label: t('X Tick Layout'),
+    choices: formatSelectOptions(['auto', 'flat', '45°', 'staggered']),
+    default: 'auto',
+    clearable: false,
+    renderTrigger: true,
+    description: t('The way the ticks are laid out on the X-axis'),
+  },
+};
+
+export const xAxisFormat = {
+  name: 'x_axis_format',
+  config: {
+    type: 'SelectControl',
+    freeForm: true,
+    label: t('X Axis Format'),
+    renderTrigger: true,
+    choices: D3_TIME_FORMAT_OPTIONS,
+    default: 'smart_date',
+    description: D3_FORMAT_DOCS,
+  },
+};
+
+export const yLogScale = {
+  name: 'y_log_scale',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Y Log Scale'),
+    default: false,
+    renderTrigger: true,
+    description: t('Use a log scale for the Y-axis'),
+  },
+};
+
+export const yAxisBounds = {
+  name: 'y_axis_bounds',
+  config: {
+    type: 'BoundsControl',
+    label: t('Y Axis Bounds'),
+    renderTrigger: true,
+    default: [null, null],
+    description: t(
+      'Bounds for the Y-axis. When left empty, the bounds are ' +
+        'dynamically defined based on the min/max of the data. Note that ' +
+        "this feature will only expand the axis range. It won't " +
+        "narrow the data's extent.",
+    ),
+  },
+};
+
+export const xAxisShowMinmax = {
+  name: 'x_axis_showminmax',
+  config: {
+    type: 'CheckboxControl',
+    label: t('X bounds'),
+    renderTrigger: true,
+    default: false,
+    description: t('Whether to display the min and max values of the X-axis'),
+  },
+};
+
+export const richTooltip = {
+  name: 'rich_tooltip',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Rich Tooltip'),
+    renderTrigger: true,
+    default: true,
+    description: t(
+      'The rich tooltip shows a list of all series for that point in time',
+    ),
+  },
+};
+
+export const showBarValue = {
+  name: 'show_bar_value',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Bar Values'),
+    default: false,
+    renderTrigger: true,
+    description: t('Show the value on top of the bar'),
+  },
+};
+
+export const barStacked = {
+  name: 'bar_stacked',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Stacked Bars'),
+    renderTrigger: true,
+    default: false,
+    description: null,
+  },
+};
+
+export const reduceXTicks = {
+  name: 'reduce_x_ticks',
+  config: {
+    type: 'CheckboxControl',
+    label: t('Reduce X ticks'),
+    renderTrigger: true,
+    default: false,
+    description: t(
+      'Reduces the number of X-axis ticks to be rendered. ' +
+        'If true, the x-axis will not overflow and labels may be ' +
+        'missing. If false, a minimum width will be applied ' +
+        'to columns and the width may overflow into an ' +
+        'horizontal scroll.',
+    ),
+  },
+};
+
+export const yAxisLabel = {
+  name: 'y_axis_label',
+  config: {
+    type: 'TextControl',
+    label: t('Y Axis Label'),
+    renderTrigger: true,
+    default: '',
+  },
+};
diff --git a/superset-frontend/src/explore/controlPanels/TimePivot.js b/superset-frontend/src/explore/controlPanels/TimePivot.js
index 82604eb..3592910 100644
--- a/superset-frontend/src/explore/controlPanels/TimePivot.js
+++ b/superset-frontend/src/explore/controlPanels/TimePivot.js
@@ -17,7 +17,20 @@
  * under the License.
  */
 import { t } from '@superset-ui/translation';
-import { D3_TIME_FORMAT_OPTIONS } from '../controls';
+import { D3_FORMAT_OPTIONS } from '../controls';
+import {
+  lineInterpolation,
+  showLegend,
+  xAxisLabel,
+  bottomMargin,
+  xAxisFormat,
+  yLogScale,
+  yAxisBounds,
+  xAxisShowMinmax,
+  yAxisShowMinmax,
+  yAxisLabel,
+  leftMargin,
+} from './Shared_NVD3';
 
 export default {
   requiresTime: true,
@@ -25,13 +38,46 @@ export default {
     {
       label: t('Query'),
       expanded: true,
-      controlSetRows: [['metric'], ['adhoc_filters'], ['freq']],
+      controlSetRows: [
+        ['metric'],
+        ['adhoc_filters'],
+        [
+          {
+            name: 'freq',
+            config: {
+              type: 'SelectControl',
+              label: t('Frequency'),
+              default: 'W-MON',
+              freeForm: true,
+              clearable: false,
+              choices: [
+                ['AS', 'Year (freq=AS)'],
+                ['52W-MON', '52 weeks starting Monday (freq=52W-MON)'],
+                ['W-SUN', '1 week starting Sunday (freq=W-SUN)'],
+                ['W-MON', '1 week starting Monday (freq=W-MON)'],
+                ['D', 'Day (freq=D)'],
+                ['4W-MON', '4 weeks (freq=4W-MON)'],
+              ],
+              description: t(
+                `The periodicity over which to pivot time. Users can provide
+            "Pandas" offset alias.
+            Click on the info bubble for more details on accepted "freq" expressions.`,
+              ),
+              tooltipOnClick: () => {
+                window.open(
+                  'https://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases',
+                );
+              },
+            },
+          },
+        ],
+      ],
     },
     {
       label: t('Chart Options'),
       expanded: true,
       controlSetRows: [
-        ['show_legend', 'line_interpolation'],
+        [showLegend, lineInterpolation],
         ['color_picker', null],
       ],
     },
@@ -39,25 +85,31 @@ export default {
       label: t('X Axis'),
       expanded: true,
       controlSetRows: [
-        ['x_axis_label', 'bottom_margin'],
-        ['x_axis_showminmax', 'x_axis_format'],
+        [xAxisLabel, bottomMargin],
+        [
+          xAxisShowMinmax,
+          {
+            name: xAxisFormat.name,
+            config: {
+              ...xAxisFormat.config,
+              default: 'SMART_NUMBER',
+              choices: D3_FORMAT_OPTIONS,
+            },
+          },
+        ],
       ],
     },
     {
       label: t('Y Axis'),
       expanded: true,
       controlSetRows: [
-        ['y_axis_label', 'left_margin'],
-        ['y_axis_showminmax', 'y_log_scale'],
-        ['y_axis_format', 'y_axis_bounds'],
+        [yAxisLabel, leftMargin],
+        [yAxisShowMinmax, yLogScale],
+        ['y_axis_format', yAxisBounds],
       ],
     },
   ],
   controlOverrides: {
-    x_axis_format: {
-      choices: D3_TIME_FORMAT_OPTIONS,
-      default: 'smart_date',
-    },
     metric: {
       clearable: false,
     },
diff --git a/superset-frontend/src/explore/controlPanels/WorldMap.js b/superset-frontend/src/explore/controlPanels/WorldMap.js
index 3cf8dcf..d4c0dfb 100644
--- a/superset-frontend/src/explore/controlPanels/WorldMap.js
+++ b/superset-frontend/src/explore/controlPanels/WorldMap.js
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { t } from '@superset-ui/translation';
+import { formatSelectOptions } from '../../modules/utils';
 
 export default {
   controlPanelSections: [
@@ -66,7 +67,26 @@ export default {
           },
         ],
         ['secondary_metric'],
-        ['max_bubble_size'],
+        [
+          {
+            name: 'max_bubble_size',
+            config: {
+              type: 'SelectControl',
+              freeForm: true,
+              label: t('Max Bubble Size'),
+              default: '25',
+              choices: formatSelectOptions([
+                '5',
+                '10',
+                '15',
+                '25',
+                '50',
+                '75',
+                '100',
+              ]),
+            },
+          },
+        ],
       ],
     },
   ],
diff --git a/superset-frontend/src/explore/controls.jsx b/superset-frontend/src/explore/controls.jsx
index efd541e..40c8caf 100644
--- a/superset-frontend/src/explore/controls.jsx
+++ b/superset-frontend/src/explore/controls.jsx
@@ -213,19 +213,6 @@ export const controls = {
     description: t('The type of visualization to display'),
   },
 
-  y_axis_bounds: {
-    type: 'BoundsControl',
-    label: t('Y Axis Bounds'),
-    renderTrigger: true,
-    default: [null, null],
-    description: t(
-      'Bounds for the Y-axis. When left empty, the bounds are ' +
-        'dynamically defined based on the min/max of the data. Note that ' +
-        "this feature will only expand the axis range. It won't " +
-        "narrow the data's extent.",
-    ),
-  },
-
   color_picker: {
     label: t('Fixed Color'),
     description: t('Use this to define a static color for all circles'),
@@ -270,64 +257,6 @@ export const controls = {
     ),
   },
 
-  bar_stacked: {
-    type: 'CheckboxControl',
-    label: t('Stacked Bars'),
-    renderTrigger: true,
-    default: false,
-    description: null,
-  },
-
-  show_markers: {
-    type: 'CheckboxControl',
-    label: t('Show Markers'),
-    renderTrigger: true,
-    default: false,
-    description: t('Show data points as circle markers on the lines'),
-  },
-
-  show_bar_value: {
-    type: 'CheckboxControl',
-    label: t('Bar Values'),
-    default: false,
-    renderTrigger: true,
-    description: t('Show the value on top of the bar'),
-  },
-
-  order_bars: {
-    type: 'CheckboxControl',
-    label: t('Sort Bars'),
-    default: false,
-    renderTrigger: true,
-    description: t('Sort bars by x labels.'),
-  },
-
-  show_controls: {
-    type: 'CheckboxControl',
-    label: t('Extra Controls'),
-    renderTrigger: true,
-    default: false,
-    description: t(
-      'Whether to show extra controls or not. Extra controls ' +
-        'include things like making mulitBar charts stacked ' +
-        'or side by side.',
-    ),
-  },
-
-  reduce_x_ticks: {
-    type: 'CheckboxControl',
-    label: t('Reduce X ticks'),
-    renderTrigger: true,
-    default: false,
-    description: t(
-      'Reduces the number of X-axis ticks to be rendered. ' +
-        'If true, the x-axis will not overflow and labels may be ' +
-        'missing. If false, a minimum width will be applied ' +
-        'to columns and the width may overflow into an ' +
-        'horizontal scroll.',
-    ),
-  },
-
   secondary_metric: {
     ...metric,
     label: t('Color Metric'),
@@ -371,32 +300,6 @@ export const controls = {
     description: t('The name of the country that Superset should display'),
   },
 
-  freq: {
-    type: 'SelectControl',
-    label: t('Frequency'),
-    default: 'W-MON',
-    freeForm: true,
-    clearable: false,
-    choices: [
-      ['AS', 'Year (freq=AS)'],
-      ['52W-MON', '52 weeks starting Monday (freq=52W-MON)'],
-      ['W-SUN', '1 week starting Sunday (freq=W-SUN)'],
-      ['W-MON', '1 week starting Monday (freq=W-MON)'],
-      ['D', 'Day (freq=D)'],
-      ['4W-MON', '4 weeks (freq=4W-MON)'],
-    ],
-    description: t(
-      `The periodicity over which to pivot time. Users can provide
-      "Pandas" offset alias.
-      Click on the info bubble for more details on accepted "freq" expressions.`,
-    ),
-    tooltipOnClick: () => {
-      window.open(
-        'https://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases',
-      );
-    },
-  },
-
   groupby: groupByControl,
 
   columns: {
@@ -491,42 +394,6 @@ export const controls = {
     ),
   },
 
-  bottom_margin: {
-    type: 'SelectControl',
-    clearable: false,
-    freeForm: true,
-    label: t('Bottom Margin'),
-    choices: formatSelectOptions(['auto', 50, 75, 100, 125, 150, 200]),
-    default: 'auto',
-    renderTrigger: true,
-    description: t(
-      'Bottom margin, in pixels, allowing for more room for axis labels',
-    ),
-  },
-
-  x_ticks_layout: {
-    type: 'SelectControl',
-    label: t('X Tick Layout'),
-    choices: formatSelectOptions(['auto', 'flat', '45°', 'staggered']),
-    default: 'auto',
-    clearable: false,
-    renderTrigger: true,
-    description: t('The way the ticks are laid out on the X-axis'),
-  },
-
-  left_margin: {
-    type: 'SelectControl',
-    freeForm: true,
-    clearable: false,
-    label: t('Left Margin'),
-    choices: formatSelectOptions(['auto', 50, 75, 100, 125, 150, 200]),
-    default: 'auto',
-    renderTrigger: true,
-    description: t(
-      'Left margin, in pixels, allowing for more room for axis labels',
-    ),
-  },
-
   granularity: {
     type: 'SelectControl',
     freeForm: true,
@@ -658,14 +525,6 @@ export const controls = {
     },
   },
 
-  max_bubble_size: {
-    type: 'SelectControl',
-    freeForm: true,
-    label: t('Max Bubble Size'),
-    default: '25',
-    choices: formatSelectOptions(['5', '10', '15', '25', '50', '75', '100']),
-  },
-
   number_format: {
     type: 'SelectControl',
     freeForm: true,
@@ -803,30 +662,6 @@ export const controls = {
     default: '',
   },
 
-  x_axis_label: {
-    type: 'TextControl',
-    label: t('X Axis Label'),
-    renderTrigger: true,
-    default: '',
-  },
-
-  y_axis_label: {
-    type: 'TextControl',
-    label: t('Y Axis Label'),
-    renderTrigger: true,
-    default: '',
-  },
-
-  x_axis_format: {
-    type: 'SelectControl',
-    freeForm: true,
-    label: t('X Axis Format'),
-    renderTrigger: true,
-    default: 'SMART_NUMBER',
-    choices: D3_FORMAT_OPTIONS,
-    description: D3_FORMAT_DOCS,
-  },
-
   y_axis_format: {
     type: 'SelectControl',
     freeForm: true,
@@ -852,15 +687,6 @@ export const controls = {
     },
   },
 
-  y_axis_2_format: {
-    type: 'SelectControl',
-    freeForm: true,
-    label: t('Right Axis Format'),
-    default: 'SMART_NUMBER',
-    choices: D3_FORMAT_OPTIONS,
-    description: D3_FORMAT_DOCS,
-  },
-
   date_time_format: {
     type: 'SelectControl',
     freeForm: true,
@@ -881,22 +707,6 @@ export const controls = {
     description: t('Pick your favorite markup language'),
   },
 
-  line_interpolation: {
-    type: 'SelectControl',
-    label: t('Line Style'),
-    renderTrigger: true,
-    choices: formatSelectOptions([
-      'linear',
-      'basis',
-      'cardinal',
-      'monotone',
-      'step-before',
-      'step-after',
-    ]),
-    default: 'linear',
-    description: t('Line interpolation as defined by d3.js'),
-  },
-
   code: {
     type: 'TextAreaControl',
     label: t('Code'),
@@ -922,20 +732,6 @@ export const controls = {
     ),
   },
 
-  show_brush: {
-    type: 'SelectControl',
-    label: t('Show Range Filter'),
-    renderTrigger: true,
-    clearable: false,
-    default: 'auto',
-    choices: [
-      ['yes', 'Yes'],
-      ['no', 'No'],
-      ['auto', 'Auto'],
-    ],
-    description: t('Whether to display the time range interactive selector'),
-  },
-
   table_filter: {
     type: 'CheckboxControl',
     label: t('Emit Filter Events'),
@@ -944,33 +740,6 @@ export const controls = {
     description: t('Whether to apply filter when items are clicked'),
   },
 
-  show_legend: {
-    type: 'CheckboxControl',
-    label: t('Legend'),
-    renderTrigger: true,
-    default: true,
-    description: t('Whether to display the legend (toggles)'),
-  },
-
-  send_time_range: {
-    type: 'CheckboxControl',
-    label: t('Propagate'),
-    renderTrigger: true,
-    default: false,
-    description: t('Send range filter events to other charts'),
-  },
-
-  show_labels: {
-    type: 'CheckboxControl',
-    label: t('Show Labels'),
-    renderTrigger: true,
-    default: true,
-    description: t(
-      'Whether to display the labels. Note that the label only displays when the the 5% ' +
-        'threshold.',
-    ),
-  },
-
   show_values: {
     type: 'CheckboxControl',
     label: t('Show Values'),
@@ -979,40 +748,6 @@ export const controls = {
     description: t('Whether to display the numerical values within the cells'),
   },
 
-  x_axis_showminmax: {
-    type: 'CheckboxControl',
-    label: t('X bounds'),
-    renderTrigger: true,
-    default: false,
-    description: t('Whether to display the min and max values of the X-axis'),
-  },
-
-  y_axis_showminmax: {
-    type: 'CheckboxControl',
-    label: t('Y bounds'),
-    renderTrigger: true,
-    default: false,
-    description: t('Whether to display the min and max values of the Y-axis'),
-  },
-
-  rich_tooltip: {
-    type: 'CheckboxControl',
-    label: t('Rich Tooltip'),
-    renderTrigger: true,
-    default: true,
-    description: t(
-      'The rich tooltip shows a list of all series for that point in time',
-    ),
-  },
-
-  y_log_scale: {
-    type: 'CheckboxControl',
-    label: t('Y Log Scale'),
-    default: false,
-    renderTrigger: true,
-    description: t('Use a log scale for the Y-axis'),
-  },
-
   log_scale: {
     type: 'CheckboxControl',
     label: t('Log Scale'),
diff --git a/superset-frontend/src/utils/getControlsForVizType.js b/superset-frontend/src/utils/getControlsForVizType.js
new file mode 100644
index 0000000..3be5b7e
--- /dev/null
+++ b/superset-frontend/src/utils/getControlsForVizType.js
@@ -0,0 +1,46 @@
+/**
+ * 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.
+ */
+
+import memoize from 'lodash/memoize';
+import { getChartControlPanelRegistry } from '@superset-ui/chart';
+import controls from '../explore/controls';
+
+const getControlsForVizType = memoize(vizType => {
+  const controlsMap = {};
+  getChartControlPanelRegistry()
+    .get(vizType)
+    .controlPanelSections.forEach(section => {
+      section.controlSetRows.forEach(row => {
+        row.forEach(control => {
+          if (!control) return;
+          if (typeof control === 'string') {
+            // For now, we have to look in controls.jsx to get the config for some controls.
+            // Once everything is migrated out, delete this if statement.
+            controlsMap[control] = controls[control];
+          } else if (control.name && control.config) {
+            // condition needed because there are elements, e.g. <hr /> in some control configs (I'm looking at you, FilterBox!)
+            controlsMap[control.name] = control.config;
+          }
+        });
+      });
+    });
+  return controlsMap;
+});
+
+export default getControlsForVizType;
diff --git a/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx b/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx
index 61974ea..4f6f63c 100644
--- a/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx
+++ b/superset-frontend/src/visualizations/FilterBox/FilterBox.jsx
@@ -121,7 +121,7 @@ class FilterBox extends React.Component {
   getControlData(controlName) {
     const { selectedValues } = this.state;
     const control = {
-      ...controls[controlName],
+      ...controls[controlName], // TODO: make these controls ('druid_time_origin', 'granularity', 'granularity_sqla', 'time_grain_sqla') accessible from getControlsForVizType.
       name: controlName,
       key: `control-${controlName}`,
       value: selectedValues[TIME_FILTER_MAP[controlName]],