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-5] Remove references to slice from all deck.gl components. (#6039)
Date Fri, 12 Oct 2018 03:56:41 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 2a7b64f  [SIP-5] Remove references to slice from all deck.gl components.  (#6039)
2a7b64f is described below

commit 2a7b64fef5823760fd619f217b3161d2d7d00cc6
Author: Krist Wongsuphasawat <krist.wongz@gmail.com>
AuthorDate: Thu Oct 11 20:56:36 2018 -0700

    [SIP-5] Remove references to slice from all deck.gl components.  (#6039)
    
    * start removing slice.
    
    * migrate arc
    
    * refactor arc
    
    * refactor path
    
    * refactor deck polygon
    
    * remove commented code
    
    * refactor factory function
    
    * refactor grid
    
    * refactor hex
    
    * refactor polygon
    
    * refactor scatter
    
    * refactor geojson
    
    * refactor screengrid
    
    * remove unnecessary nesting
    
    * add proptypes
    
    * add proptypes
    
    * refactor deck.gl Multi
    
    * fix lint
    
    * Use export syntax instead of module.exports
---
 .../deckgl/CategoricalDeckGLContainer.jsx          |  21 ++--
 .../src/visualizations/deckgl/createAdaptor.jsx    |  30 ++++++
 .../assets/src/visualizations/deckgl/factory.jsx   |  87 ++++++++++++++++
 .../src/visualizations/deckgl/layers/arc.jsx       |  46 ++-------
 .../src/visualizations/deckgl/layers/common.jsx    |   9 +-
 .../src/visualizations/deckgl/layers/geojson.jsx   |  64 +++++++-----
 .../src/visualizations/deckgl/layers/grid.jsx      |  44 ++------
 .../src/visualizations/deckgl/layers/hex.jsx       |  43 ++------
 .../src/visualizations/deckgl/layers/index.js      |   1 +
 .../src/visualizations/deckgl/layers/path.jsx      |  43 ++------
 .../src/visualizations/deckgl/layers/polygon.jsx   |  46 ++-------
 .../src/visualizations/deckgl/layers/scatter.jsx   |  46 ++-------
 .../visualizations/deckgl/layers/screengrid.jsx    |  65 +++++-------
 .../assets/src/visualizations/deckgl/multi.jsx     | 114 +++++++++++++--------
 14 files changed, 312 insertions(+), 347 deletions(-)

diff --git a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx
index 0976ec0..61c9eed 100644
--- a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx
+++ b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx
@@ -31,12 +31,14 @@ function getCategories(fd, data) {
 }
 
 const propTypes = {
-  slice: PropTypes.object.isRequired,
+  formData: PropTypes.object.isRequired,
   mapboxApiKey: PropTypes.string.isRequired,
   setControlValue: PropTypes.func.isRequired,
   viewport: PropTypes.object.isRequired,
   getLayer: PropTypes.func.isRequired,
   payload: PropTypes.object.isRequired,
+  onAddFilter: PropTypes.func,
+  onTooltip: PropTypes.func,
 };
 
 export default class CategoricalDeckGLContainer extends React.PureComponent {
@@ -49,7 +51,7 @@ export default class CategoricalDeckGLContainer extends React.PureComponent
{
 
   /* eslint-disable-next-line react/sort-comp */
   static getDerivedStateFromProps(nextProps) {
-    const fd = nextProps.slice.formData;
+    const fd = nextProps.formData;
 
     const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
     const timestamps = nextProps.payload.data.features.map(f => f.__timestamp);
@@ -70,8 +72,13 @@ export default class CategoricalDeckGLContainer extends React.PureComponent
{
     this.setState(CategoricalDeckGLContainer.getDerivedStateFromProps(nextProps, this.state));
   }
   getLayers(values) {
-    const { getLayer, payload, slice } = this.props;
-    const fd = slice.formData;
+    const {
+      getLayer,
+      payload,
+      formData: fd,
+      onAddFilter,
+      onTooltip,
+    } = this.props;
     let data = [...payload.data.features];
 
     // Add colors from categories or fixed color
@@ -96,7 +103,7 @@ export default class CategoricalDeckGLContainer extends React.PureComponent
{
     }
 
     payload.data.features = data;
-    return [getLayer(fd, payload, slice)];
+    return [getLayer(fd, payload, onAddFilter, onTooltip)];
   }
   addColor(data, fd) {
     const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
@@ -142,14 +149,14 @@ export default class CategoricalDeckGLContainer extends React.PureComponent
{
           disabled={this.state.disabled}
           viewport={this.props.viewport}
           mapboxApiAccessToken={this.props.mapboxApiKey}
-          mapStyle={this.props.slice.formData.mapbox_style}
+          mapStyle={this.props.formData.mapbox_style}
           setControlValue={this.props.setControlValue}
         >
           <Legend
             categories={this.state.categories}
             toggleCategory={this.toggleCategory}
             showSingleCategory={this.showSingleCategory}
-            position={this.props.slice.formData.legend_position}
+            position={this.props.formData.legend_position}
           />
         </AnimatableDeckGLContainer>
       </div>
diff --git a/superset/assets/src/visualizations/deckgl/createAdaptor.jsx b/superset/assets/src/visualizations/deckgl/createAdaptor.jsx
new file mode 100644
index 0000000..6dfe11b
--- /dev/null
+++ b/superset/assets/src/visualizations/deckgl/createAdaptor.jsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+const IDENTITY = x => x;
+
+class DeckGlChartInput {
+  constructor(slice, payload, setControlValue) {
+    this.formData = slice.formData;
+    this.payload = payload;
+    this.setControlValue = setControlValue;
+    this.viewport = {
+      ...this.formData.viewport,
+      width: slice.width(),
+      height: slice.height(),
+    };
+
+    this.onAddFilter = ((...args) => { slice.addFilter(...args); });
+    this.onTooltip = ((...args) => { slice.tooltip(...args); });
+  }
+}
+
+export default function createAdaptor(Component, transformProps = IDENTITY) {
+  return function adaptor(slice, payload, setControlValue) {
+    const chartInput = new DeckGlChartInput(slice, payload, setControlValue);
+    ReactDOM.render(
+      <Component {...transformProps(chartInput)} />,
+      document.querySelector(slice.selector),
+    );
+  };
+}
diff --git a/superset/assets/src/visualizations/deckgl/factory.jsx b/superset/assets/src/visualizations/deckgl/factory.jsx
new file mode 100644
index 0000000..fc61e34
--- /dev/null
+++ b/superset/assets/src/visualizations/deckgl/factory.jsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import DeckGLContainer from './DeckGLContainer';
+import CategoricalDeckGLContainer from './CategoricalDeckGLContainer';
+import { fitViewport } from './layers/common';
+
+const propTypes = {
+  formData: PropTypes.object.isRequired,
+  payload: PropTypes.object.isRequired,
+  setControlValue: PropTypes.func.isRequired,
+  viewport: PropTypes.object.isRequired,
+  onAddFilter: PropTypes.func,
+  onTooltip: PropTypes.func,
+};
+const defaultProps = {
+  onAddFilter() {},
+  onTooltip() {},
+};
+
+export function createDeckGLComponent(getLayer, getPoints) {
+  function Component(props) {
+    const {
+      formData,
+      payload,
+      setControlValue,
+      onAddFilter,
+      onTooltip,
+      viewport: originalViewport,
+    } = props;
+
+    const viewport = formData.autozoom
+      ? fitViewport(originalViewport, getPoints(payload.data.features))
+      : originalViewport;
+
+    const layer = getLayer(formData, payload, onAddFilter, onTooltip);
+
+    return (
+      <DeckGLContainer
+        mapboxApiAccessToken={payload.data.mapboxApiKey}
+        viewport={viewport}
+        layers={[layer]}
+        mapStyle={formData.mapbox_style}
+        setControlValue={setControlValue}
+      />
+    );
+  }
+
+  Component.propTypes = propTypes;
+  Component.defaultProps = defaultProps;
+
+  return Component;
+}
+
+export function createCategoricalDeckGLComponent(getLayer, getPoints) {
+  function Component(props) {
+    const {
+      formData,
+      payload,
+      setControlValue,
+      onAddFilter,
+      onTooltip,
+      viewport: originalViewport,
+    } = props;
+
+    const viewport = formData.autozoom
+      ? fitViewport(originalViewport, getPoints(payload.data.features))
+      : originalViewport;
+
+    return (
+      <CategoricalDeckGLContainer
+        formData={formData}
+        mapboxApiKey={payload.data.mapboxApiKey}
+        setControlValue={setControlValue}
+        viewport={viewport}
+        getLayer={getLayer}
+        payload={payload}
+        onAddFilter={onAddFilter}
+        onTooltip={onTooltip}
+      />
+    );
+  }
+
+  Component.propTypes = propTypes;
+  Component.defaultProps = defaultProps;
+
+  return Component;
+}
diff --git a/superset/assets/src/visualizations/deckgl/layers/arc.jsx b/superset/assets/src/visualizations/deckgl/layers/arc.jsx
index 883ffbd..e2a3233 100644
--- a/superset/assets/src/visualizations/deckgl/layers/arc.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/arc.jsx
@@ -1,13 +1,7 @@
-/* eslint no-underscore-dangle: ["error", { "allow": ["", "__timestamp"] }] */
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-
 import { ArcLayer } from 'deck.gl';
-
-import CategoricalDeckGLContainer from '../CategoricalDeckGLContainer';
-
-import * as common from './common';
+import { commonLayerProps } from './common';
+import createAdaptor from '../createAdaptor';
+import { createCategoricalDeckGLComponent } from '../factory';
 
 function getPoints(data) {
   const points = [];
@@ -18,7 +12,7 @@ function getPoints(data) {
   return points;
 }
 
-function getLayer(fd, payload, slice) {
+export function getLayer(fd, payload, onAddFilter, onTooltip) {
   const data = payload.data.features;
   const sc = fd.color_picker;
   const tc = fd.target_color_picker;
@@ -28,36 +22,8 @@ function getLayer(fd, payload, slice) {
     getSourceColor: d => d.sourceColor || d.color || [sc.r, sc.g, sc.b, 255 * sc.a],
     getTargetColor: d => d.targetColor || d.color || [tc.r, tc.g, tc.b, 255 * tc.a],
     strokeWidth: (fd.stroke_width) ? fd.stroke_width : 3,
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
-function deckArc(slice, payload, setControlValue) {
-  const fd = slice.formData;
-  let viewport = {
-    ...fd.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (fd.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <CategoricalDeckGLContainer
-      slice={slice}
-      mapboxApiKey={payload.data.mapboxApiKey}
-      setControlValue={setControlValue}
-      viewport={viewport}
-      getLayer={getLayer}
-      payload={payload}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckArc,
-  getLayer,
-};
+export default createAdaptor(createCategoricalDeckGLComponent(getLayer, getPoints));
diff --git a/superset/assets/src/visualizations/deckgl/layers/common.jsx b/superset/assets/src/visualizations/deckgl/layers/common.jsx
index 8a3ecc5..a0b1e13 100644
--- a/superset/assets/src/visualizations/deckgl/layers/common.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/common.jsx
@@ -1,7 +1,6 @@
 import React from 'react';
 import { fitBounds } from 'viewport-mercator-project';
 import d3 from 'd3';
-
 import sandboxedEval from '../../../modules/sandbox';
 
 export function getBounds(points) {
@@ -32,7 +31,7 @@ export function fitViewport(viewport, points, padding = 10) {
   }
 }
 
-export function commonLayerProps(formData, slice) {
+export function commonLayerProps(formData, onAddFilter, onTooltip) {
   const fd = formData;
   let onHover;
   let tooltipContentGenerator;
@@ -49,13 +48,13 @@ export function commonLayerProps(formData, slice) {
   if (tooltipContentGenerator) {
     onHover = (o) => {
       if (o.picked) {
-        slice.setTooltip({
+        onTooltip({
           content: tooltipContentGenerator(o),
           x: o.x,
           y: o.y,
         });
       } else {
-        slice.setTooltip(null);
+        onTooltip(null);
       }
     };
   }
@@ -66,7 +65,7 @@ export function commonLayerProps(formData, slice) {
       window.open(href);
     };
   } else if (fd.table_filter && fd.line_type === 'geohash') {
-    onClick = o => slice.addFilter(fd.line_column, [o.object[fd.line_column]], false);
+    onClick = o => onAddFilter(fd.line_column, [o.object[fd.line_column]], false);
   }
   return {
     onClick,
diff --git a/superset/assets/src/visualizations/deckgl/layers/geojson.jsx b/superset/assets/src/visualizations/deckgl/layers/geojson.jsx
index 72790f1..64a39a9 100644
--- a/superset/assets/src/visualizations/deckgl/layers/geojson.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/geojson.jsx
@@ -1,12 +1,13 @@
 import React from 'react';
-import ReactDOM from 'react-dom';
+import PropTypes from 'prop-types';
 import { GeoJsonLayer } from 'deck.gl';
 // TODO import geojsonExtent from 'geojson-extent';
 
 import DeckGLContainer from './../DeckGLContainer';
-import * as common from './common';
 import { hexToRGB } from '../../../modules/colors';
 import sandboxedEval from '../../../modules/sandbox';
+import { commonLayerProps } from './common';
+import createAdaptor from '../createAdaptor';
 
 const propertyMap = {
   fillColor: 'fillColor',
@@ -57,7 +58,7 @@ const recurseGeoJson = (node, propOverrides, extraProps) => {
   }
 };
 
-function getLayer(formData, payload, slice) {
+export function getLayer(formData, payload, onAddFilter, onTooltip) {
   const fd = formData;
   const fc = fd.fill_color_picker;
   const sc = fd.stroke_color_picker;
@@ -88,35 +89,52 @@ function getLayer(formData, payload, slice) {
     stroked: fd.stroked,
     extruded: fd.extruded,
     pointRadiusScale: fd.point_radius_scale,
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
-function deckGeoJson(slice, payload, setControlValue) {
-  const layer = getLayer(slice.formData, payload, slice);
-  const viewport = {
-    ...slice.formData.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-  if (slice.formData.autozoom) {
-    // TODO get this to work
-    // viewport = common.fitViewport(viewport, geojsonExtent(payload.data.features));
-  }
+const propTypes = {
+  formData: PropTypes.object.isRequired,
+  payload: PropTypes.object.isRequired,
+  setControlValue: PropTypes.func.isRequired,
+  viewport: PropTypes.object.isRequired,
+  onAddFilter: PropTypes.func,
+  onTooltip: PropTypes.func,
+};
+const defaultProps = {
+  onAddFilter() {},
+  onTooltip() {},
+};
+
+function deckGeoJson(props) {
+  const {
+    formData,
+    payload,
+    setControlValue,
+    onAddFilter,
+    onTooltip,
+    viewport,
+  } = props;
 
-  ReactDOM.render(
+  // TODO get this to work
+  // if (formData.autozoom) {
+  //   viewport = common.fitViewport(viewport, geojsonExtent(payload.data.features));
+  // }
+
+  const layer = getLayer(formData, payload, onAddFilter, onTooltip);
+
+  return (
     <DeckGLContainer
       mapboxApiAccessToken={payload.data.mapboxApiKey}
       viewport={viewport}
       layers={[layer]}
-      mapStyle={slice.formData.mapbox_style}
+      mapStyle={formData.mapbox_style}
       setControlValue={setControlValue}
-    />,
-    document.getElementById(slice.containerId),
+    />
   );
 }
 
-module.exports = {
-  default: deckGeoJson,
-  getLayer,
-};
+deckGeoJson.propTypes = propTypes;
+deckGeoJson.defaultProps = defaultProps;
+
+export default createAdaptor(deckGeoJson);
diff --git a/superset/assets/src/visualizations/deckgl/layers/grid.jsx b/superset/assets/src/visualizations/deckgl/layers/grid.jsx
index 16a538c..0f5cf31 100644
--- a/superset/assets/src/visualizations/deckgl/layers/grid.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/grid.jsx
@@ -1,14 +1,10 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-
 import { GridLayer } from 'deck.gl';
-
-import DeckGLContainer from './../DeckGLContainer';
-
-import * as common from './common';
+import { commonLayerProps } from './common';
 import sandboxedEval from '../../../modules/sandbox';
+import createAdaptor from '../createAdaptor';
+import { createDeckGLComponent } from '../factory';
 
-function getLayer(formData, payload, slice) {
+export function getLayer(formData, payload, onAddFilter, onTooltip) {
   const fd = formData;
   const c = fd.color_picker;
   let data = payload.data.features.map(d => ({
@@ -21,6 +17,7 @@ function getLayer(formData, payload, slice) {
     const jsFnMutator = sandboxedEval(fd.js_data_mutator);
     data = jsFnMutator(data);
   }
+
   return new GridLayer({
     id: `grid-layer-${fd.slice_id}`,
     data,
@@ -32,7 +29,7 @@ function getLayer(formData, payload, slice) {
     outline: false,
     getElevationValue: points => points.reduce((sum, point) => sum + point.weight,
0),
     getColorValue: points => points.reduce((sum, point) => sum + point.weight, 0),
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
@@ -40,31 +37,4 @@ function getPoints(data) {
   return data.map(d => d.position);
 }
 
-function deckGrid(slice, payload, setControlValue) {
-  const layer = getLayer(slice.formData, payload, slice);
-  let viewport = {
-    ...slice.formData.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (slice.formData.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <DeckGLContainer
-      mapboxApiAccessToken={payload.data.mapboxApiKey}
-      viewport={viewport}
-      layers={[layer]}
-      mapStyle={slice.formData.mapbox_style}
-      setControlValue={setControlValue}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckGrid,
-  getLayer,
-};
+export default createAdaptor(createDeckGLComponent(getLayer, getPoints));
diff --git a/superset/assets/src/visualizations/deckgl/layers/hex.jsx b/superset/assets/src/visualizations/deckgl/layers/hex.jsx
index 14c9951..3dabb76 100644
--- a/superset/assets/src/visualizations/deckgl/layers/hex.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/hex.jsx
@@ -1,14 +1,10 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-
 import { HexagonLayer } from 'deck.gl';
-
-import DeckGLContainer from './../DeckGLContainer';
-
-import * as common from './common';
+import { commonLayerProps } from './common';
 import sandboxedEval from '../../../modules/sandbox';
+import createAdaptor from '../createAdaptor';
+import { createDeckGLComponent } from '../factory';
 
-function getLayer(formData, payload, slice) {
+export function getLayer(formData, payload, onAddFilter, onTooltip) {
   const fd = formData;
   const c = fd.color_picker;
   let data = payload.data.features.map(d => ({
@@ -33,7 +29,7 @@ function getLayer(formData, payload, slice) {
     outline: false,
     getElevationValue: points => points.reduce((sum, point) => sum + point.weight,
0),
     getColorValue: points => points.reduce((sum, point) => sum + point.weight, 0),
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
@@ -41,31 +37,4 @@ function getPoints(data) {
   return data.map(d => d.position);
 }
 
-function deckHex(slice, payload, setControlValue) {
-  const layer = getLayer(slice.formData, payload, slice);
-  let viewport = {
-    ...slice.formData.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (slice.formData.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <DeckGLContainer
-      mapboxApiAccessToken={payload.data.mapboxApiKey}
-      viewport={viewport}
-      layers={[layer]}
-      mapStyle={slice.formData.mapbox_style}
-      setControlValue={setControlValue}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckHex,
-  getLayer,
-};
+export default createAdaptor(createDeckGLComponent(getLayer, getPoints));
diff --git a/superset/assets/src/visualizations/deckgl/layers/index.js b/superset/assets/src/visualizations/deckgl/layers/index.js
index d8d25d5..e98f743 100644
--- a/superset/assets/src/visualizations/deckgl/layers/index.js
+++ b/superset/assets/src/visualizations/deckgl/layers/index.js
@@ -18,4 +18,5 @@ const layerGenerators = {
   deck_arc,
   deck_polygon,
 };
+
 export default layerGenerators;
diff --git a/superset/assets/src/visualizations/deckgl/layers/path.jsx b/superset/assets/src/visualizations/deckgl/layers/path.jsx
index 0951edd..ede01b2 100644
--- a/superset/assets/src/visualizations/deckgl/layers/path.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/path.jsx
@@ -1,14 +1,10 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-
 import { PathLayer } from 'deck.gl';
-
-import DeckGLContainer from './../DeckGLContainer';
-
-import * as common from './common';
+import { commonLayerProps } from './common';
 import sandboxedEval from '../../../modules/sandbox';
+import createAdaptor from '../createAdaptor';
+import { createDeckGLComponent } from '../factory';
 
-function getLayer(formData, payload, slice) {
+export function getLayer(formData, payload, onAddFilter, onTooltip) {
   const fd = formData;
   const c = fd.color_picker;
   const fixedColor = [c.r, c.g, c.b, 255 * c.a];
@@ -29,7 +25,7 @@ function getLayer(formData, payload, slice) {
     data,
     rounded: true,
     widthScale: 1,
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
@@ -41,31 +37,4 @@ function getPoints(data) {
   return points;
 }
 
-function deckPath(slice, payload, setControlValue) {
-  const layer = getLayer(slice.formData, payload, slice);
-  let viewport = {
-    ...slice.formData.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (slice.formData.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <DeckGLContainer
-      mapboxApiAccessToken={payload.data.mapboxApiKey}
-      viewport={viewport}
-      layers={[layer]}
-      mapStyle={slice.formData.mapbox_style}
-      setControlValue={setControlValue}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckPath,
-  getLayer,
-};
+export default createAdaptor(createDeckGLComponent(getLayer, getPoints));
diff --git a/superset/assets/src/visualizations/deckgl/layers/polygon.jsx b/superset/assets/src/visualizations/deckgl/layers/polygon.jsx
index e84b943..73537dc 100644
--- a/superset/assets/src/visualizations/deckgl/layers/polygon.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/polygon.jsx
@@ -1,21 +1,17 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-
+import d3 from 'd3';
 import { PolygonLayer } from 'deck.gl';
 import { flatten } from 'lodash';
-import d3 from 'd3';
-
-import DeckGLContainer from './../DeckGLContainer';
-
-import * as common from './common';
 import { colorScalerFactory } from '../../../modules/colors';
+import { commonLayerProps } from './common';
 import sandboxedEval from '../../../modules/sandbox';
+import createAdaptor from '../createAdaptor';
+import { createDeckGLComponent } from '../factory';
 
 function getPoints(features) {
   return flatten(features.map(d => d.polygon), true);
 }
 
-function getLayer(formData, payload, slice) {
+export function getLayer(formData, payload, onAddFilter, onTooltip) {
   const fd = formData;
   const fc = fd.fill_color_picker;
   const sc = fd.stroke_color_picker;
@@ -49,36 +45,8 @@ function getLayer(formData, payload, slice) {
     getLineWidth: fd.line_width,
     extruded: fd.extruded,
     fp64: true,
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
-function deckPolygon(slice, payload, setControlValue) {
-  const layer = getLayer(slice.formData, payload, slice);
-  const fd = slice.formData;
-  let viewport = {
-    ...slice.formData.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (fd.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <DeckGLContainer
-      mapboxApiAccessToken={payload.data.mapboxApiKey}
-      viewport={viewport}
-      layers={[layer]}
-      mapStyle={slice.formData.mapbox_style}
-      setControlValue={setControlValue}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckPolygon,
-  getLayer,
-};
+export default createAdaptor(createDeckGLComponent(getLayer, getPoints));
diff --git a/superset/assets/src/visualizations/deckgl/layers/scatter.jsx b/superset/assets/src/visualizations/deckgl/layers/scatter.jsx
index d9474a4..b740ce1 100644
--- a/superset/assets/src/visualizations/deckgl/layers/scatter.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/scatter.jsx
@@ -1,20 +1,14 @@
-/* eslint no-underscore-dangle: ["error", { "allow": ["", "__timestamp"] }] */
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-
 import { ScatterplotLayer } from 'deck.gl';
-
-import CategoricalDeckGLContainer from '../CategoricalDeckGLContainer';
-import * as common from './common';
+import { commonLayerProps } from './common';
+import createAdaptor from '../createAdaptor';
+import { createCategoricalDeckGLComponent } from '../factory';
 import { unitToRadius } from '../../../modules/geo';
 
-
 function getPoints(data) {
   return data.map(d => d.position);
 }
 
-function getLayer(fd, payload, slice) {
+export function getLayer(fd, payload, slice) {
   const dataWithRadius = payload.data.features.map((d) => {
     let radius = unitToRadius(fd.point_unit, d.radius) || 10;
     if (fd.multiplier) {
@@ -35,36 +29,8 @@ function getLayer(fd, payload, slice) {
     radiusMinPixels: fd.min_radius || null,
     radiusMaxPixels: fd.max_radius || null,
     outline: false,
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, slice),
   });
 }
 
-function deckScatter(slice, payload, setControlValue) {
-  const fd = slice.formData;
-  let viewport = {
-    ...fd.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (fd.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <CategoricalDeckGLContainer
-      slice={slice}
-      mapboxApiKey={payload.data.mapboxApiKey}
-      setControlValue={setControlValue}
-      viewport={viewport}
-      getLayer={getLayer}
-      payload={payload}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckScatter,
-  getLayer,
-};
+export default createAdaptor(createCategoricalDeckGLComponent(getLayer, getPoints));
diff --git a/superset/assets/src/visualizations/deckgl/layers/screengrid.jsx b/superset/assets/src/visualizations/deckgl/layers/screengrid.jsx
index b4df577..4331cec 100644
--- a/superset/assets/src/visualizations/deckgl/layers/screengrid.jsx
+++ b/superset/assets/src/visualizations/deckgl/layers/screengrid.jsx
@@ -1,22 +1,19 @@
 /* eslint no-underscore-dangle: ["error", { "allow": ["", "__timestamp"] }] */
 
 import React from 'react';
-import ReactDOM from 'react-dom';
 import PropTypes from 'prop-types';
-
 import { ScreenGridLayer } from 'deck.gl';
-
 import AnimatableDeckGLContainer from '../AnimatableDeckGLContainer';
-
-import * as common from './common';
 import { getPlaySliderParams } from '../../../modules/time';
 import sandboxedEval from '../../../modules/sandbox';
+import { commonLayerProps, fitViewport } from './common';
+import createAdaptor from '../createAdaptor';
 
 function getPoints(data) {
   return data.map(d => d.position);
 }
 
-function getLayer(formData, payload, slice, filters) {
+export function getLayer(formData, payload, onAddFilter, onTooltip, filters) {
   const fd = formData;
   const c = fd.color_picker;
   let data = payload.data.features.map(d => ({
@@ -47,21 +44,27 @@ function getLayer(formData, payload, slice, filters) {
     maxColor: [c.r, c.g, c.b, 255 * c.a],
     outline: false,
     getWeight: d => d.weight || 0,
-    ...common.commonLayerProps(fd, slice),
+    ...commonLayerProps(fd, onAddFilter, onTooltip),
   });
 }
 
 const propTypes = {
-  slice: PropTypes.object.isRequired,
+  formData: PropTypes.object.isRequired,
   payload: PropTypes.object.isRequired,
   setControlValue: PropTypes.func.isRequired,
   viewport: PropTypes.object.isRequired,
+  onAddFilter: PropTypes.func,
+  onTooltip: PropTypes.func,
+};
+const defaultProps = {
+  onAddFilter() {},
+  onTooltip() {},
 };
 
 class DeckGLScreenGrid extends React.PureComponent {
   /* eslint-disable-next-line react/sort-comp */
   static getDerivedStateFromProps(nextProps) {
-    const fd = nextProps.slice.formData;
+    const fd = nextProps.formData;
 
     const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
     const timestamps = nextProps.payload.data.features.map(f => f.__timestamp);
@@ -89,14 +92,21 @@ class DeckGLScreenGrid extends React.PureComponent {
     }
 
     const layer = getLayer(
-      this.props.slice.formData,
+      this.props.formData,
       this.props.payload,
-      this.props.slice,
+      this.props.onAddFilter,
+      this.props.onTooltip,
       filters);
 
     return [layer];
   }
+
   render() {
+    const { formData, payload } = this.props;
+    const viewport = formData.autozoom
+      ? fitViewport(this.props.viewport, getPoints(payload.data.features))
+      : this.props.viewport;
+
     return (
       <div>
         <AnimatableDeckGLContainer
@@ -106,9 +116,9 @@ class DeckGLScreenGrid extends React.PureComponent {
           getStep={this.state.getStep}
           values={this.state.values}
           disabled={this.state.disabled}
-          viewport={this.props.viewport}
+          viewport={viewport}
           mapboxApiAccessToken={this.props.payload.data.mapboxApiKey}
-          mapStyle={this.props.slice.formData.mapbox_style}
+          mapStyle={this.props.formData.mapbox_style}
           setControlValue={this.props.setControlValue}
           aggregation
         />
@@ -118,31 +128,6 @@ class DeckGLScreenGrid extends React.PureComponent {
 }
 
 DeckGLScreenGrid.propTypes = propTypes;
+DeckGLScreenGrid.defaultProps = defaultProps;
 
-function deckScreenGrid(slice, payload, setControlValue) {
-  const fd = slice.formData;
-  let viewport = {
-    ...fd.viewport,
-    width: slice.width(),
-    height: slice.height(),
-  };
-
-  if (fd.autozoom) {
-    viewport = common.fitViewport(viewport, getPoints(payload.data.features));
-  }
-
-  ReactDOM.render(
-    <DeckGLScreenGrid
-      slice={slice}
-      payload={payload}
-      setControlValue={setControlValue}
-      viewport={viewport}
-    />,
-    document.getElementById(slice.containerId),
-  );
-}
-
-module.exports = {
-  default: deckScreenGrid,
-  getLayer,
-};
+export default createAdaptor(DeckGLScreenGrid);
diff --git a/superset/assets/src/visualizations/deckgl/multi.jsx b/superset/assets/src/visualizations/deckgl/multi.jsx
index 8b35c86..76a7072 100644
--- a/superset/assets/src/visualizations/deckgl/multi.jsx
+++ b/superset/assets/src/visualizations/deckgl/multi.jsx
@@ -1,57 +1,87 @@
 import React from 'react';
-import ReactDOM from 'react-dom';
+import PropTypes from 'prop-types';
 import $ from 'jquery';
-
 import DeckGLContainer from './DeckGLContainer';
 import { getExploreLongUrl } from '../../explore/exploreUtils';
 import layerGenerators from './layers';
+import createAdaptor from './createAdaptor';
+
+const propTypes = {
+  formData: PropTypes.object.isRequired,
+  payload: PropTypes.object.isRequired,
+  setControlValue: PropTypes.func.isRequired,
+  viewport: PropTypes.object.isRequired,
+};
+
+class DeckMulti extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = { subSlicesLayers: {} };
+  }
+
+  componentDidMount() {
+    const { formData, payload } = this.props;
+    this.loadLayers(formData, payload);
+  }
+
+  componentWillReceiveProps(nextProps) {
+    const { formData, payload } = nextProps;
+    this.loadLayers(formData, payload);
+  }
+
+  loadLayers(formData, payload) {
+    this.setState({ subSlicesLayers: {} });
+    payload.data.slices.forEach((subslice) => {
+      // Filters applied to multi_deck are passed down to underlying charts
+      // note that dashboard contextual information (filter_immune_slices and such) aren't
+      // taken into consideration here
+      const filters = [
+        ...(subslice.form_data.filters || []),
+        ...(formData.filters || []),
+        ...(formData.extra_filters || []),
+      ];
+      const subsliceCopy = {
+        ...subslice,
+        form_data: {
+          ...subslice.form_data,
+          filters,
+        },
+      };
 
+      const url = getExploreLongUrl(subsliceCopy.form_data, 'json');
+      $.get(url, (data) => {
+        const layer = layerGenerators[subsliceCopy.form_data.viz_type](
+          subsliceCopy.form_data,
+          data,
+        );
+        this.setState({
+          subSlicesLayers: {
+            ...this.state.subSlicesLayers,
+            [subsliceCopy.slice_id]: layer,
+          },
+        });
+      });
+    });
+  }
+
+  render() {
+    const { payload, viewport, formData, setControlValue } = this.props;
+    const { subSlicesLayers } = this.state;
 
-function deckMulti(slice, payload, setControlValue) {
-  const subSlicesLayers = {};
-  const fd = slice.formData;
-  const render = () => {
-    const viewport = {
-      ...fd.viewport,
-      width: slice.width(),
-      height: slice.height(),
-    };
     const layers = Object.keys(subSlicesLayers).map(k => subSlicesLayers[k]);
-    ReactDOM.render(
+
+    return (
       <DeckGLContainer
         mapboxApiAccessToken={payload.data.mapboxApiKey}
         viewport={viewport}
         layers={layers}
-        mapStyle={fd.mapbox_style}
+        mapStyle={formData.mapbox_style}
         setControlValue={setControlValue}
-      />,
-      document.getElementById(slice.containerId),
+      />
     );
-  };
-  render();
-  payload.data.slices.forEach((subslice) => {
-    // Filters applied to multi_deck are passed down to underlying charts
-    // note that dashboard contextual information (filter_immune_slices and such) aren't
-    // taken into consideration here
-    const filters = [
-      ...(subslice.form_data.filters || []),
-      ...(fd.filters || []),
-      ...(fd.extra_filters || []),
-    ];
-    const subsliceCopy = {
-      ...subslice,
-      form_data: {
-        ...subslice.form_data,
-        filters,
-      },
-    };
-
-    const url = getExploreLongUrl(subsliceCopy.form_data, 'json');
-    $.get(url, (data) => {
-      const layer = layerGenerators[subsliceCopy.form_data.viz_type](subsliceCopy.form_data,
data);
-      subSlicesLayers[subsliceCopy.slice_id] = layer;
-      render();
-    });
-  });
+  }
 }
-module.exports = deckMulti;
+
+DeckMulti.propTypes = propTypes;
+
+export default createAdaptor(DeckMulti);


Mime
View raw message