Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

[WIP] Cherry-pick(s): <description> #82

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Select from 'react-select';

import InfoTooltipWithTrigger from '../../../components/InfoTooltipWithTrigger';
import BoundsControl from './BoundsControl';
import CheckboxControl from './CheckboxControl';

const propTypes = {
onChange: PropTypes.func,
Expand Down Expand Up @@ -47,9 +48,15 @@ export default class TimeSeriesColumnControl extends React.Component {
onTextInputChange(attr, event) {
this.setState({ [attr]: event.target.value }, this.onChange);
}
onCheckboxChange(attr, value) {
this.setState({ [attr]: value }, this.onChange);
}
onBoundsChange(bounds) {
this.setState({ bounds }, this.onChange);
}
onYAxisBoundsChange(yAxisBounds) {
this.setState({ yAxisBounds }, this.onChange);
}
setType() {
}
textSummary() {
Expand Down Expand Up @@ -165,6 +172,28 @@ export default class TimeSeriesColumnControl extends React.Component {
options={comparisonTypeOptions}
/>,
)}
{this.state.colType === 'spark' && this.formRow(
'Show Y-axis',
(
'Show Y-axis on the sparkline. Will display the manually set min/max if set or min/max values in the data otherwise.'
),
'show-y-axis-bounds',
<CheckboxControl
value={this.state.showYAxis}
onChange={this.onCheckboxChange.bind(this, 'showYAxis')}
/>,
)}
{this.state.colType === 'spark' && this.formRow(
'Y-axis bounds',
(
'Manually set min/max values for the y-axis.'
),
'y-axis-bounds',
<BoundsControl
value={this.state.yAxisBounds}
onChange={this.onYAxisBoundsChange.bind(this)}
/>,
)}
{this.state.colType !== 'spark' && this.formRow(
'Color bounds',
(
Expand Down
2 changes: 1 addition & 1 deletion superset/assets/src/modules/visUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function getTextDimension({
}

if (isDefined(style)) {
['font', 'fontWeight', 'fontStyle', 'fontSize', 'fontFamily']
['font', 'fontWeight', 'fontStyle', 'fontSize', 'fontFamily', 'letterSpacing']
.filter(field => isDefined(style[field]))
.forEach((field) => {
textNode.style[field] = style[field];
Expand Down
173 changes: 173 additions & 0 deletions superset/assets/src/visualizations/SparklineCell.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Sparkline, LineSeries, PointSeries, HorizontalReferenceLine, VerticalReferenceLine, WithTooltip } from '@data-ui/sparkline';
import { d3format } from '../modules/utils';
import { getTextDimension } from '../modules/visUtils';

const propTypes = {
className: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
data: PropTypes.array.isRequired,
ariaLabel: PropTypes.string,
numberFormat: PropTypes.string,
yAxisBounds: PropTypes.array,
showYAxis: PropTypes.bool,
renderTooltip: PropTypes.func,
};
const defaultProps = {
className: '',
width: 300,
height: 50,
ariaLabel: '',
numberFormat: undefined,
yAxisBounds: [null, null],
showYAxis: false,
renderTooltip() { return <div />; },
};

const MARGIN = {
top: 8,
right: 8,
bottom: 8,
left: 8,
};
const tooltipProps = {
style: {
opacity: 0.8,
},
offsetTop: 0,
};

function getSparklineTextWidth(text) {
return getTextDimension({
text,
style: {
fontSize: '12px',
fontWeight: 200,
letterSpacing: 0.4,
},
}).width + 5;
}

function isValidBoundValue(value) {
return value !== null && value !== undefined && value !== '' && !Number.isNaN(value);
}

class SparklineCell extends React.Component {
renderHorizontalReferenceLine(value, label) {
return (
<HorizontalReferenceLine
reference={value}
labelPosition="right"
renderLabel={() => label}
stroke="#bbb"
strokeDasharray="3 3"
strokeWidth={1}
/>
);
}

render() {
const {
width,
height,
data,
ariaLabel,
numberFormat,
yAxisBounds,
showYAxis,
renderTooltip,
} = this.props;

const yScale = {};
let hasMinBound = false;
let hasMaxBound = false;

if (yAxisBounds) {
const [minBound, maxBound] = yAxisBounds;
hasMinBound = isValidBoundValue(minBound);
if (hasMinBound) {
yScale.min = minBound;
}
hasMaxBound = isValidBoundValue(maxBound);
if (hasMaxBound) {
yScale.max = maxBound;
}
}

let min;
let max;
let minLabel;
let maxLabel;
let labelLength = 0;
if (showYAxis) {
const [minBound, maxBound] = yAxisBounds;
min = hasMinBound
? minBound
: data.reduce((acc, current) => Math.min(acc, current), data[0]);
max = hasMaxBound
? maxBound
: data.reduce((acc, current) => Math.max(acc, current), data[0]);

minLabel = d3format(numberFormat, min);
maxLabel = d3format(numberFormat, max);
labelLength = Math.max(
getSparklineTextWidth(minLabel),
getSparklineTextWidth(maxLabel),
);
}

const margin = {
...MARGIN,
right: MARGIN.right + labelLength,
};

return (
<WithTooltip
tooltipProps={tooltipProps}
hoverStyles={null}
renderTooltip={renderTooltip}
>
{({ onMouseLeave, onMouseMove, tooltipData }) => (
<Sparkline
ariaLabel={ariaLabel}
width={width}
height={height}
margin={margin}
data={data}
onMouseLeave={onMouseLeave}
onMouseMove={onMouseMove}
{...yScale}
>
{showYAxis &&
this.renderHorizontalReferenceLine(min, minLabel)}
{showYAxis &&
this.renderHorizontalReferenceLine(max, maxLabel)}
<LineSeries
showArea={false}
stroke="#767676"
/>
{tooltipData &&
<VerticalReferenceLine
reference={tooltipData.index}
strokeDasharray="3 3"
strokeWidth={1}
/>}
{tooltipData &&
<PointSeries
points={[tooltipData.index]}
fill="#767676"
strokeWidth={1}
/>}
</Sparkline>
)}
</WithTooltip>
);
}
}

SparklineCell.propTypes = propTypes;
SparklineCell.defaultProps = defaultProps;

export default SparklineCell;
70 changes: 18 additions & 52 deletions superset/assets/src/visualizations/time_table.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
import ReactDOM from 'react-dom';
import React from 'react';
import propTypes from 'prop-types';
import PropTypes from 'prop-types';
import { Table, Thead, Th, Tr, Td } from 'reactable';
import d3 from 'd3';
import Mustache from 'mustache';
import { Sparkline, LineSeries, PointSeries, VerticalReferenceLine, WithTooltip } from '@data-ui/sparkline';

import MetricOption from '../components/MetricOption';
import { d3format } from '../modules/utils';
import { formatDateThunk } from '../modules/dates';
import { d3format } from '../modules/utils';
import InfoTooltipWithTrigger from '../components/InfoTooltipWithTrigger';
import SparklineCell from './SparklineCell';
import './time_table.css';

const SPARKLINE_MARGIN = {
top: 8,
right: 8,
bottom: 8,
left: 8,
};
const sparklineTooltipProps = {
style: {
opacity: 0.8,
},
offsetTop: 0,
};

const ACCESSIBLE_COLOR_BOUNDS = ['#ca0020', '#0571b0'];

function FormattedNumber({ num, format }) {
Expand All @@ -37,8 +24,8 @@ function FormattedNumber({ num, format }) {
}

FormattedNumber.propTypes = {
num: propTypes.number,
format: propTypes.string,
num: PropTypes.number,
format: PropTypes.string,
};

function viz(slice, payload) {
Expand Down Expand Up @@ -93,49 +80,27 @@ function viz(slice, payload) {
}
}
}

const formatDate = formatDateThunk(column.dateFormat);

row[column.key] = {
data: sparkData[sparkData.length - 1],
display: (
<WithTooltip
tooltipProps={sparklineTooltipProps}
hoverStyles={null}
<SparklineCell
width={parseInt(column.width, 10) || 300}
height={parseInt(column.height, 10) || 50}
data={sparkData}
ariaLabel={`spark-${metricLabel}`}
numberFormat={column.d3format}
yAxisBounds={column.yAxisBounds}
showYAxis={column.showYAxis}
renderTooltip={({ index }) => (
<div>
<strong>{d3format(column.d3format, sparkData[index])}</strong>
<strong>{d3format(column.d3Format, sparkData[index])}</strong>
<div>{formatDate(data[index].iso)}</div>
</div>
)}
>
{({ onMouseLeave, onMouseMove, tooltipData }) => (
<Sparkline
ariaLabel={`spark-${metricLabel}`}
width={parseInt(column.width, 10) || 300}
height={parseInt(column.height, 10) || 50}
margin={SPARKLINE_MARGIN}
data={sparkData}
onMouseLeave={onMouseLeave}
onMouseMove={onMouseMove}
>
<LineSeries
showArea={false}
stroke="#767676"
/>
{tooltipData &&
<VerticalReferenceLine
reference={tooltipData.index}
strokeDasharray="3 3"
strokeWidth={1}
/>}
{tooltipData &&
<PointSeries
points={[tooltipData.index]}
fill="#767676"
strokeWidth={1}
/>}
</Sparkline>
)}
</WithTooltip>
/>
),
};
} else {
Expand Down Expand Up @@ -200,6 +165,7 @@ function viz(slice, payload) {
});
return row;
});

ReactDOM.render(
<Table
className="table table-no-hover"
Expand Down