Skip to content

Commit

Permalink
[formats] add better defaults for time + number formatting (apache#4843)
Browse files Browse the repository at this point in the history
* [formats] add better defaults for time + number formatting

* [formatDate] add tests for concise formatDate

* [nvd3] use verbose time format in tooltips

* [number format] improve number format description

* [formats] revert to .3s defaults, tweak number format preview

* [formats] default number vis to .3s
  • Loading branch information
williaster authored and michellethomas committed May 23, 2018
1 parent 11c2a6f commit d7d4627
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 34 deletions.
29 changes: 26 additions & 3 deletions superset/assets/spec/javascripts/modules/dates_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { expect } from 'chai';
import {
tickMultiFormat,
formatDate,
formatDateVerbose,
fDuration,
now,
epochTimeXHoursAgo,
Expand All @@ -25,13 +26,35 @@ describe('formatDate', () => {
expect(formatDate(new Date('2020-01-01'))).to.equal('2020');
});

it('shows only month when 1st of month', () => {
expect(formatDate(new Date('2020-03-01'))).to.equal('March');
});

it('does not show day of week when it is Sunday', () => {
expect(formatDate(new Date('2020-03-15'))).to.equal('Mar 15');
});

it('shows weekday when it is not Sunday (and no ms/sec/min/hr)', () => {
expect(formatDate(new Date('2020-03-03'))).to.equal('Tue 03');
});
});

describe('formatDateVerbose', () => {
it('is a function', () => {
assert.isFunction(formatDateVerbose);
});

it('shows only year when 1st day of the year', () => {
expect(formatDateVerbose(new Date('2020-01-01'))).to.equal('2020');
});

it('shows month and year when 1st of month', () => {
expect(formatDate(new Date('2020-03-01'))).to.equal('Mar 2020');
expect(formatDateVerbose(new Date('2020-03-01'))).to.equal('Mar 2020');
});

it('shows weekday when any day of the month', () => {
expect(formatDate(new Date('2020-03-03'))).to.equal('Tue Mar 3');
expect(formatDate(new Date('2020-03-15'))).to.equal('Sun Mar 15');
expect(formatDateVerbose(new Date('2020-03-03'))).to.equal('Tue Mar 3');
expect(formatDateVerbose(new Date('2020-03-15'))).to.equal('Sun Mar 15');
});
});

Expand Down
13 changes: 10 additions & 3 deletions superset/assets/spec/javascripts/modules/utils_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { it, describe } from 'mocha';
import { expect } from 'chai';
import {
tryNumify, slugify, formatSelectOptionsForRange, d3format,
d3FormatPreset, d3TimeFormatPreset, defaultNumberFormatter,
tryNumify,
slugify,
formatSelectOptionsForRange,
d3format,
d3FormatPreset,
d3TimeFormatPreset,
defaultNumberFormatter,
mainMetric,
} from '../../../src/modules/utils';

Expand Down Expand Up @@ -53,12 +58,13 @@ describe('utils', () => {
expect(d3FormatPreset('smart_date')(0)).to.equal('1970');
});
});
describe('d3TimeFormatPreset', () => {
describe('defaultNumberFormatter', () => {
expect(defaultNumberFormatter(10)).to.equal('10');
expect(defaultNumberFormatter(1)).to.equal('1');
expect(defaultNumberFormatter(1.0)).to.equal('1');
expect(defaultNumberFormatter(10.0)).to.equal('10');
expect(defaultNumberFormatter(10001)).to.equal('10.0k');
expect(defaultNumberFormatter(10100)).to.equal('10.1k');
expect(defaultNumberFormatter(111000000)).to.equal('111M');
expect(defaultNumberFormatter(0.23)).to.equal('230m');

Expand All @@ -67,6 +73,7 @@ describe('utils', () => {
expect(defaultNumberFormatter(-1.0)).to.equal('-1');
expect(defaultNumberFormatter(-10.0)).to.equal('-10');
expect(defaultNumberFormatter(-10001)).to.equal('-10.0k');
expect(defaultNumberFormatter(-10101)).to.equal('-10.1k');
expect(defaultNumberFormatter(-111000000)).to.equal('-111M');
expect(defaultNumberFormatter(-0.23)).to.equal('-230m');
});
Expand Down
18 changes: 9 additions & 9 deletions superset/assets/src/explore/stores/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ const D3_FORMAT_DOCS = 'D3 format syntax: https://github.com/d3/d3-format';

// input choices & options
const D3_FORMAT_OPTIONS = [
['.1s', '.1s | 12k'],
['.3s', '.3s | 12.3k'],
['.1%', '.1% | 12.3%'],
['.3%', '.3% | 1234543.210%'],
['.4r', '.4r | 12350'],
['.3f', '.3f | 12345.432'],
['+,', '+, | +12,345.4321'],
['$,.2f', '$,.2f | $12,345.43'],
['.1s', '.1s (12345.432 => 10k)'],
['.3s', '.3s (12345.432 => 12.3k)'],
[',.1%', ',.1% (12345.432 => 1,234,543.2%)'],
['.3%', '.3% (12345.432 => 1234543.200%)'],
['.4r', '.4r (12345.432 => 12350)'],
[',.3f', ',.3f (12345.432 => 12,345.432)'],
['+,', '+, (12345.432 => +12,345.432)'],
['$,.2f', '$,.2f (12345.432 => $12,345.43)'],
];

const ROW_LIMIT_OPTIONS = [10, 50, 100, 250, 500, 1000, 5000, 10000, 50000];
Expand Down Expand Up @@ -1537,7 +1537,7 @@ export const controls = {
type: 'CheckboxControl',
label: t('Rich Tooltip'),
renderTrigger: true,
default: true,
default: false,
description: t('The rich tooltip shows a list of all series for that ' +
'point in time'),
},
Expand Down
40 changes: 39 additions & 1 deletion superset/assets/src/modules/dates.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,40 @@ export function UTC(dttm) {
dttm.getUTCSeconds(),
);
}
export const tickMultiFormat = d3.time.format.multi([

export const tickMultiFormat = (() => {
const formatMillisecond = d3.time.format('.%Lms');
const formatSecond = d3.time.format(':%Ss');
const formatMinute = d3.time.format('%I:%M');
const formatHour = d3.time.format('%I %p');
const formatDay = d3.time.format('%a %d');
const formatWeek = d3.time.format('%b %d');
const formatMonth = d3.time.format('%B');
const formatYear = d3.time.format('%Y');

return function tickMultiFormatConcise(date) {
let formatter;
if (d3.time.second(date) < date) {
formatter = formatMillisecond;
} else if (d3.time.minute(date) < date) {
formatter = formatSecond;
} else if (d3.time.hour(date) < date) {
formatter = formatMinute;
} else if (d3.time.day(date) < date) {
formatter = formatHour;
} else if (d3.time.month(date) < date) {
formatter = d3.time.week(date) < date ? formatDay : formatWeek;
} else if (d3.time.year(date) < date) {
formatter = formatMonth;
} else {
formatter = formatYear;
}

return formatter(date);
};
})();

export const tickMultiFormatVerbose = d3.time.format.multi([
[
'.%L',
function (d) {
Expand Down Expand Up @@ -74,6 +107,11 @@ export const formatDate = function (dttm) {
return tickMultiFormat(d);
};

export const formatDateVerbose = function (dttm) {
const d = UTC(new Date(dttm));
return tickMultiFormatVerbose(d);
};

export const formatDateThunk = function (format) {
if (!format) {
return formatDate;
Expand Down
35 changes: 22 additions & 13 deletions superset/assets/src/visualizations/nvd3_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import AnnotationTypes, {
applyNativeColumns,
} from '../modules/AnnotationTypes';
import { customizeToolTip, d3TimeFormatPreset, d3FormatPreset, tryNumify } from '../modules/utils';
import { formatDateVerbose } from '../modules/dates';
import { isTruthy } from '../utils/common';
import { t } from '../locales';

Expand Down Expand Up @@ -136,7 +137,7 @@ export default function nvd3Vis(slice, payload) {
};

const vizType = fd.viz_type;
const f = d3.format('.3s');
const formatter = d3.format('.3s');
const reduceXTicks = fd.reduce_x_ticks || false;
let stacked = false;
let row;
Expand All @@ -156,8 +157,6 @@ export default function nvd3Vis(slice, payload) {
if (fd.x_ticks_layout === 'auto') {
if (['column', 'dist_bar'].indexOf(vizType) >= 0) {
xLabelRotation = 45;
} else if (isTimeSeries) {
staggerLabels = true;
}
} else if (fd.x_ticks_layout === 'staggered') {
staggerLabels = true;
Expand Down Expand Up @@ -187,8 +186,6 @@ export default function nvd3Vis(slice, payload) {
} else {
chart = nv.models.lineChart();
}
// To alter the tooltip header
// chart.interactiveLayer.tooltip.headerFormatter(function(){return '';});
chart.xScale(d3.time.scale.utc());
chart.interpolate(fd.line_interpolation);
break;
Expand Down Expand Up @@ -303,9 +300,9 @@ export default function nvd3Vis(slice, payload) {
`<tr><td style="color: ${p.color};">` +
`<strong>${p[fd.entity]}</strong> (${p.group})` +
'</td></tr>');
s += row(fd.x, f(p.x));
s += row(fd.y, f(p.y));
s += row(fd.size, f(p.size));
s += row(fd.x, formatter(p.x));
s += row(fd.y, formatter(p.y));
s += row(fd.size, formatter(p.size));
s += '</table>';
return s;
});
Expand Down Expand Up @@ -375,6 +372,8 @@ export default function nvd3Vis(slice, payload) {
let xAxisFormatter = d3FormatPreset(fd.x_axis_format);
if (isTimeSeries) {
xAxisFormatter = d3TimeFormatPreset(fd.x_axis_format);
// In tooltips, always use the verbose time format
chart.interactiveLayer.tooltip.headerFormatter(formatDateVerbose);
}
if (chart.x2Axis && chart.x2Axis.tickFormat) {
chart.x2Axis.tickFormat(xAxisFormatter);
Expand All @@ -397,17 +396,26 @@ export default function nvd3Vis(slice, payload) {
chart.y2Axis.tickFormat(yAxisFormatter);
}

if (chart.yAxis) {
chart.yAxis.ticks(5);
}
if (chart.y2Axis) {
chart.y2Axis.ticks(5);
}


// Set showMaxMin for all axis
function setAxisShowMaxMin(axis, showminmax) {
if (axis && axis.showMaxMin && showminmax !== undefined) {
axis.showMaxMin(showminmax);
}
}
setAxisShowMaxMin(chart.xAxis, fd.x_axis_showminmax);
setAxisShowMaxMin(chart.x2Axis, fd.x_axis_showminmax);
setAxisShowMaxMin(chart.yAxis, fd.y_axis_showminmax);
setAxisShowMaxMin(chart.y2Axis, fd.y_axis_showminmax);

// If these are undefined, they register as truthy
setAxisShowMaxMin(chart.xAxis, fd.x_axis_showminmax || false);
setAxisShowMaxMin(chart.x2Axis, fd.x_axis_showminmax || false);
setAxisShowMaxMin(chart.yAxis, fd.y_axis_showminmax || false);
setAxisShowMaxMin(chart.y2Axis, fd.y_axis_showminmax || false);

if (vizType === 'time_pivot') {
chart.color((d) => {
Expand All @@ -425,10 +433,11 @@ export default function nvd3Vis(slice, payload) {
chart.useInteractiveGuideline(true);
if (vizType === 'line') {
// Custom sorted tooltip
// use a verbose formatter for times
chart.interactiveLayer.tooltip.contentGenerator((d) => {
let tooltip = '';
tooltip += "<table><thead><tr><td colspan='3'>"
+ `<strong class='x-value'>${xAxisFormatter(d.value)}</strong>`
+ `<strong class='x-value'>${formatDateVerbose(d.value)}</strong>`
+ '</td></tr></thead><tbody>';
d.series.sort((a, b) => a.value >= b.value ? -1 : 1);
d.series.forEach((series) => {
Expand Down
4 changes: 2 additions & 2 deletions superset/assets/src/visualizations/sunburst.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ function sunburstVis(slice, payload) {
return Math.sqrt(d.y + d.dy);
});

const formatNum = d3.format('.3s');
const formatPerc = d3.format('.3p');
const formatNum = d3.format('.1s');
const formatPerc = d3.format('.1p');

container.select('svg').remove();

Expand Down
6 changes: 3 additions & 3 deletions superset/assets/src/visualizations/world_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function worldMapChart(slice, payload) {
mapData[d.country] = d;
});

const f = d3.format('.3s');
const formatter = d3.format('.3s');

container.show();

Expand All @@ -58,7 +58,7 @@ function worldMapChart(slice, payload) {
highlightFillColor: '#005a63',
highlightBorderWidth: 1,
popupTemplate: (geo, d) => (
`<div class="hoverinfo"><strong>${d.name}</strong><br>${f(d.m1)}</div>`
`<div class="hoverinfo"><strong>${d.name}</strong><br>${formatter(d.m1)}</div>`
),
},
bubblesConfig: {
Expand All @@ -68,7 +68,7 @@ function worldMapChart(slice, payload) {
popupOnHover: true,
radius: null,
popupTemplate: (geo, d) => (
`<div class="hoverinfo"><strong>${d.name}</strong><br>${f(d.m2)}</div>`
`<div class="hoverinfo"><strong>${d.name}</strong><br>${formatter(d.m2)}</div>`
),
fillOpacity: 0.5,
animate: true,
Expand Down

0 comments on commit d7d4627

Please sign in to comment.