You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by kg...@apache.org on 2023/08/02 17:22:53 UTC
[superset] branch master updated: feat: Add currencies controls in control panels (#24718)
This is an automated email from the ASF dual-hosted git repository.
kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new f7e76d02b7 feat: Add currencies controls in control panels (#24718)
f7e76d02b7 is described below
commit f7e76d02b7cbe4940946673590bb979984ace9f5
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Wed Aug 2 19:22:45 2023 +0200
feat: Add currencies controls in control panels (#24718)
---
.../src/components/ControlForm/controls.tsx | 92 ---------------
.../ColumnConfigControl/ColumnConfigPopover.tsx | 73 ------------
.../src/shared-controls/components/index.tsx | 3 -
.../src/shared-controls/sharedControls.tsx | 7 ++
.../superset-ui-chart-controls/src/types.ts | 64 +++++++++-
.../src/currency-format/CurrencyFormatter.ts | 2 +-
.../superset-ui-core/src/currency-format/utils.ts | 42 +++++--
.../test/currency-format/utils.test.ts | 70 +++++++++--
.../src/controlPanel.tsx | 1 +
.../src/transformProps.js | 2 +
.../src/controlPanel.ts | 5 +
.../src/transformProps.js | 5 +-
.../src/BigNumber/BigNumberTotal/controlPanel.ts | 1 +
.../src/BigNumber/BigNumberTotal/transformProps.ts | 2 +
.../BigNumberWithTrendline/controlPanel.tsx | 1 +
.../BigNumberWithTrendline/transformProps.ts | 2 +
.../src/Funnel/controlPanel.tsx | 1 +
.../src/Funnel/transformProps.ts | 2 +
.../src/Gauge/controlPanel.tsx | 1 +
.../src/Gauge/transformProps.ts | 2 +
.../src/MixedTimeseries/controlPanel.tsx | 10 ++
.../src/MixedTimeseries/transformProps.ts | 29 +++--
.../plugin-chart-echarts/src/Pie/controlPanel.tsx | 1 +
.../plugin-chart-echarts/src/Pie/transformProps.ts | 2 +
.../src/Sunburst/controlPanel.tsx | 1 +
.../src/Sunburst/transformProps.ts | 3 +
.../src/Timeseries/Area/controlPanel.tsx | 1 +
.../src/Timeseries/Regular/Bar/controlPanel.tsx | 1 +
.../src/Timeseries/Regular/Line/controlPanel.tsx | 1 +
.../Timeseries/Regular/Scatter/controlPanel.tsx | 1 +
.../Timeseries/Regular/SmoothLine/controlPanel.tsx | 1 +
.../src/Timeseries/Step/controlPanel.tsx | 1 +
.../src/Timeseries/transformProps.ts | 9 +-
.../src/Treemap/controlPanel.tsx | 1 +
.../src/Treemap/transformProps.ts | 2 +
.../src/utils/getYAxisFormatter.ts | 5 +-
.../plugins/plugin-chart-handlebars/src/types.ts | 2 -
.../src/PivotTableChart.tsx | 15 ++-
.../src/plugin/controlPanel.tsx | 1 +
.../src/plugin/transformProps.ts | 2 +
.../plugins/plugin-chart-pivot-table/src/types.ts | 1 +
.../test/plugin/buildQuery.test.ts | 1 +
.../test/plugin/transformProps.test.ts | 2 +
.../plugin-chart-table/src/controlPanel.tsx | 2 +
.../plugin-chart-table/src/transformProps.ts | 10 +-
.../plugins/plugin-chart-table/src/types.ts | 20 +++-
superset-frontend/src/GlobalStyles.tsx | 24 ++++
.../src/components/Datasource/DatasourceEditor.jsx | 54 ++-------
.../Datasource/DatasourceEditor.test.jsx | 8 +-
.../src/explore/components/ControlHeader.tsx | 2 +-
.../ColumnConfigControl/ColumnConfigControl.tsx | 14 ++-
.../ColumnConfigControl/ColumnConfigItem.tsx | 8 +-
.../ColumnConfigControl/ColumnConfigPopover.tsx | 95 +++++++++++++++
.../ControlForm/ControlFormItem.tsx | 28 ++---
.../ColumnConfigControl/ControlForm/controls.ts} | 28 +++--
.../ColumnConfigControl}/ControlForm/index.tsx | 2 +-
.../controls}/ColumnConfigControl/constants.tsx | 68 +++++++----
.../controls}/ColumnConfigControl/index.tsx | 0
.../controls}/ColumnConfigControl/types.ts | 24 +++-
.../controls/CurrencyControl/CurrencyControl.tsx | 129 +++++++++++++++++++++
.../components/controls/CurrencyControl/index.ts | 3 +
.../src/explore/components/controls/index.js | 4 +
superset-frontend/src/views/types.ts | 1 +
63 files changed, 677 insertions(+), 318 deletions(-)
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/controls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/controls.tsx
deleted file mode 100644
index a1b689cbaa..0000000000
--- a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/controls.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * 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 React, { ReactNode } from 'react';
-import { Slider, InputNumber, Input } from 'antd';
-import Checkbox, { CheckboxProps } from 'antd/lib/checkbox';
-import Select, { SelectOption } from '../Select';
-import RadioButtonControl, {
- RadioButtonOption,
-} from '../../shared-controls/components/RadioButtonControl';
-
-export const ControlFormItemComponents = {
- Slider,
- InputNumber,
- Input,
- Select,
- // Directly export Checkbox will result in "using name from external module" error
- // ref: https://stackoverflow.com/questions/43900035/ts4023-exported-variable-x-has-or-is-using-name-y-from-external-module-but
- Checkbox: Checkbox as React.ForwardRefExoticComponent<
- CheckboxProps & React.RefAttributes<HTMLInputElement>
- >,
- RadioButtonControl,
-};
-
-export type ControlType = keyof typeof ControlFormItemComponents;
-
-export type ControlFormValueValidator<V> = (value: V) => string | false;
-
-export type ControlFormItemSpec<T extends ControlType = ControlType> = {
- controlType: T;
- label: ReactNode;
- description: ReactNode;
- placeholder?: string;
- required?: boolean;
- validators?: ControlFormValueValidator<any>[];
- width?: number | string;
- /**
- * Time to delay change propagation.
- */
- debounceDelay?: number;
-} & (T extends 'Select'
- ? {
- options: SelectOption<any>[];
- value?: string;
- defaultValue?: string;
- creatable?: boolean;
- minWidth?: number | string;
- validators?: ControlFormValueValidator<string>[];
- }
- : T extends 'RadioButtonControl'
- ? {
- options: RadioButtonOption[];
- value?: string;
- defaultValue?: string;
- }
- : T extends 'Checkbox'
- ? {
- value?: boolean;
- defaultValue?: boolean;
- }
- : T extends 'InputNumber' | 'Slider'
- ? {
- min?: number;
- max?: number;
- step?: number;
- value?: number;
- defaultValue?: number;
- validators?: ControlFormValueValidator<number>[];
- }
- : T extends 'Input'
- ? {
- controlType: 'Input';
- value?: string;
- defaultValue?: string;
- validators?: ControlFormValueValidator<string>[];
- }
- : {});
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigPopover.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigPopover.tsx
deleted file mode 100644
index dbc40b216e..0000000000
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigPopover.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * 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 React from 'react';
-import { GenericDataType } from '@superset-ui/core';
-import ControlForm, {
- ControlFormRow,
- ControlFormItem,
- ControlFormItemSpec,
-} from '../../../components/ControlForm';
-import {
- SHARED_COLUMN_CONFIG_PROPS,
- SharedColumnConfigProp,
-} from './constants';
-import {
- ColumnConfig,
- ColumnConfigFormLayout,
- ColumnConfigInfo,
-} from './types';
-
-export type ColumnConfigPopoverProps = {
- column: ColumnConfigInfo;
- configFormLayout: ColumnConfigFormLayout;
- onChange: (value: ColumnConfig) => void;
-};
-
-export default function ColumnConfigPopover({
- column,
- configFormLayout,
- onChange,
-}: ColumnConfigPopoverProps) {
- return (
- <ControlForm onChange={onChange} value={column.config}>
- {configFormLayout[
- column.type === undefined ? GenericDataType.STRING : column.type
- ].map((row, i) => (
- <ControlFormRow key={i}>
- {row.map(meta => {
- const key = typeof meta === 'string' ? meta : meta.name;
- const override =
- typeof meta === 'string'
- ? {}
- : 'override' in meta
- ? meta.override
- : meta.config;
- const props = {
- ...(key in SHARED_COLUMN_CONFIG_PROPS
- ? SHARED_COLUMN_CONFIG_PROPS[key as SharedColumnConfigProp]
- : undefined),
- ...override,
- } as ControlFormItemSpec;
- return <ControlFormItem key={key} name={key} {...props} />;
- })}
- </ControlFormRow>
- ))}
- </ControlForm>
- );
-}
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx
index b6e635e25f..93bfbcbe68 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx
@@ -17,10 +17,8 @@
* under the License.
*/
import RadioButtonControl from './RadioButtonControl';
-import ColumnConfigControl from './ColumnConfigControl';
export * from './RadioButtonControl';
-export * from './ColumnConfigControl';
/**
* Shared chart controls. Can be referred via string shortcuts in chart control
@@ -28,5 +26,4 @@ export * from './ColumnConfigControl';
*/
export default {
RadioButtonControl,
- ColumnConfigControl,
};
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
index a4af8bcbf2..abf5153bb0 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
@@ -317,6 +317,12 @@ const y_axis_format: SharedControlConfig<'SelectControl', SelectDefaultOption> =
},
};
+const currency_format: SharedControlConfig<'CurrencyControl'> = {
+ type: 'CurrencyControl',
+ label: t('Currency format'),
+ renderTrigger: true,
+};
+
const x_axis_time_format: SharedControlConfig<
'SelectControl',
SelectDefaultOption
@@ -406,4 +412,5 @@ export default {
x_axis: dndXAxisControl,
show_empty_columns,
temporal_columns_lookup,
+ currency_format,
};
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
index dadd78cdd0..889bc9a47c 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
@@ -34,7 +34,6 @@ import type {
import { sharedControls, sharedControlComponents } from './shared-controls';
export type { Metric } from '@superset-ui/core';
-export type { ControlFormItemSpec } from './components/ControlForm';
export type { ControlComponentProps } from './shared-controls/components/types';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -167,6 +166,12 @@ export type InternalControlType =
| 'DndColumnSelect'
| 'DndFilterSelect'
| 'DndMetricSelect'
+ | 'CurrencyControl'
+ | 'InputNumber'
+ | 'Checkbox'
+ | 'Select'
+ | 'Slider'
+ | 'Input'
| keyof SharedControlComponents; // expanded in `expandControlConfig`
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -495,3 +500,60 @@ export type SortSeriesData = {
sort_series_type: SortSeriesType;
sort_series_ascending: boolean;
};
+
+export type ControlFormValueValidator<V> = (value: V) => string | false;
+
+export type ControlFormItemSpec<T extends ControlType = ControlType> = {
+ controlType: T;
+ label: ReactNode;
+ description: ReactNode;
+ placeholder?: string;
+ validators?: ControlFormValueValidator<any>[];
+ width?: number | string;
+ /**
+ * Time to delay change propagation.
+ */
+ debounceDelay?: number;
+} & (T extends 'Select'
+ ? {
+ options: any;
+ value?: string;
+ defaultValue?: string;
+ creatable?: boolean;
+ minWidth?: number | string;
+ validators?: ControlFormValueValidator<string>[];
+ }
+ : T extends 'RadioButtonControl'
+ ? {
+ options: [string, ReactNode][];
+ value?: string;
+ defaultValue?: string;
+ }
+ : T extends 'Checkbox'
+ ? {
+ value?: boolean;
+ defaultValue?: boolean;
+ }
+ : T extends 'InputNumber' | 'Slider'
+ ? {
+ min?: number;
+ max?: number;
+ step?: number;
+ value?: number;
+ defaultValue?: number;
+ validators?: ControlFormValueValidator<number>[];
+ }
+ : T extends 'Input'
+ ? {
+ controlType: 'Input';
+ value?: string;
+ defaultValue?: string;
+ validators?: ControlFormValueValidator<string>[];
+ }
+ : T extends 'CurrencyControl'
+ ? {
+ controlType: 'CurrencyControl';
+ value?: Currency;
+ defaultValue?: Currency;
+ }
+ : {});
diff --git a/superset-frontend/packages/superset-ui-core/src/currency-format/CurrencyFormatter.ts b/superset-frontend/packages/superset-ui-core/src/currency-format/CurrencyFormatter.ts
index 7c082abd34..a5f215fe95 100644
--- a/superset-frontend/packages/superset-ui-core/src/currency-format/CurrencyFormatter.ts
+++ b/superset-frontend/packages/superset-ui-core/src/currency-format/CurrencyFormatter.ts
@@ -31,7 +31,7 @@ interface CurrencyFormatter {
(value: number | null | undefined): string;
}
-export const getCurrencySymbol = (currency: Currency) =>
+export const getCurrencySymbol = (currency: Partial<Currency>) =>
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency.symbol,
diff --git a/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts b/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts
index 014388b86d..243aa92a9f 100644
--- a/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts
+++ b/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts
@@ -28,20 +28,24 @@ import {
export const buildCustomFormatters = (
metrics: QueryFormMetric | QueryFormMetric[] | undefined,
- currencyFormats: Record<string, Currency>,
- columnFormats: Record<string, string>,
+ savedCurrencyFormats: Record<string, Currency>,
+ savedColumnFormats: Record<string, string>,
d3Format: string | undefined,
+ currencyFormat: Currency | undefined,
) => {
const metricsArray = ensureIsArray(metrics);
return metricsArray.reduce((acc, metric) => {
if (isSavedMetric(metric)) {
- const actualD3Format = d3Format ?? columnFormats[metric];
- return currencyFormats[metric]
+ const actualD3Format = d3Format ?? savedColumnFormats[metric];
+ const actualCurrencyFormat = currencyFormat?.symbol
+ ? currencyFormat
+ : savedCurrencyFormats[metric];
+ return actualCurrencyFormat
? {
...acc,
[metric]: new CurrencyFormatter({
d3Format: actualD3Format,
- currency: currencyFormats[metric],
+ currency: actualCurrencyFormat,
}),
}
: {
@@ -67,13 +71,29 @@ export const getCustomFormatter = (
export const getValueFormatter = (
metrics: QueryFormMetric | QueryFormMetric[] | undefined,
- currencyFormats: Record<string, Currency>,
- columnFormats: Record<string, string>,
+ savedCurrencyFormats: Record<string, Currency>,
+ savedColumnFormats: Record<string, string>,
d3Format: string | undefined,
+ currencyFormat: Currency | undefined,
key?: string,
-) =>
- getCustomFormatter(
- buildCustomFormatters(metrics, currencyFormats, columnFormats, d3Format),
+) => {
+ const customFormatter = getCustomFormatter(
+ buildCustomFormatters(
+ metrics,
+ savedCurrencyFormats,
+ savedColumnFormats,
+ d3Format,
+ currencyFormat,
+ ),
metrics,
key,
- ) ?? getNumberFormatter(d3Format);
+ );
+
+ if (customFormatter) {
+ return customFormatter;
+ }
+ if (currencyFormat?.symbol) {
+ return new CurrencyFormatter({ currency: currencyFormat, d3Format });
+ }
+ return getNumberFormatter(d3Format);
+};
diff --git a/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts b/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
index 9e35ce8d99..cb65528a89 100644
--- a/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
@@ -27,7 +27,7 @@ import {
ValueFormatter,
} from '@superset-ui/core';
-it('buildCustomFormatters without saved metrics returns empty object', () => {
+test('buildCustomFormatters without saved metrics returns empty object', () => {
expect(
buildCustomFormatters(
[
@@ -42,6 +42,7 @@ it('buildCustomFormatters without saved metrics returns empty object', () => {
},
{},
',.1f',
+ undefined,
),
).toEqual({});
@@ -53,11 +54,12 @@ it('buildCustomFormatters without saved metrics returns empty object', () => {
},
{},
',.1f',
+ undefined,
),
).toEqual({});
});
-it('buildCustomFormatters with saved metrics returns custom formatters object', () => {
+test('buildCustomFormatters with saved metrics returns custom formatters object', () => {
const customFormatters: Record<string, ValueFormatter> =
buildCustomFormatters(
[
@@ -74,6 +76,7 @@ it('buildCustomFormatters with saved metrics returns custom formatters object',
},
{ sum__num: ',.2' },
',.1f',
+ undefined,
);
expect(customFormatters).toEqual({
@@ -88,7 +91,7 @@ it('buildCustomFormatters with saved metrics returns custom formatters object',
);
});
-it('buildCustomFormatters uses dataset d3 format if not provided in control panel', () => {
+test('buildCustomFormatters uses dataset d3 format if not provided in control panel', () => {
const customFormatters: Record<string, ValueFormatter> =
buildCustomFormatters(
[
@@ -105,6 +108,7 @@ it('buildCustomFormatters uses dataset d3 format if not provided in control pane
},
{ sum__num: ',.2' },
undefined,
+ undefined,
);
expect((customFormatters.sum__num as CurrencyFormatter).d3Format).toEqual(
@@ -112,7 +116,7 @@ it('buildCustomFormatters uses dataset d3 format if not provided in control pane
);
});
-it('getCustomFormatter', () => {
+test('getCustomFormatter', () => {
const customFormatters = {
sum__num: new CurrencyFormatter({
currency: { symbol: 'USD', symbolPosition: 'prefix' },
@@ -130,13 +134,20 @@ it('getCustomFormatter', () => {
);
});
-it('getValueFormatter', () => {
+test('getValueFormatter', () => {
expect(
- getValueFormatter(['count', 'sum__num'], {}, {}, ',.1f'),
+ getValueFormatter(['count', 'sum__num'], {}, {}, ',.1f', undefined),
).toBeInstanceOf(NumberFormatter);
expect(
- getValueFormatter(['count', 'sum__num'], {}, {}, ',.1f', 'count'),
+ getValueFormatter(
+ ['count', 'sum__num'],
+ {},
+ {},
+ ',.1f',
+ undefined,
+ 'count',
+ ),
).toBeInstanceOf(NumberFormatter);
expect(
@@ -145,7 +156,52 @@ it('getValueFormatter', () => {
{ count: { symbol: 'USD', symbolPosition: 'prefix' } },
{},
',.1f',
+ undefined,
'count',
),
).toBeInstanceOf(CurrencyFormatter);
});
+
+test('getValueFormatter with currency from control panel', () => {
+ const countFormatter = getValueFormatter(
+ ['count', 'sum__num'],
+ { count: { symbol: 'USD', symbolPosition: 'prefix' } },
+ {},
+ ',.1f',
+ { symbol: 'EUR', symbolPosition: 'suffix' },
+ 'count',
+ );
+ expect(countFormatter).toBeInstanceOf(CurrencyFormatter);
+ expect((countFormatter as CurrencyFormatter).currency).toEqual({
+ symbol: 'EUR',
+ symbolPosition: 'suffix',
+ });
+});
+
+test('getValueFormatter with currency from control panel when no saved currencies', () => {
+ const formatter = getValueFormatter(
+ ['count', 'sum__num'],
+ {},
+ {},
+ ',.1f',
+ { symbol: 'EUR', symbolPosition: 'suffix' },
+ undefined,
+ );
+ expect(formatter).toBeInstanceOf(CurrencyFormatter);
+ expect((formatter as CurrencyFormatter).currency).toEqual({
+ symbol: 'EUR',
+ symbolPosition: 'suffix',
+ });
+});
+
+test('getValueFormatter return NumberFormatter when no currency formatters', () => {
+ const formatter = getValueFormatter(
+ ['count', 'sum__num'],
+ {},
+ {},
+ ',.1f',
+ undefined,
+ undefined,
+ );
+ expect(formatter).toBeInstanceOf(NumberFormatter);
+});
diff --git a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.tsx b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.tsx
index 3032654ba2..70a71e5024 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.tsx
+++ b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.tsx
@@ -257,6 +257,7 @@ const config: ControlPanelConfig = {
},
],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'sort_x_axis',
diff --git a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/transformProps.js b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/transformProps.js
index a6adf5f8b8..c4906ddbe9 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/transformProps.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/transformProps.js
@@ -38,6 +38,7 @@ export default function transformProps(chartProps) {
yscaleInterval,
yAxisBounds,
yAxisFormat,
+ currencyFormat,
} = formData;
const { columnFormats = {}, currencyFormats = {} } = datasource;
const valueFormatter = getValueFormatter(
@@ -45,6 +46,7 @@ export default function transformProps(chartProps) {
currencyFormats,
columnFormats,
yAxisFormat,
+ currencyFormat,
);
return {
width,
diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts
index b0f3be22c5..4d1a21561b 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts
+++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts
@@ -129,6 +129,11 @@ const config: ControlPanelConfig = {
['color_scheme'],
],
},
+ {
+ label: t('Chart Options'),
+ expanded: true,
+ controlSetRows: [['y_axis_format'], ['currency_format']],
+ },
],
controlOverrides: {
entity: {
diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/transformProps.js b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/transformProps.js
index 182d1b0b11..4069e29428 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/transformProps.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/transformProps.js
@@ -43,6 +43,8 @@ export default function transformProps(chartProps) {
colorScheme,
sliceId,
metric,
+ yAxisFormat,
+ currencyFormat,
} = formData;
const { r, g, b } = colorPicker;
const { currencyFormats = {}, columnFormats = {} } = datasource;
@@ -51,7 +53,8 @@ export default function transformProps(chartProps) {
metric,
currencyFormats,
columnFormats,
- undefined,
+ yAxisFormat,
+ currencyFormat,
);
return {
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts
index abe4ce215f..2bb1c1d1d6 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts
@@ -62,6 +62,7 @@ export default {
[headerFontSize],
[subheaderFontSize],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'time_format',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts
index bdef7852d1..4a68e1ff9f 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.ts
@@ -53,6 +53,7 @@ export default function transformProps(
timeFormat,
yAxisFormat,
conditionalFormatting,
+ currencyFormat,
} = formData;
const refs: Refs = {};
const { data = [], coltypes = [] } = queriesData[0];
@@ -80,6 +81,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
yAxisFormat,
+ currencyFormat,
);
const headerFormatter =
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx
index 3b98f05645..26bae683c3 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx
@@ -136,6 +136,7 @@ const config: ControlPanelConfig = {
[headerFontSize],
[subheaderFontSize],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'time_format',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts
index 4daa8f4401..ce35b65761 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts
@@ -91,6 +91,7 @@ export default function transformProps(
subheaderFontSize,
forceTimestampFormatting,
yAxisFormat,
+ currencyFormat,
timeRangeFixed,
} = formData;
const granularity = extractTimegrain(rawFormData);
@@ -180,6 +181,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
yAxisFormat,
+ currencyFormat,
);
const headerFormatter =
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx
index 75d062ce92..44a88fee4b 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx
@@ -119,6 +119,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'show_labels',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts
index 41319079dc..538ec27750 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts
@@ -112,6 +112,7 @@ export default function transformProps(
legendType,
metric = '',
numberFormat,
+ currencyFormat,
showLabels,
showLegend,
sliceId,
@@ -147,6 +148,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
numberFormat,
+ currencyFormat,
);
const transformedData: FunnelSeriesOption[] = data.map(datum => {
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx
index 5193961556..a7e0d3fdce 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx
@@ -154,6 +154,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'value_formatter',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts
index 0fd41e88a0..f32993b1df 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts
@@ -118,6 +118,7 @@ export default function transformProps(
colorScheme,
fontSize,
numberFormat,
+ currencyFormat,
animation,
showProgress,
overlap,
@@ -141,6 +142,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
numberFormat,
+ currencyFormat,
);
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const axisLineWidth = calculateAxisLineWidth(data, fontSize, overlap);
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx
index 58be782859..c9f9027a3e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx
@@ -389,6 +389,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'logAxis',
@@ -427,6 +428,15 @@ const config: ControlPanelConfig = {
},
},
],
+ [
+ {
+ name: 'currency_format_secondary',
+ config: {
+ ...sharedControls.currency_format,
+ label: t('Secondary currency format'),
+ },
+ },
+ ],
[
{
name: 'yAxisTitleSecondary',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
index 1ce4089357..25b7e5364a 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
@@ -36,9 +36,9 @@ import {
ensureIsArray,
buildCustomFormatters,
ValueFormatter,
- NumberFormatter,
QueryFormMetric,
getCustomFormatter,
+ CurrencyFormatter,
} from '@superset-ui/core';
import { getOriginalSeries } from '@superset-ui/chart-controls';
import { EChartsCoreOption, SeriesOption } from 'echarts';
@@ -92,7 +92,7 @@ import { getYAxisFormatter } from '../utils/getYAxisFormatter';
const getFormatter = (
customFormatters: Record<string, ValueFormatter>,
- defaultFormatter: NumberFormatter,
+ defaultFormatter: ValueFormatter,
metrics: QueryFormMetric[],
formatterKey: string,
forcePercentFormat: boolean,
@@ -167,7 +167,9 @@ export default function transformProps(
truncateYAxis,
tooltipTimeFormat,
yAxisFormat,
+ currencyFormat,
yAxisFormatSecondary,
+ currencyFormatSecondary,
xAxisTimeFormat,
yAxisBounds,
yAxisBoundsSecondary,
@@ -221,21 +223,32 @@ export default function transformProps(
const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig];
const xAxisType = getAxisType(xAxisDataType);
const series: SeriesOption[] = [];
- const formatter = getNumberFormatter(contributionMode ? ',.0%' : yAxisFormat);
- const formatterSecondary = getNumberFormatter(
- contributionMode ? ',.0%' : yAxisFormatSecondary,
- );
+ const formatter = contributionMode
+ ? getNumberFormatter(',.0%')
+ : currencyFormat?.symbol
+ ? new CurrencyFormatter({ d3Format: yAxisFormat, currency: currencyFormat })
+ : getNumberFormatter(yAxisFormat);
+ const formatterSecondary = contributionMode
+ ? getNumberFormatter(',.0%')
+ : currencyFormatSecondary?.symbol
+ ? new CurrencyFormatter({
+ d3Format: yAxisFormatSecondary,
+ currency: currencyFormatSecondary,
+ })
+ : getNumberFormatter(yAxisFormatSecondary);
const customFormatters = buildCustomFormatters(
[...ensureIsArray(metrics), ...ensureIsArray(metricsB)],
currencyFormats,
columnFormats,
yAxisFormat,
+ currencyFormat,
);
const customFormattersSecondary = buildCustomFormatters(
[...ensureIsArray(metrics), ...ensureIsArray(metricsB)],
currencyFormats,
columnFormats,
yAxisFormatSecondary,
+ currencyFormatSecondary,
);
const primarySeries = new Set<string>();
@@ -498,7 +511,7 @@ export default function transformProps(
metrics,
!!contributionMode,
customFormatters,
- yAxisFormat,
+ formatter,
),
},
scale: truncateYAxis,
@@ -520,7 +533,7 @@ export default function transformProps(
metricsB,
!!contributionMode,
customFormattersSecondary,
- yAxisFormatSecondary,
+ formatterSecondary,
),
},
scale: truncateYAxis,
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
index a82768b085..53d406538d 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
@@ -127,6 +127,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'date_format',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
index ea616cb201..4ad92b0bb2 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts
@@ -165,6 +165,7 @@ export default function transformProps(
legendType,
metric = '',
numberFormat,
+ currencyFormat,
dateFormat,
outerRadius,
showLabels,
@@ -211,6 +212,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
numberFormat,
+ currencyFormat,
);
let totalValue = 0;
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx
index bc0a515141..1957caa234 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/controlPanel.tsx
@@ -136,6 +136,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'date_format',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
index 6af4c7f653..a3a9b8777f 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
@@ -195,6 +195,7 @@ export default function transformProps(
linearColorScheme,
labelType,
numberFormat,
+ currencyFormat,
dateFormat,
showLabels,
showLabelsThreshold,
@@ -208,6 +209,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
numberFormat,
+ currencyFormat,
);
const secondaryValueFormatter = secondaryMetric
? getValueFormatter(
@@ -215,6 +217,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
numberFormat,
+ currencyFormat,
)
: undefined;
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx
index 941face406..8515139548 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx
@@ -215,6 +215,7 @@ const config: ControlPanelConfig = {
// eslint-disable-next-line react/jsx-key
[<ControlSubSectionHeader>{t('Y Axis')}</ControlSubSectionHeader>],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'logAxis',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
index ea43c875a1..47fe550ad7 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
@@ -194,6 +194,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
},
},
],
+ ['currency_format'],
[
{
name: 'logAxis',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx
index 396b3c7289..637a5fbc57 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx
@@ -203,6 +203,7 @@ const config: ControlPanelConfig = {
// eslint-disable-next-line react/jsx-key
[<ControlSubSectionHeader>{t('Y Axis')}</ControlSubSectionHeader>],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'logAxis',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx
index ae4ea31e1d..ffcee71792 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx
@@ -147,6 +147,7 @@ const config: ControlPanelConfig = {
// eslint-disable-next-line react/jsx-key
[<ControlSubSectionHeader>{t('Y Axis')}</ControlSubSectionHeader>],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'logAxis',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx
index 67b896d225..cb7164e0ab 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx
@@ -147,6 +147,7 @@ const config: ControlPanelConfig = {
[<ControlSubSectionHeader>{t('Y Axis')}</ControlSubSectionHeader>],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'logAxis',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx
index b9a9da8573..1921e698c2 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx
@@ -197,6 +197,7 @@ const config: ControlPanelConfig = {
// eslint-disable-next-line react/jsx-key
[<ControlSubSectionHeader>{t('Y Axis')}</ControlSubSectionHeader>],
['y_axis_format'],
+ ['currency_format'],
[
{
name: 'logAxis',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
index 64aafc0237..0f29282c5f 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -37,6 +37,7 @@ import {
TimeseriesChartDataResponseResult,
buildCustomFormatters,
getCustomFormatter,
+ CurrencyFormatter,
} from '@superset-ui/core';
import {
extractExtraMetrics,
@@ -168,6 +169,7 @@ export default function transformProps(
xAxisTitleMargin,
yAxisBounds,
yAxisFormat,
+ currencyFormat,
yAxisTitle,
yAxisTitleMargin,
yAxisTitlePosition,
@@ -245,12 +247,15 @@ export default function transformProps(
const forcePercentFormatter = Boolean(contributionMode || isAreaExpand);
const percentFormatter = getNumberFormatter(',.0%');
- const defaultFormatter = getNumberFormatter(yAxisFormat);
+ const defaultFormatter = currencyFormat?.symbol
+ ? new CurrencyFormatter({ d3Format: yAxisFormat, currency: currencyFormat })
+ : getNumberFormatter(yAxisFormat);
const customFormatters = buildCustomFormatters(
metrics,
currencyFormats,
columnFormats,
yAxisFormat,
+ currencyFormat,
);
const array = ensureIsArray(chartProps.rawFormData?.time_compare);
@@ -468,7 +473,7 @@ export default function transformProps(
metrics,
forcePercentFormatter,
customFormatters,
- yAxisFormat,
+ defaultFormatter,
),
},
scale: truncateYAxis,
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx
index 3ba079e180..e171000018 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx
@@ -119,6 +119,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'date_format',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts
index a9e909abf8..340ccdc93d 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts
@@ -133,6 +133,7 @@ export default function transformProps(
labelType,
labelPosition,
numberFormat,
+ currencyFormat,
dateFormat,
showLabels,
showUpperLabels,
@@ -149,6 +150,7 @@ export default function transformProps(
currencyFormats,
columnFormats,
numberFormat,
+ currencyFormat,
);
const formatter = (params: TreemapSeriesCallbackDataParams) =>
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/getYAxisFormatter.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/getYAxisFormatter.ts
index 8d52c44464..00843c1612 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/getYAxisFormatter.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/getYAxisFormatter.ts
@@ -22,7 +22,6 @@ import {
ensureIsArray,
getNumberFormatter,
isSavedMetric,
- NumberFormats,
QueryFormMetric,
ValueFormatter,
} from '@superset-ui/core';
@@ -31,7 +30,7 @@ export const getYAxisFormatter = (
metrics: QueryFormMetric[],
forcePercentFormatter: boolean,
customFormatters: Record<string, ValueFormatter>,
- yAxisFormat: string = NumberFormats.SMART_NUMBER,
+ defaultFormatter: ValueFormatter,
) => {
if (forcePercentFormatter) {
return getNumberFormatter(',.0%');
@@ -50,5 +49,5 @@ export const getYAxisFormatter = (
) {
return customFormatters[metricsArray[0]];
}
- return getNumberFormatter(yAxisFormat);
+ return defaultFormatter ?? getNumberFormatter();
};
diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/types.ts b/superset-frontend/plugins/plugin-chart-handlebars/src/types.ts
index 741d3b982c..ff66f1a133 100644
--- a/superset-frontend/plugins/plugin-chart-handlebars/src/types.ts
+++ b/superset-frontend/plugins/plugin-chart-handlebars/src/types.ts
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { ColumnConfig } from '@superset-ui/chart-controls';
import {
QueryFormData,
QueryFormMetric,
@@ -53,7 +52,6 @@ export type HandlebarsQueryFormData = QueryFormData &
table_timestamp_format?: string;
granularitySqla?: string;
time_grain_sqla?: TimeGranularity;
- column_config?: Record<string, ColumnConfig>;
};
export type HandlebarsProps = HandlebarsStylesProps &
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
index f463990b1d..19211998a4 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
@@ -140,6 +140,7 @@ export default function PivotTableChart(props: PivotTableProps) {
colTotals,
rowTotals,
valueFormat,
+ currencyFormat,
emitCrossFilters,
setDataMask,
selectedFilters,
@@ -155,8 +156,14 @@ export default function PivotTableChart(props: PivotTableProps) {
const theme = useTheme();
const defaultFormatter = useMemo(
- () => getNumberFormatter(valueFormat),
- [valueFormat],
+ () =>
+ currencyFormat?.symbol
+ ? new CurrencyFormatter({
+ currency: currencyFormat,
+ d3Format: valueFormat,
+ })
+ : getNumberFormatter(valueFormat),
+ [valueFormat, currencyFormat],
);
const customFormatsArray = useMemo(
() =>
@@ -168,9 +175,9 @@ export default function PivotTableChart(props: PivotTableProps) {
).map(metricName => [
metricName,
columnFormats[metricName] || valueFormat,
- currencyFormats[metricName],
+ currencyFormats[metricName] || currencyFormat,
]),
- [columnFormats, currencyFormats, valueFormat],
+ [columnFormats, currencyFormat, currencyFormats, valueFormat],
);
const hasCustomMetricFormatters = customFormatsArray.length > 0;
const metricFormatters = useMemo(
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx
index d099406c55..3fbddffc98 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx
@@ -272,6 +272,7 @@ const config: ControlPanelConfig = {
},
},
],
+ ['currency_format'],
[
{
name: 'date_format',
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts
index f335c6978e..d4b972c249 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts
@@ -102,6 +102,7 @@ export default function transformProps(chartProps: ChartProps<QueryFormData>) {
metricsLayout,
conditionalFormatting,
timeGrainSqla,
+ currencyFormat,
} = formData;
const { selectedFilters } = filterState;
const granularity = extractTimegrain(rawFormData);
@@ -157,6 +158,7 @@ export default function transformProps(chartProps: ChartProps<QueryFormData>) {
colTotals,
rowTotals,
valueFormat,
+ currencyFormat,
emitCrossFilters,
setDataMask,
selectedFilters,
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts
index dea5236666..ebe1eb090c 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts
@@ -65,6 +65,7 @@ interface PivotTableCustomizeProps {
colTotals: boolean;
rowTotals: boolean;
valueFormat: string;
+ currencyFormat: Currency;
setDataMask: SetDataMaskHook;
emitCrossFilters?: boolean;
selectedFilters?: SelectedFiltersType;
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts
index 7bb47d785c..fa13f5fcce 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts
@@ -52,6 +52,7 @@ const formData: PivotTableQueryFormData = {
margin: 0,
time_grain_sqla: TimeGranularity.MONTH,
temporal_columns_lookup: { col1: true },
+ currencyFormat: { symbol: 'USD', symbolPosition: 'prefix' },
};
test('should build groupby with series in form data', () => {
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts
index 91fc5d260e..c639140abe 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts
@@ -45,6 +45,7 @@ describe('PivotTableChart transformProps', () => {
dateFormat: '',
legacy_order_by: 'count',
order_desc: true,
+ currencyFormat: { symbol: 'USD', symbolPosition: 'prefix' },
};
const chartProps = new ChartProps<QueryFormData>({
formData,
@@ -91,6 +92,7 @@ describe('PivotTableChart transformProps', () => {
emitCrossFilters: false,
columnFormats: {},
currencyFormats: {},
+ currencyFormat: { symbol: 'USD', symbolPosition: 'prefix' },
});
});
});
diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
index 8630dc78b6..788643e130 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
@@ -481,6 +481,8 @@ const config: ControlPanelConfig = {
type: 'ColumnConfigControl',
label: t('Customize columns'),
description: t('Further customize how to display each column'),
+ width: 400,
+ height: 320,
renderTrigger: true,
shouldMapStateToProps() {
return true;
diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
index 1de9daefdd..4a48cdc8df 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
@@ -124,8 +124,11 @@ const processColumns = memoizeOne(function processColumns(
const isTime = dataType === GenericDataType.TEMPORAL;
const isNumber = dataType === GenericDataType.NUMERIC;
const savedFormat = columnFormats?.[key];
- const currency = currencyFormats?.[key];
+ const savedCurrency = currencyFormats?.[key];
const numberFormat = config.d3NumberFormat || savedFormat;
+ const currency = config.currencyFormat?.symbol
+ ? config.currencyFormat
+ : savedCurrency;
let formatter;
@@ -158,7 +161,10 @@ const processColumns = memoizeOne(function processColumns(
formatter = getNumberFormatter(numberFormat || PERCENT_3_POINT);
} else if (isMetric || (isNumber && numberFormat)) {
formatter = currency
- ? new CurrencyFormatter({ d3Format: numberFormat, currency })
+ ? new CurrencyFormatter({
+ d3Format: numberFormat,
+ currency,
+ })
: getNumberFormatter(numberFormat);
}
return {
diff --git a/superset-frontend/plugins/plugin-chart-table/src/types.ts b/superset-frontend/plugins/plugin-chart-table/src/types.ts
index 35a463fe2c..02bae809fe 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/types.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/types.ts
@@ -32,11 +32,25 @@ import {
SetDataMaskHook,
ContextMenuFilters,
CurrencyFormatter,
+ Currency,
} from '@superset-ui/core';
-import { ColorFormatters, ColumnConfig } from '@superset-ui/chart-controls';
+import { ColorFormatters } from '@superset-ui/chart-controls';
export type CustomFormatter = (value: DataRecordValue) => string;
+export type TableColumnConfig = {
+ d3NumberFormat?: string;
+ d3SmallNumberFormat?: string;
+ d3TimeFormat?: string;
+ columnWidth?: number;
+ horizontalAlign?: 'left' | 'right' | 'center';
+ showCellBars?: boolean;
+ alignPositiveNegative?: boolean;
+ colorPositiveNegative?: boolean;
+ truncateLongCells?: boolean;
+ currencyFormat?: Currency;
+};
+
export interface DataColumnMeta {
// `key` is what is called `label` in the input props
key: string;
@@ -51,7 +65,7 @@ export interface DataColumnMeta {
isMetric?: boolean;
isPercentMetric?: boolean;
isNumeric?: boolean;
- config?: ColumnConfig;
+ config?: TableColumnConfig;
}
export interface TableChartData {
@@ -75,7 +89,7 @@ export type TableChartFormData = QueryFormData & {
show_cell_bars?: boolean;
table_timestamp_format?: string;
time_grain_sqla?: TimeGranularity;
- column_config?: Record<string, ColumnConfig>;
+ column_config?: Record<string, TableColumnConfig>;
allow_rearrange_columns?: boolean;
};
diff --git a/superset-frontend/src/GlobalStyles.tsx b/superset-frontend/src/GlobalStyles.tsx
index 50f54ba88f..f983c262d7 100644
--- a/superset-frontend/src/GlobalStyles.tsx
+++ b/superset-frontend/src/GlobalStyles.tsx
@@ -66,6 +66,30 @@ export const GlobalStyles = () => (
)};
}
}
+ .column-config-popover {
+ & .ant-input-number {
+ width: 100%;
+ }
+ && .btn-group svg {
+ line-height: 0;
+ top: 0;
+ }
+ & .btn-group > .btn {
+ padding: 5px 10px 6px;
+ }
+ && .ant-tabs {
+ margin-top: ${theme.gridUnit * -3}px;
+ }
+ & .ant-tabs-nav {
+ margin-left: ${theme.gridUnit * -4}px;
+ margin-right: ${theme.gridUnit * -4}px;
+ margin-bottom: ${theme.gridUnit * 2}px;
+ }
+ && .ant-tabs-tab {
+ flex: 1;
+ margin-right: 0;
+ }
+ }
`}
/>
);
diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
index d95839d972..3013da71d8 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
@@ -53,6 +53,7 @@ import SpatialControl from 'src/explore/components/controls/SpatialControl';
import withToasts from 'src/components/MessageToasts/withToasts';
import { isFeatureEnabled } from 'src/featureFlags';
import Icons from 'src/components/Icons';
+import CurrencyControl from 'src/explore/components/controls/CurrencyControl';
import CollectionTable from './CollectionTable';
import Fieldset from './Fieldset';
import Field from './Field';
@@ -149,11 +150,6 @@ const DATA_TYPES = [
{ value: 'BOOLEAN', label: t('BOOLEAN') },
];
-const CURRENCY_SYMBOL_POSITION = [
- { value: 'prefix', label: t('Prefix') },
- { value: 'suffix', label: t('Suffix') },
-];
-
const DATASOURCE_TYPES_ARR = [
{ key: 'physical', label: t('Physical (table or view)') },
{ key: 'virtual', label: t('Virtual (SQL)') },
@@ -580,43 +576,6 @@ function OwnersSelector({ datasource, onChange }) {
);
}
-const CurrencyControlContainer = styled.div`
- ${({ theme }) => css`
- display: flex;
- align-items: center;
-
- & > :first-child {
- width: 25%;
- margin-right: ${theme.gridUnit * 4}px;
- }
- `}
-`;
-const CurrencyControl = ({ onChange, value: currency = {}, currencies }) => (
- <CurrencyControlContainer>
- <Select
- ariaLabel={t('Currency prefix or suffix')}
- options={CURRENCY_SYMBOL_POSITION}
- placeholder={t('Prefix or suffix')}
- onChange={symbolPosition => {
- onChange({ ...currency, symbolPosition });
- }}
- value={currency?.symbolPosition}
- allowClear
- />
- <Select
- ariaLabel={t('Currency symbol')}
- options={currencies}
- placeholder={t('Select or type currency symbol')}
- onChange={symbol => {
- onChange({ ...currency, symbol });
- }}
- value={currency?.symbol}
- allowClear
- allowNewOptions
- />
- </CurrencyControlContainer>
-);
-
class DatasourceEditor extends React.PureComponent {
constructor(props) {
super(props);
@@ -1296,7 +1255,16 @@ class DatasourceEditor extends React.PureComponent {
<Field
fieldKey="currency"
label={t('Metric currency')}
- control={<CurrencyControl currencies={this.currencies} />}
+ control={
+ <CurrencyControl
+ currencySelectOverrideProps={{
+ placeholder: t('Select or type currency symbol'),
+ }}
+ symbolSelectAdditionalStyles={css`
+ max-width: 30%;
+ `}
+ />
+ }
/>
<Field
label={t('Certified by')}
diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx b/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx
index abb77c7f19..0cd051ace3 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx
@@ -39,7 +39,12 @@ const props = {
const DATASOURCE_ENDPOINT = 'glob:*/datasource/external_metadata_by_name/*';
const asyncRender = props =>
- waitFor(() => render(<DatasourceEditor {...props} />, { useRedux: true }));
+ waitFor(() =>
+ render(<DatasourceEditor {...props} />, {
+ useRedux: true,
+ initialState: { common: { currencies: ['USD', 'GBP', 'EUR'] } },
+ }),
+ );
describe('DatasourceEditor', () => {
fetchMock.get(DATASOURCE_ENDPOINT, []);
@@ -220,7 +225,6 @@ describe('DatasourceEditor RTL', () => {
it('renders currency controls', async () => {
const propsWithCurrency = {
...props,
- currencies: ['USD', 'GBP', 'EUR'],
datasource: {
...props.datasource,
metrics: [
diff --git a/superset-frontend/src/explore/components/ControlHeader.tsx b/superset-frontend/src/explore/components/ControlHeader.tsx
index 9633fba613..503bbbcd2c 100644
--- a/superset-frontend/src/explore/components/ControlHeader.tsx
+++ b/superset-frontend/src/explore/components/ControlHeader.tsx
@@ -171,7 +171,7 @@ const ControlHeader: FC<ControlHeaderProps> = ({
>
<Icons.ExclamationCircleOutlined
css={css`
- ${iconStyles}
+ ${iconStyles};
color: ${labelColor};
`}
/>
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigControl.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx
similarity index 94%
rename from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigControl.tsx
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx
index 548dd4ae4d..9b10aa32b2 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigControl.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx
@@ -23,9 +23,11 @@ import {
t,
GenericDataType,
} from '@superset-ui/core';
-import ControlHeader from '../../../components/ControlHeader';
-import { ControlComponentProps } from '../types';
+import {
+ COLUMN_NAME_ALIASES,
+ ControlComponentProps,
+} from '@superset-ui/chart-controls';
import ColumnConfigItem from './ColumnConfigItem';
import {
ColumnConfigInfo,
@@ -33,13 +35,15 @@ import {
ColumnConfigFormLayout,
} from './types';
import { DEFAULT_CONFIG_FORM_LAYOUT } from './constants';
-import { COLUMN_NAME_ALIASES } from '../../../constants';
+import ControlHeader from '../../ControlHeader';
export type ColumnConfigControlProps<T extends ColumnConfig> =
ControlComponentProps<Record<string, T>> & {
queryResponse?: ChartDataResponseResult;
configFormLayout?: ColumnConfigFormLayout;
appliedColumnNames?: string[];
+ width?: number | string;
+ height?: number | string;
};
/**
@@ -56,6 +60,8 @@ export default function ColumnConfigControl<T extends ColumnConfig>({
value,
onChange,
configFormLayout = DEFAULT_CONFIG_FORM_LAYOUT,
+ width,
+ height,
...props
}: ColumnConfigControlProps<T>) {
const { colnames: _colnames, coltypes: _coltypes } = queryResponse || {};
@@ -127,6 +133,8 @@ export default function ColumnConfigControl<T extends ColumnConfig>({
column={getColumnInfo(col)}
onChange={config => setColumnConfig(col, config as T)}
configFormLayout={configFormLayout}
+ width={width}
+ height={height}
/>
))}
{needShowMoreButton && (
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigItem.tsx
similarity index 91%
rename from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigItem.tsx
index f28d5b8d23..7e7d554e0f 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigItem.tsx
@@ -18,8 +18,8 @@
*/
import React from 'react';
import { useTheme } from '@superset-ui/core';
-import { Popover } from 'antd';
-import ColumnTypeLabel from '../../../components/ColumnTypeLabel/ColumnTypeLabel';
+import Popover from 'src/components/Popover';
+import { ColumnTypeLabel } from '@superset-ui/chart-controls';
import ColumnConfigPopover, {
ColumnConfigPopoverProps,
} from './ColumnConfigPopover';
@@ -30,6 +30,8 @@ export default React.memo(function ColumnConfigItem({
column,
onChange,
configFormLayout,
+ width,
+ height,
}: ColumnConfigItemProps) {
const { colors, gridUnit } = useTheme();
const caretWidth = gridUnit * 6;
@@ -45,6 +47,8 @@ export default React.memo(function ColumnConfigItem({
)}
trigger="click"
placement="right"
+ overlayInnerStyle={{ width, height }}
+ overlayClassName="column-config-popover"
>
<div
css={{
diff --git a/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigPopover.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigPopover.tsx
new file mode 100644
index 0000000000..d35f46a671
--- /dev/null
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigPopover.tsx
@@ -0,0 +1,95 @@
+/**
+ * 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 React from 'react';
+import { GenericDataType } from '@superset-ui/core';
+import Tabs from 'src/components/Tabs';
+import {
+ SHARED_COLUMN_CONFIG_PROPS,
+ SharedColumnConfigProp,
+} from './constants';
+import {
+ ColumnConfig,
+ ColumnConfigFormItem,
+ ColumnConfigFormLayout,
+ ColumnConfigInfo,
+ ControlFormItemDefaultSpec,
+ isTabLayoutItem,
+} from './types';
+import ControlForm, { ControlFormItem, ControlFormRow } from './ControlForm';
+
+export type ColumnConfigPopoverProps = {
+ column: ColumnConfigInfo;
+ configFormLayout: ColumnConfigFormLayout;
+ onChange: (value: ColumnConfig) => void;
+ width?: number | string;
+ height?: number | string;
+};
+
+export default function ColumnConfigPopover({
+ column,
+ configFormLayout,
+ onChange,
+}: ColumnConfigPopoverProps) {
+ const renderRow = (row: ColumnConfigFormItem[], i: number) => (
+ <ControlFormRow key={i}>
+ {row.map(meta => {
+ const key = typeof meta === 'string' ? meta : meta.name;
+ const override =
+ typeof meta === 'string'
+ ? {}
+ : 'override' in meta
+ ? meta.override
+ : meta.config;
+ const props = {
+ ...(key in SHARED_COLUMN_CONFIG_PROPS
+ ? SHARED_COLUMN_CONFIG_PROPS[key as SharedColumnConfigProp]
+ : undefined),
+ ...override,
+ } as ControlFormItemDefaultSpec;
+ return <ControlFormItem key={key} name={key} {...props} />;
+ })}
+ </ControlFormRow>
+ );
+
+ const layout =
+ configFormLayout[
+ column.type === undefined ? GenericDataType.STRING : column.type
+ ];
+
+ if (isTabLayoutItem(layout[0])) {
+ return (
+ <Tabs centered>
+ {layout.map((item, i) =>
+ isTabLayoutItem(item) ? (
+ <Tabs.TabPane tab={item.tab} key={i}>
+ <ControlForm onChange={onChange} value={column.config}>
+ {item.children.map((row, i) => renderRow(row, i))}
+ </ControlForm>
+ </Tabs.TabPane>
+ ) : null,
+ )}
+ </Tabs>
+ );
+ }
+ return (
+ <ControlForm onChange={onChange} value={column.config}>
+ {layout.map((row, i) => renderRow(row as ColumnConfigFormItem[], i))}
+ </ControlForm>
+ );
+}
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/ControlFormItem.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/ControlFormItem.tsx
similarity index 83%
rename from superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/ControlFormItem.tsx
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/ControlFormItem.tsx
index 470972a81b..cacc3984fd 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/ControlFormItem.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/ControlFormItem.tsx
@@ -18,13 +18,13 @@
*/
import React, { useState, FunctionComponentElement, ChangeEvent } from 'react';
import { JsonValue, useTheme } from '@superset-ui/core';
-import ControlHeader, { ControlHeaderProps } from '../ControlHeader';
-import InfoTooltipWithTrigger from '../InfoTooltipWithTrigger';
-import { ControlFormItemComponents, ControlFormItemSpec } from './controls';
+import { ControlFormItemComponents } from './controls';
+import ControlHeader, { ControlHeaderProps } from '../../../ControlHeader';
+import { ControlFormItemDefaultSpec } from '../types';
export * from './controls';
-export type ControlFormItemProps = ControlFormItemSpec & {
+export type ControlFormItemProps = ControlFormItemDefaultSpec & {
name: string;
onChange?: (fieldValue: JsonValue) => void;
};
@@ -45,7 +45,6 @@ export function ControlFormItem({
description,
width,
validators,
- required,
onChange,
value: initialValue,
defaultValue,
@@ -70,7 +69,7 @@ export function ControlFormItem({
const errors =
(validators
?.map(validator =>
- !required && isEmptyValue(fieldValue) ? false : validator(fieldValue),
+ isEmptyValue(fieldValue) ? false : validator(fieldValue),
)
.filter(x => !!x) as string[]) || [];
setValidationErrors(errors);
@@ -87,20 +86,22 @@ export function ControlFormItem({
css={{
margin: 2 * gridUnit,
width,
+ maxWidth: '100%',
+ flex: 1,
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{controlType === 'Checkbox' ? (
<ControlFormItemComponents.Checkbox
- checked={value as boolean}
+ value={value as boolean}
onChange={handleChange}
- >
- {label}{' '}
- {hovered && description && (
- <InfoTooltipWithTrigger tooltip={description} />
- )}
- </ControlFormItemComponents.Checkbox>
+ name={name}
+ label={label}
+ description={description}
+ validationErrors={validationErrors}
+ {...props}
+ />
) : (
<>
{label && (
@@ -110,7 +111,6 @@ export function ControlFormItem({
description={description}
validationErrors={validationErrors}
hovered={hovered}
- required={required}
/>
)}
{/* @ts-ignore */}
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts
similarity index 52%
copy from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx
copy to superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts
index b6e635e25f..beded787bd 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/index.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts
@@ -16,17 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-import RadioButtonControl from './RadioButtonControl';
-import ColumnConfigControl from './ColumnConfigControl';
+import { sharedControlComponents } from '@superset-ui/chart-controls';
+import { Select } from 'src/components';
+import { Input, InputNumber } from 'src/components/Input';
+import Slider from 'src/components/Slider';
+import CurrencyControl from '../../CurrencyControl';
+import CheckboxControl from '../../CheckboxControl';
-export * from './RadioButtonControl';
-export * from './ColumnConfigControl';
-
-/**
- * Shared chart controls. Can be referred via string shortcuts in chart control
- * configs.
- */
-export default {
- RadioButtonControl,
- ColumnConfigControl,
+export const ControlFormItemComponents = {
+ Slider,
+ InputNumber,
+ Input,
+ Select,
+ // Directly export Checkbox will result in "using name from external module" error
+ // ref: https://stackoverflow.com/questions/43900035/ts4023-exported-variable-x-has-or-is-using-name-y-from-external-module-but
+ Checkbox: CheckboxControl,
+ RadioButtonControl: sharedControlComponents.RadioButtonControl,
+ CurrencyControl,
};
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/index.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/index.tsx
similarity index 99%
rename from superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/index.tsx
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/index.tsx
index 9dafb3c39f..4f4f739b85 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/index.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/index.tsx
@@ -39,8 +39,8 @@ export function ControlFormRow({ children }: ControlFormRowProps) {
css={{
display: 'flex',
flexWrap: 'nowrap',
- margin: -2 * gridUnit,
marginBottom: gridUnit,
+ maxWidth: '100%',
}}
>
{children}
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/constants.tsx
similarity index 79%
rename from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/constants.tsx
index 7bf6c95c74..684d8faf14 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/constants.tsx
@@ -18,16 +18,14 @@
*/
import React from 'react';
import { GenericDataType, t, validateNumber } from '@superset-ui/core';
-import { FaAlignLeft } from '@react-icons/all-files/fa/FaAlignLeft';
-import { FaAlignRight } from '@react-icons/all-files/fa/FaAlignRight';
-import { FaAlignCenter } from '@react-icons/all-files/fa/FaAlignCenter';
import {
+ ControlFormItemSpec,
D3_FORMAT_DOCS,
D3_FORMAT_OPTIONS,
D3_TIME_FORMAT_DOCS,
D3_TIME_FORMAT_OPTIONS,
-} from '../../../utils';
-import { ControlFormItemSpec } from '../../../components/ControlForm';
+} from '@superset-ui/chart-controls';
+import Icons from 'src/components/Icons';
import { ColumnConfigFormLayout } from './types';
export type SharedColumnConfigProp =
@@ -40,13 +38,17 @@ export type SharedColumnConfigProp =
| 'd3TimeFormat'
| 'horizontalAlign'
| 'truncateLongCells'
- | 'showCellBars';
+ | 'showCellBars'
+ | 'currencyFormat';
const d3NumberFormat: ControlFormItemSpec<'Select'> = {
controlType: 'Select',
label: t('D3 format'),
description: D3_FORMAT_DOCS,
- options: D3_FORMAT_OPTIONS,
+ options: D3_FORMAT_OPTIONS.map(option => ({
+ value: option[0],
+ label: option[1],
+ })),
defaultValue: D3_FORMAT_OPTIONS[0][0],
creatable: true,
minWidth: '14em',
@@ -57,7 +59,10 @@ const d3TimeFormat: ControlFormItemSpec<'Select'> = {
controlType: 'Select',
label: t('D3 format'),
description: D3_TIME_FORMAT_DOCS,
- options: D3_TIME_FORMAT_OPTIONS,
+ options: D3_TIME_FORMAT_OPTIONS.map(option => ({
+ value: option[0],
+ label: option[1],
+ })),
defaultValue: D3_TIME_FORMAT_OPTIONS[0][0],
creatable: true,
minWidth: '10em',
@@ -97,9 +102,9 @@ const horizontalAlign: ControlFormItemSpec<'RadioButtonControl'> & {
debounceDelay: 50,
defaultValue: 'left',
options: [
- ['left', <FaAlignLeft title={t('Left')} />],
- ['center', <FaAlignCenter title={t('Center')} />],
- ['right', <FaAlignRight title={t('Right')} />],
+ ['left', <Icons.AlignLeftOutlined iconSize="m" />],
+ ['center', <Icons.AlignCenterOutlined iconSize="m" />],
+ ['right', <Icons.AlignRightOutlined iconSize="m" />],
],
};
@@ -139,6 +144,14 @@ const truncateLongCells: ControlFormItemSpec<'Checkbox'> = {
debounceDelay: 400,
};
+const currencyFormat: ControlFormItemSpec<'CurrencyControl'> = {
+ controlType: 'CurrencyControl',
+ label: t('Currency format'),
+ description: t(
+ 'Customize chart metrics or columns with currency symbols as prefixes or suffixes. Choose a symbol from dropdown or type your own.',
+ ),
+ debounceDelay: 200,
+};
/**
* All configurable column formatting properties.
*/
@@ -160,10 +173,7 @@ export const SHARED_COLUMN_CONFIG_PROPS = {
showCellBars,
alignPositiveNegative,
colorPositiveNegative,
-};
-
-export type SharedColumnConfig = {
- [key in SharedColumnConfigProp]?: typeof SHARED_COLUMN_CONFIG_PROPS[key]['value'];
+ currencyFormat,
};
export const DEFAULT_CONFIG_FORM_LAYOUT: ColumnConfigFormLayout = {
@@ -175,14 +185,26 @@ export const DEFAULT_CONFIG_FORM_LAYOUT: ColumnConfigFormLayout = {
['truncateLongCells'],
],
[GenericDataType.NUMERIC]: [
- [
- 'columnWidth',
- { name: 'horizontalAlign', override: { defaultValue: 'right' } },
- ],
- ['d3NumberFormat'],
- ['d3SmallNumberFormat'],
- ['alignPositiveNegative', 'colorPositiveNegative'],
- ['showCellBars'],
+ {
+ tab: t('Display'),
+ children: [
+ [
+ 'columnWidth',
+ { name: 'horizontalAlign', override: { defaultValue: 'right' } },
+ ],
+ ['showCellBars'],
+ ['alignPositiveNegative'],
+ ['colorPositiveNegative'],
+ ],
+ },
+ {
+ tab: t('Number formatting'),
+ children: [
+ ['d3NumberFormat'],
+ ['d3SmallNumberFormat'],
+ ['currencyFormat'],
+ ],
+ },
],
[GenericDataType.TEMPORAL]: [
[
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/index.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/index.tsx
similarity index 100%
rename from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/index.tsx
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/index.tsx
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/types.ts b/superset-frontend/src/explore/components/controls/ColumnConfigControl/types.ts
similarity index 68%
rename from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/types.ts
rename to superset-frontend/src/explore/components/controls/ColumnConfigControl/types.ts
index ef449ef8ed..c101c30435 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/types.ts
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/types.ts
@@ -21,11 +21,12 @@ import {
JsonObject,
StrictJsonValue,
} from '@superset-ui/core';
-import { ControlFormItemSpec } from '../../../components/ControlForm';
+import { ControlFormItemSpec } from '@superset-ui/chart-controls';
import {
SHARED_COLUMN_CONFIG_PROPS,
SharedColumnConfigProp,
} from './constants';
+import { ControlFormItemComponents } from './ControlForm';
/**
* Column formatting configs.
@@ -44,14 +45,29 @@ export interface ColumnConfigInfo {
config: JsonObject;
}
+export type ControlFormItemDefaultSpec = ControlFormItemSpec<
+ keyof typeof ControlFormItemComponents
+>;
+
export type ColumnConfigFormItem =
| SharedColumnConfigProp
- | { name: SharedColumnConfigProp; override: Partial<ControlFormItemSpec> }
- | { name: string; config: ControlFormItemSpec };
+ | {
+ name: SharedColumnConfigProp;
+ override: Partial<ControlFormItemDefaultSpec>;
+ }
+ | { name: string; config: ControlFormItemDefaultSpec };
+
+export type TabLayoutItem = { tab: string; children: ColumnConfigFormItem[][] };
export type ColumnConfigFormLayout = Record<
GenericDataType,
- ColumnConfigFormItem[][]
+ ColumnConfigFormItem[][] | TabLayoutItem[]
>;
+export function isTabLayoutItem(
+ layoutItem: ColumnConfigFormItem[] | TabLayoutItem,
+): layoutItem is TabLayoutItem {
+ return !!(layoutItem as TabLayoutItem)?.tab;
+}
+
export default {};
diff --git a/superset-frontend/src/explore/components/controls/CurrencyControl/CurrencyControl.tsx b/superset-frontend/src/explore/components/controls/CurrencyControl/CurrencyControl.tsx
new file mode 100644
index 0000000000..5bbe271150
--- /dev/null
+++ b/superset-frontend/src/explore/components/controls/CurrencyControl/CurrencyControl.tsx
@@ -0,0 +1,129 @@
+/**
+ * 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 React, { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import {
+ css,
+ Currency,
+ ensureIsArray,
+ getCurrencySymbol,
+ styled,
+ t,
+} from '@superset-ui/core';
+import { CSSObject } from '@emotion/react';
+import { Select } from 'src/components';
+import { ViewState } from 'src/views/types';
+import { SelectProps } from 'src/components/Select/types';
+import ControlHeader from '../../ControlHeader';
+
+export interface CurrencyControlProps {
+ onChange: (currency: Partial<Currency>) => void;
+ value?: Partial<Currency>;
+ symbolSelectOverrideProps?: Partial<SelectProps>;
+ currencySelectOverrideProps?: Partial<SelectProps>;
+ symbolSelectAdditionalStyles?: CSSObject;
+ currencySelectAdditionalStyles?: CSSObject;
+}
+
+const CurrencyControlContainer = styled.div`
+ ${({ theme }) => css`
+ display: flex;
+ align-items: center;
+
+ & > :first-child {
+ margin-right: ${theme.gridUnit * 4}px;
+ min-width: 0;
+ flex: 1;
+ }
+
+ & > :nth-child(2) {
+ min-width: 0;
+ flex: 1;
+ }
+ `}
+`;
+
+export const CURRENCY_SYMBOL_POSITION_OPTIONS = [
+ { value: 'prefix', label: t('Prefix') },
+ { value: 'suffix', label: t('Suffix') },
+];
+
+export const CurrencyControl = ({
+ onChange,
+ value: currency = {},
+ symbolSelectOverrideProps = {},
+ currencySelectOverrideProps = {},
+ symbolSelectAdditionalStyles,
+ currencySelectAdditionalStyles,
+ ...props
+}: CurrencyControlProps) => {
+ const currencies = useSelector<ViewState, string[]>(
+ state => state.common?.currencies,
+ );
+ const currenciesOptions = useMemo(
+ () =>
+ ensureIsArray(currencies).map(currencyCode => ({
+ value: currencyCode,
+ label: `${getCurrencySymbol({
+ symbol: currencyCode,
+ })} (${currencyCode})`,
+ })),
+ [currencies],
+ );
+ return (
+ <>
+ <ControlHeader {...props} />
+ <CurrencyControlContainer
+ css={css`
+ & > :first-child {
+ ${symbolSelectAdditionalStyles};
+ }
+ & > :nth-child(2) {
+ ${currencySelectAdditionalStyles};
+ }
+ `}
+ className="currency-control-container"
+ >
+ <Select
+ ariaLabel={t('Currency prefix or suffix')}
+ options={CURRENCY_SYMBOL_POSITION_OPTIONS}
+ placeholder={t('Prefix or suffix')}
+ onChange={(symbolPosition: string) => {
+ onChange({ ...currency, symbolPosition });
+ }}
+ value={currency?.symbolPosition}
+ allowClear
+ {...symbolSelectOverrideProps}
+ />
+ <Select
+ ariaLabel={t('Currency symbol')}
+ options={currenciesOptions}
+ placeholder={t('Currency')}
+ onChange={(symbol: string) => {
+ onChange({ ...currency, symbol });
+ }}
+ value={currency?.symbol}
+ allowClear
+ allowNewOptions
+ {...currencySelectOverrideProps}
+ />
+ </CurrencyControlContainer>
+ </>
+ );
+};
diff --git a/superset-frontend/src/explore/components/controls/CurrencyControl/index.ts b/superset-frontend/src/explore/components/controls/CurrencyControl/index.ts
new file mode 100644
index 0000000000..d99ab57767
--- /dev/null
+++ b/superset-frontend/src/explore/components/controls/CurrencyControl/index.ts
@@ -0,0 +1,3 @@
+import { CurrencyControl } from './CurrencyControl';
+
+export { CurrencyControl as default };
diff --git a/superset-frontend/src/explore/components/controls/index.js b/superset-frontend/src/explore/components/controls/index.js
index 21e3bd4cf2..725077cc8f 100644
--- a/superset-frontend/src/explore/components/controls/index.js
+++ b/superset-frontend/src/explore/components/controls/index.js
@@ -46,6 +46,8 @@ import DndColumnSelectControl, {
DndMetricSelect,
} from './DndColumnSelectControl';
import XAxisSortControl from './XAxisSortControl';
+import CurrencyControl from './CurrencyControl';
+import ColumnConfigControl from './ColumnConfigControl';
const controlMap = {
AnnotationLayerControl,
@@ -54,6 +56,8 @@ const controlMap = {
CollectionControl,
ColorPickerControl,
ColorSchemeControl,
+ ColumnConfigControl,
+ CurrencyControl,
DatasourceControl,
DateFilterControl,
DndColumnSelectControl,
diff --git a/superset-frontend/src/views/types.ts b/superset-frontend/src/views/types.ts
index 437e7d77ef..f6b542c76b 100644
--- a/superset-frontend/src/views/types.ts
+++ b/superset-frontend/src/views/types.ts
@@ -25,6 +25,7 @@ export interface ViewState {
SQLALCHEMY_DISPLAY_TEXT: string;
ALERT_REPORTS_NOTIFICATION_METHODS: NotificationMethodOption[];
};
+ currencies: string[];
};
messageToast: Array<Object>;
}