This is an automated email from the ASF dual-hosted git repository. ccwilliams 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 66fcf9b [formats] add better defaults for time + number formatting (#4843) 66fcf9b is described below commit 66fcf9b687bdd6cc8a6039f002ed87193a16b365 Author: Chris Williams <willias...@users.noreply.github.com> AuthorDate: Fri Apr 20 15:55:25 2018 -0700 [formats] add better defaults for time + number formatting (#4843) * [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 --- .../assets/spec/javascripts/modules/dates_spec.js | 29 ++++++++++++++-- .../assets/spec/javascripts/modules/utils_spec.jsx | 13 +++++-- superset/assets/src/explore/stores/controls.jsx | 18 +++++----- superset/assets/src/modules/dates.js | 40 +++++++++++++++++++++- superset/assets/src/visualizations/nvd3_vis.js | 35 ++++++++++++------- superset/assets/src/visualizations/sunburst.js | 4 +-- superset/assets/src/visualizations/world_map.js | 6 ++-- 7 files changed, 111 insertions(+), 34 deletions(-) diff --git a/superset/assets/spec/javascripts/modules/dates_spec.js b/superset/assets/spec/javascripts/modules/dates_spec.js index 94dbfa6..7101b1d 100644 --- a/superset/assets/spec/javascripts/modules/dates_spec.js +++ b/superset/assets/spec/javascripts/modules/dates_spec.js @@ -3,6 +3,7 @@ import { expect } from 'chai'; import { tickMultiFormat, formatDate, + formatDateVerbose, fDuration, now, epochTimeXHoursAgo, @@ -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'); }); }); diff --git a/superset/assets/spec/javascripts/modules/utils_spec.jsx b/superset/assets/spec/javascripts/modules/utils_spec.jsx index f219c6e..4a319af 100644 --- a/superset/assets/spec/javascripts/modules/utils_spec.jsx +++ b/superset/assets/spec/javascripts/modules/utils_spec.jsx @@ -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'; @@ -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'); @@ -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'); }); diff --git a/superset/assets/src/explore/stores/controls.jsx b/superset/assets/src/explore/stores/controls.jsx index 0a64433..2dc7cf2 100644 --- a/superset/assets/src/explore/stores/controls.jsx +++ b/superset/assets/src/explore/stores/controls.jsx @@ -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]; @@ -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'), }, diff --git a/superset/assets/src/modules/dates.js b/superset/assets/src/modules/dates.js index 91fde25..f8d8684 100644 --- a/superset/assets/src/modules/dates.js +++ b/superset/assets/src/modules/dates.js @@ -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) { @@ -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; diff --git a/superset/assets/src/visualizations/nvd3_vis.js b/superset/assets/src/visualizations/nvd3_vis.js index bf87287..4a3faba 100644 --- a/superset/assets/src/visualizations/nvd3_vis.js +++ b/superset/assets/src/visualizations/nvd3_vis.js @@ -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'; @@ -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; @@ -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; @@ -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; @@ -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; }); @@ -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); @@ -397,6 +396,13 @@ 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) { @@ -404,10 +410,12 @@ export default function nvd3Vis(slice, payload) { 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) => { @@ -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) => { diff --git a/superset/assets/src/visualizations/sunburst.js b/superset/assets/src/visualizations/sunburst.js index 0c04622..0c9c2cc 100644 --- a/superset/assets/src/visualizations/sunburst.js +++ b/superset/assets/src/visualizations/sunburst.js @@ -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(); diff --git a/superset/assets/src/visualizations/world_map.js b/superset/assets/src/visualizations/world_map.js index a9ab714..e7c1047 100644 --- a/superset/assets/src/visualizations/world_map.js +++ b/superset/assets/src/visualizations/world_map.js @@ -39,7 +39,7 @@ function worldMapChart(slice, payload) { mapData[d.country] = d; }); - const f = d3.format('.3s'); + const formatter = d3.format('.3s'); container.show(); @@ -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: { @@ -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, -- To stop receiving notification emails like this one, please contact ccwilli...@apache.org.