This commit is contained in:
Matheus Batista 2024-05-05 02:12:30 -03:00 committed by GitHub
commit 5a6b3858d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 917 additions and 191 deletions

View File

@ -48,6 +48,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",
@ -5381,6 +5382,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",
@ -22702,9 +22708,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",
@ -67780,6 +67786,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",
@ -74975,6 +74986,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",
@ -86396,6 +86412,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",
@ -89663,9 +89684,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",

View File

@ -114,6 +114,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",

View File

@ -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'],

View File

@ -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',
],
};

View File

@ -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;

View File

@ -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,

View File

@ -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,
});
}

View File

@ -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,
});
}

View File

@ -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,
});
}

View File

@ -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';

View File

@ -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');
});
});
});
});

View File

@ -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');
});
});
});
});

View File

@ -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');
});
});
});

View File

@ -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',
);
});
});
});

View File

@ -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');
});
});
});

View File

@ -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());

View File

@ -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) {

View File

@ -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);
}

View File

@ -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');

View File

@ -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,
},
},
],

View File

@ -20,7 +20,8 @@ import React, { MouseEvent } from 'react';
import {
t,
getNumberFormatter,
smartDateVerboseFormatter,
getTimeFormatter,
SMART_DATE_VERBOSE_ID,
computeMaxFontSize,
BRAND_COLOR,
styled,
@ -45,7 +46,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVizProps> {
static defaultProps = {
className: '',
headerFormatter: defaultNumberFormatter,
formatTime: smartDateVerboseFormatter,
formatTime: getTimeFormatter(SMART_DATE_VERBOSE_ID),
headerFontSize: PROPORTION.HEADER,
kickerFontSize: PROPORTION.KICKER,
mainColor: BRAND_COLOR,

View File

@ -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,
@ -145,7 +145,7 @@ const config: ControlPanelConfig = {
renderTrigger: true,
choices: D3_TIME_FORMAT_OPTIONS,
description: D3_FORMAT_DOCS,
default: smartDateFormatter.id,
default: SMART_DATE_ID,
},
},
],

View File

@ -19,11 +19,12 @@
import {
extractTimegrain,
getNumberFormatter,
getTimeFormatter,
NumberFormats,
GenericDataType,
getMetricLabel,
t,
smartDateVerboseFormatter,
SMART_DATE_VERBOSE_ID,
TimeFormatter,
getXAxisLabel,
Metric,
@ -42,6 +43,8 @@ import { getDefaultTooltip } from '../../utils/tooltip';
import { Refs } from '../../types';
const defaultNumberFormatter = getNumberFormatter();
const smartDateVerboseFormatter = getTimeFormatter(SMART_DATE_VERBOSE_ID);
export function renderTooltipFactory(
formatDate: TimeFormatter = smartDateVerboseFormatter,
formatValue: ValueFormatter | TimeFormatter = defaultNumberFormatter,

View File

@ -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);

View File

@ -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) {

View File

@ -22,7 +22,7 @@ import {
isAdhocColumn,
isPhysicalColumn,
QueryFormMetric,
smartDateFormatter,
SMART_DATE_ID,
t,
validateNonEmpty,
} from '@superset-ui/core';
@ -299,7 +299,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'),

View File

@ -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);

View File

@ -26,7 +26,7 @@ import {
isPhysicalColumn,
QueryFormColumn,
QueryMode,
smartDateFormatter,
SMART_DATE_ID,
t,
} from '@superset-ui/core';
import {
@ -362,7 +362,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,

View File

@ -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);

View File

@ -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 = {

View File

@ -61,7 +61,10 @@ setupColors(
);
// Setup number formatters
setupFormatters(bootstrapData.common.d3_format);
setupFormatters(
bootstrapData.common.d3_format,
bootstrapData.common.d3_time_format,
);
setupDashboardComponents();

View File

@ -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);
}

View File

@ -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 {

View File

@ -398,6 +398,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"]
# ---------------------------------------------------

View File

@ -415,6 +415,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"],