From 2a7b64fef5823760fd619f217b3161d2d7d00cc6 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 11 Oct 2018 20:56:36 -0700 Subject: [PATCH] [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 ++-- .../visualizations/deckgl/createAdaptor.jsx | 30 +++++ .../src/visualizations/deckgl/factory.jsx | 87 +++++++++++++ .../src/visualizations/deckgl/layers/arc.jsx | 46 +------ .../visualizations/deckgl/layers/common.jsx | 9 +- .../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 +------ .../visualizations/deckgl/layers/polygon.jsx | 46 ++----- .../visualizations/deckgl/layers/scatter.jsx | 46 +------ .../deckgl/layers/screengrid.jsx | 65 ++++------ .../src/visualizations/deckgl/multi.jsx | 114 +++++++++++------- 14 files changed, 312 insertions(+), 347 deletions(-) create mode 100644 superset/assets/src/visualizations/deckgl/createAdaptor.jsx create mode 100644 superset/assets/src/visualizations/deckgl/factory.jsx diff --git a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx index 0976ec00dfce1..61c9eedc74902 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} > diff --git a/superset/assets/src/visualizations/deckgl/createAdaptor.jsx b/superset/assets/src/visualizations/deckgl/createAdaptor.jsx new file mode 100644 index 0000000000000..6dfe11b9d9d38 --- /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( + , + 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 0000000000000..fc61e34a5ecf6 --- /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 ( + + ); + } + + 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 ( + + ); + } + + 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 883ffbd6c68fd..e2a32330574ad 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( - , - 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 8a3ecc5c7c270..a0b1e137c319b 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 72790f1efdbba..64a39a9d2111a 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 ( , - 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 16a538c906267..0f5cf3149cca6 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( - , - 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 14c9951df42d6..3dabb765dc91e 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( - , - 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 d8d25d5b0e91b..e98f74395b303 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 0951edd93c166..ede01b2384868 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( - , - 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 e84b943f61783..73537dcaa4e2a 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( - , - 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 d9474a4770438..b740ce19baf41 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( - , - 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 b4df577d78253..4331cec5f3974 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 (
@@ -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( - , - 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 8b35c86768990..76a70722213b4 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 ( , - 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);