mirror of https://github.com/apache/superset.git
feat(formatters): Add custom d3-time-format locale (#24263)
This commit is contained in:
parent
8e15d4807f
commit
024cfd86e4
|
@ -47,6 +47,7 @@
|
|||
"@superset-ui/plugin-chart-word-cloud": "file:./plugins/plugin-chart-word-cloud",
|
||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
"@types/d3-time-format": "^3.0.1",
|
||||
"@visx/axis": "^3.8.0",
|
||||
"@visx/grid": "^3.5.0",
|
||||
"@visx/responsive": "^3.0.0",
|
||||
|
@ -5361,6 +5362,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz",
|
||||
"integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ=="
|
||||
},
|
||||
"node_modules/@encodable/format/node_modules/@types/d3-time-format": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
|
||||
"integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA=="
|
||||
},
|
||||
"node_modules/@encodable/format/node_modules/d3-array": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
|
||||
|
@ -22612,9 +22618,9 @@
|
|||
"integrity": "sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw=="
|
||||
},
|
||||
"node_modules/@types/d3-time-format": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
|
||||
"integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-3.0.1.tgz",
|
||||
"integrity": "sha512-5GIimz5IqaRsdnxs4YlyTZPwAMfALu/wA4jqSiuqgdbCxUZ2WjrnwANqOtoBJQgeaUTdYNfALJO0Yb0YrDqduA=="
|
||||
},
|
||||
"node_modules/@types/d3-voronoi": {
|
||||
"version": "1.1.12",
|
||||
|
@ -67693,6 +67699,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz",
|
||||
"integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA=="
|
||||
},
|
||||
"packages/superset-ui-core/node_modules/@types/d3-time-format": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
|
||||
"integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA=="
|
||||
},
|
||||
"packages/superset-ui-core/node_modules/@types/math-expression-evaluator": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/math-expression-evaluator/-/math-expression-evaluator-1.3.3.tgz",
|
||||
|
@ -74967,6 +74978,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz",
|
||||
"integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ=="
|
||||
},
|
||||
"@types/d3-time-format": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
|
||||
"integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA=="
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
|
||||
|
@ -86338,6 +86354,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz",
|
||||
"integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA=="
|
||||
},
|
||||
"@types/d3-time-format": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
|
||||
"integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA=="
|
||||
},
|
||||
"@types/math-expression-evaluator": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/math-expression-evaluator/-/math-expression-evaluator-1.3.3.tgz",
|
||||
|
@ -89668,9 +89689,9 @@
|
|||
"integrity": "sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw=="
|
||||
},
|
||||
"@types/d3-time-format": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
|
||||
"integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-3.0.1.tgz",
|
||||
"integrity": "sha512-5GIimz5IqaRsdnxs4YlyTZPwAMfALu/wA4jqSiuqgdbCxUZ2WjrnwANqOtoBJQgeaUTdYNfALJO0Yb0YrDqduA=="
|
||||
},
|
||||
"@types/d3-voronoi": {
|
||||
"version": "1.1.12",
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
"@superset-ui/plugin-chart-word-cloud": "file:./plugins/plugin-chart-word-cloud",
|
||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
"@types/d3-time-format": "^3.0.1",
|
||||
"@visx/axis": "^3.8.0",
|
||||
"@visx/grid": "^3.5.0",
|
||||
"@visx/responsive": "^3.0.0",
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import {
|
||||
t,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_ID,
|
||||
NumberFormats,
|
||||
getNumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
@ -64,7 +64,7 @@ export const D3_TIME_FORMAT_DOCS = t(
|
|||
);
|
||||
|
||||
export const D3_TIME_FORMAT_OPTIONS: [string, string][] = [
|
||||
[smartDateFormatter.id, t('Adaptive formatting')],
|
||||
[SMART_DATE_ID, t('Adaptive formatting')],
|
||||
['%d/%m/%Y', '%d/%m/%Y | 14/01/2019'],
|
||||
['%m/%d/%Y', '%m/%d/%Y | 01/14/2019'],
|
||||
['%Y-%m-%d', '%Y-%m-%d | 2019-01-14'],
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* 'License'); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
|
||||
export const DEFAULT_D3_TIME_FORMAT: TimeLocaleDefinition = {
|
||||
dateTime: '%x, %X',
|
||||
date: '%-m/%-d/%Y',
|
||||
time: '%-I:%M:%S %p',
|
||||
periods: ['AM', 'PM'],
|
||||
days: [
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
],
|
||||
shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
months: [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
],
|
||||
};
|
|
@ -4,19 +4,22 @@
|
|||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* 'License'); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import { RegistryWithDefaultKey, OverwritePolicy } from '../models';
|
||||
import { DEFAULT_D3_TIME_FORMAT } from './D3FormatConfig';
|
||||
import TimeFormats, { LOCAL_PREFIX } from './TimeFormats';
|
||||
import createD3TimeFormatter from './factories/createD3TimeFormatter';
|
||||
import TimeFormatter from './TimeFormatter';
|
||||
|
@ -25,12 +28,21 @@ export default class TimeFormatterRegistry extends RegistryWithDefaultKey<
|
|||
TimeFormatter,
|
||||
TimeFormatter
|
||||
> {
|
||||
d3Format: TimeLocaleDefinition;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
initialDefaultKey: TimeFormats.DATABASE_DATETIME,
|
||||
name: 'TimeFormatter',
|
||||
overwritePolicy: OverwritePolicy.Warn,
|
||||
});
|
||||
|
||||
this.d3Format = DEFAULT_D3_TIME_FORMAT;
|
||||
}
|
||||
|
||||
setD3Format(d3Format: Partial<TimeLocaleDefinition>) {
|
||||
this.d3Format = { ...DEFAULT_D3_TIME_FORMAT, ...d3Format };
|
||||
return this;
|
||||
}
|
||||
|
||||
get(format?: string) {
|
||||
|
@ -47,7 +59,13 @@ export default class TimeFormatterRegistry extends RegistryWithDefaultKey<
|
|||
// Create new formatter if does not exist
|
||||
const useLocalTime = targetFormat.startsWith(LOCAL_PREFIX);
|
||||
const formatString = targetFormat.replace(LOCAL_PREFIX, '');
|
||||
const formatter = createD3TimeFormatter({ formatString, useLocalTime });
|
||||
const locale = this.d3Format;
|
||||
const formatter = createD3TimeFormatter({
|
||||
formatString,
|
||||
useLocalTime,
|
||||
locale,
|
||||
});
|
||||
|
||||
this.registerValue(targetFormat, formatter);
|
||||
|
||||
return formatter;
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { utcFormat, timeFormat } from 'd3-time-format';
|
||||
import {
|
||||
timeFormatLocale,
|
||||
TimeLocaleDefinition,
|
||||
timeFormat,
|
||||
utcFormat,
|
||||
} from 'd3-time-format';
|
||||
import { utcUtils, localTimeUtils } from '../utils/d3Time';
|
||||
import TimeFormatter from '../TimeFormatter';
|
||||
|
||||
|
@ -38,12 +42,14 @@ export default function createMultiFormatter({
|
|||
description,
|
||||
formats = {},
|
||||
useLocalTime = false,
|
||||
locale,
|
||||
}: {
|
||||
id: string;
|
||||
label?: string;
|
||||
description?: string;
|
||||
formats?: FormatsByStep;
|
||||
useLocalTime?: boolean;
|
||||
locale?: TimeLocaleDefinition;
|
||||
}) {
|
||||
const {
|
||||
millisecond = '.%L',
|
||||
|
@ -56,16 +62,23 @@ export default function createMultiFormatter({
|
|||
year = '%Y',
|
||||
} = formats;
|
||||
|
||||
const format = useLocalTime ? timeFormat : utcFormat;
|
||||
let formatFunc;
|
||||
|
||||
const formatMillisecond = format(millisecond);
|
||||
const formatSecond = format(second);
|
||||
const formatMinute = format(minute);
|
||||
const formatHour = format(hour);
|
||||
const formatDay = format(day);
|
||||
const formatFirstDayOfWeek = format(week);
|
||||
const formatMonth = format(month);
|
||||
const formatYear = format(year);
|
||||
if (typeof locale === 'undefined') {
|
||||
formatFunc = useLocalTime ? timeFormat : utcFormat;
|
||||
} else {
|
||||
const formatLocale = timeFormatLocale(locale);
|
||||
formatFunc = useLocalTime ? formatLocale.format : formatLocale.utcFormat;
|
||||
}
|
||||
|
||||
const formatMillisecond = formatFunc(millisecond);
|
||||
const formatSecond = formatFunc(second);
|
||||
const formatMinute = formatFunc(minute);
|
||||
const formatHour = formatFunc(hour);
|
||||
const formatDay = formatFunc(day);
|
||||
const formatFirstDayOfWeek = formatFunc(week);
|
||||
const formatMonth = formatFunc(month);
|
||||
const formatYear = formatFunc(year);
|
||||
|
||||
const {
|
||||
hasMillisecond,
|
||||
|
|
|
@ -17,21 +17,25 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import createMultiFormatter from '../factories/createMultiFormatter';
|
||||
|
||||
const smartDateFormatter = createMultiFormatter({
|
||||
id: 'smart_date',
|
||||
label: 'Adaptative Formatting',
|
||||
formats: {
|
||||
millisecond: '.%Lms',
|
||||
second: ':%Ss',
|
||||
minute: '%I:%M',
|
||||
hour: '%I %p',
|
||||
day: '%a %d',
|
||||
week: '%b %d',
|
||||
month: '%B',
|
||||
year: '%Y',
|
||||
},
|
||||
});
|
||||
export const SMART_DATE_ID = 'smart_date';
|
||||
|
||||
export default smartDateFormatter;
|
||||
export function createSmartDateFormatter(locale?: TimeLocaleDefinition) {
|
||||
return createMultiFormatter({
|
||||
id: SMART_DATE_ID,
|
||||
label: 'Adaptative Formatting',
|
||||
formats: {
|
||||
millisecond: '.%Lms',
|
||||
second: ':%Ss',
|
||||
minute: '%I:%M',
|
||||
hour: '%I %p',
|
||||
day: '%a %d',
|
||||
week: '%b %d',
|
||||
month: '%B',
|
||||
year: '%Y',
|
||||
},
|
||||
locale,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,21 +17,27 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import createMultiFormatter from '../factories/createMultiFormatter';
|
||||
|
||||
const smartDateDetailedFormatter = createMultiFormatter({
|
||||
id: 'smart_date_detailed',
|
||||
label: 'Detailed adaptive formatter',
|
||||
formats: {
|
||||
millisecond: '%Y-%m-%d %H:%M:%S.%L',
|
||||
second: '%Y-%m-%d %H:%M:%S',
|
||||
minute: '%Y-%m-%d %H:%M',
|
||||
hour: '%Y-%m-%d %H:%M',
|
||||
day: '%Y-%m-%d',
|
||||
week: '%Y-%m-%d',
|
||||
month: '%Y-%m-%d',
|
||||
year: '%Y',
|
||||
},
|
||||
});
|
||||
export const SMART_DATE_DETAILED_ID = 'smart_date_detailed';
|
||||
|
||||
export default smartDateDetailedFormatter;
|
||||
export function createSmartDateDetailedFormatter(
|
||||
locale?: TimeLocaleDefinition,
|
||||
) {
|
||||
return createMultiFormatter({
|
||||
id: 'smart_date_detailed',
|
||||
label: 'Detailed adaptive formatter',
|
||||
formats: {
|
||||
millisecond: '%Y-%m-%d %H:%M:%S.%L',
|
||||
second: '%Y-%m-%d %H:%M:%S',
|
||||
minute: '%Y-%m-%d %H:%M',
|
||||
hour: '%Y-%m-%d %H:%M',
|
||||
day: '%Y-%m-%d',
|
||||
week: '%Y-%m-%d',
|
||||
month: '%Y-%m-%d',
|
||||
year: '%Y',
|
||||
},
|
||||
locale,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,21 +17,25 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import createMultiFormatter from '../factories/createMultiFormatter';
|
||||
|
||||
const smartDateFormatter = createMultiFormatter({
|
||||
id: 'smart_date_verbose',
|
||||
label: 'Verbose Adaptative Formatting',
|
||||
formats: {
|
||||
millisecond: '.%L',
|
||||
second: '%a %b %d, %I:%M:%S %p',
|
||||
minute: '%a %b %d, %I:%M %p',
|
||||
hour: '%a %b %d, %I %p',
|
||||
day: '%a %b %-e',
|
||||
week: '%a %b %-e',
|
||||
month: '%b %Y',
|
||||
year: '%Y',
|
||||
},
|
||||
});
|
||||
export const SMART_DATE_VERBOSE_ID = 'smart_date_verbose';
|
||||
|
||||
export default smartDateFormatter;
|
||||
export function createSmartDateVerboseFormatter(locale?: TimeLocaleDefinition) {
|
||||
return createMultiFormatter({
|
||||
id: SMART_DATE_VERBOSE_ID,
|
||||
label: 'Verbose Adaptative Formatting',
|
||||
formats: {
|
||||
millisecond: '.%L',
|
||||
second: '%a %b %d, %I:%M:%S %p',
|
||||
minute: '%a %b %d, %I:%M %p',
|
||||
hour: '%a %b %d, %I %p',
|
||||
day: '%a %b %-e',
|
||||
week: '%a %b %-e',
|
||||
month: '%b %Y',
|
||||
year: '%Y',
|
||||
},
|
||||
locale,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
export { default as TimeFormats, LOCAL_PREFIX } from './TimeFormats';
|
||||
export { default as TimeFormatter, PREVIEW_TIME } from './TimeFormatter';
|
||||
export { DEFAULT_D3_TIME_FORMAT } from './D3FormatConfig';
|
||||
|
||||
export {
|
||||
default as getTimeFormatterRegistry,
|
||||
|
@ -32,9 +33,18 @@ export {
|
|||
export { default as createD3TimeFormatter } from './factories/createD3TimeFormatter';
|
||||
export { default as createMultiFormatter } from './factories/createMultiFormatter';
|
||||
|
||||
export { default as smartDateFormatter } from './formatters/smartDate';
|
||||
export { default as smartDateDetailedFormatter } from './formatters/smartDateDetailed';
|
||||
export { default as smartDateVerboseFormatter } from './formatters/smartDateVerbose';
|
||||
export {
|
||||
SMART_DATE_ID,
|
||||
createSmartDateFormatter,
|
||||
} from './formatters/smartDate';
|
||||
export {
|
||||
SMART_DATE_DETAILED_ID,
|
||||
createSmartDateDetailedFormatter,
|
||||
} from './formatters/smartDateDetailed';
|
||||
export {
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
createSmartDateVerboseFormatter,
|
||||
} from './formatters/smartDateVerbose';
|
||||
export { default as finestTemporalGrainFormatter } from './formatters/finestTemporalGrain';
|
||||
|
||||
export { default as normalizeTimestamp } from './utils/normalizeTimestamp';
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import { TimeFormats, TimeFormatter, PREVIEW_TIME } from '@superset-ui/core';
|
||||
import TimeFormatterRegistry from '../../src/time-format/TimeFormatterRegistry';
|
||||
import { DEFAULT_D3_TIME_FORMAT } from '../../src/time-format';
|
||||
|
||||
describe('TimeFormatterRegistry', () => {
|
||||
let registry: TimeFormatterRegistry;
|
||||
|
@ -78,4 +80,108 @@ describe('TimeFormatterRegistry', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
describe('.setD3Format(d3Format)', () => {
|
||||
describe('when partial value is specified', () => {
|
||||
const timeFormat: Partial<TimeLocaleDefinition> = {
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda',
|
||||
'Terça',
|
||||
'Quarta',
|
||||
'Quinta',
|
||||
'Sexta',
|
||||
'Sábado',
|
||||
],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
registry.setD3Format(timeFormat);
|
||||
});
|
||||
|
||||
it('sets the specified value and default', () => {
|
||||
expect(registry.d3Format).toEqual({
|
||||
...DEFAULT_D3_TIME_FORMAT,
|
||||
...timeFormat,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not change short days of week name format', () => {
|
||||
expect(registry.format('%a', PREVIEW_TIME)).toEqual('Tue');
|
||||
});
|
||||
|
||||
it('changes full days of week name format', () => {
|
||||
expect(registry.format('%A', PREVIEW_TIME)).toEqual('Terça');
|
||||
});
|
||||
|
||||
it('does not change months format', () => {
|
||||
expect(registry.format('%b', PREVIEW_TIME)).toEqual('Feb');
|
||||
expect(registry.format('%B', PREVIEW_TIME)).toEqual('February');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when full value is specified', () => {
|
||||
const timeFormat: TimeLocaleDefinition = {
|
||||
dateTime: '%A, %e de %B de %Y. %X',
|
||||
date: '%d/%m/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda',
|
||||
'Terça',
|
||||
'Quarta',
|
||||
'Quinta',
|
||||
'Sexta',
|
||||
'Sábado',
|
||||
],
|
||||
shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro',
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez',
|
||||
],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
registry.setD3Format(timeFormat);
|
||||
});
|
||||
|
||||
it('sets the specified value ignoring default', () => {
|
||||
expect(registry.d3Format).toEqual(timeFormat);
|
||||
});
|
||||
|
||||
it('changes days of week format', () => {
|
||||
expect(registry.format('%a', PREVIEW_TIME)).toEqual('Ter');
|
||||
expect(registry.format('%A', PREVIEW_TIME)).toEqual('Terça');
|
||||
});
|
||||
|
||||
it('changes months format', () => {
|
||||
expect(registry.format('%b', PREVIEW_TIME)).toEqual('Fev');
|
||||
expect(registry.format('%B', PREVIEW_TIME)).toEqual('Fevereiro');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,38 +18,187 @@
|
|||
*/
|
||||
|
||||
import { createMultiFormatter } from '@superset-ui/core';
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
|
||||
describe('createMultiFormatter()', () => {
|
||||
describe('creates a multi-step formatter', () => {
|
||||
const formatter = createMultiFormatter({
|
||||
id: 'my_format',
|
||||
useLocalTime: true,
|
||||
describe('when locale is undefined', () => {
|
||||
describe('and use local time is false', () => {
|
||||
const formatter = createMultiFormatter({
|
||||
id: 'my_format',
|
||||
useLocalTime: false,
|
||||
});
|
||||
it('formats millisecond', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33, 100))).toEqual(
|
||||
'.100',
|
||||
);
|
||||
});
|
||||
it('formats second', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33))).toEqual(':33');
|
||||
});
|
||||
it('format minutes', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22))).toEqual('04:22');
|
||||
});
|
||||
it('format hours', () => {
|
||||
expect(formatter(new Date('2018-11-18 11:00 UTC'))).toEqual('11 AM');
|
||||
});
|
||||
it('format first day of week', () => {
|
||||
expect(formatter(new Date('2018-11-18 UTC'))).toEqual('Nov 18');
|
||||
});
|
||||
it('format other day of week', () => {
|
||||
expect(formatter(new Date('2018-11-20 UTC'))).toEqual('Tue 20');
|
||||
});
|
||||
it('format month', () => {
|
||||
expect(formatter(new Date('2018-11-1 UTC'))).toEqual('November');
|
||||
});
|
||||
it('format year', () => {
|
||||
expect(formatter(new Date('2018-1-1 UTC'))).toEqual('2018');
|
||||
});
|
||||
});
|
||||
describe('and use local time is true', () => {
|
||||
const formatter = createMultiFormatter({
|
||||
id: 'my_format',
|
||||
useLocalTime: true,
|
||||
});
|
||||
it('formats millisecond', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33, 100))).toEqual(
|
||||
'.100',
|
||||
);
|
||||
});
|
||||
it('formats second', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33))).toEqual(':33');
|
||||
});
|
||||
it('format minutes', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22))).toEqual('11:22');
|
||||
});
|
||||
it('format hours', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11))).toEqual('11 AM');
|
||||
});
|
||||
it('format first day of week', () => {
|
||||
expect(formatter(new Date(2018, 10, 18))).toEqual('Nov 18');
|
||||
});
|
||||
it('format other day of week', () => {
|
||||
expect(formatter(new Date(2018, 10, 20))).toEqual('Tue 20');
|
||||
});
|
||||
it('format month', () => {
|
||||
expect(formatter(new Date(2018, 10))).toEqual('November');
|
||||
});
|
||||
it('format year', () => {
|
||||
expect(formatter(new Date(2018, 0))).toEqual('2018');
|
||||
});
|
||||
});
|
||||
});
|
||||
it('formats millisecond', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33, 100))).toEqual(
|
||||
'.100',
|
||||
);
|
||||
});
|
||||
describe('when locale is not default', () => {
|
||||
const locale: TimeLocaleDefinition = {
|
||||
dateTime: '%A, %e de %B de %Y. %X',
|
||||
date: '%d/%m/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda',
|
||||
'Terça',
|
||||
'Quarta',
|
||||
'Quinta',
|
||||
'Sexta',
|
||||
'Sábado',
|
||||
],
|
||||
shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro',
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez',
|
||||
],
|
||||
};
|
||||
describe('and use local time is false', () => {
|
||||
const formatter = createMultiFormatter({
|
||||
id: 'my_format',
|
||||
useLocalTime: false,
|
||||
locale,
|
||||
});
|
||||
it('formats millisecond', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33, 100))).toEqual(
|
||||
'.100',
|
||||
);
|
||||
});
|
||||
it('formats second', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33))).toEqual(':33');
|
||||
});
|
||||
it('format minutes', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22))).toEqual('04:22');
|
||||
});
|
||||
it('format hours', () => {
|
||||
expect(formatter(new Date('2018-11-18 11:00 UTC'))).toEqual('11 AM');
|
||||
});
|
||||
it('format first day of week', () => {
|
||||
expect(formatter(new Date('2018-11-18 UTC'))).toEqual('Nov 18');
|
||||
});
|
||||
it('format other day of week', () => {
|
||||
expect(formatter(new Date('2018-11-20 UTC'))).toEqual('Ter 20');
|
||||
});
|
||||
it('format month', () => {
|
||||
expect(formatter(new Date('2018-11-1 UTC'))).toEqual('Novembro');
|
||||
});
|
||||
it('format year', () => {
|
||||
expect(formatter(new Date('2018-1-1 UTC'))).toEqual('2018');
|
||||
});
|
||||
});
|
||||
it('formats second', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33))).toEqual(':33');
|
||||
});
|
||||
it('format minutes', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22))).toEqual('11:22');
|
||||
});
|
||||
it('format hours', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11))).toEqual('11 AM');
|
||||
});
|
||||
it('format first day of week', () => {
|
||||
expect(formatter(new Date(2018, 10, 18))).toEqual('Nov 18');
|
||||
});
|
||||
it('format other day of week', () => {
|
||||
expect(formatter(new Date(2018, 10, 20))).toEqual('Tue 20');
|
||||
});
|
||||
it('format month', () => {
|
||||
expect(formatter(new Date(2018, 10))).toEqual('November');
|
||||
});
|
||||
it('format year', () => {
|
||||
expect(formatter(new Date(2018, 0))).toEqual('2018');
|
||||
describe('and use local time is true', () => {
|
||||
const formatter = createMultiFormatter({
|
||||
id: 'my_format',
|
||||
useLocalTime: true,
|
||||
locale,
|
||||
});
|
||||
it('formats millisecond', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33, 100))).toEqual(
|
||||
'.100',
|
||||
);
|
||||
});
|
||||
it('formats second', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22, 33))).toEqual(':33');
|
||||
});
|
||||
it('format minutes', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11, 22))).toEqual('11:22');
|
||||
});
|
||||
it('format hours', () => {
|
||||
expect(formatter(new Date(2018, 10, 20, 11))).toEqual('11 AM');
|
||||
});
|
||||
it('format first day of week', () => {
|
||||
expect(formatter(new Date(2018, 10, 18))).toEqual('Nov 18');
|
||||
});
|
||||
it('format other day of week', () => {
|
||||
expect(formatter(new Date(2018, 10, 20))).toEqual('Ter 20');
|
||||
});
|
||||
it('format month', () => {
|
||||
expect(formatter(new Date(2018, 10))).toEqual('Novembro');
|
||||
});
|
||||
it('format year', () => {
|
||||
expect(formatter(new Date(2018, 0))).toEqual('2018');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,26 +17,98 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeFormatter, smartDateFormatter } from '@superset-ui/core';
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import { TimeFormatter, createSmartDateFormatter } from '@superset-ui/core';
|
||||
|
||||
describe('smartDateFormatter', () => {
|
||||
it('is a function', () => {
|
||||
expect(smartDateFormatter).toBeInstanceOf(TimeFormatter);
|
||||
describe('createSmartDateFormatter', () => {
|
||||
describe('when locale is default', () => {
|
||||
const smartDateFormatter = createSmartDateFormatter();
|
||||
|
||||
it('is a function', () => {
|
||||
expect(smartDateFormatter).toBeInstanceOf(TimeFormatter);
|
||||
});
|
||||
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(smartDateFormatter(new Date('2020-01-01'))).toBe('2020');
|
||||
});
|
||||
|
||||
it('shows only month when 1st of month', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-01'))).toBe('March');
|
||||
});
|
||||
|
||||
it('does not show day of week when it is Sunday', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-15'))).toBe('Mar 15');
|
||||
});
|
||||
|
||||
it('shows weekday when it is not Sunday (and no ms/sec/min/hr)', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-03'))).toBe('Tue 03');
|
||||
});
|
||||
});
|
||||
describe('when different locale is not default', () => {
|
||||
const locale: TimeLocaleDefinition = {
|
||||
dateTime: '%A, %e de %B de %Y. %X',
|
||||
date: '%d/%m/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda',
|
||||
'Terça',
|
||||
'Quarta',
|
||||
'Quinta',
|
||||
'Sexta',
|
||||
'Sábado',
|
||||
],
|
||||
shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro',
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez',
|
||||
],
|
||||
};
|
||||
const smartDateFormatter = createSmartDateFormatter(locale);
|
||||
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(smartDateFormatter(new Date('2020-01-01'))).toBe('2020');
|
||||
});
|
||||
it('is a function', () => {
|
||||
expect(smartDateFormatter).toBeInstanceOf(TimeFormatter);
|
||||
});
|
||||
|
||||
it('shows only month when 1st of month', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-01'))).toBe('March');
|
||||
});
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(smartDateFormatter(new Date('2020-01-01'))).toBe('2020');
|
||||
});
|
||||
|
||||
it('does not show day of week when it is Sunday', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-15'))).toBe('Mar 15');
|
||||
});
|
||||
it('shows only month when 1st of month', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-01'))).toBe('Março');
|
||||
});
|
||||
|
||||
it('shows weekday when it is not Sunday (and no ms/sec/min/hr)', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-03'))).toBe('Tue 03');
|
||||
it('does not show day of week when it is Sunday', () => {
|
||||
expect(smartDateFormatter(new Date('2023-10-15'))).toBe('Out 15');
|
||||
});
|
||||
|
||||
it('shows weekday when it is not Sunday (and no ms/sec/min/hr)', () => {
|
||||
expect(smartDateFormatter(new Date('2020-03-03'))).toBe('Ter 03');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,40 +17,125 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeFormatter, smartDateDetailedFormatter } from '@superset-ui/core';
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import {
|
||||
TimeFormatter,
|
||||
createSmartDateDetailedFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
describe('smartDateDetailedFormatter', () => {
|
||||
const formatter = smartDateDetailedFormatter;
|
||||
describe('when locale is default', () => {
|
||||
const formatter = createSmartDateDetailedFormatter();
|
||||
|
||||
it('is a function', () => {
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
it('is a function', () => {
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
});
|
||||
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(formatter(new Date('2020-01-01T00:00:00.000+00:00'))).toBe('2020');
|
||||
});
|
||||
|
||||
it('shows full date when a regular date', () => {
|
||||
expect(formatter(new Date('2020-03-01T00:00:00.000+00:00'))).toBe(
|
||||
'2020-03-01',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows full date including time of day without seconds when hour precision', () => {
|
||||
expect(formatter(new Date('2020-03-01T13:00:00.000+00:00'))).toBe(
|
||||
'2020-03-01 13:00',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows full date including time of day when minute precision', () => {
|
||||
expect(formatter(new Date('2020-03-10T13:10:00.000+00:00'))).toBe(
|
||||
'2020-03-10 13:10',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows full date including time of day when subsecond precision', () => {
|
||||
expect(formatter(new Date('2020-03-10T13:10:00.100+00:00'))).toBe(
|
||||
'2020-03-10 13:10:00.100',
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('when locale is specified', () => {
|
||||
const locale: TimeLocaleDefinition = {
|
||||
dateTime: '%A, %e de %B de %Y. %X',
|
||||
date: '%d/%m/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda',
|
||||
'Terça',
|
||||
'Quarta',
|
||||
'Quinta',
|
||||
'Sexta',
|
||||
'Sábado',
|
||||
],
|
||||
shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro',
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez',
|
||||
],
|
||||
};
|
||||
const formatter = createSmartDateDetailedFormatter(locale);
|
||||
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(formatter(new Date('2020-01-01T00:00:00.000+00:00'))).toBe('2020');
|
||||
});
|
||||
it('is a function', () => {
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
});
|
||||
|
||||
it('shows full date when a regular date', () => {
|
||||
expect(formatter(new Date('2020-03-01T00:00:00.000+00:00'))).toBe(
|
||||
'2020-03-01',
|
||||
);
|
||||
});
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(formatter(new Date('2020-01-01T00:00:00.000+00:00'))).toBe('2020');
|
||||
});
|
||||
|
||||
it('shows full date including time of day without seconds when hour precision', () => {
|
||||
expect(formatter(new Date('2020-03-01T13:00:00.000+00:00'))).toBe(
|
||||
'2020-03-01 13:00',
|
||||
);
|
||||
});
|
||||
it('shows full date when a regular date', () => {
|
||||
expect(formatter(new Date('2020-03-01T00:00:00.000+00:00'))).toBe(
|
||||
'2020-03-01',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows full date including time of day when minute precision', () => {
|
||||
expect(formatter(new Date('2020-03-10T13:10:00.000+00:00'))).toBe(
|
||||
'2020-03-10 13:10',
|
||||
);
|
||||
});
|
||||
it('shows full date including time of day without seconds when hour precision', () => {
|
||||
expect(formatter(new Date('2020-03-01T13:00:00.000+00:00'))).toBe(
|
||||
'2020-03-01 13:00',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows full date including time of day when subsecond precision', () => {
|
||||
expect(formatter(new Date('2020-03-10T13:10:00.100+00:00'))).toBe(
|
||||
'2020-03-10 13:10:00.100',
|
||||
);
|
||||
it('shows full date including time of day when minute precision', () => {
|
||||
expect(formatter(new Date('2020-03-10T13:10:00.000+00:00'))).toBe(
|
||||
'2020-03-10 13:10',
|
||||
);
|
||||
});
|
||||
|
||||
it('shows full date including time of day when subsecond precision', () => {
|
||||
expect(formatter(new Date('2020-03-10T13:10:00.100+00:00'))).toBe(
|
||||
'2020-03-10 13:10:00.100',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,25 +17,95 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimeFormatter, smartDateVerboseFormatter } from '@superset-ui/core';
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import {
|
||||
TimeFormatter,
|
||||
createSmartDateVerboseFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
describe('smartDateVerboseFormatter', () => {
|
||||
const formatter = smartDateVerboseFormatter;
|
||||
describe('when locale is default', () => {
|
||||
const formatter = createSmartDateVerboseFormatter();
|
||||
|
||||
it('is a function', () => {
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
it('is a function', () => {
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
});
|
||||
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(formatter(new Date('2020-01-01'))).toBe('2020');
|
||||
});
|
||||
|
||||
it('shows month and year when 1st of month', () => {
|
||||
expect(formatter(new Date('2020-03-01'))).toBe('Mar 2020');
|
||||
});
|
||||
|
||||
it('shows weekday when any day of the month', () => {
|
||||
expect(formatter(new Date('2020-03-03'))).toBe('Tue Mar 3');
|
||||
expect(formatter(new Date('2020-03-15'))).toBe('Sun Mar 15');
|
||||
});
|
||||
});
|
||||
describe('when locale is not default', () => {
|
||||
const locale: TimeLocaleDefinition = {
|
||||
dateTime: '%A, %e de %B de %Y. %X',
|
||||
date: '%d/%m/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda',
|
||||
'Terça',
|
||||
'Quarta',
|
||||
'Quinta',
|
||||
'Sexta',
|
||||
'Sábado',
|
||||
],
|
||||
shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro',
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez',
|
||||
],
|
||||
};
|
||||
const formatter = createSmartDateVerboseFormatter(locale);
|
||||
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(formatter(new Date('2020-01-01'))).toBe('2020');
|
||||
});
|
||||
it('is a function', () => {
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
});
|
||||
|
||||
it('shows month and year when 1st of month', () => {
|
||||
expect(formatter(new Date('2020-03-01'))).toBe('Mar 2020');
|
||||
});
|
||||
it('shows only year when 1st day of the year', () => {
|
||||
expect(formatter(new Date('2020-01-01'))).toBe('2020');
|
||||
});
|
||||
|
||||
it('shows weekday when any day of the month', () => {
|
||||
expect(formatter(new Date('2020-03-03'))).toBe('Tue Mar 3');
|
||||
expect(formatter(new Date('2020-03-15'))).toBe('Sun Mar 15');
|
||||
it('shows month and year when 1st of month', () => {
|
||||
expect(formatter(new Date('2020-04-01'))).toBe('Abr 2020');
|
||||
});
|
||||
|
||||
it('shows weekday when any day of the month', () => {
|
||||
expect(formatter(new Date('2020-03-03'))).toBe('Ter Mar 3');
|
||||
expect(formatter(new Date('2020-03-15'))).toBe('Dom Mar 15');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,8 +25,12 @@ import {
|
|||
getTimeFormatterRegistry,
|
||||
LOCAL_PREFIX,
|
||||
PREVIEW_TIME,
|
||||
smartDateFormatter,
|
||||
smartDateVerboseFormatter,
|
||||
SMART_DATE_ID,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
SMART_DATE_DETAILED_ID,
|
||||
createSmartDateFormatter,
|
||||
createSmartDateVerboseFormatter,
|
||||
createSmartDateDetailedFormatter,
|
||||
TimeFormats,
|
||||
TimeFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
@ -41,8 +45,12 @@ describe('index', () => {
|
|||
getTimeFormatterRegistry,
|
||||
LOCAL_PREFIX,
|
||||
PREVIEW_TIME,
|
||||
smartDateFormatter,
|
||||
smartDateVerboseFormatter,
|
||||
SMART_DATE_ID,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
SMART_DATE_DETAILED_ID,
|
||||
createSmartDateFormatter,
|
||||
createSmartDateVerboseFormatter,
|
||||
createSmartDateDetailedFormatter,
|
||||
TimeFormats,
|
||||
TimeFormatter,
|
||||
].forEach(x => expect(x).toBeDefined());
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
getTimeFormatter,
|
||||
isDefined,
|
||||
NumberFormats,
|
||||
smartDateVerboseFormatter,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
|
@ -82,6 +82,8 @@ const NO_DATA_RENDER_DATA = [
|
|||
},
|
||||
];
|
||||
|
||||
const smartDateVerboseFormatter = getTimeFormatter(SMART_DATE_VERBOSE_ID);
|
||||
|
||||
// Override the noData render function to make a prettier UX
|
||||
// Code adapted from https://github.com/novus/nvd3/blob/master/src/utils.js#L653
|
||||
nv.utils.noData = function noData(chart, container) {
|
||||
|
|
|
@ -19,7 +19,11 @@
|
|||
import d3 from 'd3';
|
||||
import d3tip from 'd3-tip';
|
||||
import dompurify from 'dompurify';
|
||||
import { smartDateFormatter, getNumberFormatter } from '@superset-ui/core';
|
||||
import {
|
||||
SMART_DATE_ID,
|
||||
getTimeFormatter,
|
||||
getNumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
// Regexp for the label added to time shifted series
|
||||
// (1 hour offset, 2 days offset, etc.)
|
||||
const TIME_SHIFT_PATTERN = /\d+ \w+ offset/;
|
||||
|
@ -42,8 +46,8 @@ export function cleanColorInput(value) {
|
|||
* @param {*} format
|
||||
*/
|
||||
export function getTimeOrNumberFormatter(format) {
|
||||
return format === 'smart_date'
|
||||
? smartDateFormatter
|
||||
return format === SMART_DATE_ID
|
||||
? getTimeFormatter(SMART_DATE_ID)
|
||||
: getNumberFormatter(format);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
getTimeFormatterRegistry,
|
||||
SMART_DATE_ID,
|
||||
createSmartDateFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
import {
|
||||
computeStackedYDomain,
|
||||
computeYDomain,
|
||||
|
@ -111,6 +117,13 @@ const DATA_WITH_DISABLED_SERIES = [
|
|||
];
|
||||
|
||||
describe('nvd3/utils', () => {
|
||||
beforeEach(() => {
|
||||
getTimeFormatterRegistry().registerValue(
|
||||
SMART_DATE_ID,
|
||||
createSmartDateFormatter(),
|
||||
);
|
||||
});
|
||||
|
||||
describe('getTimeOrNumberFormatter(format)', () => {
|
||||
it('is a function', () => {
|
||||
expect(typeof getTimeOrNumberFormatter).toBe('function');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { GenericDataType, smartDateFormatter, t } from '@superset-ui/core';
|
||||
import { GenericDataType, SMART_DATE_ID, t } from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelConfig,
|
||||
D3_FORMAT_DOCS,
|
||||
|
@ -71,7 +71,7 @@ export default {
|
|||
renderTrigger: true,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
default: smartDateFormatter.id,
|
||||
default: SMART_DATE_ID,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -20,7 +20,8 @@ import { PureComponent, MouseEvent } from 'react';
|
|||
import {
|
||||
t,
|
||||
getNumberFormatter,
|
||||
smartDateVerboseFormatter,
|
||||
getTimeFormatter,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
computeMaxFontSize,
|
||||
BRAND_COLOR,
|
||||
styled,
|
||||
|
@ -45,7 +46,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
|
|||
static defaultProps = {
|
||||
className: '',
|
||||
headerFormatter: defaultNumberFormatter,
|
||||
formatTime: smartDateVerboseFormatter,
|
||||
formatTime: getTimeFormatter(SMART_DATE_VERBOSE_ID),
|
||||
headerFontSize: PROPORTION.HEADER,
|
||||
kickerFontSize: PROPORTION.KICKER,
|
||||
mainColor: BRAND_COLOR,
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { smartDateFormatter, t } from '@superset-ui/core';
|
||||
import { SMART_DATE_ID, t } from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelConfig,
|
||||
ControlSubSectionHeader,
|
||||
|
@ -144,7 +144,7 @@ const config: ControlPanelConfig = {
|
|||
renderTrigger: true,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
default: smartDateFormatter.id,
|
||||
default: SMART_DATE_ID,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -21,7 +21,7 @@ import moment from 'moment';
|
|||
import {
|
||||
getTimeFormatter,
|
||||
getTimeFormatterForGranularity,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_ID,
|
||||
TimeGranularity,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
|
@ -41,6 +41,6 @@ export const getDateFormatter = (
|
|||
granularity?: TimeGranularity,
|
||||
fallbackFormat?: string | null,
|
||||
) =>
|
||||
timeFormat === smartDateFormatter.id
|
||||
timeFormat === SMART_DATE_ID
|
||||
? getTimeFormatterForGranularity(granularity)
|
||||
: getTimeFormatter(timeFormat ?? fallbackFormat);
|
||||
|
|
|
@ -25,12 +25,21 @@ import {
|
|||
isSavedMetric,
|
||||
NumberFormats,
|
||||
QueryFormMetric,
|
||||
smartDateDetailedFormatter,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_DETAILED_ID,
|
||||
SMART_DATE_ID,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
TimeFormatter,
|
||||
ValueFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
export const getSmartDateDetailedFormatter = () =>
|
||||
getTimeFormatter(SMART_DATE_DETAILED_ID);
|
||||
|
||||
export const getSmartDateFormatter = () => getTimeFormatter(SMART_DATE_ID);
|
||||
|
||||
export const getSmartDateVerboseFormatter = () =>
|
||||
getTimeFormatter(SMART_DATE_VERBOSE_ID);
|
||||
|
||||
export const getPercentFormatter = (format?: string) =>
|
||||
getNumberFormatter(
|
||||
!format || format === NumberFormats.SMART_NUMBER
|
||||
|
@ -68,8 +77,8 @@ export const getYAxisFormatter = (
|
|||
export function getTooltipTimeFormatter(
|
||||
format?: string,
|
||||
): TimeFormatter | StringConstructor {
|
||||
if (format === smartDateFormatter.id) {
|
||||
return smartDateDetailedFormatter;
|
||||
if (format === SMART_DATE_ID) {
|
||||
return getSmartDateDetailedFormatter();
|
||||
}
|
||||
if (format) {
|
||||
return getTimeFormatter(format);
|
||||
|
@ -80,7 +89,7 @@ export function getTooltipTimeFormatter(
|
|||
export function getXAxisFormatter(
|
||||
format?: string,
|
||||
): TimeFormatter | StringConstructor | undefined {
|
||||
if (format === smartDateFormatter.id || !format) {
|
||||
if (format === SMART_DATE_ID || !format) {
|
||||
return undefined;
|
||||
}
|
||||
if (format) {
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
isAdhocColumn,
|
||||
isPhysicalColumn,
|
||||
QueryFormMetric,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_ID,
|
||||
t,
|
||||
validateNonEmpty,
|
||||
} from '@superset-ui/core';
|
||||
|
@ -298,7 +298,7 @@ const config: ControlPanelConfig = {
|
|||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Date format'),
|
||||
default: smartDateFormatter.id,
|
||||
default: SMART_DATE_ID,
|
||||
renderTrigger: true,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: t('D3 time format for datetime columns'),
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
getTimeFormatter,
|
||||
getTimeFormatterForGranularity,
|
||||
QueryFormData,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_ID,
|
||||
TimeFormats,
|
||||
} from '@superset-ui/core';
|
||||
import { getColorFormatters } from '@superset-ui/chart-controls';
|
||||
|
@ -120,7 +120,7 @@ export default function transformProps(chartProps: ChartProps<QueryFormData>) {
|
|||
temporalColname: string,
|
||||
) => {
|
||||
let formatter: DateFormatter | undefined;
|
||||
if (dateFormat === smartDateFormatter.id) {
|
||||
if (dateFormat === SMART_DATE_ID) {
|
||||
if (granularity) {
|
||||
// time column use formats based on granularity
|
||||
formatter = getTimeFormatterForGranularity(granularity);
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
isPhysicalColumn,
|
||||
QueryFormColumn,
|
||||
QueryMode,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_ID,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
|
@ -361,7 +361,7 @@ const config: ControlPanelConfig = {
|
|||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Timestamp format'),
|
||||
default: smartDateFormatter.id,
|
||||
default: SMART_DATE_ID,
|
||||
renderTrigger: true,
|
||||
clearable: false,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
getTimeFormatterForGranularity,
|
||||
NumberFormats,
|
||||
QueryMode,
|
||||
smartDateFormatter,
|
||||
SMART_DATE_ID,
|
||||
TimeFormats,
|
||||
TimeFormatter,
|
||||
} from '@superset-ui/core';
|
||||
|
@ -140,7 +140,7 @@ const processColumns = memoizeOne(function processColumns(
|
|||
const customFormat = config.d3TimeFormat || savedFormat;
|
||||
const timeFormat = customFormat || tableTimestampFormat;
|
||||
// When format is "Adaptive Formatting" (smart_date)
|
||||
if (timeFormat === smartDateFormatter.id) {
|
||||
if (timeFormat === SMART_DATE_ID) {
|
||||
if (granularity) {
|
||||
// time column use formats based on granularity
|
||||
formatter = getTimeFormatterForGranularity(granularity);
|
||||
|
|
|
@ -16,7 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { t, DEFAULT_D3_FORMAT } from '@superset-ui/core';
|
||||
import {
|
||||
t,
|
||||
DEFAULT_D3_FORMAT,
|
||||
DEFAULT_D3_TIME_FORMAT,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
import { BootstrapData, CommonBootstrapData } from './types/bootstrapTypes';
|
||||
|
||||
|
@ -184,6 +188,7 @@ export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = {
|
|||
},
|
||||
},
|
||||
d3_format: DEFAULT_D3_FORMAT,
|
||||
d3_time_format: DEFAULT_D3_TIME_FORMAT,
|
||||
};
|
||||
|
||||
export const DEFAULT_BOOTSTRAP_DATA: BootstrapData = {
|
||||
|
|
|
@ -61,7 +61,10 @@ setupColors(
|
|||
);
|
||||
|
||||
// Setup number formatters
|
||||
setupFormatters(bootstrapData.common.d3_format);
|
||||
setupFormatters(
|
||||
bootstrapData.common.d3_format,
|
||||
bootstrapData.common.d3_time_format,
|
||||
);
|
||||
|
||||
setupDashboardComponents();
|
||||
|
||||
|
|
|
@ -22,16 +22,22 @@ import {
|
|||
getNumberFormatterRegistry,
|
||||
NumberFormats,
|
||||
getTimeFormatterRegistry,
|
||||
smartDateFormatter,
|
||||
smartDateVerboseFormatter,
|
||||
SMART_DATE_ID,
|
||||
SMART_DATE_DETAILED_ID,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
createSmartDateFormatter,
|
||||
createSmartDateVerboseFormatter,
|
||||
createSmartDateDetailedFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { FormatLocaleDefinition } from 'd3-format';
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
|
||||
export default function setupFormatters(
|
||||
d3Format: Partial<FormatLocaleDefinition>,
|
||||
d3NumberFormat: Partial<FormatLocaleDefinition>,
|
||||
d3TimeFormat: Partial<TimeLocaleDefinition>,
|
||||
) {
|
||||
getNumberFormatterRegistry()
|
||||
.setD3Format(d3Format)
|
||||
.setD3Format(d3NumberFormat)
|
||||
// Add shims for format strings that are deprecated or common typos.
|
||||
// Temporary solution until performing a db migration to fix this.
|
||||
.registerValue(',0', getNumberFormatter(',.4~f'))
|
||||
|
@ -72,8 +78,21 @@ export default function setupFormatters(
|
|||
createDurationFormatter({ formatSubMilliseconds: true }),
|
||||
);
|
||||
|
||||
getTimeFormatterRegistry()
|
||||
.registerValue('smart_date', smartDateFormatter)
|
||||
.registerValue('smart_date_verbose', smartDateVerboseFormatter)
|
||||
.setDefaultKey('smart_date');
|
||||
const timeFormatterRegistry = getTimeFormatterRegistry();
|
||||
|
||||
timeFormatterRegistry
|
||||
.setD3Format(d3TimeFormat)
|
||||
.registerValue(
|
||||
SMART_DATE_ID,
|
||||
createSmartDateFormatter(timeFormatterRegistry.d3Format),
|
||||
)
|
||||
.registerValue(
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
createSmartDateVerboseFormatter(timeFormatterRegistry.d3Format),
|
||||
)
|
||||
.registerValue(
|
||||
SMART_DATE_DETAILED_ID,
|
||||
createSmartDateDetailedFormatter(timeFormatterRegistry.d3Format),
|
||||
)
|
||||
.setDefaultKey(SMART_DATE_ID);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
SequentialSchemeConfig,
|
||||
} from '@superset-ui/core';
|
||||
import { FormatLocaleDefinition } from 'd3-format';
|
||||
import { TimeLocaleDefinition } from 'd3-time-format';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import { Languages } from 'src/features/home/LanguagePicker';
|
||||
import { FlashMessage } from '../components/FlashProvider';
|
||||
|
@ -152,6 +153,7 @@ export interface CommonBootstrapData {
|
|||
theme_overrides: JsonObject;
|
||||
menu_data: MenuData;
|
||||
d3_format: Partial<FormatLocaleDefinition>;
|
||||
d3_time_format: Partial<TimeLocaleDefinition>;
|
||||
}
|
||||
|
||||
export interface BootstrapData {
|
||||
|
|
|
@ -400,6 +400,38 @@ class D3Format(TypedDict, total=False):
|
|||
|
||||
D3_FORMAT: D3Format = {}
|
||||
|
||||
|
||||
# Override the default d3 locale for time format
|
||||
# Default values are equivalent to
|
||||
# D3_TIME_FORMAT = {
|
||||
# "dateTime": "%x, %X",
|
||||
# "date": "%-m/%-d/%Y",
|
||||
# "time": "%-I:%M:%S %p",
|
||||
# "periods": ["AM", "PM"],
|
||||
# "days": ["Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
# "Thursday", "Friday", "Saturday"],
|
||||
# "shortDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
||||
# "months": ["January", "February", "March", "April",
|
||||
# "May", "June", "July", "August",
|
||||
# "September", "October", "November", "December"],
|
||||
# "shortMonths": ["Jan", "Feb", "Mar", "Apr",
|
||||
# "May", "Jun", "Jul", "Aug",
|
||||
# "Sep", "Oct", "Nov", "Dec"]
|
||||
# }
|
||||
# https://github.com/d3/d3-time-format/tree/main#locales
|
||||
class D3TimeFormat(TypedDict, total=False):
|
||||
date: str
|
||||
dateTime: str
|
||||
time: str
|
||||
periods: list[str]
|
||||
days: list[str]
|
||||
shortDays: list[str]
|
||||
months: list[str]
|
||||
shortMonths: list[str]
|
||||
|
||||
|
||||
D3_TIME_FORMAT: D3TimeFormat = {}
|
||||
|
||||
CURRENCIES = ["USD", "EUR", "GBP", "INR", "MXN", "JPY", "CNY"]
|
||||
|
||||
# ---------------------------------------------------
|
||||
|
|
|
@ -419,6 +419,7 @@ def cached_common_bootstrap_data( # pylint: disable=unused-argument
|
|||
"locale": language,
|
||||
"language_pack": get_language_pack(language),
|
||||
"d3_format": conf.get("D3_FORMAT"),
|
||||
"d3_time_format": conf.get("D3_TIME_FORMAT"),
|
||||
"currencies": conf.get("CURRENCIES"),
|
||||
"feature_flags": get_feature_flags(),
|
||||
"extra_sequential_color_schemes": conf["EXTRA_SEQUENTIAL_COLOR_SCHEMES"],
|
||||
|
|
Loading…
Reference in New Issue