From f7e76d02b7cbe4940946673590bb979984ace9f5 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 2 Aug 2023 19:22:45 +0200 Subject: [PATCH] feat: Add currencies controls in control panels (#24718) --- .../src/components/ControlForm/controls.tsx | 92 ------------- .../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 +- .../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 +- .../BigNumber/BigNumberTotal/controlPanel.ts | 1 + .../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 ++-- .../src/Pie/controlPanel.tsx | 1 + .../src/Pie/transformProps.ts | 2 + .../src/Sunburst/controlPanel.tsx | 1 + .../src/Sunburst/transformProps.ts | 3 + .../src/Timeseries/Area/controlPanel.tsx | 1 + .../Timeseries/Regular/Bar/controlPanel.tsx | 1 + .../Timeseries/Regular/Line/controlPanel.tsx | 1 + .../Regular/Scatter/controlPanel.tsx | 1 + .../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 +- .../plugin-chart-handlebars/src/types.ts | 2 - .../src/PivotTableChart.tsx | 15 +- .../src/plugin/controlPanel.tsx | 1 + .../src/plugin/transformProps.ts | 2 + .../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 ++++ .../Datasource/DatasourceEditor.jsx | 54 ++------ .../Datasource/DatasourceEditor.test.jsx | 8 +- .../src/explore/components/ControlHeader.tsx | 2 +- .../ColumnConfigControl.tsx | 14 +- .../ColumnConfigControl/ColumnConfigItem.tsx | 8 +- .../ColumnConfigPopover.tsx | 95 +++++++++++++ .../ControlForm/ControlFormItem.tsx | 28 ++-- .../ControlForm/controls.ts | 36 +++++ .../ControlForm/index.tsx | 2 +- .../ColumnConfigControl/constants.tsx | 68 +++++---- .../controls}/ColumnConfigControl/index.tsx | 0 .../controls}/ColumnConfigControl/types.ts | 24 +++- .../CurrencyControl/CurrencyControl.tsx | 129 ++++++++++++++++++ .../controls/CurrencyControl/index.ts | 3 + .../src/explore/components/controls/index.js | 4 + superset-frontend/src/views/types.ts | 1 + 63 files changed, 697 insertions(+), 306 deletions(-) delete mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/components/ControlForm/controls.tsx delete mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigPopover.tsx rename superset-frontend/{packages/superset-ui-chart-controls/src/shared-controls/components => src/explore/components/controls}/ColumnConfigControl/ColumnConfigControl.tsx (94%) rename superset-frontend/{packages/superset-ui-chart-controls/src/shared-controls/components => src/explore/components/controls}/ColumnConfigControl/ColumnConfigItem.tsx (91%) create mode 100644 superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigPopover.tsx rename superset-frontend/{packages/superset-ui-chart-controls/src/components => src/explore/components/controls/ColumnConfigControl}/ControlForm/ControlFormItem.tsx (83%) create mode 100644 superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts rename superset-frontend/{packages/superset-ui-chart-controls/src/components => src/explore/components/controls/ColumnConfigControl}/ControlForm/index.tsx (99%) rename superset-frontend/{packages/superset-ui-chart-controls/src/shared-controls/components => src/explore/components/controls}/ColumnConfigControl/constants.tsx (79%) rename superset-frontend/{packages/superset-ui-chart-controls/src/shared-controls/components => src/explore/components/controls}/ColumnConfigControl/index.tsx (100%) rename superset-frontend/{packages/superset-ui-chart-controls/src/shared-controls/components => src/explore/components/controls}/ColumnConfigControl/types.ts (68%) create mode 100644 superset-frontend/src/explore/components/controls/CurrencyControl/CurrencyControl.tsx create mode 100644 superset-frontend/src/explore/components/controls/CurrencyControl/index.ts 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 a1b689cbaa368..0000000000000 --- 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 - >, - RadioButtonControl, -}; - -export type ControlType = keyof typeof ControlFormItemComponents; - -export type ControlFormValueValidator = (value: V) => string | false; - -export type ControlFormItemSpec = { - controlType: T; - label: ReactNode; - description: ReactNode; - placeholder?: string; - required?: boolean; - validators?: ControlFormValueValidator[]; - width?: number | string; - /** - * Time to delay change propagation. - */ - debounceDelay?: number; -} & (T extends 'Select' - ? { - options: SelectOption[]; - value?: string; - defaultValue?: string; - creatable?: boolean; - minWidth?: number | string; - validators?: ControlFormValueValidator[]; - } - : 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[]; - } - : T extends 'Input' - ? { - controlType: 'Input'; - value?: string; - defaultValue?: string; - validators?: ControlFormValueValidator[]; - } - : {}); 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 dbc40b216e130..0000000000000 --- 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 ( - - {configFormLayout[ - column.type === undefined ? GenericDataType.STRING : column.type - ].map((row, 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 ; - })} - - ))} - - ); -} 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 b6e635e25f0fd..93bfbcbe6871e 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 a4af8bcbf23f3..abf5153bb0d51 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 dadd78cdd0fde..889bc9a47c337 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 = (value: V) => string | false; + +export type ControlFormItemSpec = { + controlType: T; + label: ReactNode; + description: ReactNode; + placeholder?: string; + validators?: ControlFormValueValidator[]; + 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[]; + } + : 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[]; + } + : T extends 'Input' + ? { + controlType: 'Input'; + value?: string; + defaultValue?: string; + validators?: ControlFormValueValidator[]; + } + : 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 7c082abd3425e..a5f215fe9556f 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) => 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 014388b86dc59..243aa92a9fdae 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, - columnFormats: Record, + savedCurrencyFormats: Record, + savedColumnFormats: Record, 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, - columnFormats: Record, + savedCurrencyFormats: Record, + savedColumnFormats: Record, 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 9e35ce8d99f4e..cb65528a89516 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 = 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 = 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 3032654ba267c..70a71e5024da5 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 a6adf5f8b818a..c4906ddbe96e9 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 b0f3be22c50ec..4d1a21561be26 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 182d1b0b11527..4069e2942891d 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 abe4ce215feef..2bb1c1d1d6dbf 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 bdef7852d1fb5..4a68e1ff9f0c7 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 3b98f05645c64..26bae683c3ba2 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 4daa8f44016c2..ce35b657616dc 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 75d062ce92968..44a88fee4bc0c 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 41319079dcd88..538ec27750d8d 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 51939615567ef..a7e0d3fdce5d1 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 0fd41e88a01a0..f32993b1dfc25 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 58be782859304..c9f9027a3efb0 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 1ce4089357d3a..25b7e5364aad7 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, - 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(); @@ -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 a82768b085ff1..53d406538d622 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 ea616cb201d2f..4ad92b0bb2e0d 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 bc0a515141ab8..1957caa234ee7 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 6af4c7f65308b..a3a9b8777f440 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 941face40630b..85151395480d2 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 [{t('Y Axis')}], ['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 ea43c875a15d6..47fe550ad7311 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 396b3c7289ecd..637a5fbc57c79 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 [{t('Y Axis')}], ['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 ae4ea31e1d398..ffcee717928e9 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 [{t('Y Axis')}], ['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 67b896d225684..cb7164e0ab5df 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 = { [{t('Y Axis')}], ['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 b9a9da85738cb..1921e698c2d2e 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 [{t('Y Axis')}], ['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 64aafc0237163..0f29282c5fb4d 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 3ba079e1807ad..e171000018dac 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 a9e909abf83f8..340ccdc93d45c 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 8d52c4446405d..00843c16126f0 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, - 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 741d3b982c48b..ff66f1a133bd9 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; }; 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 f463990b1d351..19211998a4095 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 d099406c552e4..3fbddffc98b4d 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 f335c6978e166..d4b972c249489 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) { metricsLayout, conditionalFormatting, timeGrainSqla, + currencyFormat, } = formData; const { selectedFilters } = filterState; const granularity = extractTimegrain(rawFormData); @@ -157,6 +158,7 @@ export default function transformProps(chartProps: ChartProps) { 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 dea5236666645..ebe1eb090cc25 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 7bb47d785cd8a..fa13f5fcce002 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 91fc5d260efa2..c639140abe1fd 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({ 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 8630dc78b6ba8..788643e130bbd 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 1de9daefddf51..4a48cdc8df38b 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 35a463fe2c0f7..02bae809fe57b 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; + column_config?: Record; allow_rearrange_columns?: boolean; }; diff --git a/superset-frontend/src/GlobalStyles.tsx b/superset-frontend/src/GlobalStyles.tsx index 50f54ba88fa6e..f983c262d7c20 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 d95839d972f2a..3013da71d87f1 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 }) => ( - - { - onChange({ ...currency, symbol }); - }} - value={currency?.symbol} - allowClear - allowNewOptions - /> - -); - class DatasourceEditor extends React.PureComponent { constructor(props) { super(props); @@ -1296,7 +1255,16 @@ class DatasourceEditor extends React.PureComponent { } + control={ + + } /> - waitFor(() => render(, { useRedux: true })); + waitFor(() => + render(, { + 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 9633fba6139a1..503bbbcd2c138 100644 --- a/superset-frontend/src/explore/components/ControlHeader.tsx +++ b/superset-frontend/src/explore/components/ControlHeader.tsx @@ -171,7 +171,7 @@ const ControlHeader: FC = ({ > 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 548dd4ae4d7fe..9b10aa32b2bf4 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 = ControlComponentProps> & { queryResponse?: ChartDataResponseResult; configFormLayout?: ColumnConfigFormLayout; appliedColumnNames?: string[]; + width?: number | string; + height?: number | string; }; /** @@ -56,6 +60,8 @@ export default function ColumnConfigControl({ value, onChange, configFormLayout = DEFAULT_CONFIG_FORM_LAYOUT, + width, + height, ...props }: ColumnConfigControlProps) { const { colnames: _colnames, coltypes: _coltypes } = queryResponse || {}; @@ -127,6 +133,8 @@ export default function ColumnConfigControl({ 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 f28d5b8d2332d..7e7d554e0f053 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" >
void; + width?: number | string; + height?: number | string; +}; + +export default function ColumnConfigPopover({ + column, + configFormLayout, + onChange, +}: ColumnConfigPopoverProps) { + const renderRow = (row: ColumnConfigFormItem[], i: number) => ( + + {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 ; + })} + + ); + + const layout = + configFormLayout[ + column.type === undefined ? GenericDataType.STRING : column.type + ]; + + if (isTabLayoutItem(layout[0])) { + return ( + + {layout.map((item, i) => + isTabLayoutItem(item) ? ( + + + {item.children.map((row, i) => renderRow(row, i))} + + + ) : null, + )} + + ); + } + return ( + + {layout.map((row, i) => renderRow(row as ColumnConfigFormItem[], i))} + + ); +} 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 470972a81bc11..cacc3984fdb11 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' ? ( - {label}{' '} - {hovered && description && ( - - )} - + 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/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts new file mode 100644 index 0000000000000..beded787bd720 --- /dev/null +++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ControlForm/controls.ts @@ -0,0 +1,36 @@ +/** + * 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 { 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 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 9dafb3c39f363..4f4f739b856fb 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 7bf6c95c7401f..684d8faf14b1d 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', ], - ['center', ], - ['right', ], + ['left', ], + ['center', ], + ['right', ], ], }; @@ -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 ef449ef8ed5eb..c101c304359ad 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 } - | { name: string; config: ControlFormItemSpec }; + | { + name: SharedColumnConfigProp; + override: Partial; + } + | { 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 0000000000000..5bbe2711501ab --- /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) => void; + value?: Partial; + symbolSelectOverrideProps?: Partial; + currencySelectOverrideProps?: Partial; + 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( + state => state.common?.currencies, + ); + const currenciesOptions = useMemo( + () => + ensureIsArray(currencies).map(currencyCode => ({ + value: currencyCode, + label: `${getCurrencySymbol({ + symbol: currencyCode, + })} (${currencyCode})`, + })), + [currencies], + ); + return ( + <> + + :first-child { + ${symbolSelectAdditionalStyles}; + } + & > :nth-child(2) { + ${currencySelectAdditionalStyles}; + } + `} + className="currency-control-container" + > + { + onChange({ ...currency, symbol }); + }} + value={currency?.symbol} + allowClear + allowNewOptions + {...currencySelectOverrideProps} + /> + + + ); +}; 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 0000000000000..d99ab577675e8 --- /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 21e3bd4cf285d..725077cc8f99c 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 437e7d77ef6af..f6b542c76ba4f 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; }