This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin 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 76a2f95 [time series table] visual improvements (#3957)
76a2f95 is described below
commit 76a2f952310cd66ec71e83f99f9e49d6499b7044
Author: Chris Williams <[email protected]>
AuthorDate: Thu Nov 30 20:48:17 2017 -0800
[time series table] visual improvements (#3957)
* [time series table] visual improvements
* [time series table] don't set cell color if text color isn't set
---
.../controls/TimeSeriesColumnControl.jsx | 33 +++++---
superset/assets/javascripts/modules/dates.js | 17 +++-
superset/assets/package.json | 4 +-
superset/assets/stylesheets/superset.less | 4 +
superset/assets/visualizations/time_table.jsx | 91 +++++++++++++---------
5 files changed, 96 insertions(+), 53 deletions(-)
diff --git
a/superset/assets/javascripts/explore/components/controls/TimeSeriesColumnControl.jsx
b/superset/assets/javascripts/explore/components/controls/TimeSeriesColumnControl.jsx
index cd8ec98..2634dd7 100644
---
a/superset/assets/javascripts/explore/components/controls/TimeSeriesColumnControl.jsx
+++
b/superset/assets/javascripts/explore/components/controls/TimeSeriesColumnControl.jsx
@@ -33,7 +33,7 @@ const colTypeOptions = [
export default class TimeSeriesColumnControl extends React.Component {
constructor(props) {
super(props);
- const state = Object.assign({}, props);
+ const state = { ...props };
delete state.onChange;
this.state = state;
this.onChange = this.onChange.bind(this);
@@ -61,7 +61,7 @@ export default class TimeSeriesColumnControl extends
React.Component {
return (
<Row style={{ marginTop: '5px' }}>
<Col md={5}>
- {label}{' '}
+ {`${label} `}
<InfoTooltipWithTrigger
placement="top"
tooltip={tooltip}
@@ -75,7 +75,7 @@ export default class TimeSeriesColumnControl extends
React.Component {
renderPopover() {
return (
<Popover id="ts-col-popo" title="Column Configuration">
- <div style={{ width: '280px' }}>
+ <div style={{ width: 300 }}>
{this.formRow(
'Label',
'The column header label',
@@ -166,13 +166,11 @@ export default class TimeSeriesColumnControl extends
React.Component {
/>,
)}
{this.state.colType !== 'spark' && this.formRow(
- 'Bounds',
+ 'Color bounds',
(
- 'Number bounds used for color coding from red to green. ' +
- 'Reverse the number for green to red. To get boolean ' +
- 'red or green without spectrum, you can use either only ' +
- 'min, or max, depending on whether small or big should be ' +
- 'green or red.'
+ `Number bounds used for color encoding from red to blue.
+ Reverse the numbers for blue to red. To get pure red or blue,
+ you can enter either only min or max.`
),
'bounds',
<BoundsControl
@@ -181,14 +179,25 @@ export default class TimeSeriesColumnControl extends
React.Component {
/>,
)}
{this.formRow(
- 'D3 format',
- 'D3 format string',
+ 'Number format',
+ 'Optional d3 number format string',
'd3-format',
<FormControl
value={this.state.d3format}
onChange={this.onTextInputChange.bind(this, 'd3format')}
bsSize="small"
- placeholder="D3 format string"
+ placeholder="Number format string"
+ />,
+ )}
+ {this.state.colType === 'spark' && this.formRow(
+ 'Date format',
+ 'Optional d3 date format string',
+ 'date-format',
+ <FormControl
+ value={this.state.dateFormat}
+ onChange={this.onTextInputChange.bind(this, 'dateFormat')}
+ bsSize="small"
+ placeholder="Date format string"
/>,
)}
</div>
diff --git a/superset/assets/javascripts/modules/dates.js
b/superset/assets/javascripts/modules/dates.js
index 88b62c5..c1bf47d 100644
--- a/superset/assets/javascripts/modules/dates.js
+++ b/superset/assets/javascripts/modules/dates.js
@@ -66,10 +66,23 @@ export const formatDate = function (dttm) {
const d = UTC(new Date(dttm));
return tickMultiFormat(d);
};
-export const fDuration = function (t1, t2, f = 'HH:mm:ss.SS') {
+
+export const formatDateThunk = function (format) {
+ if (!format) {
+ return formatDate;
+ }
+
+ const formatter = d3.time.format(format);
+ return (dttm) => {
+ const d = UTC(new Date(dttm));
+ return formatter(d);
+ };
+};
+
+export const fDuration = function (t1, t2, format = 'HH:mm:ss.SS') {
const diffSec = t2 - t1;
const duration = moment(new Date(diffSec));
- return duration.utc().format(f);
+ return duration.utc().format(format);
};
export const now = function () {
diff --git a/superset/assets/package.json b/superset/assets/package.json
index c3c2174..eb3af55 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -39,8 +39,8 @@
},
"homepage": "http://superset.apache.org/",
"dependencies": {
- "@data-ui/event-flow": "0.0.8",
- "@data-ui/sparkline": "0.0.47",
+ "@data-ui/event-flow": "^0.0.8",
+ "@data-ui/sparkline": "^0.0.49",
"babel-register": "^6.24.1",
"bootstrap": "^3.3.6",
"brace": "^0.10.0",
diff --git a/superset/assets/stylesheets/superset.less
b/superset/assets/stylesheets/superset.less
index 49c6544..6f6b502 100644
--- a/superset/assets/stylesheets/superset.less
+++ b/superset/assets/stylesheets/superset.less
@@ -218,6 +218,10 @@ div.widget {
float: left;
}
+table.table-no-hover tr:hover {
+ background-color: initial;
+}
+
.editable-title input {
padding: 2px 6px 3px 6px;
}
diff --git a/superset/assets/visualizations/time_table.jsx
b/superset/assets/visualizations/time_table.jsx
index cb145c8..0347c41 100644
--- a/superset/assets/visualizations/time_table.jsx
+++ b/superset/assets/visualizations/time_table.jsx
@@ -7,8 +7,8 @@ import Mustache from 'mustache';
import { Sparkline, LineSeries, PointSeries, VerticalReferenceLine,
WithTooltip } from '@data-ui/sparkline';
import MetricOption from '../javascripts/components/MetricOption';
-import { d3format, brandColor } from '../javascripts/modules/utils';
-import { formatDate } from '../javascripts/modules/dates';
+import { d3format } from '../javascripts/modules/utils';
+import { formatDateThunk } from '../javascripts/modules/dates';
import InfoTooltipWithTrigger from
'../javascripts/components/InfoTooltipWithTrigger';
import './time_table.css';
@@ -18,6 +18,13 @@ const SPARKLINE_MARGIN = {
bottom: 8,
left: 8,
};
+const sparklineTooltipProps = {
+ style: {
+ opacity: 0.8,
+ },
+ offsetTop: 0,
+};
+
const ACCESSIBLE_COLOR_BOUNDS = ['#ca0020', '#0571b0'];
function FormattedNumber({ num, format }) {
@@ -65,16 +72,16 @@ function viz(slice, payload) {
leftCell = url ? <a href={url} target="_blank">{metric}</a> : metric;
}
const row = { metric: leftCell };
- fd.column_collection.forEach((c) => {
- if (c.colType === 'spark') {
+ fd.column_collection.forEach((column) => {
+ if (column.colType === 'spark') {
let sparkData;
- if (!c.timeRatio) {
+ if (!column.timeRatio) {
sparkData = data.map(d => d[metric]);
} else {
// Period ratio sparkline
sparkData = [];
- for (let i = c.timeRatio; i < data.length; i++) {
- const prevData = data[i - c.timeRatio][metric];
+ for (let i = column.timeRatio; i < data.length; i++) {
+ const prevData = data[i - column.timeRatio][metric];
if (prevData && prevData !== 0) {
sparkData.push(data[i][metric] / prevData);
} else {
@@ -82,13 +89,16 @@ function viz(slice, payload) {
}
}
}
- row[c.key] = {
+ const formatDate = formatDateThunk(column.dateFormat);
+ row[column.key] = {
data: sparkData[sparkData.length - 1],
display: (
<WithTooltip
+ tooltipProps={sparklineTooltipProps}
+ hoverStyles={null}
renderTooltip={({ index }) => (
<div>
- <strong>{d3format(c.d3format, sparkData[index])}</strong>
+ <strong>{d3format(column.d3format,
sparkData[index])}</strong>
<div>{formatDate(data[index].iso)}</div>
</div>
)}
@@ -96,8 +106,8 @@ function viz(slice, payload) {
{({ onMouseLeave, onMouseMove, tooltipData }) => (
<Sparkline
ariaLabel={`spark-${metric}`}
- width={parseInt(c.width, 10) || 300}
- height={parseInt(c.height, 10) || 50}
+ width={parseInt(column.width, 10) || 300}
+ height={parseInt(column.height, 10) || 50}
margin={SPARKLINE_MARGIN}
data={sparkData}
onMouseLeave={onMouseLeave}
@@ -105,7 +115,7 @@ function viz(slice, payload) {
>
<LineSeries
showArea={false}
- stroke={brandColor}
+ stroke="#767676"
/>
{tooltipData &&
<VerticalReferenceLine
@@ -116,7 +126,7 @@ function viz(slice, payload) {
{tooltipData &&
<PointSeries
points={[tooltipData.index]}
- fill={brandColor}
+ fill="#767676"
strokeWidth={1}
/>}
</Sparkline>
@@ -127,47 +137,53 @@ function viz(slice, payload) {
} else {
const recent = reversedData[0][metric];
let v;
- if (c.colType === 'time') {
+ if (column.colType === 'time') {
// Time lag ratio
- v = reversedData[parseInt(c.timeLag, 10)][metric];
- if (c.comparisonType === 'diff') {
+ v = reversedData[parseInt(column.timeLag, 10)][metric];
+ if (column.comparisonType === 'diff') {
v = recent - v;
- } else if (c.comparisonType === 'perc') {
+ } else if (column.comparisonType === 'perc') {
v = recent / v;
- } else if (c.comparisonType === 'perc_change') {
+ } else if (column.comparisonType === 'perc_change') {
v = (recent / v) - 1;
}
- } else if (c.colType === 'contrib') {
+ } else if (column.colType === 'contrib') {
// contribution to column total
v = recent / Object.keys(reversedData[0])
- .map(k => k !== 'iso' ? reversedData[0][k] : null)
- .reduce((a, b) => a + b);
- } else if (c.colType === 'avg') {
+ .map(k => k !== 'iso' ? reversedData[0][k] : null)
+ .reduce((a, b) => a + b);
+ } else if (column.colType === 'avg') {
// Average over the last {timeLag}
v = reversedData
- .map((k, i) => i < c.timeLag ? k[metric] : 0)
- .reduce((a, b) => a + b) / c.timeLag;
+ .map((k, i) => i < column.timeLag ? k[metric] : 0)
+ .reduce((a, b) => a + b) / column.timeLag;
}
let color;
- if (c.bounds && c.bounds[0] !== null && c.bounds[1] !== null) {
+ if (column.bounds && column.bounds[0] !== null && column.bounds[1] !==
null) {
const scaler = d3.scale.linear()
.domain([
- c.bounds[0],
- c.bounds[0] + ((c.bounds[1] - c.bounds[0]) / 2),
- c.bounds[1]])
+ column.bounds[0],
+ column.bounds[0] + ((column.bounds[1] - column.bounds[0]) / 2),
+ column.bounds[1],
+ ])
.range([ACCESSIBLE_COLOR_BOUNDS[0], 'grey',
ACCESSIBLE_COLOR_BOUNDS[1]]);
color = scaler(v);
- } else if (c.bounds && c.bounds[0] !== null) {
- color = v >= c.bounds[0] ? ACCESSIBLE_COLOR_BOUNDS[1] :
ACCESSIBLE_COLOR_BOUNDS[0];
- } else if (c.bounds && c.bounds[1] !== null) {
- color = v < c.bounds[1] ? ACCESSIBLE_COLOR_BOUNDS[1] :
ACCESSIBLE_COLOR_BOUNDS[0];
+ } else if (column.bounds && column.bounds[0] !== null) {
+ color = v >= column.bounds[0] ? ACCESSIBLE_COLOR_BOUNDS[1] :
ACCESSIBLE_COLOR_BOUNDS[0];
+ } else if (column.bounds && column.bounds[1] !== null) {
+ color = v < column.bounds[1] ? ACCESSIBLE_COLOR_BOUNDS[1] :
ACCESSIBLE_COLOR_BOUNDS[0];
}
- row[c.key] = {
+ row[column.key] = {
data: v,
display: (
- <span style={{ color }}>
- <FormattedNumber num={v} format={c.d3format} />
- </span>),
+ <div style={{ color }}>
+ <FormattedNumber num={v} format={column.d3format} />
+ </div>
+ ),
+ style: color && {
+ boxShadow: `inset 0px -2.5px 0px 0px ${color}`,
+ borderRight: '2px solid #fff',
+ },
};
}
});
@@ -175,7 +191,7 @@ function viz(slice, payload) {
});
ReactDOM.render(
<Table
- className="table table-condensed"
+ className="table table-no-hover"
defaultSort={defaultSort}
sortBy={defaultSort}
sortable={fd.column_collection.map(c => c.key)}
@@ -201,6 +217,7 @@ function viz(slice, payload) {
column={c.key}
key={c.key}
value={row[c.key].data}
+ style={row[c.key].style}
>
{row[c.key].display}
</Td>))}
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].