From e1f8773dba0a280887edc1d6893b1f35e0e863bf Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 20 Jun 2019 14:34:37 -0700 Subject: [PATCH] feat: support locale in number and time format (#182) * feat: support locale in number and time format * test: improve coverage * test: merge js and ts test --- .../src/factories/createD3NumberFormatter.ts | 10 ++- .../factories/createD3NumberFormatter.test.ts | 15 ++++ .../src/factories/createD3TimeFormatter.ts | 17 +++- .../factories/createD3TimeFormatter.test.js | 8 -- .../factories/createD3TimeFormatter.test.ts | 78 +++++++++++++++++-- 5 files changed, 108 insertions(+), 20 deletions(-) delete mode 100644 packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.js diff --git a/packages/superset-ui-number-format/src/factories/createD3NumberFormatter.ts b/packages/superset-ui-number-format/src/factories/createD3NumberFormatter.ts index e0760e362aa9..22dbbf0b652d 100644 --- a/packages/superset-ui-number-format/src/factories/createD3NumberFormatter.ts +++ b/packages/superset-ui-number-format/src/factories/createD3NumberFormatter.ts @@ -1,4 +1,4 @@ -import { format as d3Format } from 'd3-format'; +import { format as d3Format, formatLocale, FormatLocaleDefinition } from 'd3-format'; import { isRequired } from '@superset-ui/core'; import NumberFormatter from '../NumberFormatter'; import { NumberFormatFunction } from '../types'; @@ -7,14 +7,18 @@ export default function createD3NumberFormatter(config: { description?: string; formatString: string; label?: string; + locale?: FormatLocaleDefinition; }) { - const { description, formatString = isRequired('config.formatString'), label } = config; + const { description, formatString = isRequired('config.formatString'), label, locale } = config; let formatFunc: NumberFormatFunction; let isInvalid = false; try { - formatFunc = d3Format(formatString); + formatFunc = + typeof locale === 'undefined' + ? d3Format(formatString) + : formatLocale(locale).format(formatString); } catch (e) { formatFunc = value => `${value} (Invalid format: ${formatString})`; isInvalid = true; diff --git a/packages/superset-ui-number-format/test/factories/createD3NumberFormatter.test.ts b/packages/superset-ui-number-format/test/factories/createD3NumberFormatter.test.ts index 1cf6ebb6f632..703eeaf94c09 100644 --- a/packages/superset-ui-number-format/test/factories/createD3NumberFormatter.test.ts +++ b/packages/superset-ui-number-format/test/factories/createD3NumberFormatter.test.ts @@ -45,4 +45,19 @@ describe('createD3NumberFormatter(config)', () => { expect(formatter.description).toEqual('lorem ipsum'); }); }); + describe('config.locale', () => { + it('supports locale customization such as currency', () => { + const formatter = createD3NumberFormatter({ + description: 'lorem ipsum', + formatString: '$.2f', + locale: { + decimal: '.', + thousands: ',', + grouping: [3], + currency: ['€', ''], + }, + }); + expect(formatter(200)).toEqual('€200.00'); + }); + }); }); diff --git a/packages/superset-ui-time-format/src/factories/createD3TimeFormatter.ts b/packages/superset-ui-time-format/src/factories/createD3TimeFormatter.ts index ed8b6ff9f66d..c965bff313f5 100644 --- a/packages/superset-ui-time-format/src/factories/createD3TimeFormatter.ts +++ b/packages/superset-ui-time-format/src/factories/createD3TimeFormatter.ts @@ -1,4 +1,4 @@ -import { utcFormat, timeFormat } from 'd3-time-format'; +import { utcFormat, timeFormat, timeFormatLocale, TimeLocaleDefinition } from 'd3-time-format'; import { isRequired } from '@superset-ui/core'; import TimeFormatter from '../TimeFormatter'; import { LOCAL_PREFIX } from '../TimeFormats'; @@ -7,18 +7,29 @@ export default function createD3TimeFormatter(config: { description?: string; formatString: string; label?: string; + locale?: TimeLocaleDefinition; useLocalTime?: boolean; }) { const { description, formatString = isRequired('formatString'), label, + locale, useLocalTime = false, } = config; const id = useLocalTime ? `${LOCAL_PREFIX}${formatString}` : formatString; - const format = useLocalTime ? timeFormat : utcFormat; - const formatFunc = format(formatString); + let formatFunc; + + if (typeof locale === 'undefined') { + const format = useLocalTime ? timeFormat : utcFormat; + formatFunc = format(formatString); + } else { + const localeObject = timeFormatLocale(locale); + formatFunc = useLocalTime + ? localeObject.format(formatString) + : localeObject.utcFormat(formatString); + } return new TimeFormatter({ description, diff --git a/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.js b/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.js deleted file mode 100644 index 76ad6eb9f254..000000000000 --- a/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import createD3TimeFormatter from '../../src/factories/createD3TimeFormatter'; - -describe('createD3TimeFormatter(config)', () => { - it('requires config.formatString', () => { - expect(() => createD3TimeFormatter()).toThrow(); - expect(() => createD3TimeFormatter({})).toThrow(); - }); -}); diff --git a/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.ts b/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.ts index fa975521115a..8bbbfe027aa0 100644 --- a/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.ts +++ b/packages/superset-ui-time-format/test/factories/createD3TimeFormatter.test.ts @@ -1,10 +1,60 @@ +import { TimeLocaleDefinition } from 'd3-time-format'; import createD3TimeFormatter from '../../src/factories/createD3TimeFormatter'; import { PREVIEW_TIME } from '../../src/TimeFormatter'; import { TimeFormats } from '../../src'; +const thLocale: TimeLocaleDefinition = { + dateTime: '%a %e %b %Y %X', + date: '%d/%m/%Y', + time: '%H:%M:%S', + periods: ['AM', 'PM'], + days: ['วันอาทิตย์', 'วันจันทร์', 'วันอังคาร', 'วันพุธ', 'วันพฤหัส', 'วันศุกร์', 'วันเสาร์'], + shortDays: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ', 'ศ.', 'ส.'], + months: [ + 'มกราคม', + 'กุมภาพันธ์', + 'มีนาคม', + 'เมษายน', + 'พฤษภาคม', + 'มิถุนายน', + 'กรกฎาคม', + 'สิงหาคม', + 'กันยายน', + 'ตุลาคม', + 'พฤศจิกายน', + 'ธันวาคม', + ], + shortMonths: [ + 'ม.ค.', + 'ก.พ.', + 'มี.ค.', + 'เม.ย.', + 'พ.ค.', + 'มิ.ย.', + 'ก.ค.', + 'ส.ค.', + 'ก.ย.', + 'ต.ค.', + 'พ.ย.', + 'ธ.ค.', + ], +}; + describe('createD3TimeFormatter(config)', () => { - describe('if config.useLocalTime is true', () => { - it('formats in local time', () => { + it('requires config.formatString', () => { + // @ts-ignore + expect(() => createD3TimeFormatter()).toThrow(); + // @ts-ignore + expect(() => createD3TimeFormatter({})).toThrow(); + }); + describe('config.useLocalTime', () => { + it('if falsy, formats in UTC time', () => { + const formatter = createD3TimeFormatter({ + formatString: TimeFormats.DATABASE_DATETIME, + }); + expect(formatter.format(PREVIEW_TIME)).toEqual('2017-02-14 11:22:33'); + }); + it('if true, formats in local time', () => { const formatter = createD3TimeFormatter({ formatString: TimeFormats.DATABASE_DATETIME, useLocalTime: true, @@ -17,12 +67,28 @@ describe('createD3TimeFormatter(config)', () => { } }); }); - describe('if config.useLocalTime is false', () => { - it('formats in UTC time', () => { + + describe('config.locale', () => { + const TEST_TIME = new Date(Date.UTC(2015, 11, 20)); + it('supports locale customization (utc time)', () => { const formatter = createD3TimeFormatter({ - formatString: TimeFormats.DATABASE_DATETIME, + formatString: '%c', + locale: thLocale, }); - expect(formatter.format(PREVIEW_TIME)).toEqual('2017-02-14 11:22:33'); + expect(formatter(TEST_TIME)).toEqual('อา. 20 ธ.ค. 2015 00:00:00'); + }); + it('supports locale customization (local time)', () => { + const formatter = createD3TimeFormatter({ + formatString: '%c', + locale: thLocale, + useLocalTime: true, + }); + const offset = new Date().getTimezoneOffset(); + if (offset === 0) { + expect(formatter(TEST_TIME)).toEqual('อา. 20 ธ.ค. 2015 00:00:00'); + } else { + expect(formatter(TEST_TIME)).not.toEqual('อา. 20 ธ.ค. 2015 00:00:00'); + } }); }); });