feat: [lyftga] add tooltips to deck.gl viz types (#7206)

* Add tooltips to deck.gl viz types

* Handle text overflow

* Use .css file instead of .less

* fix: add tooltip component

* Add tooltips to deck.gl viz types

* Handle text overflow

* Use .css file instead of .less

* fix: add tooltip component

* fix: casing, types, and keys
This commit is contained in:
Kim Truong 2019-04-03 13:35:27 -07:00 committed by Christine Chambers
parent e714dc19fe
commit 1132c3cbde
12 changed files with 178 additions and 20 deletions

View File

@ -22,6 +22,7 @@ import MapGL from 'react-map-gl';
import DeckGL from 'deck.gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import { isEqual } from 'lodash';
import '../stylesheets/deckgl.css';
const TICK = 2000; // milliseconds

View File

@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
};
export default class TooltipRow extends React.PureComponent {
render() {
return (
<div>{this.props.label}<strong>{this.props.value}</strong></div>
);
}
}
TooltipRow.propTypes = propTypes;

View File

@ -17,8 +17,11 @@
* under the License.
*/
import { ArcLayer } from 'deck.gl';
import React from 'react';
import { t } from '@superset-ui/translation';
import { commonLayerProps } from '../common';
import { createCategoricalDeckGLComponent } from '../../factory';
import TooltipRow from '../../TooltipRow';
function getPoints(data) {
const points = [];
@ -29,6 +32,18 @@ function getPoints(data) {
return points;
}
function setTooltipContent(formData) {
return o => (
<div className="deckgl-tooltip">
<TooltipRow label={`${t('Start (Longitude, Latitude)')}: `} value={`${o.object.sourcePosition[0]}, ${o.object.sourcePosition[1]}`} />
<TooltipRow label={`${t('End (Longitude, Latitude)')}: `} value={`${o.object.targetPosition[0]}, ${o.object.targetPosition[1]}`} />
{
formData.dimension && <TooltipRow label={`${formData.dimension}: `} value={`${o.object.cat_color}`} />
}
</div>
);
}
export function getLayer(fd, payload, onAddFilter, setTooltip) {
const data = payload.data.features;
const sc = fd.color_picker;
@ -39,7 +54,7 @@ export function getLayer(fd, payload, onAddFilter, setTooltip) {
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,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent(fd)),
});
}

View File

@ -25,6 +25,7 @@ import DeckGLContainer from '../../DeckGLContainer';
import { hexToRGB } from '../../../../modules/colors';
import sandboxedEval from '../../../../modules/sandbox';
import { commonLayerProps } from '../common';
import TooltipRow from '../../TooltipRow';
const propertyMap = {
fillColor: 'fillColor',
@ -75,6 +76,19 @@ const recurseGeoJson = (node, propOverrides, extraProps) => {
}
};
function setTooltipContent(o) {
return (
o.object.extraProps &&
<div className="deckgl-tooltip">
{
Object.keys(o.object.extraProps).map((prop, index) =>
<TooltipRow key={`prop-${index}`} label={`${prop}: `} value={`${o.object.extraProps[prop]}`} />,
)
}
</div>
);
}
export function getLayer(formData, payload, onAddFilter, setTooltip) {
const fd = formData;
const fc = fd.fill_color_picker;
@ -106,7 +120,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) {
stroked: fd.stroked,
extruded: fd.extruded,
pointRadiusScale: fd.point_radius_scale,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent),
});
}

View File

@ -17,10 +17,22 @@
* under the License.
*/
import { GridLayer } from 'deck.gl';
import React from 'react';
import { t } from '@superset-ui/translation';
import { commonLayerProps, getAggFunc } from '../common';
import sandboxedEval from '../../../../modules/sandbox';
import { createDeckGLComponent } from '../../factory';
import TooltipRow from '../../TooltipRow';
function setTooltipContent(o) {
return (
<div className="deckgl-tooltip">
<TooltipRow label={`${t('Longitude and Latitude')}: `} value={`${o.object.position[0]}, ${o.object.position[1]}`} />
<TooltipRow label={`${t('Height')}: `} value={`${o.object.elevationValue}`} />
</div>
);
}
export function getLayer(formData, payload, onAddFilter, setTooltip) {
const fd = formData;
@ -48,7 +60,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) {
outline: false,
getElevationValue: aggFunc,
getColorValue: aggFunc,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent),
});
}

View File

@ -17,10 +17,22 @@
* under the License.
*/
import { HexagonLayer } from 'deck.gl';
import React from 'react';
import { t } from '@superset-ui/translation';
import { commonLayerProps, getAggFunc } from '../common';
import sandboxedEval from '../../../../modules/sandbox';
import { createDeckGLComponent } from '../../factory';
import TooltipRow from '../../TooltipRow';
function setTooltipContent(o) {
return (
<div className="deckgl-tooltip">
<TooltipRow label={`${t('Centroid (Longitude and Latitude)')}: `} value={`(${o.object.centroid[0]}, ${o.object.centroid[1]})`} />
<TooltipRow label={`${t('Height')}: `} value={`${o.object.elevationValue}`} />
</div>
);
}
export function getLayer(formData, payload, onAddFilter, setTooltip) {
const fd = formData;
@ -47,7 +59,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) {
outline: false,
getElevationValue: aggFunc,
getColorValue: aggFunc,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent),
});
}

View File

@ -17,9 +17,24 @@
* under the License.
*/
import { PathLayer } from 'deck.gl';
import React from 'react';
import { commonLayerProps } from '../common';
import sandboxedEval from '../../../../modules/sandbox';
import { createDeckGLComponent } from '../../factory';
import TooltipRow from '../../TooltipRow';
function setTooltipContent(o) {
return (
o.object.extraProps &&
<div className="deckgl-tooltip">
{
Object.keys(o.object.extraProps).map((prop, index) =>
<TooltipRow key={`prop-${index}`} label={`${prop}: `} value={`${o.object.extraProps[prop]}`} />,
)
}
</div>
);
}
export function getLayer(formData, payload, onAddFilter, setTooltip) {
const fd = formData;
@ -42,7 +57,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) {
data,
rounded: true,
widthScale: 1,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent),
});
}

View File

@ -25,6 +25,7 @@ import { PolygonLayer } from 'deck.gl';
import AnimatableDeckGLContainer from '../../AnimatableDeckGLContainer';
import Legend from '../../../Legend';
import TooltipRow from '../../TooltipRow';
import { getBuckets, getBreakPointColorScaler } from '../../utils';
import { commonLayerProps, fitViewport } from '../common';
@ -48,6 +49,18 @@ function getElevation(d, colorScaler) {
: d.elevation;
}
function setTooltipContent(formData) {
return (o) => {
const metricLabel = formData.metric.label || formData.metric;
return (
<div className="deckgl-tooltip">
<TooltipRow label={`${formData.line_column}: `} value={`${o.object[formData.line_column]}`} />
{formData.metric && <TooltipRow label={`${metricLabel}: `} value={`${o.object[metricLabel]}`} />}
</div>
);
};
}
export function getLayer(formData, payload, setTooltip, selected, onSelect, filters) {
const fd = formData;
const fc = fd.fill_color_picker;
@ -95,7 +108,7 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt
getElevation: d => getElevation(d, colorScaler),
elevationScale: fd.multiplier,
fp64: true,
...commonLayerProps(fd, setTooltip, onSelect),
...commonLayerProps(fd, setTooltip, setTooltipContent(fd), onSelect),
});
}

View File

@ -17,14 +17,31 @@
* under the License.
*/
import { ScatterplotLayer } from 'deck.gl';
import React from 'react';
import { t } from '@superset-ui/translation';
import { commonLayerProps } from '../common';
import { createCategoricalDeckGLComponent } from '../../factory';
import TooltipRow from '../../TooltipRow';
import { unitToRadius } from '../../../../modules/geo';
function getPoints(data) {
return data.map(d => d.position);
}
function setTooltipContent(formData) {
return o => (
<div className="deckgl-tooltip">
<TooltipRow label={`${t('Longitude and Latitude')}: `} value={`${o.object.position[0]}, ${o.object.position[1]}`} />
{
o.object.cat_color && <TooltipRow label={`${t('Category')}: `} value={`${o.object.cat_color}`} />
}
{
o.object.metric && <TooltipRow label={`${formData.point_radius_fixed.value}: `} value={`${o.object.metric}`} />
}
</div>
);
}
export function getLayer(fd, payload, onAddFilter, setTooltip) {
const dataWithRadius = payload.data.features.map((d) => {
let radius = unitToRadius(fd.point_unit, d.radius) || 10;
@ -46,7 +63,7 @@ export function getLayer(fd, payload, onAddFilter, setTooltip) {
radiusMinPixels: fd.min_radius || null,
radiusMaxPixels: fd.max_radius || null,
outline: false,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent(fd)),
});
}

View File

@ -21,15 +21,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ScreenGridLayer } from 'deck.gl';
import { t } from '@superset-ui/translation';
import AnimatableDeckGLContainer from '../../AnimatableDeckGLContainer';
import { getPlaySliderParams } from '../../../../modules/time';
import sandboxedEval from '../../../../modules/sandbox';
import { commonLayerProps, fitViewport } from '../common';
import TooltipRow from '../../TooltipRow';
function getPoints(data) {
return data.map(d => d.position);
}
function setTooltipContent(o) {
return (
<div className="deckgl-tooltip">
<TooltipRow label={`${t('Longitude and Latitude')}: `} value={`${o.object.position[0]}, ${o.object.position[1]}`} />
<TooltipRow label={`${t('Weight')}: `} value={`${o.object.weight}`} />
</div>
);
}
export function getLayer(formData, payload, onAddFilter, setTooltip, filters) {
const fd = formData;
const c = fd.color_picker;
@ -61,7 +72,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip, filters) {
maxColor: [c.r, c.g, c.b, 255 * c.a],
outline: false,
getWeight: d => d.weight || 0,
...commonLayerProps(fd, setTooltip),
...commonLayerProps(fd, setTooltip, setTooltipContent),
});
}

View File

@ -16,8 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { fitBounds } from 'viewport-mercator-project';
import * as d3array from 'd3-array';
import sandboxedEval from '../../../modules/sandbox';
@ -50,20 +48,12 @@ export function fitViewport(viewport, points, padding = 10) {
}
}
export function commonLayerProps(formData, setTooltip, onSelect) {
export function commonLayerProps(formData, setTooltip, setTooltipContent, onSelect) {
const fd = formData;
let onHover;
let tooltipContentGenerator;
let tooltipContentGenerator = setTooltipContent;
if (fd.js_tooltip) {
tooltipContentGenerator = sandboxedEval(fd.js_tooltip);
} else if (fd.line_column && fd.metric && ['geohash', 'zipcode'].indexOf(fd.line_type) >= 0) {
const metricLabel = fd.metric.label || fd.metric;
tooltipContentGenerator = o => (
<div>
<div>{fd.line_column}: <strong>{o.object[fd.line_column]}</strong></div>
{fd.metric &&
<div>{metricLabel}: <strong>{o.object[metricLabel]}</strong></div>}
</div>);
}
if (tooltipContentGenerator) {
onHover = (o) => {

View File

@ -0,0 +1,22 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
.deckgl-tooltip > div {
overflow: hidden;
text-overflow: ellipsis;
}