superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ccwilli...@apache.org
Subject [incubator-superset] branch master updated: [SIP-6] Migrate visualizations to new directory structure. (#5949)
Date Thu, 27 Sep 2018 21:40:05 GMT
This is an automated email from the ASF dual-hosted git repository.

ccwilliams 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 2cd9407  [SIP-6] Migrate visualizations to new directory structure.  (#5949)
2cd9407 is described below

commit 2cd9407a50d4c82af83d90dd436ee5c101cc526f
Author: Krist Wongsuphasawat <krist.wongz@gmail.com>
AuthorDate: Thu Sep 27 14:39:56 2018 -0700

    [SIP-6] Migrate visualizations to new directory structure.  (#5949)
    
    * Migrate Chord, Calendar
    
    * Migrate CountryMap
    
    * Add display name and rename Chord.jsx to Chord.js
    
    * migrate Histogram
    
    * add force-directed
    
    * migrate Heatmap
    
    * add horizon
    
    * migrate parallel coordinates
    
    * migrate partition
    
    * migrate pivot table
    
    * migrate rose
    
    * remove react-dom
    
    * migrate Sankey
    
    * migrate sunburst
    
    * migrate table
    
    * migrate treemap
    
    * migrate filterbox
    
    * migrate wordcloud
    
    * add paired t-test
    
    * fix unit test
    
    * remove renaming
    
    * rename fields
---
 .../spec/javascripts/visualizations/table_spec.jsx |  3 +-
 .../{cal_heatmap.css => Calendar/Calendar.css}     |  0
 .../{cal_heatmap.js => Calendar/Calendar.js}       | 51 +++-------------
 .../src/visualizations/Calendar/ReactCalendar.js   |  4 ++
 .../{WorldMap => Calendar}/adaptor.jsx             |  4 +-
 .../src/visualizations/Calendar/transformProps.js  | 32 ++++++++++
 .../visualizations/{chord.css => Chord/Chord.css}  |  0
 .../visualizations/{chord.jsx => Chord/Chord.js}   | 27 ++-------
 .../assets/src/visualizations/Chord/ReactChord.js  |  4 ++
 .../visualizations/{WorldMap => Chord}/adaptor.jsx |  4 +-
 .../src/visualizations/Chord/transformProps.js     | 10 ++++
 .../{country_map.css => CountryMap/CountryMap.css} |  0
 .../{country_map.js => CountryMap/CountryMap.js}   | 28 ++-------
 .../visualizations/CountryMap/ReactCountryMap.js   |  4 ++
 .../{WorldMap => CountryMap}/adaptor.jsx           |  4 +-
 .../visualizations/CountryMap/transformProps.js    | 15 +++++
 .../{filter_box.css => FilterBox/FilterBox.css}    |  0
 .../{filter_box.jsx => FilterBox/FilterBox.jsx}    | 57 +++---------------
 .../{WorldMap => FilterBox}/adaptor.jsx            |  4 +-
 .../src/visualizations/FilterBox/transformProps.js | 39 ++++++++++++
 .../ForceDirected.css}                             |  0
 .../ForceDirected.js}                              | 23 ++------
 .../ForceDirected/ReactForceDirected.js            |  4 ++
 .../{WorldMap => ForceDirected}/adaptor.jsx        |  4 +-
 .../visualizations/ForceDirected/transformProps.js | 10 ++++
 .../{heatmap.css => Heatmap/Heatmap.css}           |  0
 .../{heatmap.js => Heatmap/Heatmap.js}             | 58 ++----------------
 .../src/visualizations/Heatmap/ReactHeatmap.js     |  4 ++
 .../{WorldMap => Heatmap}/adaptor.jsx              |  4 +-
 .../src/visualizations/Heatmap/transformProps.js   | 43 ++++++++++++++
 .../visualizations/{ => Histogram}/Histogram.jsx   | 34 +----------
 .../{WorldMap => Histogram}/adaptor.jsx            |  4 +-
 .../src/visualizations/Histogram/transformProps.js | 21 +++++++
 .../visualizations/{ => Horizon}/HorizonChart.css  |  0
 .../visualizations/{ => Horizon}/HorizonChart.jsx  | 22 +------
 .../visualizations/{ => Horizon}/HorizonRow.jsx    |  0
 .../{WorldMap => Horizon}/adaptor.jsx              |  4 +-
 .../src/visualizations/Horizon/transformProps.js   | 13 ++++
 .../src/visualizations/PairedTTest/PairedTTest.jsx | 27 +--------
 .../{WorldMap => PairedTTest}/adaptor.jsx          |  4 +-
 .../visualizations/PairedTTest/transformProps.js   | 19 ++++++
 .../ParallelCoordinates.css}                       |  0
 .../ParallelCoordinates.js}                        | 42 +++----------
 .../ReactParallelCoordinates.js                    |  4 ++
 .../visualizations/ParallelCoordinates/adaptor.jsx |  5 ++
 .../ParallelCoordinates/transformProps.js          | 23 ++++++++
 .../{partition.css => Partition/Partition.css}     |  0
 .../{partition.js => Partition/Partition.js}       | 57 ++++--------------
 .../src/visualizations/Partition/ReactPartition.js |  4 ++
 .../{WorldMap => Partition}/adaptor.jsx            |  4 +-
 .../src/visualizations/Partition/transformProps.js | 32 ++++++++++
 .../{pivot_table.css => PivotTable/PivotTable.css} |  0
 .../{pivot_table.js => PivotTable/PivotTable.js}   | 38 +++---------
 .../visualizations/PivotTable/ReactPivotTable.js   |  4 ++
 .../{WorldMap => PivotTable}/adaptor.jsx           |  4 +-
 .../visualizations/PivotTable/transformProps.js    | 19 ++++++
 .../assets/src/visualizations/Rose/ReactRose.js    |  4 ++
 .../src/visualizations/{rose.css => Rose/Rose.css} |  0
 .../src/visualizations/{rose.js => Rose/Rose.js}   | 34 ++---------
 .../visualizations/{WorldMap => Rose}/adaptor.jsx  |  4 +-
 .../src/visualizations/Rose/transformProps.js      | 19 ++++++
 .../src/visualizations/Sankey/ReactSankey.js       |  4 ++
 .../{sankey.css => Sankey/Sankey.css}              |  0
 .../visualizations/{sankey.js => Sankey/Sankey.js} | 21 ++-----
 .../{WorldMap => Sankey}/adaptor.jsx               |  4 +-
 .../src/visualizations/Sankey/transformProps.js    |  9 +++
 .../src/visualizations/Sunburst/ReactSunburst.js   |  4 ++
 .../{sunburst.css => Sunburst/Sunburst.css}        |  0
 .../{sunburst.js => Sunburst/Sunburst.js}          | 25 ++------
 .../{WorldMap => Sunburst}/adaptor.jsx             |  4 +-
 .../src/visualizations/Sunburst/transformProps.js  | 10 ++++
 .../assets/src/visualizations/Table/ReactTable.js  |  4 ++
 .../visualizations/{table.css => Table/Table.css}  |  0
 .../visualizations/{table.js => Table/Table.js}    | 69 ++--------------------
 .../visualizations/{WorldMap => Table}/adaptor.jsx |  4 +-
 .../src/visualizations/Table/transformProps.js     | 58 ++++++++++++++++++
 .../src/visualizations/Treemap/ReactTreemap.js     |  4 ++
 .../{treemap.css => Treemap/Treemap.css}           |  0
 .../{treemap.js => Treemap/Treemap.js}             | 32 ++--------
 .../{WorldMap => Treemap}/adaptor.jsx              |  4 +-
 .../src/visualizations/Treemap/transformProps.js   | 15 +++++
 .../assets/src/visualizations/WorldMap/WorldMap.js |  3 +-
 .../assets/src/visualizations/WorldMap/adaptor.jsx |  4 +-
 superset/assets/src/visualizations/index.js        | 36 +++++------
 .../src/visualizations/models/BasicChartInput.js   |  6 ++
 .../src/visualizations/wordcloud/ReactWordCloud.js |  4 ++
 .../src/visualizations/wordcloud/WordCloud.js      | 46 ++-------------
 .../{WorldMap => wordcloud}/adaptor.jsx            |  4 +-
 .../src/visualizations/wordcloud/transformProps.js | 27 +++++++++
 89 files changed, 633 insertions(+), 653 deletions(-)

diff --git a/superset/assets/spec/javascripts/visualizations/table_spec.jsx b/superset/assets/spec/javascripts/visualizations/table_spec.jsx
index 5252e9d..adbbf9d 100644
--- a/superset/assets/spec/javascripts/visualizations/table_spec.jsx
+++ b/superset/assets/spec/javascripts/visualizations/table_spec.jsx
@@ -1,7 +1,7 @@
 import { expect } from 'chai';
 import $ from 'jquery';
 import '../../helpers/shim';
-import tableVis from '../../../src/visualizations/table';
+import tableVis from '../../../src/visualizations/Table/adaptor';
 
 describe('table viz', () => {
   const div = '<div id="slice-container"><div class="dataTables_wrapper"></div></div>';
@@ -17,6 +17,7 @@ describe('table viz', () => {
     getFilters: () => ({}),
     removeFilter() {},
     addFilter() {},
+    width: () => 0,
     height: () => 0,
   };
   const basePayload = {
diff --git a/superset/assets/src/visualizations/cal_heatmap.css b/superset/assets/src/visualizations/Calendar/Calendar.css
similarity index 100%
rename from superset/assets/src/visualizations/cal_heatmap.css
rename to superset/assets/src/visualizations/Calendar/Calendar.css
diff --git a/superset/assets/src/visualizations/cal_heatmap.js b/superset/assets/src/visualizations/Calendar/Calendar.js
similarity index 73%
rename from superset/assets/src/visualizations/cal_heatmap.js
rename to superset/assets/src/visualizations/Calendar/Calendar.js
index af91dce..bcecb3c 100644
--- a/superset/assets/src/visualizations/cal_heatmap.js
+++ b/superset/assets/src/visualizations/Calendar/Calendar.js
@@ -1,11 +1,11 @@
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import { colorScalerFactory } from '../modules/colors';
-import CalHeatMap from '../../vendor/cal-heatmap/cal-heatmap';
-import { d3TimeFormatPreset, d3FormatPreset } from '../modules/utils';
-import { UTC } from '../modules/dates';
-import '../../vendor/cal-heatmap/cal-heatmap.css';
-import './cal_heatmap.css';
+import { colorScalerFactory } from '../../modules/colors';
+import CalHeatMap from '../../../vendor/cal-heatmap/cal-heatmap';
+import { d3TimeFormatPreset, d3FormatPreset } from '../../modules/utils';
+import { UTC } from '../../modules/dates';
+import '../../../vendor/cal-heatmap/cal-heatmap.css';
+import './Calendar.css';
 
 const UTCTS = uts => UTC(new Date(uts)).getTime();
 
@@ -37,8 +37,6 @@ const propTypes = {
 };
 
 function Calendar(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Calendar');
-
   const {
     data,
     height,
@@ -124,40 +122,7 @@ function Calendar(element, props) {
   });
 }
 
+Calendar.displayName = 'Calendar';
 Calendar.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData, datasource } = slice;
-  const {
-    cell_padding: cellPadding,
-    cell_radius: cellRadius,
-    cell_size: cellSize,
-    linear_color_scheme: linearColorScheme,
-    show_legend: showLegend,
-    show_metric_name: showMetricName,
-    show_values: showValues,
-    steps,
-    x_axis_time_format: timeFormat,
-    y_axis_format: valueFormat,
-  } = formData;
-  const { verbose_map: verboseMap } = datasource;
-  const element = document.querySelector(selector);
-
-  return Calendar(element, {
-    data: payload.data,
-    height: slice.height(),
-    cellPadding,
-    cellRadius,
-    cellSize,
-    linearColorScheme,
-    showLegend,
-    showMetricName,
-    showValues,
-    steps,
-    timeFormat,
-    valueFormat,
-    verboseMap,
-  });
-}
-
-export default adaptor;
+export default Calendar;
diff --git a/superset/assets/src/visualizations/Calendar/ReactCalendar.js b/superset/assets/src/visualizations/Calendar/ReactCalendar.js
new file mode 100644
index 0000000..349930b
--- /dev/null
+++ b/superset/assets/src/visualizations/Calendar/ReactCalendar.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Calendar';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Calendar/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Calendar/adaptor.jsx
index 30d0400..6df2361 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Calendar/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactCalendar';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Calendar/transformProps.js b/superset/assets/src/visualizations/Calendar/transformProps.js
new file mode 100644
index 0000000..e97e166
--- /dev/null
+++ b/superset/assets/src/visualizations/Calendar/transformProps.js
@@ -0,0 +1,32 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload, datasource } = basicChartInput;
+  const {
+    cellPadding,
+    cellRadius,
+    cellSize,
+    linearColorScheme,
+    showLegend,
+    showMetricName,
+    showValues,
+    steps,
+    xAxisTimeFormat,
+    yAxisFormat,
+  } = formData;
+
+  const { verboseMap } = datasource;
+
+  return {
+    data: payload.data,
+    cellPadding,
+    cellRadius,
+    cellSize,
+    linearColorScheme,
+    showLegend,
+    showMetricName,
+    showValues,
+    steps,
+    timeFormat: xAxisTimeFormat,
+    valueFormat: yAxisFormat,
+    verboseMap,
+  };
+}
diff --git a/superset/assets/src/visualizations/chord.css b/superset/assets/src/visualizations/Chord/Chord.css
similarity index 100%
rename from superset/assets/src/visualizations/chord.css
rename to superset/assets/src/visualizations/Chord/Chord.css
diff --git a/superset/assets/src/visualizations/chord.jsx b/superset/assets/src/visualizations/Chord/Chord.js
similarity index 82%
rename from superset/assets/src/visualizations/chord.jsx
rename to superset/assets/src/visualizations/Chord/Chord.js
index 672a31e..cb5ae6d 100644
--- a/superset/assets/src/visualizations/chord.jsx
+++ b/superset/assets/src/visualizations/Chord/Chord.js
@@ -1,8 +1,8 @@
 /* eslint-disable no-param-reassign */
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import { getScale } from '../modules/CategoricalColorNamespace';
-import './chord.css';
+import { getScale } from '../../modules/CategoricalColorNamespace';
+import './Chord.css';
 
 const propTypes = {
   data: PropTypes.shape({
@@ -15,9 +15,7 @@ const propTypes = {
   colorScheme: PropTypes.string,
 };
 
-function chordVis(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'ChordVis');
-
+function Chord(element, props) {
   const {
     data,
     width,
@@ -117,20 +115,7 @@ function chordVis(element, props) {
   });
 }
 
-chordVis.propTypes = propTypes;
-
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const { y_axis_format: numberFormat, color_scheme: colorScheme } = formData;
-  const element = document.querySelector(selector);
-
-  return chordVis(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    numberFormat,
-    colorScheme,
-  });
-}
+Chord.displayName = 'Chord';
+Chord.propTypes = propTypes;
 
-export default adaptor;
+export default Chord;
diff --git a/superset/assets/src/visualizations/Chord/ReactChord.js b/superset/assets/src/visualizations/Chord/ReactChord.js
new file mode 100644
index 0000000..3a41d7f
--- /dev/null
+++ b/superset/assets/src/visualizations/Chord/ReactChord.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Chord';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Chord/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Chord/adaptor.jsx
index 30d0400..ced5895 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Chord/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactChord';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Chord/transformProps.js b/superset/assets/src/visualizations/Chord/transformProps.js
new file mode 100644
index 0000000..f52a1dc
--- /dev/null
+++ b/superset/assets/src/visualizations/Chord/transformProps.js
@@ -0,0 +1,10 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const { yAxisFormat, colorScheme } = formData;
+
+  return {
+    data: payload.data,
+    numberFormat: yAxisFormat,
+    colorScheme,
+  };
+}
diff --git a/superset/assets/src/visualizations/country_map.css b/superset/assets/src/visualizations/CountryMap/CountryMap.css
similarity index 100%
rename from superset/assets/src/visualizations/country_map.css
rename to superset/assets/src/visualizations/CountryMap/CountryMap.css
diff --git a/superset/assets/src/visualizations/country_map.js b/superset/assets/src/visualizations/CountryMap/CountryMap.js
similarity index 89%
rename from superset/assets/src/visualizations/country_map.js
rename to superset/assets/src/visualizations/CountryMap/CountryMap.js
index 92c799b..52e961d 100644
--- a/superset/assets/src/visualizations/country_map.js
+++ b/superset/assets/src/visualizations/CountryMap/CountryMap.js
@@ -1,7 +1,7 @@
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import { colorScalerFactory } from '../modules/colors';
-import './country_map.css';
+import { colorScalerFactory } from '../../modules/colors';
+import './CountryMap.css';
 
 const propTypes = {
   data: PropTypes.arrayOf(PropTypes.shape({
@@ -19,8 +19,6 @@ const propTypes = {
 const maps = {};
 
 function CountryMap(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'CountryMap');
-
   const {
     data,
     width,
@@ -196,25 +194,7 @@ function CountryMap(element, props) {
 
 }
 
+CountryMap.displayName = 'CountryMap';
 CountryMap.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const {
-    linear_color_scheme: linearColorScheme,
-    number_format: numberFormat,
-    select_country: country,
-  } = formData;
-  const element = document.querySelector(selector);
-
-  return CountryMap(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    country,
-    linearColorScheme,
-    numberFormat,
-  });
-}
-
-export default adaptor;
+export default CountryMap;
diff --git a/superset/assets/src/visualizations/CountryMap/ReactCountryMap.js b/superset/assets/src/visualizations/CountryMap/ReactCountryMap.js
new file mode 100644
index 0000000..9b88976
--- /dev/null
+++ b/superset/assets/src/visualizations/CountryMap/ReactCountryMap.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './CountryMap';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/CountryMap/adaptor.jsx
similarity index 50%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/CountryMap/adaptor.jsx
index 30d0400..8553cb4 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/CountryMap/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactCountryMap';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/CountryMap/transformProps.js b/superset/assets/src/visualizations/CountryMap/transformProps.js
new file mode 100644
index 0000000..4026335
--- /dev/null
+++ b/superset/assets/src/visualizations/CountryMap/transformProps.js
@@ -0,0 +1,15 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    linearColorScheme,
+    numberFormat,
+    selectCountry,
+  } = formData;
+
+  return {
+    data: payload.data,
+    country: selectCountry,
+    linearColorScheme,
+    numberFormat,
+  };
+}
diff --git a/superset/assets/src/visualizations/filter_box.css b/superset/assets/src/visualizations/FilterBox/FilterBox.css
similarity index 100%
rename from superset/assets/src/visualizations/filter_box.css
rename to superset/assets/src/visualizations/FilterBox/FilterBox.css
diff --git a/superset/assets/src/visualizations/filter_box.jsx b/superset/assets/src/visualizations/FilterBox/FilterBox.jsx
similarity index 81%
rename from superset/assets/src/visualizations/filter_box.jsx
rename to superset/assets/src/visualizations/FilterBox/FilterBox.jsx
index 34c8d77..7882261 100644
--- a/superset/assets/src/visualizations/filter_box.jsx
+++ b/superset/assets/src/visualizations/FilterBox/FilterBox.jsx
@@ -1,18 +1,17 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import ReactDOM from 'react-dom';
 import VirtualizedSelect from 'react-virtualized-select';
 import { Creatable } from 'react-select';
 import { Button } from 'react-bootstrap';
 
-import DateFilterControl from '../explore/components/controls/DateFilterControl';
-import ControlRow from '../explore/components/ControlRow';
-import Control from '../explore/components/Control';
-import controls from '../explore/controls';
-import OnPasteSelect from '../components/OnPasteSelect';
-import VirtualizedRendererWrap from '../components/VirtualizedRendererWrap';
-import { t } from '../locales';
-import './filter_box.css';
+import DateFilterControl from '../../explore/components/controls/DateFilterControl';
+import ControlRow from '../../explore/components/ControlRow';
+import Control from '../../explore/components/Control';
+import controls from '../../explore/controls';
+import OnPasteSelect from '../../components/OnPasteSelect';
+import VirtualizedRendererWrap from '../../components/VirtualizedRendererWrap';
+import { t } from '../../locales';
+import './FilterBox.css';
 
 // maps control names to their key in extra_filters
 const TIME_FILTER_MAP = {
@@ -257,42 +256,4 @@ class FilterBox extends React.Component {
 FilterBox.propTypes = propTypes;
 FilterBox.defaultProps = defaultProps;
 
-function adaptor(slice, payload) {
-  // filter box should ignore the dashboard's filters
-  // const url = slice.jsonEndpoint({ extraFilters: false });
-  const { formData, datasource } = slice;
-  const { verbose_map: verboseMap } = datasource;
-  const {
-    groupby,
-    instant_filtering: instantFiltering,
-    date_filter: showDateFilter,
-    show_sqla_time_granularity: showSqlaTimeGrain,
-    show_sqla_time_column: showSqlaTimeColumn,
-    show_druid_time_granularity: showDruidTimeGrain,
-    show_druid_time_origin: showDruidTimeOrigin,
-  } = formData;
-
-  const filtersFields = groupby.map(key => ({
-    key,
-    label: verboseMap[key] || key,
-  }));
-
-  ReactDOM.render(
-    <FilterBox
-      datasource={datasource}
-      filtersFields={filtersFields}
-      filtersChoices={payload.data}
-      onChange={slice.addFilter}
-      showDateFilter={showDateFilter}
-      showSqlaTimeGrain={showSqlaTimeGrain}
-      showSqlaTimeColumn={showSqlaTimeColumn}
-      showDruidTimeGrain={showDruidTimeGrain}
-      showDruidTimeOrigin={showDruidTimeOrigin}
-      origSelectedValues={slice.getFilters() || {}}
-      instantFiltering={instantFiltering}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-export default adaptor;
+export default FilterBox;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/FilterBox/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/FilterBox/adaptor.jsx
index 30d0400..9d502af 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/FilterBox/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './FilterBox';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/FilterBox/transformProps.js b/superset/assets/src/visualizations/FilterBox/transformProps.js
new file mode 100644
index 0000000..e5077f8
--- /dev/null
+++ b/superset/assets/src/visualizations/FilterBox/transformProps.js
@@ -0,0 +1,39 @@
+export default function transformProps(basicChartInput) {
+  const {
+    datasource,
+    filters,
+    formData,
+    onAddFilter,
+    payload,
+    rawDatasource,
+  } = basicChartInput;
+  const {
+    dateFilter,
+    groupby,
+    instantFiltering,
+    showDruidTimeGranularity,
+    showDruidTimeOrigin,
+    showSqlaTimeColumn,
+    showSqlaTimeGranularity,
+  } = formData;
+  const { verboseMap } = datasource;
+
+  const filtersFields = groupby.map(key => ({
+    key,
+    label: verboseMap[key] || key,
+  }));
+
+  return {
+    datasource: rawDatasource,
+    filtersFields,
+    filtersChoices: payload.data,
+    instantFiltering,
+    onChange: onAddFilter,
+    origSelectedValues: filters || {},
+    showDateFilter: dateFilter,
+    showDruidTimeGrain: showDruidTimeGranularity,
+    showDruidTimeOrigin,
+    showSqlaTimeColumn,
+    showSqlaTimeGrain: showSqlaTimeGranularity,
+  };
+}
diff --git a/superset/assets/src/visualizations/directed_force.css b/superset/assets/src/visualizations/ForceDirected/ForceDirected.css
similarity index 100%
rename from superset/assets/src/visualizations/directed_force.css
rename to superset/assets/src/visualizations/ForceDirected/ForceDirected.css
diff --git a/superset/assets/src/visualizations/directed_force.js b/superset/assets/src/visualizations/ForceDirected/ForceDirected.js
similarity index 89%
rename from superset/assets/src/visualizations/directed_force.js
rename to superset/assets/src/visualizations/ForceDirected/ForceDirected.js
index b3bf0f3..2c6c4e3 100644
--- a/superset/assets/src/visualizations/directed_force.js
+++ b/superset/assets/src/visualizations/ForceDirected/ForceDirected.js
@@ -1,7 +1,7 @@
 /* eslint-disable no-param-reassign */
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import './directed_force.css';
+import './ForceDirected.css';
 
 const propTypes = {
   data: PropTypes.arrayOf(PropTypes.shape({
@@ -16,9 +16,7 @@ const propTypes = {
 };
 
 /* Modified from http://bl.ocks.org/d3noob/5141278 */
-function ForceDirectedGraph(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'ForceDirectedGraph');
-
+function ForceDirected(element, props) {
   const {
     data,
     width,
@@ -179,18 +177,7 @@ function ForceDirectedGraph(element, props) {
     .text(d => d.name);
 }
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const { link_length: linkLength, charge } = formData;
-  const element = document.querySelector(selector);
-
-  return ForceDirectedGraph(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    linkLength,
-    charge,
-  });
-}
+ForceDirected.displayName = 'ForceDirected';
+ForceDirected.propTypes = propTypes;
 
-export default adaptor;
+export default ForceDirected;
diff --git a/superset/assets/src/visualizations/ForceDirected/ReactForceDirected.js b/superset/assets/src/visualizations/ForceDirected/ReactForceDirected.js
new file mode 100644
index 0000000..d769de7
--- /dev/null
+++ b/superset/assets/src/visualizations/ForceDirected/ReactForceDirected.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './ForceDirected';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/ForceDirected/adaptor.jsx
similarity index 50%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/ForceDirected/adaptor.jsx
index 30d0400..be20f6d 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/ForceDirected/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactForceDirected';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/ForceDirected/transformProps.js b/superset/assets/src/visualizations/ForceDirected/transformProps.js
new file mode 100644
index 0000000..115f353
--- /dev/null
+++ b/superset/assets/src/visualizations/ForceDirected/transformProps.js
@@ -0,0 +1,10 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const { charge, linkLength } = formData;
+
+  return {
+    data: payload.data,
+    charge,
+    linkLength,
+  };
+}
diff --git a/superset/assets/src/visualizations/heatmap.css b/superset/assets/src/visualizations/Heatmap/Heatmap.css
similarity index 100%
rename from superset/assets/src/visualizations/heatmap.css
rename to superset/assets/src/visualizations/Heatmap/Heatmap.css
diff --git a/superset/assets/src/visualizations/heatmap.js b/superset/assets/src/visualizations/Heatmap/Heatmap.js
similarity index 87%
rename from superset/assets/src/visualizations/heatmap.js
rename to superset/assets/src/visualizations/Heatmap/Heatmap.js
index 3a6b351..ca6d7a1 100644
--- a/superset/assets/src/visualizations/heatmap.js
+++ b/superset/assets/src/visualizations/Heatmap/Heatmap.js
@@ -3,9 +3,9 @@ import PropTypes from 'prop-types';
 import 'd3-svg-legend';
 import d3tip from 'd3-tip';
 
-import { colorScalerFactory } from '../modules/colors';
-import '../../stylesheets/d3tip.css';
-import './heatmap.css';
+import { colorScalerFactory } from '../../modules/colors';
+import '../../../stylesheets/d3tip.css';
+import './Heatmap.css';
 
 const propTypes = {
   data: PropTypes.shape({
@@ -54,8 +54,6 @@ function cmp(a, b) {
 // Inspired from http://bl.ocks.org/mbostock/3074470
 // https://jsfiddle.net/cyril123/h0reyumq/
 function Heatmap(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Heatmap');
-
   const {
     data,
     width,
@@ -342,53 +340,7 @@ function Heatmap(element, props) {
   createImageObj();
 }
 
+Heatmap.displayName = 'Heatmap';
 Heatmap.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const {
-    bottom_margin: bottomMargin,
-    canvas_image_rendering: canvasImageRendering,
-    all_columns_x: columnX,
-    all_columns_y: columnY,
-    linear_color_scheme: colorScheme,
-    left_margin: leftMargin,
-    metric,
-    normalized,
-    show_legend: showLegend,
-    show_perc: showPercentage,
-    show_values: showValues,
-    sort_x_axis: sortXAxis,
-    sort_y_axis: sortYAxis,
-    xscale_interval: xScaleInterval,
-    yscale_interval: yScaleInterval,
-    y_axis_bounds: yAxisBounds,
-    y_axis_format: numberFormat,
-  } = formData;
-  const element = document.querySelector(selector);
-
-  return Heatmap(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    bottomMargin,
-    canvasImageRendering,
-    colorScheme,
-    columnX,
-    columnY,
-    leftMargin,
-    metric,
-    normalized,
-    numberFormat,
-    showLegend,
-    showPercentage,
-    showValues,
-    sortXAxis,
-    sortYAxis,
-    xScaleInterval: parseInt(xScaleInterval, 10),
-    yScaleInterval: parseInt(yScaleInterval, 10),
-    yAxisBounds,
-  });
-}
-
-export default adaptor;
+export default Heatmap;
diff --git a/superset/assets/src/visualizations/Heatmap/ReactHeatmap.js b/superset/assets/src/visualizations/Heatmap/ReactHeatmap.js
new file mode 100644
index 0000000..4537978
--- /dev/null
+++ b/superset/assets/src/visualizations/Heatmap/ReactHeatmap.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Heatmap';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Heatmap/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Heatmap/adaptor.jsx
index 30d0400..f46baa8 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Heatmap/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactHeatmap';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Heatmap/transformProps.js b/superset/assets/src/visualizations/Heatmap/transformProps.js
new file mode 100644
index 0000000..9c4ad10
--- /dev/null
+++ b/superset/assets/src/visualizations/Heatmap/transformProps.js
@@ -0,0 +1,43 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    bottomMargin,
+    canvasImageRendering,
+    allColumnsX,
+    allColumnsY,
+    linearColorScheme,
+    leftMargin,
+    metric,
+    normalized,
+    showLegend,
+    showPerc,
+    showValues,
+    sortXAxis,
+    sortYAxis,
+    xscaleInterval,
+    yscaleInterval,
+    yAxisBounds,
+    yAxisFormat,
+  } = formData;
+
+  return {
+    data: payload.data,
+    bottomMargin,
+    canvasImageRendering,
+    colorScheme: linearColorScheme,
+    columnX: allColumnsX,
+    columnY: allColumnsY,
+    leftMargin,
+    metric,
+    normalized,
+    numberFormat: yAxisFormat,
+    showLegend,
+    showPercentage: showPerc,
+    showValues,
+    sortXAxis,
+    sortYAxis,
+    xScaleInterval: parseInt(xscaleInterval, 10),
+    yScaleInterval: parseInt(yscaleInterval, 10),
+    yAxisBounds,
+  };
+}
diff --git a/superset/assets/src/visualizations/Histogram.jsx b/superset/assets/src/visualizations/Histogram/Histogram.jsx
similarity index 78%
rename from superset/assets/src/visualizations/Histogram.jsx
rename to superset/assets/src/visualizations/Histogram/Histogram.jsx
index 44ffafb..34540bb 100644
--- a/superset/assets/src/visualizations/Histogram.jsx
+++ b/superset/assets/src/visualizations/Histogram/Histogram.jsx
@@ -1,12 +1,11 @@
 import PropTypes from 'prop-types';
 import React from 'react';
-import ReactDOM from 'react-dom';
 import { Histogram, BarSeries, XAxis, YAxis } from '@data-ui/histogram';
 import { chartTheme } from '@data-ui/theme';
 import { LegendOrdinal } from '@vx/legend';
 import { scaleOrdinal } from '@vx/scale';
-import WithLegend from './WithLegend';
-import { getScale } from '../modules/CategoricalColorNamespace';
+import WithLegend from '../WithLegend';
+import { getScale } from '../../modules/CategoricalColorNamespace';
 
 const propTypes = {
   className: PropTypes.string,
@@ -108,31 +107,4 @@ class CustomHistogram extends React.PureComponent {
 CustomHistogram.propTypes = propTypes;
 CustomHistogram.defaultProps = defaultProps;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const {
-    color_scheme: colorScheme,
-    link_length: binCount,
-    normalized,
-    global_opacity: opacity,
-    x_axis_label: xAxisLabel,
-    y_axis_label: yAxisLabel,
-  } = formData;
-
-  ReactDOM.render(
-    <CustomHistogram
-      data={payload.data}
-      width={slice.width()}
-      height={slice.height()}
-      binCount={parseInt(binCount, 10)}
-      colorScheme={colorScheme}
-      normalized={normalized}
-      opacity={opacity}
-      xAxisLabel={xAxisLabel}
-      yAxisLabel={yAxisLabel}
-    />,
-    document.querySelector(selector),
-  );
-}
-
-export default adaptor;
+export default CustomHistogram;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Histogram/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Histogram/adaptor.jsx
index 30d0400..e72932f 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Histogram/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './Histogram';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Histogram/transformProps.js b/superset/assets/src/visualizations/Histogram/transformProps.js
new file mode 100644
index 0000000..0c522a1
--- /dev/null
+++ b/superset/assets/src/visualizations/Histogram/transformProps.js
@@ -0,0 +1,21 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    colorScheme,
+    linkLength,
+    normalized,
+    globalOpacity,
+    xAxisLabel,
+    yAxisLabel,
+  } = formData;
+
+  return {
+    data: payload.data,
+    binCount: parseInt(linkLength, 10),
+    colorScheme,
+    normalized,
+    opacity: globalOpacity,
+    xAxisLabel,
+    yAxisLabel,
+  };
+}
diff --git a/superset/assets/src/visualizations/HorizonChart.css b/superset/assets/src/visualizations/Horizon/HorizonChart.css
similarity index 100%
rename from superset/assets/src/visualizations/HorizonChart.css
rename to superset/assets/src/visualizations/Horizon/HorizonChart.css
diff --git a/superset/assets/src/visualizations/HorizonChart.jsx b/superset/assets/src/visualizations/Horizon/HorizonChart.jsx
similarity index 80%
rename from superset/assets/src/visualizations/HorizonChart.jsx
rename to superset/assets/src/visualizations/Horizon/HorizonChart.jsx
index c17e982..1bc1e05 100644
--- a/superset/assets/src/visualizations/HorizonChart.jsx
+++ b/superset/assets/src/visualizations/Horizon/HorizonChart.jsx
@@ -1,5 +1,4 @@
 import React from 'react';
-import ReactDOM from 'react-dom';
 import PropTypes from 'prop-types';
 import d3 from 'd3';
 import HorizonRow, { DEFAULT_COLORS } from './HorizonRow';
@@ -81,23 +80,4 @@ class HorizonChart extends React.PureComponent {
 HorizonChart.propTypes = propTypes;
 HorizonChart.defaultProps = defaultProps;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const element = document.querySelector(selector);
-  const {
-    horizon_color_scale: colorScale,
-    series_height: seriesHeight,
-  } = formData;
-
-  ReactDOM.render(
-    <HorizonChart
-      data={payload.data}
-      width={slice.width()}
-      seriesHeight={parseInt(seriesHeight, 10)}
-      colorScale={colorScale}
-    />,
-    element,
-  );
-}
-
-export default adaptor;
+export default HorizonChart;
diff --git a/superset/assets/src/visualizations/HorizonRow.jsx b/superset/assets/src/visualizations/Horizon/HorizonRow.jsx
similarity index 100%
rename from superset/assets/src/visualizations/HorizonRow.jsx
rename to superset/assets/src/visualizations/Horizon/HorizonRow.jsx
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Horizon/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Horizon/adaptor.jsx
index 30d0400..53b08a8 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Horizon/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './HorizonChart';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Horizon/transformProps.js b/superset/assets/src/visualizations/Horizon/transformProps.js
new file mode 100644
index 0000000..b5838c2
--- /dev/null
+++ b/superset/assets/src/visualizations/Horizon/transformProps.js
@@ -0,0 +1,13 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    horizonColorScale,
+    seriesHeight,
+  } = formData;
+
+  return {
+    data: payload.data,
+    seriesHeight: parseInt(seriesHeight, 10),
+    colorScale: horizonColorScale,
+  };
+}
diff --git a/superset/assets/src/visualizations/PairedTTest/PairedTTest.jsx b/superset/assets/src/visualizations/PairedTTest/PairedTTest.jsx
index 3fd9f61..cc157c3 100644
--- a/superset/assets/src/visualizations/PairedTTest/PairedTTest.jsx
+++ b/superset/assets/src/visualizations/PairedTTest/PairedTTest.jsx
@@ -1,6 +1,5 @@
 
 import PropTypes from 'prop-types';
-import ReactDOM from 'react-dom';
 import React from 'react';
 import TTestTable, { dataPropType } from './TTestTable';
 import './PairedTTest.css';
@@ -56,28 +55,4 @@ class PairedTTest extends React.PureComponent {
 PairedTTest.propTypes = propTypes;
 PairedTTest.defaultProps = defaultProps;
 
-function adaptor(slice, payload) {
-  const { formData, selector } = slice;
-  const element = document.querySelector(selector);
-  const {
-    groupby: groups,
-    metrics,
-    liftvalue_precision: liftValPrec,
-    pvalue_precision: pValPrec,
-    significance_level: alpha,
-  } = formData;
-
-  ReactDOM.render(
-    <PairedTTest
-      metrics={metrics}
-      groups={groups}
-      data={payload.data}
-      alpha={alpha}
-      pValPrec={parseInt(pValPrec, 10)}
-      liftValPrec={parseInt(liftValPrec, 10)}
-    />,
-    element,
-  );
-}
-
-export default adaptor;
+export default PairedTTest;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/PairedTTest/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/PairedTTest/adaptor.jsx
index 30d0400..c71b907 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/PairedTTest/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './PairedTTest';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/PairedTTest/transformProps.js b/superset/assets/src/visualizations/PairedTTest/transformProps.js
new file mode 100644
index 0000000..84abe5b
--- /dev/null
+++ b/superset/assets/src/visualizations/PairedTTest/transformProps.js
@@ -0,0 +1,19 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    groupby,
+    liftvaluePrecision,
+    metrics,
+    pvaluePrecision,
+    significanceLevel,
+  } = formData;
+
+  return {
+    data: payload.data,
+    alpha: significanceLevel,
+    groups: groupby,
+    liftValPrec: parseInt(liftvaluePrecision, 10),
+    metrics,
+    pValPrec: parseInt(pvaluePrecision, 10),
+  };
+}
diff --git a/superset/assets/src/visualizations/parallel_coordinates.css b/superset/assets/src/visualizations/ParallelCoordinates/ParallelCoordinates.css
similarity index 100%
rename from superset/assets/src/visualizations/parallel_coordinates.css
rename to superset/assets/src/visualizations/ParallelCoordinates/ParallelCoordinates.css
diff --git a/superset/assets/src/visualizations/parallel_coordinates.js b/superset/assets/src/visualizations/ParallelCoordinates/ParallelCoordinates.js
similarity index 68%
rename from superset/assets/src/visualizations/parallel_coordinates.js
rename to superset/assets/src/visualizations/ParallelCoordinates/ParallelCoordinates.js
index 7d454e1..d52d08d 100644
--- a/superset/assets/src/visualizations/parallel_coordinates.js
+++ b/superset/assets/src/visualizations/ParallelCoordinates/ParallelCoordinates.js
@@ -1,10 +1,10 @@
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import { colorScalerFactory } from '../modules/colors';
-import parcoords from '../../vendor/parallel_coordinates/d3.parcoords';
-import divgrid from '../../vendor/parallel_coordinates/divgrid';
-import '../../vendor/parallel_coordinates/d3.parcoords.css';
-import './parallel_coordinates.css';
+import { colorScalerFactory } from '../../modules/colors';
+import parcoords from '../../../vendor/parallel_coordinates/d3.parcoords';
+import divgrid from '../../../vendor/parallel_coordinates/divgrid';
+import '../../../vendor/parallel_coordinates/d3.parcoords.css';
+import './ParallelCoordinates.css';
 
 const propTypes = {
   // Standard tabular data [{ fieldName1: value1, fieldName2: value2 }]
@@ -20,8 +20,6 @@ const propTypes = {
 };
 
 function ParallelCoordinates(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'ParallelCoordinates');
-
   const {
     data,
     width,
@@ -98,33 +96,7 @@ function ParallelCoordinates(element, props) {
   }
 }
 
+ParallelCoordinates.displayName = 'ParallelCoordinates';
 ParallelCoordinates.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const {
-    include_series: includeSeries,
-    linear_color_scheme: linearColorScheme,
-    metrics,
-    secondary_metric: secondaryMetric,
-    series,
-    show_datatable: showDatatable,
-  } = formData;
-  const element = document.querySelector(selector);
-
-  return ParallelCoordinates(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    includeSeries,
-    linearColorScheme,
-    metrics: metrics.map(m => m.label || m),
-    colorMetric: secondaryMetric && secondaryMetric.label
-      ? secondaryMetric.label
-      : secondaryMetric,
-    series,
-    showDatatable,
-  });
-}
-
-export default adaptor;
+export default ParallelCoordinates;
diff --git a/superset/assets/src/visualizations/ParallelCoordinates/ReactParallelCoordinates.js b/superset/assets/src/visualizations/ParallelCoordinates/ReactParallelCoordinates.js
new file mode 100644
index 0000000..b0236ca
--- /dev/null
+++ b/superset/assets/src/visualizations/ParallelCoordinates/ReactParallelCoordinates.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './ParallelCoordinates';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/ParallelCoordinates/adaptor.jsx b/superset/assets/src/visualizations/ParallelCoordinates/adaptor.jsx
new file mode 100644
index 0000000..d4c9be9
--- /dev/null
+++ b/superset/assets/src/visualizations/ParallelCoordinates/adaptor.jsx
@@ -0,0 +1,5 @@
+import createAdaptor from '../../utils/createAdaptor';
+import Component from './ReactParallelCoordinates';
+import transformProps from './transformProps';
+
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/ParallelCoordinates/transformProps.js b/superset/assets/src/visualizations/ParallelCoordinates/transformProps.js
new file mode 100644
index 0000000..6f5c7c4
--- /dev/null
+++ b/superset/assets/src/visualizations/ParallelCoordinates/transformProps.js
@@ -0,0 +1,23 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    includeSeries,
+    linearColorScheme,
+    metrics,
+    secondaryMetric,
+    series,
+    showDatatable,
+  } = formData;
+
+  return {
+    data: payload.data,
+    includeSeries,
+    linearColorScheme,
+    metrics: metrics.map(m => m.label || m),
+    colorMetric: secondaryMetric && secondaryMetric.label
+      ? secondaryMetric.label
+      : secondaryMetric,
+    series,
+    showDatatable,
+  };
+}
diff --git a/superset/assets/src/visualizations/partition.css b/superset/assets/src/visualizations/Partition/Partition.css
similarity index 100%
rename from superset/assets/src/visualizations/partition.css
rename to superset/assets/src/visualizations/Partition/Partition.css
diff --git a/superset/assets/src/visualizations/partition.js b/superset/assets/src/visualizations/Partition/Partition.js
similarity index 88%
rename from superset/assets/src/visualizations/partition.js
rename to superset/assets/src/visualizations/Partition/Partition.js
index e70a1ee..9093d55 100644
--- a/superset/assets/src/visualizations/partition.js
+++ b/superset/assets/src/visualizations/Partition/Partition.js
@@ -2,9 +2,9 @@
 import d3 from 'd3';
 import PropTypes from 'prop-types';
 import { hierarchy } from 'd3-hierarchy';
-import { getScale } from '../modules/CategoricalColorNamespace';
-import { d3TimeFormatPreset } from '../modules/utils';
-import './partition.css';
+import { getScale } from '../../modules/CategoricalColorNamespace';
+import { d3TimeFormatPreset } from '../../modules/utils';
+import './Partition.css';
 
 // Compute dx, dy, x, y for each node and
 // return an array of nodes in breadth-first order
@@ -55,8 +55,7 @@ const propTypes = {
   colorScheme: PropTypes.string,
   dateTimeFormat: PropTypes.string,
   equalDateSize: PropTypes.bool,
-  groupBy: PropTypes.arrayOf(PropTypes.string),
-  useLogScale: PropTypes.bool,
+  levels: PropTypes.arrayOf(PropTypes.string),
   metrics: PropTypes.arrayOf(PropTypes.oneOfType([
     PropTypes.string,
     PropTypes.object,
@@ -64,15 +63,14 @@ const propTypes = {
   numberFormat: PropTypes.string,
   partitionLimit: PropTypes.number,
   partitionThreshold: PropTypes.number,
-  useRichTooltip: PropTypes.bool,
   timeSeriesOption: PropTypes.string,
+  useLogScale: PropTypes.bool,
+  useRichTooltip: PropTypes.bool,
 };
 
 // This vis is based on
 // http://mbostock.github.io/d3/talk/20111018/partition.html
 function Icicle(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Icicle');
-
   const {
     width,
     height,
@@ -80,7 +78,7 @@ function Icicle(element, props) {
     colorScheme,
     dateTimeFormat,
     equalDateSize,
-    groupBy,
+    levels,
     useLogScale = false,
     metrics = [],
     numberFormat,
@@ -220,7 +218,7 @@ function Icicle(element, props) {
       if (hasTime && depth === 1) {
         return 'Date';
       }
-      return groupBy[depth - (hasTime ? 2 : 1)];
+      return levels[depth - (hasTime ? 2 : 1)];
     }
 
     function getAncestors(d) {
@@ -374,42 +372,7 @@ function Icicle(element, props) {
   }
 }
 
+Icicle.displayName = 'Icicle';
 Icicle.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData, datasource } = slice;
-  const {
-    color_scheme: colorScheme,
-    date_time_format: dateTimeFormat,
-    equal_date_size: equalDateSize,
-    groupby: groupBy,
-    log_scale: useLogScale,
-    metrics,
-    number_format: numberFormat,
-    partition_limit: partitionLimit,
-    partition_threshold: partitionThreshold,
-    rich_tooltip: useRichTooltip,
-    time_series_option: timeSeriesOption,
-  } = formData;
-  const { verbose_map: verboseMap } = datasource;
-  const element = document.querySelector(selector);
-
-  return Icicle(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    colorScheme,
-    dateTimeFormat,
-    equalDateSize,
-    groupBy: groupBy.map(g => verboseMap[g] || g),
-    useLogScale,
-    metrics,
-    numberFormat,
-    partitionLimit: partitionLimit && parseInt(partitionLimit, 10),
-    partitionThreshold: partitionThreshold && parseInt(partitionThreshold, 10),
-    useRichTooltip,
-    timeSeriesOption,
-  });
-}
-
-export default adaptor;
+export default Icicle;
diff --git a/superset/assets/src/visualizations/Partition/ReactPartition.js b/superset/assets/src/visualizations/Partition/ReactPartition.js
new file mode 100644
index 0000000..42785b4
--- /dev/null
+++ b/superset/assets/src/visualizations/Partition/ReactPartition.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Partition';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Partition/adaptor.jsx
similarity index 50%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Partition/adaptor.jsx
index 30d0400..9c279a2 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Partition/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactPartition';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Partition/transformProps.js b/superset/assets/src/visualizations/Partition/transformProps.js
new file mode 100644
index 0000000..04ed059
--- /dev/null
+++ b/superset/assets/src/visualizations/Partition/transformProps.js
@@ -0,0 +1,32 @@
+export default function transformProps(basicChartInput) {
+  const { datasource, formData, payload } = basicChartInput;
+  const {
+    colorScheme,
+    dateTimeFormat,
+    equalDateSize,
+    groupby,
+    logScale,
+    metrics,
+    numberFormat,
+    partitionLimit,
+    partitionThreshold,
+    richTooltip,
+    timeSeriesOption,
+  } = formData;
+  const { verboseMap } = datasource;
+
+  return {
+    data: payload.data,
+    colorScheme,
+    dateTimeFormat,
+    equalDateSize,
+    levels: groupby.map(g => verboseMap[g] || g),
+    metrics,
+    numberFormat,
+    partitionLimit: partitionLimit && parseInt(partitionLimit, 10),
+    partitionThreshold: partitionThreshold && parseInt(partitionThreshold, 10),
+    timeSeriesOption,
+    useLogScale: logScale,
+    useRichTooltip: richTooltip,
+  };
+}
diff --git a/superset/assets/src/visualizations/pivot_table.css b/superset/assets/src/visualizations/PivotTable/PivotTable.css
similarity index 100%
rename from superset/assets/src/visualizations/pivot_table.css
rename to superset/assets/src/visualizations/PivotTable/PivotTable.css
diff --git a/superset/assets/src/visualizations/pivot_table.js b/superset/assets/src/visualizations/PivotTable/PivotTable.js
similarity index 77%
rename from superset/assets/src/visualizations/pivot_table.js
rename to superset/assets/src/visualizations/PivotTable/PivotTable.js
index 2d8c6fb..71d0cfa 100644
--- a/superset/assets/src/visualizations/pivot_table.js
+++ b/superset/assets/src/visualizations/PivotTable/PivotTable.js
@@ -2,8 +2,8 @@ import dt from 'datatables.net-bs';
 import 'datatables.net-bs/css/dataTables.bootstrap.css';
 import $ from 'jquery';
 import PropTypes from 'prop-types';
-import { d3format, fixDataTableBodyHeight } from '../modules/utils';
-import './pivot_table.css';
+import { d3format, fixDataTableBodyHeight } from '../../modules/utils';
+import './PivotTable.css';
 
 dt(window, $);
 
@@ -18,20 +18,18 @@ const propTypes = {
   }),
   height: PropTypes.number,
   columnFormats: PropTypes.objectOf(PropTypes.string),
-  groupBy: PropTypes.arrayOf(PropTypes.string),
   numberFormat: PropTypes.string,
+  numGroups: PropTypes.number,
   verboseMap: PropTypes.objectOf(PropTypes.string),
 };
 
 function PivotTable(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'PivotTable');
-
   const {
     data,
     height,
     columnFormats,
-    groupBy,
     numberFormat,
+    numGroups,
     verboseMap,
   } = props;
 
@@ -67,7 +65,7 @@ function PivotTable(element, props) {
     });
   });
 
-  if (groupBy.length === 1) {
+  if (numGroups === 1) {
     // When there is only 1 group by column,
     // we use the DataTable plugin to make the header fixed.
     // The plugin takes care of the scrolling so we don't need
@@ -92,27 +90,7 @@ function PivotTable(element, props) {
   }
 }
 
-function adaptor(slice, payload) {
-  const { selector, formData, datasource } = slice;
-  const {
-    groupby: groupBy,
-    number_format: numberFormat,
-  } = formData;
-  const {
-    column_formats: columnFormats,
-    verbose_map: verboseMap,
-  } = datasource;
-  const element = document.querySelector(selector);
-
-  return PivotTable(element, {
-    data: payload.data,
-    height: slice.height(),
-    columnFormats,
-    groupBy,
-    numberFormat,
-    verboseMap,
-  });
-}
-
-export default adaptor;
+PivotTable.displayName = 'PivotTable';
+PivotTable.propTypes = propTypes;
 
+export default PivotTable;
diff --git a/superset/assets/src/visualizations/PivotTable/ReactPivotTable.js b/superset/assets/src/visualizations/PivotTable/ReactPivotTable.js
new file mode 100644
index 0000000..9afda85
--- /dev/null
+++ b/superset/assets/src/visualizations/PivotTable/ReactPivotTable.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './PivotTable';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/PivotTable/adaptor.jsx
similarity index 50%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/PivotTable/adaptor.jsx
index 30d0400..4a23926 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/PivotTable/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactPivotTable';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/PivotTable/transformProps.js b/superset/assets/src/visualizations/PivotTable/transformProps.js
new file mode 100644
index 0000000..880cd28
--- /dev/null
+++ b/superset/assets/src/visualizations/PivotTable/transformProps.js
@@ -0,0 +1,19 @@
+export default function transformProps(basicChartInput) {
+  const { datasource, formData, payload } = basicChartInput;
+  const {
+    groupby,
+    numberFormat,
+  } = formData;
+  const {
+    columnFormats,
+    verboseMap,
+  } = datasource;
+
+  return {
+    data: payload.data,
+    columnFormats,
+    numGroups: groupby.length,
+    numberFormat,
+    verboseMap,
+  };
+}
diff --git a/superset/assets/src/visualizations/Rose/ReactRose.js b/superset/assets/src/visualizations/Rose/ReactRose.js
new file mode 100644
index 0000000..d5cad081
--- /dev/null
+++ b/superset/assets/src/visualizations/Rose/ReactRose.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Rose';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/rose.css b/superset/assets/src/visualizations/Rose/Rose.css
similarity index 100%
rename from superset/assets/src/visualizations/rose.css
rename to superset/assets/src/visualizations/Rose/Rose.css
diff --git a/superset/assets/src/visualizations/rose.js b/superset/assets/src/visualizations/Rose/Rose.js
similarity index 95%
rename from superset/assets/src/visualizations/rose.js
rename to superset/assets/src/visualizations/Rose/Rose.js
index 62df302..3c76d8f 100644
--- a/superset/assets/src/visualizations/rose.js
+++ b/superset/assets/src/visualizations/Rose/Rose.js
@@ -2,9 +2,9 @@
 import d3 from 'd3';
 import PropTypes from 'prop-types';
 import nv from 'nvd3';
-import { getScale } from '../modules/CategoricalColorNamespace';
-import { d3TimeFormatPreset } from '../modules/utils';
-import './rose.css';
+import { getScale } from '../../modules/CategoricalColorNamespace';
+import { d3TimeFormatPreset } from '../../modules/utils';
+import './Rose.css';
 
 const propTypes = {
   // Data is an object hashed by numeric value, perhaps timestamp
@@ -39,8 +39,6 @@ function sortValues(a, b) {
 }
 
 function Rose(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Rose');
-
   const {
     data,
     width,
@@ -563,29 +561,7 @@ function Rose(element, props) {
   });
 }
 
+Rose.displayName = 'Rose';
 Rose.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const {
-    color_scheme: colorScheme,
-    date_time_format: dateTimeFormat,
-    number_format: numberFormat,
-    rich_tooltip: useRichTooltip,
-    rose_area_proportion: useAreaProportions,
-  } = formData;
-  const element = document.querySelector(selector);
-
-  return Rose(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    colorScheme,
-    dateTimeFormat,
-    numberFormat,
-    useRichTooltip,
-    useAreaProportions,
-  });
-}
-
-export default adaptor;
+export default Rose;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Rose/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Rose/adaptor.jsx
index 30d0400..8819150 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Rose/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactRose';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Rose/transformProps.js b/superset/assets/src/visualizations/Rose/transformProps.js
new file mode 100644
index 0000000..afc09ea
--- /dev/null
+++ b/superset/assets/src/visualizations/Rose/transformProps.js
@@ -0,0 +1,19 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    colorScheme,
+    dateTimeFormat,
+    numberFormat,
+    richTooltip,
+    roseAreaProportion,
+  } = formData;
+
+  return {
+    data: payload.data,
+    colorScheme,
+    dateTimeFormat,
+    numberFormat,
+    useAreaProportions: roseAreaProportion,
+    useRichTooltip: richTooltip,
+  };
+}
diff --git a/superset/assets/src/visualizations/Sankey/ReactSankey.js b/superset/assets/src/visualizations/Sankey/ReactSankey.js
new file mode 100644
index 0000000..444f720
--- /dev/null
+++ b/superset/assets/src/visualizations/Sankey/ReactSankey.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Sankey';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/sankey.css b/superset/assets/src/visualizations/Sankey/Sankey.css
similarity index 100%
rename from superset/assets/src/visualizations/sankey.css
rename to superset/assets/src/visualizations/Sankey/Sankey.css
diff --git a/superset/assets/src/visualizations/sankey.js b/superset/assets/src/visualizations/Sankey/Sankey.js
similarity index 90%
rename from superset/assets/src/visualizations/sankey.js
rename to superset/assets/src/visualizations/Sankey/Sankey.js
index 2509a50..0efca77 100644
--- a/superset/assets/src/visualizations/sankey.js
+++ b/superset/assets/src/visualizations/Sankey/Sankey.js
@@ -2,8 +2,8 @@
 import d3 from 'd3';
 import PropTypes from 'prop-types';
 import { sankey as d3Sankey } from 'd3-sankey';
-import { getScale } from '../modules/CategoricalColorNamespace';
-import './sankey.css';
+import { getScale } from '../../modules/CategoricalColorNamespace';
+import './Sankey.css';
 
 const propTypes = {
   data: PropTypes.arrayOf(PropTypes.shape({
@@ -19,8 +19,6 @@ const propTypes = {
 const formatNumber = d3.format(',.2f');
 
 function Sankey(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Sankey');
-
   const {
     data,
     width,
@@ -174,19 +172,8 @@ function Sankey(element, props) {
     .attr('text-anchor', 'start');
 }
 
+Sankey.displayName = 'Sankey';
 Sankey.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const { color_scheme: colorScheme } = formData;
-  const element = document.querySelector(selector);
-
-  return Sankey(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    colorScheme,
-  });
-}
+export default Sankey;
 
-export default adaptor;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Sankey/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Sankey/adaptor.jsx
index 30d0400..c1f36be 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Sankey/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactSankey';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Sankey/transformProps.js b/superset/assets/src/visualizations/Sankey/transformProps.js
new file mode 100644
index 0000000..4d354b2
--- /dev/null
+++ b/superset/assets/src/visualizations/Sankey/transformProps.js
@@ -0,0 +1,9 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const { colorScheme } = formData;
+
+  return {
+    data: payload.data,
+    colorScheme,
+  };
+}
diff --git a/superset/assets/src/visualizations/Sunburst/ReactSunburst.js b/superset/assets/src/visualizations/Sunburst/ReactSunburst.js
new file mode 100644
index 0000000..960b677
--- /dev/null
+++ b/superset/assets/src/visualizations/Sunburst/ReactSunburst.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Sunburst';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/sunburst.css b/superset/assets/src/visualizations/Sunburst/Sunburst.css
similarity index 100%
rename from superset/assets/src/visualizations/sunburst.css
rename to superset/assets/src/visualizations/Sunburst/Sunburst.css
diff --git a/superset/assets/src/visualizations/sunburst.js b/superset/assets/src/visualizations/Sunburst/Sunburst.js
similarity index 95%
rename from superset/assets/src/visualizations/sunburst.js
rename to superset/assets/src/visualizations/Sunburst/Sunburst.js
index 7a87173..bd9b56a 100644
--- a/superset/assets/src/visualizations/sunburst.js
+++ b/superset/assets/src/visualizations/Sunburst/Sunburst.js
@@ -1,9 +1,9 @@
 /* eslint-disable no-param-reassign */
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import { getScale } from '../modules/CategoricalColorNamespace';
-import { wrapSvgText } from '../modules/utils';
-import './sunburst.css';
+import { getScale } from '../../modules/CategoricalColorNamespace';
+import { wrapSvgText } from '../../modules/utils';
+import './Sunburst.css';
 
 const propTypes = {
   // Each row is an array of [hierarchy-lvl1, hierarchy-lvl2, metric1, metric2]
@@ -38,8 +38,6 @@ function getAncestors(node) {
 
 // Modified from http://bl.ocks.org/kerryrodden/7090426
 function Sunburst(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Sunburst');
-
   const container = d3.select(element);
   const {
     data,
@@ -391,20 +389,7 @@ function Sunburst(element, props) {
   createVisualization(data);
 }
 
+Sunburst.displayName = 'Sunburst';
 Sunburst.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const { color_scheme: colorScheme, metric, secondary_metric: secondaryMetric } = formData;
-  const element = document.querySelector(selector);
-
-  return Sunburst(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    colorScheme,
-    metrics: [metric, secondaryMetric],
-  });
-}
-
-export default adaptor;
+export default Sunburst;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Sunburst/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Sunburst/adaptor.jsx
index 30d0400..9c3ffaf 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Sunburst/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactSunburst';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Sunburst/transformProps.js b/superset/assets/src/visualizations/Sunburst/transformProps.js
new file mode 100644
index 0000000..465bd7c
--- /dev/null
+++ b/superset/assets/src/visualizations/Sunburst/transformProps.js
@@ -0,0 +1,10 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const { colorScheme, metric, secondaryMetric } = formData;
+
+  return {
+    data: payload.data,
+    colorScheme,
+    metrics: [metric, secondaryMetric],
+  };
+}
diff --git a/superset/assets/src/visualizations/Table/ReactTable.js b/superset/assets/src/visualizations/Table/ReactTable.js
new file mode 100644
index 0000000..459a48a
--- /dev/null
+++ b/superset/assets/src/visualizations/Table/ReactTable.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Table';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/table.css b/superset/assets/src/visualizations/Table/Table.css
similarity index 100%
rename from superset/assets/src/visualizations/table.css
rename to superset/assets/src/visualizations/Table/Table.css
diff --git a/superset/assets/src/visualizations/table.js b/superset/assets/src/visualizations/Table/Table.js
similarity index 80%
rename from superset/assets/src/visualizations/table.js
rename to superset/assets/src/visualizations/Table/Table.js
index 2b20a7d..070a96c 100644
--- a/superset/assets/src/visualizations/table.js
+++ b/superset/assets/src/visualizations/Table/Table.js
@@ -4,8 +4,8 @@ import PropTypes from 'prop-types';
 import dt from 'datatables.net-bs';
 import 'datatables.net-bs/css/dataTables.bootstrap.css';
 import dompurify from 'dompurify';
-import { fixDataTableBodyHeight, d3TimeFormatPreset } from '../modules/utils';
-import './table.css';
+import { fixDataTableBodyHeight, d3TimeFormatPreset } from '../../modules/utils';
+import './Table.css';
 
 dt(window, $);
 
@@ -50,8 +50,6 @@ const formatPercent = d3.format('.3p');
 function NOOP() {}
 
 function TableVis(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'TableVis');
-
   const {
     data,
     height,
@@ -235,66 +233,7 @@ function TableVis(element, props) {
   datatable.draw();
 }
 
+TableVis.displayName = 'TableVis';
 TableVis.propTypes = propTypes;
 
-function adaptor(slice, payload) {
-  const { selector, formData, datasource } = slice;
-  const {
-    align_pn: alignPositiveNegative,
-    color_pn: colorPositiveNegative,
-    include_search: includeSearch,
-    metrics,
-    order_desc: orderDesc,
-    page_length: pageLength,
-    percent_metrics: percentMetrics,
-    table_filter: tableFilter,
-    table_timestamp_format: tableTimestampFormat,
-    timeseries_limit_metric: timeseriesLimitMetric,
-  } = formData;
-  const {
-    verbose_map: verboseMap,
-    column_formats: columnFormats,
-  } = datasource;
-
-  const { records, columns } = payload.data;
-
-  const processedColumns = columns.map((key) => {
-    let label = verboseMap[key];
-    // Handle verbose names for percents
-    if (!label) {
-      if (key[0] === '%') {
-        const cleanedKey = key.substring(1);
-        label = '% ' + (verboseMap[cleanedKey] || cleanedKey);
-      } else {
-        label = key;
-      }
-    }
-    return {
-      key,
-      label,
-      format: columnFormats && columnFormats[key],
-    };
-  });
-
-  const element = document.querySelector(selector);
-
-  return TableVis(element, {
-    data: records,
-    height: slice.height(),
-    alignPositiveNegative,
-    colorPositiveNegative,
-    columns: processedColumns,
-    filters: slice.getFilters(),
-    includeSearch,
-    metrics,
-    onAddFilter(...args) { slice.addFilter(...args); },
-    orderDesc,
-    pageLength: pageLength && parseInt(pageLength, 10),
-    percentMetrics,
-    tableFilter,
-    tableTimestampFormat,
-    timeseriesLimitMetric,
-  });
-}
-
-export default adaptor;
+export default TableVis;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Table/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Table/adaptor.jsx
index 30d0400..7eaf61e 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Table/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactTable';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Table/transformProps.js b/superset/assets/src/visualizations/Table/transformProps.js
new file mode 100644
index 0000000..381931b
--- /dev/null
+++ b/superset/assets/src/visualizations/Table/transformProps.js
@@ -0,0 +1,58 @@
+export default function transformProps(basicChartInput) {
+  const {
+    datasource,
+    filters,
+    formData,
+    onAddFilter,
+    payload,
+  } = basicChartInput;
+  const {
+    alignPn,
+    colorPn,
+    includeSearch,
+    metrics,
+    orderDesc,
+    pageLength,
+    percentMetrics,
+    tableFilter,
+    tableTimestampFormat,
+    timeseriesLimitMetric,
+  } = formData;
+  const { columnFormats, verboseMap } = datasource;
+  const { records, columns } = payload.data;
+
+  const processedColumns = columns.map((key) => {
+    let label = verboseMap[key];
+    // Handle verbose names for percents
+    if (!label) {
+      if (key[0] === '%') {
+        const cleanedKey = key.substring(1);
+        label = '% ' + (verboseMap[cleanedKey] || cleanedKey);
+      } else {
+        label = key;
+      }
+    }
+    return {
+      key,
+      label,
+      format: columnFormats && columnFormats[key],
+    };
+  });
+
+  return {
+    data: records,
+    alignPositiveNegative: alignPn,
+    colorPositiveNegative: colorPn,
+    columns: processedColumns,
+    filters,
+    includeSearch,
+    metrics,
+    onAddFilter,
+    orderDesc,
+    pageLength: pageLength && parseInt(pageLength, 10),
+    percentMetrics,
+    tableFilter,
+    tableTimestampFormat,
+    timeseriesLimitMetric,
+  };
+}
diff --git a/superset/assets/src/visualizations/Treemap/ReactTreemap.js b/superset/assets/src/visualizations/Treemap/ReactTreemap.js
new file mode 100644
index 0000000..68c9802
--- /dev/null
+++ b/superset/assets/src/visualizations/Treemap/ReactTreemap.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './Treemap';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/treemap.css b/superset/assets/src/visualizations/Treemap/Treemap.css
similarity index 100%
rename from superset/assets/src/visualizations/treemap.css
rename to superset/assets/src/visualizations/Treemap/Treemap.css
diff --git a/superset/assets/src/visualizations/treemap.js b/superset/assets/src/visualizations/Treemap/Treemap.js
similarity index 92%
rename from superset/assets/src/visualizations/treemap.js
rename to superset/assets/src/visualizations/Treemap/Treemap.js
index 8ffd140..3596357 100644
--- a/superset/assets/src/visualizations/treemap.js
+++ b/superset/assets/src/visualizations/Treemap/Treemap.js
@@ -1,8 +1,8 @@
 /* eslint-disable no-shadow, no-param-reassign */
 import d3 from 'd3';
 import PropTypes from 'prop-types';
-import { getScale } from '../modules/CategoricalColorNamespace';
-import './treemap.css';
+import { getScale } from '../../modules/CategoricalColorNamespace';
+import './Treemap.css';
 
 // Declare PropTypes for recursive data structures
 // https://github.com/facebook/react/issues/5676
@@ -49,9 +49,7 @@ const DEFAULT_MARGIN = {
 };
 
 /* Modified from http://bl.ocks.org/ganeshv/6a8e9ada3ab7f2d88022 */
-function treemap(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'Treemap');
-
+function Treemap(element, props) {
   const {
     data,
     width,
@@ -299,25 +297,7 @@ function treemap(element, props) {
   data.forEach(d => draw(d, width, eachHeight));
 }
 
-treemap.propTypes = propTypes;
-
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-  const {
-    number_format: numberFormat,
-    color_scheme: colorScheme,
-    treemap_ratio: treemapRatio,
-  } = formData;
-  const element = document.querySelector(selector);
-
-  return treemap(element, {
-    data: payload.data,
-    width: slice.width(),
-    height: slice.height(),
-    numberFormat,
-    colorScheme,
-    treemapRatio,
-  });
-}
+Treemap.displayName = 'Treemap';
+Treemap.propTypes = propTypes;
 
-export default adaptor;
+export default Treemap;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/Treemap/adaptor.jsx
similarity index 51%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/Treemap/adaptor.jsx
index 30d0400..b763c7f 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/Treemap/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactTreemap';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/Treemap/transformProps.js b/superset/assets/src/visualizations/Treemap/transformProps.js
new file mode 100644
index 0000000..c0c3c03
--- /dev/null
+++ b/superset/assets/src/visualizations/Treemap/transformProps.js
@@ -0,0 +1,15 @@
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    colorScheme,
+    numberFormat,
+    treemapRatio,
+  } = formData;
+
+  return {
+    data: payload.data,
+    colorScheme,
+    numberFormat,
+    treemapRatio,
+  };
+}
diff --git a/superset/assets/src/visualizations/WorldMap/WorldMap.js b/superset/assets/src/visualizations/WorldMap/WorldMap.js
index d83d794..924c514 100644
--- a/superset/assets/src/visualizations/WorldMap/WorldMap.js
+++ b/superset/assets/src/visualizations/WorldMap/WorldMap.js
@@ -20,8 +20,6 @@ const propTypes = {
 const formatter = d3.format('.3s');
 
 function WorldMap(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'WorldMap');
-
   const {
     data,
     height,
@@ -107,6 +105,7 @@ function WorldMap(element, props) {
   }
 }
 
+WorldMap.displayName = 'WorldMap';
 WorldMap.propTypes = propTypes;
 
 export default WorldMap;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/WorldMap/adaptor.jsx
index 30d0400..faa1a2c 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/WorldMap/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactWorldMap';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/index.js b/superset/assets/src/visualizations/index.js
index e924dd4..6507b97 100644
--- a/superset/assets/src/visualizations/index.js
+++ b/superset/assets/src/visualizations/index.js
@@ -72,18 +72,18 @@ const vizMap = {
   [VIZ_TYPES.bubble]: loadNvd3,
   [VIZ_TYPES.bullet]: loadNvd3,
   [VIZ_TYPES.cal_heatmap]: () =>
-    loadVis(import(/* webpackChunkName: "cal_heatmap" */ './cal_heatmap.js')),
+    loadVis(import(/* webpackChunkName: "cal_heatmap" */ './Calendar/adaptor.jsx')),
   [VIZ_TYPES.compare]: loadNvd3,
   [VIZ_TYPES.directed_force]: () =>
-    loadVis(import(/* webpackChunkName: "directed_force" */ './directed_force.js')),
-  [VIZ_TYPES.chord]: () => loadVis(import(/* webpackChunkName: "chord" */ './chord.jsx')),
+    loadVis(import(/* webpackChunkName: "directed_force" */ './ForceDirected/adaptor.jsx')),
+  [VIZ_TYPES.chord]: () => loadVis(import(/* webpackChunkName: "chord" */ './Chord/adaptor.jsx')),
   [VIZ_TYPES.dist_bar]: loadNvd3,
   [VIZ_TYPES.filter_box]: () =>
-    loadVis(import(/* webpackChunkName: "filter_box" */ './filter_box.jsx')),
-  [VIZ_TYPES.heatmap]: () => loadVis(import(/* webpackChunkName: "heatmap" */ './heatmap.js')),
+    loadVis(import(/* webpackChunkName: "filter_box" */ './FilterBox/adaptor.jsx')),
+  [VIZ_TYPES.heatmap]: () => loadVis(import(/* webpackChunkName: "heatmap" */ './Heatmap/adaptor.jsx')),
   [VIZ_TYPES.histogram]: () =>
-    loadVis(import(/* webpackChunkName: "histogram" */ './Histogram.jsx')),
-  [VIZ_TYPES.horizon]: () => loadVis(import(/* webpackChunkName: "horizon" */ './HorizonChart.jsx')),
+    loadVis(import(/* webpackChunkName: "histogram" */ './Histogram/adaptor.jsx')),
+  [VIZ_TYPES.horizon]: () => loadVis(import(/* webpackChunkName: "horizon" */ './Horizon/adaptor.jsx')),
   [VIZ_TYPES.iframe]: () => loadVis(import(/* webpackChunkName: "iframe" */ './iframe.js')),
   [VIZ_TYPES.line]: loadNvd3,
   [VIZ_TYPES.line_multi]: () =>
@@ -92,30 +92,30 @@ const vizMap = {
   [VIZ_TYPES.mapbox]: () => loadVis(import(/* webpackChunkName: "mapbox" */ './MapBox/MapBox.jsx')),
   [VIZ_TYPES.markup]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')),
   [VIZ_TYPES.para]: () =>
-    loadVis(import(/* webpackChunkName: "parallel_coordinates" */ './parallel_coordinates.js')),
+    loadVis(import(/* webpackChunkName: "parallel_coordinates" */ './ParallelCoordinates/adaptor.jsx')),
   [VIZ_TYPES.pie]: loadNvd3,
   [VIZ_TYPES.pivot_table]: () =>
-    loadVis(import(/* webpackChunkName: "pivot_table" */ './pivot_table.js')),
-  [VIZ_TYPES.sankey]: () => loadVis(import(/* webpackChunkName: "sankey" */ './sankey.js')),
+    loadVis(import(/* webpackChunkName: "pivot_table" */ './PivotTable/adaptor.jsx')),
+  [VIZ_TYPES.sankey]: () => loadVis(import(/* webpackChunkName: "sankey" */ './Sankey/adaptor.jsx')),
   [VIZ_TYPES.separator]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')),
-  [VIZ_TYPES.sunburst]: () => loadVis(import(/* webpackChunkName: "sunburst" */ './sunburst.js')),
-  [VIZ_TYPES.table]: () => loadVis(import(/* webpackChunkName: "table" */ './table.js')),
+  [VIZ_TYPES.sunburst]: () => loadVis(import(/* webpackChunkName: "sunburst" */ './Sunburst/adaptor.jsx')),
+  [VIZ_TYPES.table]: () => loadVis(import(/* webpackChunkName: "table" */ './Table/adaptor.jsx')),
   [VIZ_TYPES.time_table]: () =>
     loadVis(import(/* webpackChunkName: "time_table" */ './TimeTable/TimeTable.jsx')),
-  [VIZ_TYPES.treemap]: () => loadVis(import(/* webpackChunkName: "treemap" */ './treemap.js')),
+  [VIZ_TYPES.treemap]: () => loadVis(import(/* webpackChunkName: "treemap" */ './Treemap/adaptor.jsx')),
   [VIZ_TYPES.country_map]: () =>
-    loadVis(import(/* webpackChunkName: "country_map" */ './country_map.js')),
+    loadVis(import(/* webpackChunkName: "country_map" */ './CountryMap/adaptor.jsx')),
   [VIZ_TYPES.word_cloud]: () =>
-    loadVis(import(/* webpackChunkName: "word_cloud" */ './wordcloud/WordCloud.js')),
+    loadVis(import(/* webpackChunkName: "word_cloud" */ './wordcloud/adaptor.jsx')),
   [VIZ_TYPES.world_map]: () =>
     loadVis(import(/* webpackChunkName: "world_map" */ './WorldMap/adaptor.jsx')),
   [VIZ_TYPES.dual_line]: loadNvd3,
   [VIZ_TYPES.event_flow]: () =>
     loadVis(import(/* webpackChunkName: "EventFlow" */ './EventFlow.jsx')),
   [VIZ_TYPES.paired_ttest]: () =>
-    loadVis(import(/* webpackChunkName: "paired_ttest" */ './PairedTTest/PairedTTest.jsx')),
+    loadVis(import(/* webpackChunkName: "paired_ttest" */ './PairedTTest/adaptor.jsx')),
   [VIZ_TYPES.partition]: () =>
-    loadVis(import(/* webpackChunkName: "partition" */ './partition.js')),
+    loadVis(import(/* webpackChunkName: "partition" */ './Partition/adaptor.jsx')),
   [VIZ_TYPES.deck_scatter]: () =>
     loadVis(import(/* webpackChunkName: "deckgl/layers/scatter" */ './deckgl/layers/scatter.jsx')),
   [VIZ_TYPES.deck_screengrid]: () =>
@@ -136,7 +136,7 @@ const vizMap = {
     loadVis(import(/* webpackChunkName: "deckgl/layers/polygon" */ './deckgl/layers/polygon.jsx')),
   [VIZ_TYPES.deck_multi]: () =>
     loadVis(import(/* webpackChunkName: "deckgl/multi" */ './deckgl/multi.jsx')),
-  [VIZ_TYPES.rose]: () => loadVis(import(/* webpackChunkName: "rose" */ './rose.js')),
+  [VIZ_TYPES.rose]: () => loadVis(import(/* webpackChunkName: "rose" */ './Rose/adaptor.jsx')),
 };
 
 export default vizMap;
diff --git a/superset/assets/src/visualizations/models/BasicChartInput.js b/superset/assets/src/visualizations/models/BasicChartInput.js
index de4add5..73aff95 100644
--- a/superset/assets/src/visualizations/models/BasicChartInput.js
+++ b/superset/assets/src/visualizations/models/BasicChartInput.js
@@ -3,7 +3,13 @@ import convertKeysToCamelCase from '../../utils/convertKeysToCamelCase';
 export default class BasicChartInput {
   constructor(slice, payload, setControlValue) {
     this.annotationData = slice.annotationData;
+    this.datasource = convertKeysToCamelCase(slice.datasource);
+    this.rawDatasource = slice.datasource;
+    this.filters = slice.getFilters();
     this.formData = convertKeysToCamelCase(slice.formData);
+    this.onAddFilter = (...args) => {
+      slice.addFilter(...args);
+    };
     this.payload = payload;
     this.setControlValue = setControlValue;
   }
diff --git a/superset/assets/src/visualizations/wordcloud/ReactWordCloud.js b/superset/assets/src/visualizations/wordcloud/ReactWordCloud.js
new file mode 100644
index 0000000..80d0024
--- /dev/null
+++ b/superset/assets/src/visualizations/wordcloud/ReactWordCloud.js
@@ -0,0 +1,4 @@
+import reactify from '../../utils/reactify';
+import Component from './WordCloud';
+
+export default reactify(Component);
diff --git a/superset/assets/src/visualizations/wordcloud/WordCloud.js b/superset/assets/src/visualizations/wordcloud/WordCloud.js
index 8766263..2b91665 100644
--- a/superset/assets/src/visualizations/wordcloud/WordCloud.js
+++ b/superset/assets/src/visualizations/wordcloud/WordCloud.js
@@ -21,9 +21,7 @@ const propTypes = {
   colorScheme: PropTypes.string,
 };
 
-function wordCloud(element, props) {
-  PropTypes.checkPropTypes(propTypes, props, 'prop', 'WordCloud');
-
+function WordCloud(element, props) {
   const {
     data,
     width,
@@ -78,43 +76,7 @@ function wordCloud(element, props) {
   layout.on('end', draw).start();
 }
 
-wordCloud.propTypes = propTypes;
-
-function transform(data, formData) {
-  const {
-    metric,
-    series,
-  } = formData;
-
-  const transformedData = data.map(datum => ({
-    text: datum[series],
-    size: datum[metric.label || metric],
-  }));
-
-  return transformedData;
-}
-
-function adaptor(slice, payload) {
-  const { selector, formData } = slice;
-
-  const {
-    rotation,
-    size_to: sizeTo,
-    size_from: sizeFrom,
-    color_scheme: colorScheme,
-  } = formData;
-  const element = document.querySelector(selector);
-
-  const data = transform(payload.data, formData);
-
-  return wordCloud(element, {
-    data,
-    width: slice.width(),
-    height: slice.height(),
-    rotation,
-    sizeRange: [sizeFrom, sizeTo],
-    colorScheme,
-  });
-}
+WordCloud.displayName = 'WordCloud';
+WordCloud.propTypes = propTypes;
 
-export default adaptor;
+export default WordCloud;
diff --git a/superset/assets/src/visualizations/WorldMap/adaptor.jsx b/superset/assets/src/visualizations/wordcloud/adaptor.jsx
similarity index 50%
copy from superset/assets/src/visualizations/WorldMap/adaptor.jsx
copy to superset/assets/src/visualizations/wordcloud/adaptor.jsx
index 30d0400..cbe00ea 100644
--- a/superset/assets/src/visualizations/WorldMap/adaptor.jsx
+++ b/superset/assets/src/visualizations/wordcloud/adaptor.jsx
@@ -1,5 +1,5 @@
 import createAdaptor from '../../utils/createAdaptor';
-import WorldMap from './ReactWorldMap';
+import Component from './ReactWordCloud';
 import transformProps from './transformProps';
 
-export default createAdaptor(WorldMap, transformProps);
+export default createAdaptor(Component, transformProps);
diff --git a/superset/assets/src/visualizations/wordcloud/transformProps.js b/superset/assets/src/visualizations/wordcloud/transformProps.js
new file mode 100644
index 0000000..7400a3a
--- /dev/null
+++ b/superset/assets/src/visualizations/wordcloud/transformProps.js
@@ -0,0 +1,27 @@
+function transformData(data, formData) {
+  const { metric, series } = formData;
+
+  const transformedData = data.map(datum => ({
+    text: datum[series],
+    size: datum[metric.label || metric],
+  }));
+
+  return transformedData;
+}
+
+export default function transformProps(basicChartInput) {
+  const { formData, payload } = basicChartInput;
+  const {
+    colorScheme,
+    rotation,
+    sizeTo,
+    sizeFrom,
+  } = formData;
+
+  return {
+    data: transformData(payload.data, formData),
+    colorScheme,
+    rotation,
+    sizeRange: [sizeFrom, sizeTo],
+  };
+}


Mime
View raw message