This is an automated email from the ASF dual-hosted git repository.
kristw 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 fcec748 Use @superset-ui/number-format and @superset-ui/time-format
for formatting. (#6470)
fcec748 is described below
commit fcec748b62b333fbfbba23c6d49cdf98f7d6a280
Author: Krist Wongsuphasawat <[email protected]>
AuthorDate: Tue Dec 4 13:24:07 2018 -0800
Use @superset-ui/number-format and @superset-ui/time-format for formatting.
(#6470)
refactor: proxy all d3 number and time formatting calls
---
superset/assets/package.json | 2 +
.../assets/spec/javascripts/modules/dates_spec.js | 52 +---------
.../assets/spec/javascripts/modules/utils_spec.jsx | 52 ----------
.../javascripts/visualizations/nvd3/utils_spec.js | 26 ++++-
.../src/explore/components/RowCountLabel.jsx | 5 +-
superset/assets/src/modules/dates.js | 112 ---------------------
superset/assets/src/modules/utils.js | 64 ------------
superset/assets/src/preamble.js | 4 +
superset/assets/src/setup/setupFormatters.js | 35 +++++++
.../src/visualizations/BigNumber/BigNumber.jsx | 4 +-
.../src/visualizations/BigNumber/transformProps.js | 7 +-
.../assets/src/visualizations/Calendar/Calendar.js | 8 +-
superset/assets/src/visualizations/Chord/Chord.js | 3 +-
.../src/visualizations/CountryMap/CountryMap.js | 4 +-
.../assets/src/visualizations/Heatmap/Heatmap.js | 5 +-
.../src/visualizations/Partition/Partition.js | 7 +-
.../src/visualizations/PivotTable/PivotTable.js | 5 +-
superset/assets/src/visualizations/Rose/Rose.js | 7 +-
.../assets/src/visualizations/Sankey/Sankey.js | 3 +-
.../assets/src/visualizations/Sunburst/Sunburst.js | 5 +-
superset/assets/src/visualizations/Table/Table.js | 13 +--
.../visualizations/TimeTable/FormattedNumber.jsx | 4 +-
.../src/visualizations/TimeTable/SparklineCell.jsx | 6 +-
.../src/visualizations/TimeTable/TimeTable.jsx | 10 +-
.../assets/src/visualizations/Treemap/Treemap.js | 3 +-
.../assets/src/visualizations/WorldMap/WorldMap.js | 3 +-
superset/assets/src/visualizations/nvd3/NVD3Vis.js | 27 ++---
superset/assets/src/visualizations/nvd3/utils.js | 15 ++-
superset/assets/yarn.lock | 17 +++-
29 files changed, 162 insertions(+), 346 deletions(-)
diff --git a/superset/assets/package.json b/superset/assets/package.json
index f1940c7..466ad3e 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -55,6 +55,8 @@
"@superset-ui/color": "^0.7.0",
"@superset-ui/connection": "^0.5.0",
"@superset-ui/core": "^0.7.0",
+ "@superset-ui/number-format": "^0.7.2",
+ "@superset-ui/time-format": "^0.7.2",
"@superset-ui/translation": "^0.7.0",
"@vx/legend": "^0.0.170",
"@vx/responsive": "0.0.172",
diff --git a/superset/assets/spec/javascripts/modules/dates_spec.js
b/superset/assets/spec/javascripts/modules/dates_spec.js
index 3f39343..d9f97e7 100644
--- a/superset/assets/spec/javascripts/modules/dates_spec.js
+++ b/superset/assets/spec/javascripts/modules/dates_spec.js
@@ -1,60 +1,10 @@
import {
- tickMultiFormat,
- formatDate,
- formatDateVerbose,
fDuration,
now,
epochTimeXHoursAgo,
epochTimeXDaysAgo,
epochTimeXYearsAgo,
- } from '../../../src/modules/dates';
-
-describe('tickMultiFormat', () => {
- it('is a function', () => {
- expect(typeof tickMultiFormat).toBe('function');
- });
-});
-
-describe('formatDate', () => {
- it('is a function', () => {
- expect(typeof formatDate).toBe('function');
- });
-
- it('shows only year when 1st day of the year', () => {
- expect(formatDate(new Date('2020-01-01'))).toBe('2020');
- });
-
- it('shows only month when 1st of month', () => {
- expect(formatDate(new Date('2020-03-01'))).toBe('March');
- });
-
- it('does not show day of week when it is Sunday', () => {
- expect(formatDate(new Date('2020-03-15'))).toBe('Mar 15');
- });
-
- it('shows weekday when it is not Sunday (and no ms/sec/min/hr)', () => {
- expect(formatDate(new Date('2020-03-03'))).toBe('Tue 03');
- });
-});
-
-describe('formatDateVerbose', () => {
- it('is a function', () => {
- expect(typeof formatDateVerbose).toBe('function');
- });
-
- it('shows only year when 1st day of the year', () => {
- expect(formatDateVerbose(new Date('2020-01-01'))).toBe('2020');
- });
-
- it('shows month and year when 1st of month', () => {
- expect(formatDateVerbose(new Date('2020-03-01'))).toBe('Mar 2020');
- });
-
- it('shows weekday when any day of the month', () => {
- expect(formatDateVerbose(new Date('2020-03-03'))).toBe('Tue Mar 3');
- expect(formatDateVerbose(new Date('2020-03-15'))).toBe('Sun Mar 15');
- });
-});
+} from '../../../src/modules/dates';
describe('fDuration', () => {
it('is a function', () => {
diff --git a/superset/assets/spec/javascripts/modules/utils_spec.jsx
b/superset/assets/spec/javascripts/modules/utils_spec.jsx
index ca16d86..79a2044 100644
--- a/superset/assets/spec/javascripts/modules/utils_spec.jsx
+++ b/superset/assets/spec/javascripts/modules/utils_spec.jsx
@@ -1,9 +1,5 @@
import {
formatSelectOptionsForRange,
- d3format,
- d3FormatPreset,
- d3TimeFormatPreset,
- defaultNumberFormatter,
mainMetric,
roundDecimal,
} from '../../../src/modules/utils';
@@ -25,54 +21,6 @@ describe('utils', () => {
});
});
- describe('d3format', () => {
- it('returns a string formatted number as specified', () => {
- expect(d3format('.3s', 1234)).toBe('1.23k');
- expect(d3format('.3s', 1237)).toBe('1.24k');
- expect(d3format('', 1237)).toBe('1.24k');
- expect(d3format('.2efd2.ef.2.e', 1237)).toBe('1237 (Invalid format:
.2efd2.ef.2.e)');
- });
- });
-
- describe('d3FormatPreset', () => {
- it('is a function', () => {
- expect(typeof d3FormatPreset).toBe('function');
- });
- it('returns a working formatter', () => {
- expect(d3FormatPreset('.3s')(3000000)).toBe('3.00M');
- });
- });
-
- describe('d3TimeFormatPreset', () => {
- it('is a function', () => {
- expect(typeof d3TimeFormatPreset).toBe('function');
- });
- it('returns a working formatter', () => {
- expect(d3FormatPreset('smart_date')(0)).toBe('1970');
- expect(d3FormatPreset('%%GIBBERISH')(0)).toBe('0 (Invalid format:
%%GIBBERISH)');
- });
- });
-
- describe('defaultNumberFormatter', () => {
- expect(defaultNumberFormatter(10)).toBe('10');
- expect(defaultNumberFormatter(1)).toBe('1');
- expect(defaultNumberFormatter(1.0)).toBe('1');
- expect(defaultNumberFormatter(10.0)).toBe('10');
- expect(defaultNumberFormatter(10001)).toBe('10.0k');
- expect(defaultNumberFormatter(10100)).toBe('10.1k');
- expect(defaultNumberFormatter(111000000)).toBe('111M');
- expect(defaultNumberFormatter(0.23)).toBe('230m');
-
- expect(defaultNumberFormatter(-10)).toBe('-10');
- expect(defaultNumberFormatter(-1)).toBe('-1');
- expect(defaultNumberFormatter(-1.0)).toBe('-1');
- expect(defaultNumberFormatter(-10.0)).toBe('-10');
- expect(defaultNumberFormatter(-10001)).toBe('-10.0k');
- expect(defaultNumberFormatter(-10101)).toBe('-10.1k');
- expect(defaultNumberFormatter(-111000000)).toBe('-111M');
- expect(defaultNumberFormatter(-0.23)).toBe('-230m');
- });
-
describe('mainMetric', () => {
it('is null when no options', () => {
expect(mainMetric([])).toBeUndefined();
diff --git a/superset/assets/spec/javascripts/visualizations/nvd3/utils_spec.js
b/superset/assets/spec/javascripts/visualizations/nvd3/utils_spec.js
index 43a824e..623be1f 100644
--- a/superset/assets/spec/javascripts/visualizations/nvd3/utils_spec.js
+++ b/superset/assets/spec/javascripts/visualizations/nvd3/utils_spec.js
@@ -1,11 +1,26 @@
-import { formatLabel, tryNumify } from
'../../../../src/visualizations/nvd3/utils';
+import { getTimeOrNumberFormatter, formatLabel, tryNumify } from
'../../../../src/visualizations/nvd3/utils';
describe('nvd3/utils', () => {
- const verboseMap = {
- foo: 'Foo',
- bar: 'Bar',
- };
+ describe('getTimeOrNumberFormatter(format)', () => {
+ it('is a function', () => {
+ expect(typeof getTimeOrNumberFormatter).toBe('function');
+ });
+ it('returns a date formatter if format is smart_date', () => {
+ const time = new Date(Date.UTC(2018, 10, 21, 22, 11));
+ expect(getTimeOrNumberFormatter('smart_date')(time)).toBe('10:11');
+ });
+ it('returns a number formatter otherwise', () => {
+ expect(getTimeOrNumberFormatter('.3s')(3000000)).toBe('3.00M');
+ expect(getTimeOrNumberFormatter()(3000100)).toBe('3.00M');
+ });
+ });
+
describe('formatLabel()', () => {
+ const verboseMap = {
+ foo: 'Foo',
+ bar: 'Bar',
+ };
+
it('formats simple labels', () => {
expect(formatLabel('foo')).toBe('foo');
expect(formatLabel(['foo'])).toBe('foo');
@@ -22,6 +37,7 @@ describe('nvd3/utils', () => {
expect(formatLabel(['foo', 'bar', 'baz', '2 hours offset'],
verboseMap)).toBe('Foo, Bar, baz, 2 hours offset');
});
});
+
describe('tryNumify()', () => {
it('tryNumify works as expected', () => {
expect(tryNumify(5)).toBe(5);
diff --git a/superset/assets/src/explore/components/RowCountLabel.jsx
b/superset/assets/src/explore/components/RowCountLabel.jsx
index aea9bc4..2eeb80d 100644
--- a/superset/assets/src/explore/components/RowCountLabel.jsx
+++ b/superset/assets/src/explore/components/RowCountLabel.jsx
@@ -1,12 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Label } from 'react-bootstrap';
+import { getNumberFormatter } from '@superset-ui/number-format';
import { t } from '@superset-ui/translation';
-import { defaultNumberFormatter } from '../../modules/utils';
import TooltipWrapper from '../../components/TooltipWrapper';
-
const propTypes = {
rowcount: PropTypes.number,
limit: PropTypes.number,
@@ -21,7 +20,7 @@ const defaultProps = {
export default function RowCountLabel({ rowcount, limit, suffix }) {
const limitReached = rowcount === limit;
const bsStyle = (limitReached || rowcount === 0) ? 'danger' : 'default';
- const formattedRowCount = defaultNumberFormatter(rowcount);
+ const formattedRowCount = getNumberFormatter()(rowcount);
const tooltip = (
<span>
{limitReached &&
diff --git a/superset/assets/src/modules/dates.js
b/superset/assets/src/modules/dates.js
index 311340b..f5063b0 100644
--- a/superset/assets/src/modules/dates.js
+++ b/superset/assets/src/modules/dates.js
@@ -1,4 +1,3 @@
-import d3 from 'd3';
import moment from 'moment';
export function UTC(dttm) {
@@ -12,117 +11,6 @@ export function UTC(dttm) {
);
}
-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) {
- return d.getMilliseconds();
- },
- ],
- // If there are millisections, show only them
- [
- ':%S',
- function (d) {
- return d.getSeconds();
- },
- ],
- // If there are seconds, show only them
- [
- '%a %b %d, %I:%M %p',
- function (d) {
- return d.getMinutes() !== 0;
- },
- ],
- // If there are non-zero minutes, show Date, Hour:Minute [AM/PM]
- [
- '%a %b %d, %I %p',
- function (d) {
- return d.getHours() !== 0;
- },
- ],
- // If there are hours that are multiples of 3, show date and AM/PM
- [
- '%a %b %e',
- function (d) {
- return d.getDate() >= 10;
- },
- ],
- // If not the first of the month: "Tue Mar 2"
- [
- '%a %b%e',
- function (d) {
- return d.getDate() > 1;
- },
- ],
- // If >= 10th of the month, compensate for padding : "Sun Mar 15"
- [
- '%b %Y',
- function (d) {
- return d.getMonth() !== 0 && d.getDate() === 1;
- },
- ],
- // If the first of the month: 'Mar 2020'
- [
- '%Y',
- function () {
- return true;
- },
- ], // fall back on just year: '2020'
-]);
-export const formatDate = function (dttm) {
- const d = UTC(new Date(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 formatDateVerbose;
- }
-
- 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));
diff --git a/superset/assets/src/modules/utils.js
b/superset/assets/src/modules/utils.js
index 9fbd1c8..11d2eb3 100644
--- a/superset/assets/src/modules/utils.js
+++ b/superset/assets/src/modules/utils.js
@@ -1,70 +1,6 @@
/* eslint camelcase: 0 */
import $ from 'jquery';
-import { format as d3Format } from 'd3-format';
import { select as d3Select } from 'd3-selection';
-import { timeFormat as d3TimeFormat } from 'd3-time-format';
-import { formatDate, UTC } from './dates';
-
-const siFormatter = d3Format('.3s');
-
-export function defaultNumberFormatter(n) {
- let si = siFormatter(n);
- // Removing trailing `.00` if any
- if (si.slice(-1) < 'A') {
- si = parseFloat(si).toString();
- }
- return si;
-}
-
-export function d3FormatPreset(format) {
- // like d3Format, but with support for presets like 'smart_date'
- if (format === 'smart_date') {
- return formatDate;
- }
- if (format) {
- try {
- return d3Format(format);
- } catch (e) {
- // eslint-disable-next-line no-console
- console.warn(e);
- return value => `${value} (Invalid format: ${format})`;
- }
- }
- return defaultNumberFormatter;
-}
-
-export const d3TimeFormatPreset = function (format) {
- const effFormat = format || 'smart_date';
- if (effFormat === 'smart_date') {
- return formatDate;
- }
- const f = d3TimeFormat(effFormat);
- return function (dttm) {
- const d = UTC(new Date(dttm));
- return f(d);
- };
-};
-
-const formatters = {};
-
-export function d3format(format, number) {
- format = format || '.3s';
- // Formats a number and memoizes formatters to be reused
- if (!(format in formatters)) {
- try {
- formatters[format] = d3Format(format);
- } catch (e) {
- // eslint-disable-next-line no-console
- console.warn(e);
- return `${number} (Invalid format: ${format})`;
- }
- }
- try {
- return formatters[format](number);
- } catch (e) {
- return `${number} (Invalid format: ${format})`;
- }
-}
/*
Utility function that takes a d3 svg:text selection and a max width, and
splits the
diff --git a/superset/assets/src/preamble.js b/superset/assets/src/preamble.js
index 9d80dc9..3e96e60 100644
--- a/superset/assets/src/preamble.js
+++ b/superset/assets/src/preamble.js
@@ -2,6 +2,7 @@ import
'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
import { configure } from '@superset-ui/translation';
import setupClient from './setup/setupClient';
import setupColors from './setup/setupColors';
+import setupFormatters from './setup/setupFormatters';
// Configure translation
if (typeof window !== 'undefined') {
@@ -22,3 +23,6 @@ setupClient();
// Setup color palettes
setupColors();
+
+// Setup number formatters
+setupFormatters();
diff --git a/superset/assets/src/setup/setupFormatters.js
b/superset/assets/src/setup/setupFormatters.js
new file mode 100644
index 0000000..dd1f39f
--- /dev/null
+++ b/superset/assets/src/setup/setupFormatters.js
@@ -0,0 +1,35 @@
+import { getNumberFormatter, getNumberFormatterRegistry,
createSiAtMostNDigitFormatter, NumberFormats } from
'@superset-ui/number-format';
+import { getTimeFormatterRegistry, smartDateFormatter,
smartDateVerboseFormatter } from '@superset-ui/time-format';
+
+export default function setupFormatters() {
+ const defaultNumberFormatter = createSiAtMostNDigitFormatter({ n: 3 });
+
+ getNumberFormatterRegistry()
+ .registerValue(defaultNumberFormatter.id, defaultNumberFormatter)
+ .setDefaultKey(defaultNumberFormatter.id)
+ // Add shims for format strings that are deprecated or common typos.
+ // Temporary solution until performing a db migration to fix this.
+ .registerValue('+,', getNumberFormatter(NumberFormats.INTEGER_CHANGE))
+ .registerValue(',0', getNumberFormatter(',.4~f'))
+ .registerValue('.', getNumberFormatter('.4~f'))
+ .registerValue(',#', getNumberFormatter(',.4~f'))
+ .registerValue(',2f', getNumberFormatter(',.4~f'))
+ .registerValue(',g', getNumberFormatter(',.4~f'))
+ .registerValue('int', getNumberFormatter(NumberFormats.INTEGER))
+ .registerValue(',.', getNumberFormatter(',.4~f'))
+ .registerValue('.0%f', getNumberFormatter('.1%'))
+ .registerValue('.1%f', getNumberFormatter('.1%'))
+ .registerValue('.r', getNumberFormatter('.4~f'))
+ .registerValue(',0s', getNumberFormatter(',.4~f'))
+ .registerValue('%%%', getNumberFormatter('.0%'))
+ .registerValue(',0f', getNumberFormatter(',.4~f'))
+ .registerValue(',1', getNumberFormatter(',.4~f'))
+ .registerValue('$,0', getNumberFormatter('$,.4f'))
+ .registerValue('$,0f', getNumberFormatter('$,.4f'))
+ .registerValue('$,.f', getNumberFormatter('$,.4f'));
+
+ getTimeFormatterRegistry()
+ .registerValue('smart_date', smartDateFormatter)
+ .registerValue('smart_date_verbose', smartDateVerboseFormatter)
+ .setDefaultKey('smart_date');
+}
diff --git a/superset/assets/src/visualizations/BigNumber/BigNumber.jsx
b/superset/assets/src/visualizations/BigNumber/BigNumber.jsx
index 9c80e04..b6c79e6 100644
--- a/superset/assets/src/visualizations/BigNumber/BigNumber.jsx
+++ b/superset/assets/src/visualizations/BigNumber/BigNumber.jsx
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import shortid from 'shortid';
import { XYChart, AreaSeries, CrossHair, LinearGradient } from
'@data-ui/xy-chart';
import { BRAND_COLOR } from '@superset-ui/color';
-import { formatDateVerbose } from '../../modules/dates';
+import { smartDateVerboseFormatter } from '@superset-ui/time-format';
import { computeMaxFontSize } from '../../modules/visUtils';
import './BigNumber.css';
@@ -26,7 +26,7 @@ const PROPORTION = {
export function renderTooltipFactory(formatValue) {
return function renderTooltip({ datum }) { // eslint-disable-line
const { x: rawDate, y: rawValue } = datum;
- const formattedDate = formatDateVerbose(rawDate);
+ const formattedDate = smartDateVerboseFormatter(rawDate);
const value = formatValue(rawValue);
return (
diff --git a/superset/assets/src/visualizations/BigNumber/transformProps.js
b/superset/assets/src/visualizations/BigNumber/transformProps.js
index 92a88f3..cb5dcc2 100644
--- a/superset/assets/src/visualizations/BigNumber/transformProps.js
+++ b/superset/assets/src/visualizations/BigNumber/transformProps.js
@@ -1,6 +1,5 @@
import * as color from 'd3-color';
-import { format as d3Format } from 'd3-format';
-import { d3FormatPreset } from '../../modules/utils';
+import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
import { renderTooltipFactory } from './BigNumber';
const TIME_COLUMN = '__timestamp';
@@ -43,7 +42,7 @@ export default function transformProps(chartProps) {
const compareValue = sortedData[compareIndex][metricName];
percentChange = compareValue === 0
? 0 : (bigNumber - compareValue) / Math.abs(compareValue);
- const formatPercentChange = d3Format('+.1%');
+ const formatPercentChange =
getNumberFormatter(NumberFormats.PERCENT_CHANGE_1_POINT);
formattedSubheader = `${formatPercentChange(percentChange)}
${compareSuffix}`;
}
}
@@ -62,7 +61,7 @@ export default function transformProps(chartProps) {
className = 'negative';
}
- const formatValue = d3FormatPreset(yAxisFormat);
+ const formatValue = getNumberFormatter(yAxisFormat);
return {
width,
diff --git a/superset/assets/src/visualizations/Calendar/Calendar.js
b/superset/assets/src/visualizations/Calendar/Calendar.js
index 6b7aada..0dd3944 100644
--- a/superset/assets/src/visualizations/Calendar/Calendar.js
+++ b/superset/assets/src/visualizations/Calendar/Calendar.js
@@ -2,8 +2,10 @@ import PropTypes from 'prop-types';
import { extent as d3Extent, range as d3Range } from 'd3-array';
import { select as d3Select } from 'd3-selection';
import { getSequentialSchemeRegistry } from '@superset-ui/color';
+import { getNumberFormatter } from '@superset-ui/number-format';
+import { getTimeFormatter } from '@superset-ui/time-format';
import CalHeatMap from '../../../vendor/cal-heatmap/cal-heatmap';
-import { d3TimeFormatPreset, d3FormatPreset } from '../../modules/utils';
import { UTC } from '../../modules/dates';
+import { UTC } from '../../modules/dates';
import '../../../vendor/cal-heatmap/cal-heatmap.css';
import './Calendar.css';
@@ -53,8 +55,8 @@ function Calendar(element, props) {
verboseMap,
} = props;
- const valueFormatter = d3FormatPreset(valueFormat);
- const timeFormatter = d3TimeFormatPreset(timeFormat);
+ const valueFormatter = getNumberFormatter(valueFormat);
+ const timeFormatter = getTimeFormatter(timeFormat);
const container = d3Select(element)
.style('height', height);
diff --git a/superset/assets/src/visualizations/Chord/Chord.js
b/superset/assets/src/visualizations/Chord/Chord.js
index 05d416e..84e399f 100644
--- a/superset/assets/src/visualizations/Chord/Chord.js
+++ b/superset/assets/src/visualizations/Chord/Chord.js
@@ -2,6 +2,7 @@
import d3 from 'd3';
import PropTypes from 'prop-types';
import { CategoricalColorNamespace } from '@superset-ui/color';
+import { getNumberFormatter } from '@superset-ui/number-format';
import './Chord.css';
const propTypes = {
@@ -28,7 +29,7 @@ function Chord(element, props) {
const div = d3.select(element);
const { nodes, matrix } = data;
- const f = d3.format(numberFormat);
+ const f = getNumberFormatter(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const outerRadius = Math.min(width, height) / 2 - 10;
diff --git a/superset/assets/src/visualizations/CountryMap/CountryMap.js
b/superset/assets/src/visualizations/CountryMap/CountryMap.js
index ff22bcf..830ff28 100644
--- a/superset/assets/src/visualizations/CountryMap/CountryMap.js
+++ b/superset/assets/src/visualizations/CountryMap/CountryMap.js
@@ -1,8 +1,8 @@
import d3 from 'd3';
import PropTypes from 'prop-types';
import { extent as d3Extent } from 'd3-array';
-import { format as d3Format } from 'd3-format';
import { getSequentialSchemeRegistry } from '@superset-ui/color';
+import { getNumberFormatter } from '@superset-ui/number-format';
import './CountryMap.css';
const propTypes = {
@@ -32,7 +32,7 @@ function CountryMap(element, props) {
} = props;
const container = element;
- const format = d3Format(numberFormat);
+ const format = getNumberFormatter(numberFormat);
const colorScale = getSequentialSchemeRegistry()
.get(linearColorScheme)
.createLinearScale(d3Extent(data, v => v.metric));
diff --git a/superset/assets/src/visualizations/Heatmap/Heatmap.js
b/superset/assets/src/visualizations/Heatmap/Heatmap.js
index 4c8f6aa..3e7d7d7 100644
--- a/superset/assets/src/visualizations/Heatmap/Heatmap.js
+++ b/superset/assets/src/visualizations/Heatmap/Heatmap.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import 'd3-svg-legend';
import d3tip from 'd3-tip';
import { getSequentialSchemeRegistry } from '@superset-ui/color';
+import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
import '../../../stylesheets/d3tip.css';
import './Heatmap.css';
@@ -85,7 +86,7 @@ function Heatmap(element, props) {
bottom: 35,
left: 35,
};
- const valueFormatter = d3.format(numberFormat);
+ const valueFormatter = getNumberFormatter(numberFormat);
// Dynamically adjusts based on max x / y category lengths
function adjustMargins() {
@@ -152,7 +153,7 @@ function Heatmap(element, props) {
const hmWidth = width - (margin.left + margin.right);
const hmHeight = height - (margin.bottom + margin.top);
- const fp = d3.format('.2%');
+ const fp = getNumberFormatter(NumberFormats.PERCENT);
const xScale = ordScale('x', null, sortXAxis);
const yScale = ordScale('y', null, sortYAxis);
diff --git a/superset/assets/src/visualizations/Partition/Partition.js
b/superset/assets/src/visualizations/Partition/Partition.js
index 539c024..dbb2e42 100644
--- a/superset/assets/src/visualizations/Partition/Partition.js
+++ b/superset/assets/src/visualizations/Partition/Partition.js
@@ -3,7 +3,8 @@ import d3 from 'd3';
import PropTypes from 'prop-types';
import { hierarchy } from 'd3-hierarchy';
import { CategoricalColorNamespace } from '@superset-ui/color';
-import { d3TimeFormatPreset } from '../../modules/utils';
+import { getNumberFormatter } from '@superset-ui/number-format';
+import { getTimeFormatter } from '@superset-ui/time-format';
import './Partition.css';
// Compute dx, dy, x, y for each node and
@@ -93,8 +94,8 @@ function Icicle(element, props) {
// Chart options
const chartType = timeSeriesOption;
const hasTime = ['adv_anal', 'time_series'].indexOf(chartType) >= 0;
- const format = d3.format(numberFormat);
- const timeFormat = d3TimeFormatPreset(dateTimeFormat);
+ const format = getNumberFormatter(numberFormat);
+ const timeFormat = getTimeFormatter(dateTimeFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
div.selectAll('*').remove();
diff --git a/superset/assets/src/visualizations/PivotTable/PivotTable.js
b/superset/assets/src/visualizations/PivotTable/PivotTable.js
index 71d0cfa..b5326bc 100644
--- a/superset/assets/src/visualizations/PivotTable/PivotTable.js
+++ b/superset/assets/src/visualizations/PivotTable/PivotTable.js
@@ -2,7 +2,8 @@ import dt from 'datatables.net-bs';
import 'datatables.net-bs/css/dataTables.bootstrap.css';
import $ from 'jquery';
import PropTypes from 'prop-types';
-import { d3format, fixDataTableBodyHeight } from '../../modules/utils';
+import { formatNumber } from '@superset-ui/number-format';
+import { fixDataTableBodyHeight } from '../../modules/utils';
import './PivotTable.css';
dt(window, $);
@@ -59,7 +60,7 @@ function PivotTable(element, props) {
const format = columnFormats[metric] || numberFormat || '.3s';
const tdText = $(this)[0].textContent;
if (!Number.isNaN(tdText) && tdText !== '') {
- $(this)[0].textContent = d3format(format, tdText);
+ $(this)[0].textContent = formatNumber(format, tdText);
$(this).attr('data-sort', tdText);
}
});
diff --git a/superset/assets/src/visualizations/Rose/Rose.js
b/superset/assets/src/visualizations/Rose/Rose.js
index 097c918..a99d03f 100644
--- a/superset/assets/src/visualizations/Rose/Rose.js
+++ b/superset/assets/src/visualizations/Rose/Rose.js
@@ -3,7 +3,8 @@ import d3 from 'd3';
import PropTypes from 'prop-types';
import nv from 'nvd3';
import { CategoricalColorNamespace } from '@superset-ui/color';
-import { d3TimeFormatPreset } from '../../modules/utils';
+import { getNumberFormatter } from '@superset-ui/number-format';
+import { getTimeFormatter } from '@superset-ui/time-format';
import './Rose.css';
const propTypes = {
@@ -58,8 +59,8 @@ function Rose(element, props) {
.sort((a, b) => a - b);
const numGrains = times.length;
const numGroups = datum[times[0]].length;
- const format = d3.format(numberFormat);
- const timeFormat = d3TimeFormatPreset(dateTimeFormat);
+ const format = getNumberFormatter(numberFormat);
+ const timeFormat = getTimeFormatter(dateTimeFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
d3.select('.nvtooltip').remove();
diff --git a/superset/assets/src/visualizations/Sankey/Sankey.js
b/superset/assets/src/visualizations/Sankey/Sankey.js
index f80d032..5e4c6eb 100644
--- a/superset/assets/src/visualizations/Sankey/Sankey.js
+++ b/superset/assets/src/visualizations/Sankey/Sankey.js
@@ -3,6 +3,7 @@ import d3 from 'd3';
import PropTypes from 'prop-types';
import { sankey as d3Sankey } from 'd3-sankey';
import { CategoricalColorNamespace } from '@superset-ui/color';
+import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
import './Sankey.css';
const propTypes = {
@@ -16,7 +17,7 @@ const propTypes = {
colorScheme: PropTypes.string,
};
-const formatNumber = d3.format(',.2f');
+const formatNumber = getNumberFormatter(NumberFormats.FLOAT);
function Sankey(element, props) {
const {
diff --git a/superset/assets/src/visualizations/Sunburst/Sunburst.js
b/superset/assets/src/visualizations/Sunburst/Sunburst.js
index 29496a6..5e13c19 100644
--- a/superset/assets/src/visualizations/Sunburst/Sunburst.js
+++ b/superset/assets/src/visualizations/Sunburst/Sunburst.js
@@ -2,6 +2,7 @@
import d3 from 'd3';
import PropTypes from 'prop-types';
import { CategoricalColorNamespace } from '@superset-ui/color';
+import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
import { wrapSvgText } from '../../modules/utils';
import './Sunburst.css';
@@ -79,8 +80,8 @@ function Sunburst(element, props) {
.innerRadius(d => Math.sqrt(d.y))
.outerRadius(d => Math.sqrt(d.y + d.dy));
- const formatNum = d3.format('.3s');
- const formatPerc = d3.format('.3p');
+ const formatNum = getNumberFormatter(NumberFormats.SI_3_DIGIT);
+ const formatPerc = getNumberFormatter(NumberFormats.PERCENT_3_POINT);
container.select('svg').remove();
diff --git a/superset/assets/src/visualizations/Table/Table.js
b/superset/assets/src/visualizations/Table/Table.js
index 7056235..23cdc07 100644
--- a/superset/assets/src/visualizations/Table/Table.js
+++ b/superset/assets/src/visualizations/Table/Table.js
@@ -4,8 +4,9 @@ import PropTypes from 'prop-types';
import dt from 'datatables.net-bs';
import 'datatables.net-bs/css/dataTables.bootstrap.css';
import dompurify from 'dompurify';
-import { format as d3Format } from 'd3-format';
-import { fixDataTableBodyHeight, d3TimeFormatPreset } from
'../../modules/utils';
+import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
+import { getTimeFormatter } from '@superset-ui/time-format';
+import { fixDataTableBodyHeight } from '../../modules/utils';
import './Table.css';
dt(window, $);
@@ -46,8 +47,8 @@ const propTypes = {
]),
};
-const formatValue = d3Format(',.0d');
-const formatPercent = d3Format('.3p');
+const formatValue = getNumberFormatter(NumberFormats.INTEGER);
+const formatPercent = getNumberFormatter(NumberFormats.PERCENT_3_POINT);
function NOOP() {}
function TableVis(element, props) {
@@ -96,7 +97,7 @@ function TableVis(element, props) {
}
}
- const tsFormatter = d3TimeFormatPreset(tableTimestampFormat);
+ const tsFormatter = getTimeFormatter(tableTimestampFormat);
const div = d3.select(element);
div.html('');
@@ -130,7 +131,7 @@ function TableVis(element, props) {
html = `<span class="like-pre">${dompurify.sanitize(val)}</span>`;
}
if (isMetric) {
- html = d3Format(format || '0.3s')(val);
+ html = getNumberFormatter(format)(val);
}
if (key[0] === '%') {
html = formatPercent(val);
diff --git a/superset/assets/src/visualizations/TimeTable/FormattedNumber.jsx
b/superset/assets/src/visualizations/TimeTable/FormattedNumber.jsx
index eabbb0e..a4751e5 100644
--- a/superset/assets/src/visualizations/TimeTable/FormattedNumber.jsx
+++ b/superset/assets/src/visualizations/TimeTable/FormattedNumber.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { d3format } from '../../modules/utils';
+import { formatNumber } from '@superset-ui/number-format';
const propTypes = {
num: PropTypes.number,
@@ -15,7 +15,7 @@ const defaultProps = {
function FormattedNumber({ num, format }) {
if (format) {
return (
- <span title={num}>{d3format(format, num)}</span>
+ <span title={num}>{formatNumber(format, num)}</span>
);
}
return <span>{num}</span>;
diff --git a/superset/assets/src/visualizations/TimeTable/SparklineCell.jsx
b/superset/assets/src/visualizations/TimeTable/SparklineCell.jsx
index 1a49e35..bc58c10 100644
--- a/superset/assets/src/visualizations/TimeTable/SparklineCell.jsx
+++ b/superset/assets/src/visualizations/TimeTable/SparklineCell.jsx
@@ -1,7 +1,7 @@
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 { formatNumber } from '@superset-ui/number-format';
import { getTextDimension } from '../../modules/visUtils';
const propTypes = {
@@ -110,8 +110,8 @@ class SparklineCell extends React.Component {
? maxBound
: data.reduce((acc, current) => Math.max(acc, current), data[0]);
- minLabel = d3format(numberFormat, min);
- maxLabel = d3format(numberFormat, max);
+ minLabel = formatNumber(numberFormat, min);
+ maxLabel = formatNumber(numberFormat, max);
labelLength = Math.max(
getSparklineTextWidth(minLabel),
getSparklineTextWidth(maxLabel),
diff --git a/superset/assets/src/visualizations/TimeTable/TimeTable.jsx
b/superset/assets/src/visualizations/TimeTable/TimeTable.jsx
index 38bf058..667b377 100644
--- a/superset/assets/src/visualizations/TimeTable/TimeTable.jsx
+++ b/superset/assets/src/visualizations/TimeTable/TimeTable.jsx
@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
import Mustache from 'mustache';
import { scaleLinear } from 'd3-scale';
import { Table, Thead, Th, Tr, Td } from 'reactable';
+import { formatNumber } from '@superset-ui/number-format';
+import { formatTime } from '@superset-ui/time-format';
import MetricOption from '../../components/MetricOption';
-import { formatDateThunk } from '../../modules/dates';
-import { d3format } from '../../modules/utils';
import InfoTooltipWithTrigger from '../../components/InfoTooltipWithTrigger';
import FormattedNumber from './FormattedNumber';
import SparklineCell from './SparklineCell';
@@ -113,8 +113,6 @@ class TimeTable extends React.PureComponent {
sparkData = entries.map(d => d[valueField]);
}
- const formatDate = formatDateThunk(column.dateFormat);
-
return (
<Td
column={column.key}
@@ -131,8 +129,8 @@ class TimeTable extends React.PureComponent {
showYAxis={column.showYAxis}
renderTooltip={({ index }) => (
<div>
- <strong>{d3format(column.d3format, sparkData[index])}</strong>
- <div>{formatDate(entries[index].time)}</div>
+ <strong>{formatNumber(column.d3format,
sparkData[index])}</strong>
+ <div>{formatTime(column.dateFormat, entries[index].time)}</div>
</div>
)}
/>
diff --git a/superset/assets/src/visualizations/Treemap/Treemap.js
b/superset/assets/src/visualizations/Treemap/Treemap.js
index 17669d6..7ac0e1b 100644
--- a/superset/assets/src/visualizations/Treemap/Treemap.js
+++ b/superset/assets/src/visualizations/Treemap/Treemap.js
@@ -2,6 +2,7 @@
import d3 from 'd3';
import PropTypes from 'prop-types';
import { CategoricalColorNamespace } from '@superset-ui/color';
+import { getNumberFormatter } from '@superset-ui/number-format';
import './Treemap.css';
// Declare PropTypes for recursive data structures
@@ -67,7 +68,7 @@ function Treemap(element, props) {
treemapRatio,
} = props;
const div = d3.select(element);
- const formatNumber = d3.format(numberFormat);
+ const formatNumber = getNumberFormatter(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const data = clone(rawData);
diff --git a/superset/assets/src/visualizations/WorldMap/WorldMap.js
b/superset/assets/src/visualizations/WorldMap/WorldMap.js
index b2cb2c0..91f654b 100644
--- a/superset/assets/src/visualizations/WorldMap/WorldMap.js
+++ b/superset/assets/src/visualizations/WorldMap/WorldMap.js
@@ -1,6 +1,7 @@
import d3 from 'd3';
import PropTypes from 'prop-types';
import Datamap from 'datamaps/dist/datamaps.world.min';
+import { getNumberFormatter } from '@superset-ui/number-format';
import './WorldMap.css';
const propTypes = {
@@ -17,7 +18,7 @@ const propTypes = {
showBubbles: PropTypes.bool,
};
-const formatter = d3.format('.3s');
+const formatter = getNumberFormatter();
function WorldMap(element, props) {
const {
diff --git a/superset/assets/src/visualizations/nvd3/NVD3Vis.js
b/superset/assets/src/visualizations/nvd3/NVD3Vis.js
index 526406b..5fbfed0 100644
--- a/superset/assets/src/visualizations/nvd3/NVD3Vis.js
+++ b/superset/assets/src/visualizations/nvd3/NVD3Vis.js
@@ -6,11 +6,11 @@ import moment from 'moment';
import PropTypes from 'prop-types';
import { t } from '@superset-ui/translation';
import { CategoricalColorNamespace } from '@superset-ui/color';
+import { getNumberFormatter, formatNumber, NumberFormats } from
'@superset-ui/number-format';
+import { getTimeFormatter, smartDateVerboseFormatter } from
'@superset-ui/time-format';
import 'nvd3/build/nv.d3.min.css';
import ANNOTATION_TYPES, { applyNativeColumns } from
'../../modules/AnnotationTypes';
-import { formatDateVerbose } from '../../modules/dates';
-import { d3TimeFormatPreset, d3FormatPreset } from '../../modules/utils';
import { isTruthy } from '../../utils/common';
import {
cleanColorInput,
@@ -20,6 +20,7 @@ import {
generateMultiLineTooltipContent,
generateRichLineTooltipContent,
getMaxLabelSize,
+ getTimeOrNumberFormatter,
hideTooltips,
tipFactory,
tryNumify,
@@ -175,7 +176,7 @@ const propTypes = {
};
const NOOP = () => {};
-const formatter = d3.format('.3s');
+const formatter = getNumberFormatter();
function nvd3Vis(element, props) {
const {
@@ -342,7 +343,7 @@ function nvd3Vis(element, props) {
if (pieLabelType !== 'key_percent' && pieLabelType !== 'key_value') {
chart.labelType(pieLabelType);
} else if (pieLabelType === 'key_value') {
- chart.labelType(d => `${d.data.x}: ${d3.format('.3s')(d.data.y)}`);
+ chart.labelType(d => `${d.data.x}: ${formatNumber(NumberFormats.SI,
d.data.y)}`);
}
if (pieLabelType === 'percent' || pieLabelType === 'key_percent') {
@@ -377,8 +378,8 @@ function nvd3Vis(element, props) {
xField,
yField,
sizeField,
- xFormatter: d3FormatPreset(xAxisFormat),
- yFormatter: d3FormatPreset(yAxisFormat),
+ xFormatter: getTimeOrNumberFormatter(xAxisFormat),
+ yFormatter: getTimeOrNumberFormatter(yAxisFormat),
sizeFormatter: formatter,
}));
chart.pointRange([5, maxBubbleSize ** 2]);
@@ -458,11 +459,11 @@ function nvd3Vis(element, props) {
let xAxisFormatter;
if (isTimeSeries) {
- xAxisFormatter = d3TimeFormatPreset(xAxisFormat);
+ xAxisFormatter = getTimeFormatter(xAxisFormat);
// In tooltips, always use the verbose time format
- chart.interactiveLayer.tooltip.headerFormatter(formatDateVerbose);
+
chart.interactiveLayer.tooltip.headerFormatter(smartDateVerboseFormatter);
} else {
- xAxisFormatter = d3FormatPreset(xAxisFormat);
+ xAxisFormatter = getTimeOrNumberFormatter(xAxisFormat);
}
if (chart.x2Axis && chart.x2Axis.tickFormat) {
chart.x2Axis.tickFormat(xAxisFormatter);
@@ -472,11 +473,11 @@ function nvd3Vis(element, props) {
chart.xAxis.tickFormat(xAxisFormatter);
}
- let yAxisFormatter = d3FormatPreset(yAxisFormat);
+ let yAxisFormatter = getTimeOrNumberFormatter(yAxisFormat);
if (chart.yAxis && chart.yAxis.tickFormat) {
if (contribution || comparisonType === 'percentage') {
// When computing a "Percentage" or "Contribution" selected, we force
a percentage format
- yAxisFormatter = d3.format('.1%');
+ yAxisFormatter = getNumberFormatter(NumberFormats.PERCENT_1_POINT);
}
chart.yAxis.tickFormat(yAxisFormatter);
}
@@ -519,8 +520,8 @@ function nvd3Vis(element, props) {
}
if (isVizTypes(['dual_line', 'line_multi'])) {
- const yAxisFormatter1 = d3.format(yAxisFormat);
- const yAxisFormatter2 = d3.format(yAxis2Format);
+ const yAxisFormatter1 = getNumberFormatter(yAxisFormat);
+ const yAxisFormatter2 = getNumberFormatter(yAxis2Format);
chart.yAxis1.tickFormat(yAxisFormatter1);
chart.yAxis2.tickFormat(yAxisFormatter2);
const yAxisFormatters = data.map(datum => (
diff --git a/superset/assets/src/visualizations/nvd3/utils.js
b/superset/assets/src/visualizations/nvd3/utils.js
index 52e0c3e..86ee61f 100644
--- a/superset/assets/src/visualizations/nvd3/utils.js
+++ b/superset/assets/src/visualizations/nvd3/utils.js
@@ -1,6 +1,8 @@
import d3 from 'd3';
import d3tip from 'd3-tip';
import dompurify from 'dompurify';
+import { getNumberFormatter } from '@superset-ui/number-format';
+import { smartDateFormatter } from '@superset-ui/time-format';
// Regexp for the label added to time shifted series
// (1 hour offset, 2 days offset, etc.)
@@ -14,8 +16,19 @@ export function cleanColorInput(value) {
.join(', ');
}
+/**
+ * If format is smart_date, format date
+ * Otherwise, format number with the given format name
+ * @param {*} format
+ */
+export function getTimeOrNumberFormatter(format) {
+ return (format === 'smart_date')
+ ? smartDateFormatter
+ : getNumberFormatter(format);
+}
+
export function drawBarValues(svg, data, stacked, axisFormat) {
- const format = d3.format(axisFormat || '.3s');
+ const format = getNumberFormatter(axisFormat);
const countSeriesDisplayed = data.length;
const totalStackedValues = stacked && data.length !== 0 ?
diff --git a/superset/assets/yarn.lock b/superset/assets/yarn.lock
index dec3789..9cdccb9 100644
--- a/superset/assets/yarn.lock
+++ b/superset/assets/yarn.lock
@@ -432,6 +432,21 @@
dependencies:
lodash "^4.17.11"
+"@superset-ui/number-format@^0.7.2":
+ version "0.7.2"
+ resolved
"https://registry.yarnpkg.com/@superset-ui/number-format/-/number-format-0.7.2.tgz#c193181d4bdc6eb63a48996012fae7baac5ad802"
+ dependencies:
+ "@superset-ui/core" "^0.7.0"
+ d3-format "^1.3.2"
+
+"@superset-ui/time-format@^0.7.2":
+ version "0.7.2"
+ resolved
"https://registry.yarnpkg.com/@superset-ui/time-format/-/time-format-0.7.2.tgz#f5d21a8c46d76fc9603f2377f927ebf11593b4e1"
+ dependencies:
+ "@superset-ui/core" "^0.7.0"
+ d3-time "^1.0.10"
+ d3-time-format "^2.1.3"
+
"@superset-ui/translation@^0.7.0":
version "0.7.0"
resolved
"https://registry.yarnpkg.com/@superset-ui/translation/-/translation-0.7.0.tgz#8b9426a97d523df5aefe9242084264897efe252c"
@@ -3546,7 +3561,7 @@ d3-time-format@2, d3-time-format@^2.1.3:
dependencies:
d3-time "1"
-d3-time@1:
+d3-time@1, d3-time@^1.0.10:
version "1.0.10"
resolved
"https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.10.tgz#8259dd71288d72eeacfd8de281c4bf5c7393053c"