Skip to content

Commit

Permalink
feat: custom d3 number locale (#20075)
Browse files Browse the repository at this point in the history
  • Loading branch information
ebaratte authored May 2, 2023
1 parent f2fc4a0 commit a170ae4
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 22 deletions.
33 changes: 27 additions & 6 deletions superset-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions superset-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"@superset-ui/plugin-chart-word-cloud": "file:./plugins/plugin-chart-word-cloud",
"@superset-ui/preset-chart-xy": "file:./plugins/preset-chart-xy",
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
"@types/d3-format": "^3.0.1",
"@visx/axis": "^3.0.1",
"@visx/grid": "^3.0.1",
"@visx/responsive": "^3.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
import { t, smartDateFormatter, NumberFormats } from '@superset-ui/core';
import {
t,
smartDateFormatter,
NumberFormats,
getNumberFormatter,
} from '@superset-ui/core';

// D3 specific formatting config
export const D3_FORMAT_DOCS = t(
Expand All @@ -30,22 +35,26 @@ export const D3_NUMBER_FORMAT_DESCRIPTION_PERCENTAGE_TEXT = t(
'Only applies when "Label Type" is not set to a percentage.',
);

const d3Formatted: [string, string][] = [
',d',
'.1s',
'.3s',
',.1%',
'.2%',
'.3%',
'.4r',
',.1f',
',.2f',
',.3f',
'+,',
'$,.2f',
].map(fmt => [fmt, `${fmt} (${getNumberFormatter(fmt).preview()})`]);

// input choices & options
export const D3_FORMAT_OPTIONS: [string, string][] = [
[NumberFormats.SMART_NUMBER, t('Adaptive formatting')],
['~g', t('Original value')],
[',d', ',d (12345.432 => 12,345)'],
['.1s', '.1s (12345.432 => 10k)'],
['.3s', '.3s (12345.432 => 12.3k)'],
[',.1%', ',.1% (12345.432 => 1,234,543.2%)'],
['.2%', '.2% (12345.432 => 1234543.20%)'],
['.3%', '.3% (12345.432 => 1234543.200%)'],
['.4r', '.4r (12345.432 => 12350)'],
[',.1f', ',.1f (12345.432 => 12,345.4)'],
[',.2f', ',.2f (12345.432 => 12,345.43)'],
[',.3f', ',.3f (12345.432 => 12,345.432)'],
['+,', '+, (12345.432 => +12,345.432)'],
['$,.2f', '$,.2f (12345.432 => $12,345.43)'],
...d3Formatted,
['DURATION', t('Duration in ms (66000 => 1m 6s)')],
['DURATION_SUB', t('Duration in ms (1.40008 => 1ms 400µs 80ns)')],
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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 { FormatLocaleDefinition } from 'd3-format';

export const DEFAULT_D3_FORMAT: FormatLocaleDefinition = {
decimal: '.',
thousands: ',',
grouping: [3],
currency: ['$', ''],
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FormatLocaleDefinition } from 'd3-format';
import { RegistryWithDefaultKey, OverwritePolicy } from '../models';
import { DEFAULT_D3_FORMAT } from './D3FormatConfig';
import createD3NumberFormatter from './factories/createD3NumberFormatter';
import createSmartNumberFormatter from './factories/createSmartNumberFormatter';
import NumberFormats from './NumberFormats';
Expand All @@ -26,6 +28,8 @@ export default class NumberFormatterRegistry extends RegistryWithDefaultKey<
NumberFormatter,
NumberFormatter
> {
d3Format: FormatLocaleDefinition;

constructor() {
super({
name: 'NumberFormatter',
Expand All @@ -41,6 +45,12 @@ export default class NumberFormatterRegistry extends RegistryWithDefaultKey<
createSmartNumberFormatter({ signed: true }),
);
this.setDefaultKey(NumberFormats.SMART_NUMBER);
this.d3Format = DEFAULT_D3_FORMAT;
}

setD3Format(d3Format: Partial<FormatLocaleDefinition>) {
this.d3Format = { ...DEFAULT_D3_FORMAT, ...d3Format };
return this;
}

get(formatterId?: string) {
Expand All @@ -59,6 +69,7 @@ export default class NumberFormatterRegistry extends RegistryWithDefaultKey<
// Create new formatter if does not exist
const formatter = createD3NumberFormatter({
formatString: targetFormat,
locale: this.d3Format,
});
this.registerValue(targetFormat, formatter);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FormatLocaleDefinition } from 'd3-format';
import { makeSingleton } from '../utils';
import NumberFormatterRegistry from './NumberFormatterRegistry';

Expand All @@ -27,6 +28,10 @@ export function getNumberFormatter(format?: string) {
return getInstance().get(format);
}

export function setD3Format(d3Format: Partial<FormatLocaleDefinition>) {
getInstance().setD3Format(d3Format);
}

export function formatNumber(
format: string | undefined,
value: number | null | undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

export { default as NumberFormats } from './NumberFormats';
export { default as NumberFormatter, PREVIEW_VALUE } from './NumberFormatter';
export { DEFAULT_D3_FORMAT } from './D3FormatConfig';

export {
default as getNumberFormatterRegistry,
formatNumber,
setD3Format,
getNumberFormatter,
} from './NumberFormatterRegistrySingleton';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import {
NumberFormatterRegistry,
getNumberFormatterRegistry,
setD3Format,
getNumberFormatter,
formatNumber,
} from '@superset-ui/core';
Expand Down Expand Up @@ -55,4 +56,23 @@ describe('NumberFormatterRegistrySingleton', () => {
expect(formatNumber(undefined, 1000)).toEqual('1k');
});
});
describe('setD3Format()', () => {
it('sets a specific FormatLocaleDefinition', () => {
setD3Format({
decimal: ';',
thousands: '-',
currency: ['€', ''],
grouping: [2],
});
const formatter = getNumberFormatter('$,.2f');
expect(formatter.format(12345.67)).toEqual('€1-23-45;67');
});
it('falls back to default value for unspecified locale format parameters', () => {
setD3Format({
currency: ['€', ''],
});
const formatter = getNumberFormatter('$,.1f');
expect(formatter.format(12345.67)).toEqual('€12,345.7');
});
});
});
3 changes: 2 additions & 1 deletion superset-frontend/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { t } from '@superset-ui/core';
import { t, DEFAULT_D3_FORMAT } from '@superset-ui/core';

import { BootstrapData, CommonBootstrapData } from './types/bootstrapTypes';

Expand Down Expand Up @@ -184,6 +184,7 @@ export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = {
color: '',
},
},
d3_format: DEFAULT_D3_FORMAT,
};

export const DEFAULT_BOOTSTRAP_DATA: BootstrapData = {
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/preamble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ setupColors(
);

// Setup number formatters
setupFormatters();
setupFormatters(bootstrapData.common.d3_format);

setupDashboardComponents();

Expand Down
6 changes: 5 additions & 1 deletion superset-frontend/src/setup/setupFormatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ import {
smartDateFormatter,
smartDateVerboseFormatter,
} from '@superset-ui/core';
import { FormatLocaleDefinition } from 'd3-format';

export default function setupFormatters() {
export default function setupFormatters(
d3Format: Partial<FormatLocaleDefinition>,
) {
getNumberFormatterRegistry()
.setD3Format(d3Format)
// 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'))
Expand Down
2 changes: 2 additions & 0 deletions superset-frontend/src/types/bootstrapTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Locale,
SequentialSchemeConfig,
} from '@superset-ui/core';
import { FormatLocaleDefinition } from 'd3-format';
import { isPlainObject } from 'lodash';
import { Languages } from 'src/features/home/LanguagePicker';
import { FlashMessage } from '../components/FlashProvider';
Expand Down Expand Up @@ -150,6 +151,7 @@ export interface CommonBootstrapData {
extra_sequential_color_schemes: SequentialSchemeConfig[];
theme_overrides: JsonObject;
menu_data: MenuData;
d3_format: Partial<FormatLocaleDefinition>;
}

export interface BootstrapData {
Expand Down
19 changes: 19 additions & 0 deletions superset/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,25 @@ def _try_json_readsha(filepath: str, length: int) -> Optional[str]:
# incomplete and not well maintained.
LANGUAGES = {}


# Override the default d3 locale format
# Default values are equivalent to
# D3_FORMAT = {
# "decimal": ".", # - decimal place string (e.g., ".").
# "thousands": ",", # - group separator string (e.g., ",").
# "grouping": [3], # - array of group sizes (e.g., [3]), cycled as needed.
# "currency": ["$", ""] # - currency prefix/suffix strings (e.g., ["$", ""])
# }
# https://github.com/d3/d3-format/blob/main/README.md#formatLocale
class D3Format(TypedDict, total=False):
decimal: str
thousands: str
grouping: List[int]
currency: List[str]


D3_FORMAT: D3Format = {}

# ---------------------------------------------------
# Feature flags
# ---------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions superset/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ def cached_common_bootstrap_data(user: User) -> Dict[str, Any]:
"conf": frontend_config,
"locale": locale,
"language_pack": get_language_pack(locale),
"d3_format": conf.get("D3_FORMAT"),
"feature_flags": get_feature_flags(),
"extra_sequential_color_schemes": conf["EXTRA_SEQUENTIAL_COLOR_SCHEMES"],
"extra_categorical_color_schemes": conf["EXTRA_CATEGORICAL_COLOR_SCHEMES"],
Expand Down

0 comments on commit a170ae4

Please sign in to comment.