mirror of
https://github.com/apache/superset.git
synced 2026-05-31 21:29:19 +00:00
feat: Dynamic currency (#36416)
This commit is contained in:
committed by
GitHub
parent
896947c787
commit
f4474b2e3e
@@ -82,6 +82,7 @@ export default function ColumnConfigControl<T extends ColumnConfig>({
|
||||
});
|
||||
}
|
||||
const theme = useTheme();
|
||||
|
||||
const columnConfigs = useMemo(() => {
|
||||
const configs: Record<string, ColumnConfigInfo> = {};
|
||||
colnames?.forEach((col, idx) => {
|
||||
@@ -100,6 +101,7 @@ export default function ColumnConfigControl<T extends ColumnConfig>({
|
||||
const [showAllColumns, setShowAllColumns] = useState(false);
|
||||
|
||||
const getColumnInfo = (col: string) => columnConfigs[col] || {};
|
||||
|
||||
const setColumnConfig = (col: string, config: T) => {
|
||||
if (onChange) {
|
||||
// Only keep configs for known columns
|
||||
|
||||
@@ -168,7 +168,7 @@ 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.',
|
||||
"Format metrics or columns with currency symbols as prefixes or suffixes. Choose a symbol manually or use 'Auto-detect' to apply the correct symbol based on the dataset's currency code column. When multiple currencies are present, formatting falls back to neutral numbers.",
|
||||
),
|
||||
debounceDelay: 200,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 { render } from 'spec/helpers/testing-library';
|
||||
import { CurrencyControl } from './CurrencyControl';
|
||||
|
||||
test('CurrencyControl renders position and symbol selects', () => {
|
||||
const { container } = render(
|
||||
<CurrencyControl onChange={jest.fn()} value={{}} />,
|
||||
{
|
||||
useRedux: true,
|
||||
initialState: {
|
||||
common: { currencies: ['USD', 'EUR'] },
|
||||
explore: { datasource: {} },
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
container.querySelector('[data-test="currency-control-container"]'),
|
||||
).toBeInTheDocument();
|
||||
expect(container.querySelectorAll('.ant-select')).toHaveLength(2);
|
||||
});
|
||||
@@ -16,14 +16,15 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { t } from '@apache-superset/core';
|
||||
import { Currency, ensureIsArray, getCurrencySymbol } from '@superset-ui/core';
|
||||
import { css, styled } from '@apache-superset/core/ui';
|
||||
import { css, styled, useTheme } from '@apache-superset/core/ui';
|
||||
import { CSSObject } from '@emotion/react';
|
||||
import { Select, type SelectProps } from '@superset-ui/core/components';
|
||||
import { ViewState } from 'src/views/types';
|
||||
import { ExplorePageState } from 'src/explore/types';
|
||||
import ControlHeader from '../../ControlHeader';
|
||||
|
||||
export interface CurrencyControlProps {
|
||||
@@ -67,19 +68,74 @@ export const CurrencyControl = ({
|
||||
currencySelectAdditionalStyles,
|
||||
...props
|
||||
}: CurrencyControlProps) => {
|
||||
const theme = useTheme();
|
||||
const currencies = useSelector<ViewState, string[]>(
|
||||
state => state.common?.currencies,
|
||||
);
|
||||
const currenciesOptions = useMemo(
|
||||
() =>
|
||||
ensureIsArray(currencies).map(currencyCode => ({
|
||||
value: currencyCode,
|
||||
label: `${getCurrencySymbol({
|
||||
symbol: currencyCode,
|
||||
})} (${currencyCode})`,
|
||||
})),
|
||||
[currencies],
|
||||
const currencyCodeColumn = useSelector<ExplorePageState, string | undefined>(
|
||||
state => state?.explore?.datasource?.currency_code_column,
|
||||
);
|
||||
|
||||
const currenciesOptions = useMemo(() => {
|
||||
const options = ensureIsArray(currencies).map(currencyCode => ({
|
||||
value: currencyCode,
|
||||
label: `${getCurrencySymbol({
|
||||
symbol: currencyCode,
|
||||
})} (${currencyCode})`,
|
||||
}));
|
||||
|
||||
const autoDetectOption = currencyCodeColumn
|
||||
? [
|
||||
{
|
||||
value: 'AUTO',
|
||||
label: t('Auto-detect'),
|
||||
className: 'currency-auto-detect-option',
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
return [
|
||||
...autoDetectOption,
|
||||
...options,
|
||||
{ value: '', label: t('Custom...') },
|
||||
];
|
||||
}, [currencies, currencyCodeColumn]);
|
||||
|
||||
const currencySortComparator = useCallback(
|
||||
(
|
||||
a: { value?: string | number },
|
||||
b: { value?: string | number },
|
||||
): number => {
|
||||
if (a.value === 'AUTO') return -1;
|
||||
if (b.value === 'AUTO') return 1;
|
||||
if (a.value === '') return 1;
|
||||
if (b.value === '') return -1;
|
||||
const labelA = String(a.value ?? '');
|
||||
const labelB = String(b.value ?? '');
|
||||
return labelA.localeCompare(labelB);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const renderCurrencyPopup = useMemo(
|
||||
() =>
|
||||
currencyCodeColumn
|
||||
? (menu: React.ReactNode) => (
|
||||
<div
|
||||
css={css`
|
||||
.currency-auto-detect-option {
|
||||
border-bottom: 1px solid ${theme.colorBorderSecondary};
|
||||
margin-bottom: ${theme.sizeUnit}px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
{menu}
|
||||
</div>
|
||||
)
|
||||
: undefined,
|
||||
[currencyCodeColumn, theme],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ControlHeader {...props} />
|
||||
@@ -92,7 +148,7 @@ export const CurrencyControl = ({
|
||||
${currencySelectAdditionalStyles};
|
||||
}
|
||||
`}
|
||||
className="currency-control-container"
|
||||
data-test="currency-control-container"
|
||||
>
|
||||
<Select
|
||||
ariaLabel={t('Currency prefix or suffix')}
|
||||
@@ -117,6 +173,8 @@ export const CurrencyControl = ({
|
||||
value={currency?.symbol}
|
||||
allowClear
|
||||
allowNewOptions
|
||||
sortComparator={currencySortComparator}
|
||||
popupRender={renderCurrencyPopup}
|
||||
{...currencySelectOverrideProps}
|
||||
/>
|
||||
</CurrencyControlContainer>
|
||||
|
||||
Reference in New Issue
Block a user