feat(dashboard): extend number format option to bar, line and pie charts (#23505)

https://discord.com/channels/1130383047699738754/1509604545381142649

Extends the Format option (Short/Full) added for the Number widget in
#21521 to bar, line and pie charts.

Format controls the numbers printed on the chart face: data labels and
the pie center metric. Axis ticks stay abbreviated and tooltips always
show the full value. Defaults to Short, so existing charts render
unchanged.

Server: nullable `numberFormat` on the bar/line/pie configuration DTOs,
exposed in the dashboard AI tool schema. No migration, configuration is
jsonb.

Deferred:
- The Format row has no visible effect while data labels are off, since
tooltips are always full.
- Number widget format defaults differ by field type (CURRENCY defaults
to Short, NUMBER to Full). Pre-existing, untouched here.


https://github.com/user-attachments/assets/0778f08a-6681-4e7a-8716-fb3026d1e01f



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23505?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
nitin
2026-07-30 15:12:21 +05:30
committed by GitHub
parent 72322a4d72
commit 079e9b8e56
39 changed files with 530 additions and 60 deletions
@@ -1142,6 +1142,7 @@ type PieChartConfiguration {
showCenterMetric: Boolean
displayLegend: Boolean
hideEmptyCategory: Boolean
numberFormat: ChartNumberFormat
splitMultiValueFields: Boolean
description: String
color: String
@@ -1195,6 +1196,7 @@ type LineChartConfiguration {
axisNameDisplay: AxisNameDisplay
displayDataLabel: Boolean
displayLegend: Boolean
numberFormat: ChartNumberFormat
rangeMin: Float
rangeMax: Float
description: String
@@ -1238,6 +1240,7 @@ type BarChartConfiguration {
axisNameDisplay: AxisNameDisplay
displayDataLabel: Boolean
displayLegend: Boolean
numberFormat: ChartNumberFormat
rangeMin: Float
rangeMax: Float
description: String
@@ -821,6 +821,7 @@ export interface PieChartConfiguration {
showCenterMetric?: Scalars['Boolean']
displayLegend?: Scalars['Boolean']
hideEmptyCategory?: Scalars['Boolean']
numberFormat?: ChartNumberFormat
splitMultiValueFields?: Scalars['Boolean']
description?: Scalars['String']
color?: Scalars['String']
@@ -857,6 +858,7 @@ export interface LineChartConfiguration {
axisNameDisplay?: AxisNameDisplay
displayDataLabel?: Scalars['Boolean']
displayLegend?: Scalars['Boolean']
numberFormat?: ChartNumberFormat
rangeMin?: Scalars['Float']
rangeMax?: Scalars['Float']
description?: Scalars['String']
@@ -898,6 +900,7 @@ export interface BarChartConfiguration {
axisNameDisplay?: AxisNameDisplay
displayDataLabel?: Scalars['Boolean']
displayLegend?: Scalars['Boolean']
numberFormat?: ChartNumberFormat
rangeMin?: Scalars['Float']
rangeMax?: Scalars['Float']
description?: Scalars['String']
@@ -3987,6 +3990,7 @@ export interface PieChartConfigurationGenqlSelection{
showCenterMetric?: boolean | number
displayLegend?: boolean | number
hideEmptyCategory?: boolean | number
numberFormat?: boolean | number
splitMultiValueFields?: boolean | number
description?: boolean | number
color?: boolean | number
@@ -4016,6 +4020,7 @@ export interface LineChartConfigurationGenqlSelection{
axisNameDisplay?: boolean | number
displayDataLabel?: boolean | number
displayLegend?: boolean | number
numberFormat?: boolean | number
rangeMin?: boolean | number
rangeMax?: boolean | number
description?: boolean | number
@@ -4055,6 +4060,7 @@ export interface BarChartConfigurationGenqlSelection{
axisNameDisplay?: boolean | number
displayDataLabel?: boolean | number
displayLegend?: boolean | number
numberFormat?: boolean | number
rangeMin?: boolean | number
rangeMax?: boolean | number
description?: boolean | number
@@ -2230,6 +2230,9 @@ export default {
"hideEmptyCategory": [
3
],
"numberFormat": [
91
],
"splitMultiValueFields": [
3
],
@@ -2309,6 +2312,9 @@ export default {
"displayLegend": [
3
],
"numberFormat": [
91
],
"rangeMin": [
16
],
@@ -2407,6 +2413,9 @@ export default {
"displayLegend": [
3
],
"numberFormat": [
91
],
"rangeMin": [
16
],
File diff suppressed because one or more lines are too long
@@ -63,6 +63,21 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
).toBe('153,909');
});
it('should format large COUNT values with the SHORT chart number format', () => {
expect(
transformAggregateRawValueIntoAggregateDisplayValue({
aggregateFieldMetadataItem: undefined,
aggregateOperation: AggregateOperations.COUNT,
aggregateRawValue: 153909,
dateFormat: DateFormat.DAY_FIRST,
timeFormat: TimeFormat.HOUR_24,
localeCatalog: enUS,
timeZone: 'UTC',
chartNumberFormat: ChartNumberFormat.SHORT,
}),
).toBe('153.9k');
});
it('should format large COUNT values respecting the SPACES_AND_COMMA number format', () => {
expect(
transformAggregateRawValueIntoAggregateDisplayValue({
@@ -46,7 +46,11 @@ export const transformAggregateRawValueIntoAggregateDisplayValue = ({
aggregateOperation as AggregateOperations,
)
) {
return formatNumber(Number(aggregateRawValue), { format: numberFormat });
const countValue = Number(aggregateRawValue);
return chartNumberFormat === ChartNumberFormat.SHORT
? formatToShortNumber(countValue)
: formatNumber(countValue, { format: numberFormat });
} else if (!isDefined(aggregateFieldMetadataItem)) {
return '-';
} else if (
@@ -54,6 +54,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
axisNameDisplay
displayDataLabel
displayLegend
numberFormat
rangeMin
rangeMax
color
@@ -84,6 +85,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
axisNameDisplay
displayDataLabel
displayLegend
numberFormat
rangeMin
rangeMax
color
@@ -107,6 +109,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
displayDataLabel
showCenterMetric
displayLegend
numberFormat
hideEmptyCategory
splitMultiValueFields
color
@@ -0,0 +1,3 @@
import { ChartNumberFormat } from '~/generated-metadata/graphql';
export const CHART_NUMBER_FORMAT_DEFAULT = ChartNumberFormat.SHORT;
@@ -32,6 +32,7 @@ type BarChartProps = {
effectiveValueRange: { minimum: number; maximum: number };
hasExplicitRangeBounds: boolean;
formatOptions: GraphValueFormatOptions;
axisFormatOptions?: GraphValueFormatOptions;
axisConfig?: {
xAxisLabel?: string;
yAxisLabel?: string;
@@ -69,6 +70,7 @@ export const BarChart = ({
effectiveValueRange,
hasExplicitRangeBounds,
formatOptions,
axisFormatOptions,
axisConfig,
rightTickLabels,
dataLabelsConfig,
@@ -107,7 +109,7 @@ export const BarChart = ({
data,
effectiveValueRange,
hasExplicitRangeBounds,
formatOptions,
formatOptions: axisFormatOptions ?? formatOptions,
groupMode,
indexBy,
keys,
@@ -1,3 +1,4 @@
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
import { pageLayoutDraggingWidgetIdComponentState } from '@/page-layout/states/pageLayoutDraggingWidgetIdComponentState';
import { pageLayoutResizingWidgetIdComponentState } from '@/page-layout/states/pageLayoutResizingWidgetIdComponentState';
import { GraphWidgetChartContainer } from '@/page-layout/widgets/graph/components/GraphWidgetChartContainer';
@@ -34,6 +35,7 @@ import { useDebouncedCallback } from 'use-debounce';
import { BarChartLayout } from '~/generated-metadata/graphql';
type GraphWidgetBarChartProps = {
axisDisplayType?: GraphValueFormatOptions['displayType'];
colorMode: GraphColorMode;
data: BarChartDatum[];
groupMode?: 'grouped' | 'stacked';
@@ -50,6 +52,7 @@ type GraphWidgetBarChartProps = {
showGrid?: boolean;
showLegend?: boolean;
showValues?: boolean;
tooltipDisplayType?: GraphValueFormatOptions['displayType'];
xAxisLabel?: string;
yAxisLabel?: string;
} & GraphValueFormatOptions;
@@ -82,6 +85,8 @@ export const GraphWidgetBarChart = ({
rangeMax,
omitNullValues = false,
displayType,
axisDisplayType,
tooltipDisplayType,
decimals,
prefix,
suffix,
@@ -124,15 +129,34 @@ export const GraphWidgetBarChart = ({
const allowDataTransitions = !isLayoutAnimating;
const { formatNumber } = useNumberFormat();
const formatOptions = useMemo<GraphValueFormatOptions>(
() => ({
customFormatter,
decimals,
displayType,
formatNumberFn: formatNumber,
prefix,
suffix,
}),
[customFormatter, decimals, displayType, prefix, suffix],
[customFormatter, decimals, displayType, formatNumber, prefix, suffix],
);
const axisFormatOptions = useMemo<GraphValueFormatOptions>(
() => ({
...formatOptions,
displayType: axisDisplayType ?? displayType,
}),
[formatOptions, axisDisplayType, displayType],
);
const tooltipFormatOptions = useMemo<GraphValueFormatOptions>(
() => ({
...formatOptions,
displayType: tooltipDisplayType ?? displayType,
}),
[formatOptions, tooltipDisplayType, displayType],
);
const { enrichedKeysMap, enrichedKeys, legendItems, visibleKeys } =
@@ -262,6 +286,7 @@ export const GraphWidgetBarChart = ({
hasExplicitRangeBounds={hasExplicitRangeBounds}
enrichedKeysMap={enrichedKeysMap}
formatOptions={formatOptions}
axisFormatOptions={axisFormatOptions}
rightTickLabels={rightTickLabels}
groupMode={groupMode}
hasNoData={hasNoData}
@@ -280,7 +305,7 @@ export const GraphWidgetBarChart = ({
containerRef={containerRef}
dataByIndexValue={dataByIndexValue}
enrichedKeys={enrichedKeys}
formatOptions={formatOptions}
formatOptions={tooltipFormatOptions}
onMouseEnter={handleTooltipMouseEnter}
onMouseLeave={handleTooltipMouseLeave}
onSliceClick={onSliceClick}
@@ -7,6 +7,7 @@ import { type BarChartSlice } from '@/page-layout/widgets/graph/graph-widget-bar
import { assertBarChartWidgetOrThrow } from '@/page-layout/widgets/graph/utils/assertBarChartWidget';
import { buildChartDrilldownQueryParams } from '@/page-layout/widgets/graph/utils/buildChartDrilldownQueryParams';
import { generateChartAggregateFilterKey } from '@/page-layout/widgets/graph/utils/generateChartAggregateFilterKey';
import { getChartValueFormatOptions } from '@/page-layout/widgets/graph/utils/getChartValueFormatOptions';
import { isFilteredViewRedirectionSupported } from '@/page-layout/widgets/graph/utils/isFilteredViewRedirectionSupported';
import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget';
import { useUserFirstDayOfTheWeek } from '@/ui/input/components/internal/date/hooks/useUserFirstDayOfTheWeek';
@@ -61,6 +62,13 @@ export const GraphWidgetBarChartRenderer = () => {
const configuration = widget.configuration;
const isPageLayoutInEditMode = useIsPageLayoutInEditMode();
const chartValueFormatOptions = getChartValueFormatOptions({
aggregateOperation: configuration.aggregateOperation,
aggregateFieldMetadataId: configuration.aggregateFieldMetadataId,
fieldMetadataItems: objectMetadataItem.fields,
numberFormat: configuration.numberFormat,
});
const axisNameDisplay = configuration.axisNameDisplay;
const showXLabel =
@@ -143,7 +151,10 @@ export const GraphWidgetBarChartRenderer = () => {
groupMode={groupMode}
colorMode={colorMode}
id={widget.id}
displayType="shortNumber"
decimals={chartValueFormatOptions.decimals}
displayType={chartValueFormatOptions.displayType}
axisDisplayType="shortNumber"
tooltipDisplayType="number"
rangeMin={configuration.rangeMin ?? undefined}
rangeMax={configuration.rangeMax ?? undefined}
omitNullValues={configuration.omitNullValues ?? false}
@@ -1,3 +1,4 @@
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
import { CHART_MOTION_CONFIG } from '@/page-layout/widgets/graph/constants/ChartMotionConfig';
import { GraphWidgetChartContainer } from '@/page-layout/widgets/graph/components/GraphWidgetChartContainer';
import { GraphWidgetLegend } from '@/page-layout/widgets/graph/components/GraphWidgetLegend';
@@ -46,6 +47,7 @@ type LinesLayerProps = LineCustomSvgLayerProps<LineSeries>;
type NoDataLayerWrapperProps = LineCustomSvgLayerProps<LineSeries>;
type GraphWidgetLineChartProps = {
axisDisplayType?: GraphValueFormatOptions['displayType'];
data: LineChartSeriesWithColor[];
showLegend?: boolean;
showGrid?: boolean;
@@ -60,6 +62,7 @@ type GraphWidgetLineChartProps = {
groupMode?: 'stacked';
colorMode: GraphColorMode;
onSliceClick?: (point: Point<LineSeries>) => void;
tooltipDisplayType?: GraphValueFormatOptions['displayType'];
} & GraphValueFormatOptions;
const StyledContainer = styled.div`
@@ -84,6 +87,8 @@ export const GraphWidgetLineChart = ({
rangeMax,
omitNullValues = false,
displayType,
axisDisplayType,
tooltipDisplayType,
groupMode,
colorMode,
decimals,
@@ -98,12 +103,25 @@ export const GraphWidgetLineChart = ({
const containerRef = useRef<HTMLDivElement>(null);
const [chartWidth, setChartWidth] = useState(0);
const { formatNumber } = useNumberFormat();
const formatOptions: GraphValueFormatOptions = {
displayType,
decimals,
prefix,
suffix,
customFormatter,
formatNumberFn: formatNumber,
};
const axisFormatOptions: GraphValueFormatOptions = {
...formatOptions,
displayType: axisDisplayType ?? displayType,
};
const tooltipFormatOptions: GraphValueFormatOptions = {
...formatOptions,
displayType: tooltipDisplayType ?? displayType,
};
const { enrichedSeries, nivoData, colors, legendItems, visibleData } =
@@ -168,7 +186,7 @@ export const GraphWidgetLineChart = ({
data,
xAxisLabel,
yAxisLabel,
formatOptions,
formatOptions: axisFormatOptions,
effectiveMinimumValue,
effectiveMaximumValue,
});
@@ -342,7 +360,7 @@ export const GraphWidgetLineChart = ({
<GraphLineChartTooltip
containerRef={containerRef}
enrichedSeries={enrichedSeries}
formatOptions={formatOptions}
formatOptions={tooltipFormatOptions}
isStacked={groupMode === 'stacked'}
onSliceClick={onSliceClick}
onMouseEnter={handleTooltipMouseEnter}
@@ -7,6 +7,7 @@ import { useGraphLineChartWidgetData } from '@/page-layout/widgets/graph/graph-w
import { assertLineChartWidgetOrThrow } from '@/page-layout/widgets/graph/utils/assertLineChartWidget';
import { buildChartDrilldownQueryParams } from '@/page-layout/widgets/graph/utils/buildChartDrilldownQueryParams';
import { generateChartAggregateFilterKey } from '@/page-layout/widgets/graph/utils/generateChartAggregateFilterKey';
import { getChartValueFormatOptions } from '@/page-layout/widgets/graph/utils/getChartValueFormatOptions';
import { isFilteredViewRedirectionSupported } from '@/page-layout/widgets/graph/utils/isFilteredViewRedirectionSupported';
import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget';
import { useUserFirstDayOfTheWeek } from '@/ui/input/components/internal/date/hooks/useUserFirstDayOfTheWeek';
@@ -59,6 +60,13 @@ export const GraphWidgetLineChartRenderer = () => {
const configuration = widget.configuration;
const isPageLayoutInEditMode = useIsPageLayoutInEditMode();
const chartValueFormatOptions = getChartValueFormatOptions({
aggregateOperation: configuration.aggregateOperation,
aggregateFieldMetadataId: configuration.aggregateFieldMetadataId,
fieldMetadataItems: objectMetadataItem.fields,
numberFormat: configuration.numberFormat,
});
const hasGroupByOnSecondaryAxis = isDefined(
configuration.secondaryAxisGroupByFieldMetadataId,
);
@@ -151,7 +159,10 @@ export const GraphWidgetLineChartRenderer = () => {
omitNullValues={configuration.omitNullValues ?? false}
groupMode={groupMode}
colorMode={colorMode}
displayType="shortNumber"
decimals={chartValueFormatOptions.decimals}
displayType={chartValueFormatOptions.displayType}
axisDisplayType="shortNumber"
tooltipDisplayType="number"
onSliceClick={
isPageLayoutInEditMode || !canRedirectToFilteredView
? undefined
@@ -1,3 +1,4 @@
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
import { GraphWidgetChartContainer } from '@/page-layout/widgets/graph/components/GraphWidgetChartContainer';
import { GraphWidgetLegend } from '@/page-layout/widgets/graph/components/GraphWidgetLegend';
import { CHART_MOTION_CONFIG } from '@/page-layout/widgets/graph/constants/ChartMotionConfig';
@@ -41,6 +42,7 @@ type GraphWidgetPieChartProps = {
onSliceClick?: (datum: PieChartDataItemWithColor) => void;
showDataLabels?: boolean;
showCenterMetric?: boolean;
tooltipDisplayType?: GraphValueFormatOptions['displayType'];
} & GraphValueFormatOptions;
const emptyStateData: PieChartDataItemWithColor[] = [
@@ -80,6 +82,7 @@ export const GraphWidgetPieChart = ({
configuration,
colorMode,
displayType,
tooltipDisplayType,
decimals,
prefix,
suffix,
@@ -95,12 +98,20 @@ export const GraphWidgetPieChart = ({
graphWidgetPieTooltipComponentState,
);
const { formatNumber } = useNumberFormat();
const formatOptions: GraphValueFormatOptions = {
displayType,
decimals,
prefix,
suffix,
customFormatter,
formatNumberFn: formatNumber,
};
const tooltipFormatOptions: GraphValueFormatOptions = {
...formatOptions,
displayType: tooltipDisplayType ?? displayType,
};
const { enrichedData, legendItems } = usePieChartData({
@@ -229,8 +240,8 @@ export const GraphWidgetPieChart = ({
<GraphPieChartTooltip
containerRef={containerRef}
enrichedData={enrichedData}
formatOptions={formatOptions}
displayType={displayType}
formatOptions={tooltipFormatOptions}
displayType={tooltipDisplayType ?? displayType}
onSliceClick={onSliceClick}
/>
{showLegend && data.length > 0 && (
@@ -6,6 +6,7 @@ import { useGraphPieChartWidgetData } from '@/page-layout/widgets/graph/graph-wi
import { type PieChartDataItemWithColor } from '@/page-layout/widgets/graph/graph-widget-pie-chart/types/PieChartDataItem';
import { assertPieChartWidgetOrThrow } from '@/page-layout/widgets/graph/utils/assertPieChartWidget';
import { buildChartDrilldownQueryParams } from '@/page-layout/widgets/graph/utils/buildChartDrilldownQueryParams';
import { getChartValueFormatOptions } from '@/page-layout/widgets/graph/utils/getChartValueFormatOptions';
import { isFilteredViewRedirectionSupported } from '@/page-layout/widgets/graph/utils/isFilteredViewRedirectionSupported';
import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget';
import { useUserFirstDayOfTheWeek } from '@/ui/input/components/internal/date/hooks/useUserFirstDayOfTheWeek';
@@ -58,6 +59,13 @@ export const GraphWidgetPieChartRenderer = () => {
const { userFirstDayOfTheWeek } = useUserFirstDayOfTheWeek();
const chartValueFormatOptions = getChartValueFormatOptions({
aggregateOperation: widget.configuration.aggregateOperation,
aggregateFieldMetadataId: widget.configuration.aggregateFieldMetadataId,
fieldMetadataItems: objectMetadataItem.fields,
numberFormat: widget.configuration.numberFormat,
});
const groupByField = objectMetadataItem.fields.find(
(field) => field.id === widget.configuration.groupByFieldMetadataId,
);
@@ -109,7 +117,9 @@ export const GraphWidgetPieChartRenderer = () => {
configuration={widget.configuration}
showLegend={showLegend}
colorMode={colorMode}
displayType="shortNumber"
decimals={chartValueFormatOptions.decimals}
displayType={chartValueFormatOptions.displayType}
tooltipDisplayType="number"
onSliceClick={
isPageLayoutInEditMode || !canRedirectToFilteredView
? undefined
@@ -5,6 +5,7 @@ import { transformAggregateRawValueIntoAggregateDisplayValue } from '@/object-re
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation';
import { CHART_NUMBER_FORMAT_DEFAULT } from '@/page-layout/widgets/graph/constants/ChartNumberFormatDefault';
import { useGraphWidgetQueryCommon } from '@/page-layout/widgets/graph/hooks/useGraphWidgetQueryCommon';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { UserContext } from '@/users/contexts/UserContext';
@@ -82,7 +83,9 @@ export const usePieChartCenterMetricData = ({
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
const dateLocale = useAtomStateValue(dateLocaleState);
const { numberFormat, formatNumber } = useNumberFormat();
const { numberFormat } = useNumberFormat();
const chartNumberFormat =
configuration.numberFormat ?? CHART_NUMBER_FORMAT_DEFAULT;
const aggregateFieldMetadataItem = objectMetadataItem.readableFields.find(
findById(configuration.aggregateFieldMetadataId),
@@ -122,9 +125,21 @@ export const usePieChartCenterMetricData = ({
AggregateOperations.COUNT
];
return isDefined(totalCountValue)
? formatNumber(Number(totalCountValue))
: totalCountValue;
if (!isDefined(totalCountValue)) {
return totalCountValue;
}
return transformAggregateRawValueIntoAggregateDisplayValue({
aggregateFieldMetadataItem,
aggregateOperation: extendedAggregateOperation,
aggregateRawValue: totalCountValue,
dateFormat,
localeCatalog: dateLocale.localeCatalog,
timeFormat,
timeZone,
numberFormat,
chartNumberFormat,
});
}
const aggregateRawValue =
@@ -141,6 +156,7 @@ export const usePieChartCenterMetricData = ({
timeFormat,
timeZone,
numberFormat,
chartNumberFormat,
});
}, [
aggregateFieldMetadataItem,
@@ -152,7 +168,7 @@ export const usePieChartCenterMetricData = ({
timeFormat,
timeZone,
numberFormat,
formatNumber,
chartNumberFormat,
]);
return {
@@ -0,0 +1,19 @@
import { getChartValueDisplayType } from '@/page-layout/widgets/graph/utils/getChartValueDisplayType';
import { ChartNumberFormat } from '~/generated-metadata/graphql';
describe('getChartValueDisplayType', () => {
it('should return number when the format is FULL', () => {
expect(getChartValueDisplayType(ChartNumberFormat.FULL)).toBe('number');
});
it('should return shortNumber when the format is SHORT', () => {
expect(getChartValueDisplayType(ChartNumberFormat.SHORT)).toBe(
'shortNumber',
);
});
it('should fall back to the default format when the format is unset', () => {
expect(getChartValueDisplayType(null)).toBe('shortNumber');
expect(getChartValueDisplayType(undefined)).toBe('shortNumber');
});
});
@@ -0,0 +1,96 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { getChartValueFormatOptions } from '@/page-layout/widgets/graph/utils/getChartValueFormatOptions';
import { formatGraphValue } from '@/page-layout/widgets/graph/utils/graphFormatters';
import { FieldMetadataType } from 'twenty-shared/types';
import {
AggregateOperations,
ChartNumberFormat,
} from '~/generated-metadata/graphql';
describe('getChartValueFormatOptions', () => {
const aggregateFieldMetadataItem = {
id: 'score-field-id',
type: FieldMetadataType.NUMBER,
settings: {
decimals: 2,
type: 'number',
},
} as FieldMetadataItem;
const aggregateCurrencyFieldMetadataItem = {
id: 'revenue-field-id',
type: FieldMetadataType.CURRENCY,
} as FieldMetadataItem;
it.each([
AggregateOperations.AVG,
AggregateOperations.MAX,
AggregateOperations.MIN,
AggregateOperations.SUM,
])(
'should honor the aggregate number field decimal setting for %s',
(aggregateOperation) => {
const formatOptions = getChartValueFormatOptions({
aggregateOperation,
aggregateFieldMetadataId: aggregateFieldMetadataItem.id,
fieldMetadataItems: [aggregateFieldMetadataItem],
numberFormat: ChartNumberFormat.FULL,
});
expect(formatGraphValue(123.4567, formatOptions)).toBe('123.46');
},
);
it.each([
AggregateOperations.AVG,
AggregateOperations.MAX,
AggregateOperations.MIN,
AggregateOperations.SUM,
])(
'should format aggregate currency fields with two decimals for %s',
(aggregateOperation) => {
const formatOptions = getChartValueFormatOptions({
aggregateOperation,
aggregateFieldMetadataId: aggregateCurrencyFieldMetadataItem.id,
fieldMetadataItems: [aggregateCurrencyFieldMetadataItem],
numberFormat: ChartNumberFormat.FULL,
});
expect(formatGraphValue(123.4567, formatOptions)).toBe('123.46');
},
);
it('should keep the short format compact', () => {
const formatOptions = getChartValueFormatOptions({
aggregateOperation: AggregateOperations.SUM,
aggregateFieldMetadataId: aggregateFieldMetadataItem.id,
fieldMetadataItems: [aggregateFieldMetadataItem],
numberFormat: ChartNumberFormat.SHORT,
});
expect(formatGraphValue(123.4567, formatOptions)).toBe('123.5');
});
it.each([
AggregateOperations.COUNT,
AggregateOperations.COUNT_EMPTY,
AggregateOperations.COUNT_FALSE,
AggregateOperations.COUNT_NOT_EMPTY,
AggregateOperations.COUNT_TRUE,
AggregateOperations.COUNT_UNIQUE_VALUES,
AggregateOperations.PERCENTAGE_EMPTY,
AggregateOperations.PERCENTAGE_NOT_EMPTY,
])(
'should ignore the aggregate number field decimal setting for %s',
(aggregateOperation) => {
const formatOptions = getChartValueFormatOptions({
aggregateOperation,
aggregateFieldMetadataId: aggregateFieldMetadataItem.id,
fieldMetadataItems: [aggregateFieldMetadataItem],
numberFormat: ChartNumberFormat.FULL,
});
expect(formatOptions.decimals).toBeUndefined();
},
);
});
@@ -191,4 +191,36 @@ describe('formatGraphValue', () => {
expect(result).toBeDefined();
});
});
describe('formatNumberFn option', () => {
const germanFormatNumberFn = (value: number) =>
value.toLocaleString('de-DE');
it('should use the provided formatNumberFn for number display type', () => {
const result = formatGraphValue(1234567, {
displayType: 'number',
formatNumberFn: germanFormatNumberFn,
});
expect(result).toBe('1.234.567');
});
it('should use the provided formatNumberFn for currency display type', () => {
const result = formatGraphValue(1234567, {
displayType: 'currency',
formatNumberFn: germanFormatNumberFn,
});
expect(result).toBe('$1.234.567');
});
it('should not apply formatNumberFn to abbreviated short numbers', () => {
const result = formatGraphValue(1234567, {
displayType: 'shortNumber',
formatNumberFn: germanFormatNumberFn,
});
expect(result).toBe('1.2m');
});
});
});
@@ -0,0 +1,10 @@
import { CHART_NUMBER_FORMAT_DEFAULT } from '@/page-layout/widgets/graph/constants/ChartNumberFormatDefault';
import { type GraphValueFormatOptions } from '@/page-layout/widgets/graph/utils/graphFormatters';
import { ChartNumberFormat } from '~/generated-metadata/graphql';
export const getChartValueDisplayType = (
numberFormat: ChartNumberFormat | null | undefined,
): GraphValueFormatOptions['displayType'] =>
(numberFormat ?? CHART_NUMBER_FORMAT_DEFAULT) === ChartNumberFormat.FULL
? 'number'
: 'shortNumber';
@@ -0,0 +1,58 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
import { NON_STANDARD_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/nonStandardAggregateOperationsOptions';
import { getChartValueDisplayType } from '@/page-layout/widgets/graph/utils/getChartValueDisplayType';
import { type GraphValueFormatOptions } from '@/page-layout/widgets/graph/utils/graphFormatters';
import { FieldMetadataType } from 'twenty-shared/types';
import { findById, isDefined } from 'twenty-shared/utils';
import {
type AggregateOperations as GeneratedAggregateOperations,
type ChartNumberFormat,
} from '~/generated-metadata/graphql';
type GetChartValueFormatOptionsParams = {
aggregateOperation: GeneratedAggregateOperations;
aggregateFieldMetadataId: string;
fieldMetadataItems: FieldMetadataItem[];
numberFormat: ChartNumberFormat | null | undefined;
};
export const getChartValueFormatOptions = ({
aggregateOperation,
aggregateFieldMetadataId,
fieldMetadataItems,
numberFormat,
}: GetChartValueFormatOptionsParams): GraphValueFormatOptions => {
const aggregateFieldMetadataItem = fieldMetadataItems.find(
findById(aggregateFieldMetadataId),
);
const aggregateFieldSettings =
aggregateFieldMetadataItem?.type === FieldMetadataType.NUMBER
? aggregateFieldMetadataItem.settings
: undefined;
const aggregateNumberFieldDecimals =
isDefined(aggregateFieldSettings) && 'decimals' in aggregateFieldSettings
? aggregateFieldSettings.decimals
: undefined;
const aggregateFieldDecimals =
aggregateFieldMetadataItem?.type === FieldMetadataType.CURRENCY
? 2
: aggregateNumberFieldDecimals;
const shouldUseAggregateFieldDecimals =
NON_STANDARD_AGGREGATE_OPERATION_OPTIONS.includes(
aggregateOperation as AggregateOperations,
);
const decimals = shouldUseAggregateFieldDecimals
? aggregateFieldDecimals
: undefined;
return {
decimals,
displayType: getChartValueDisplayType(numberFormat),
};
};
@@ -1,3 +1,4 @@
import { CHART_NUMBER_FORMAT_DEFAULT } from '@/page-layout/widgets/graph/constants/ChartNumberFormatDefault';
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
@@ -20,13 +21,21 @@ export const ChartNumberFormatSelectionDropdownContent = () => {
const configuration = widgetInEditMode?.configuration;
if (
!isWidgetConfigurationOfType(configuration, 'AggregateChartConfiguration')
) {
const isChartWithNumberFormat =
isWidgetConfigurationOfType(configuration, 'AggregateChartConfiguration') ||
isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') ||
isWidgetConfigurationOfType(configuration, 'LineChartConfiguration') ||
isWidgetConfigurationOfType(configuration, 'PieChartConfiguration');
if (!isChartWithNumberFormat) {
throw new Error('Invalid configuration type');
}
const currentNumberFormat = configuration.numberFormat;
const currentNumberFormat =
configuration.numberFormat ??
(configuration.__typename === 'AggregateChartConfiguration'
? undefined
: CHART_NUMBER_FORMAT_DEFAULT);
const dropdownId = useAvailableComponentInstanceIdOrThrow(
DropdownComponentInstanceContext,
@@ -9,6 +9,7 @@ import { DATA_LABELS_SETTING } from '@/side-panel/pages/page-layout/constants/se
import { DATE_GRANULARITY_X_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularityXSetting';
import { DATE_GRANULARITY_Y_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularityYSetting';
import { FILTER_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FilterSetting';
import { FORMAT_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FormatSetting';
import { GROUP_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/GroupBySetting';
import { OMIT_NULL_VALUES_SETTING } from '@/side-panel/pages/page-layout/constants/settings/OmitNullValuesSetting';
import { PRIMARY_SORT_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/PrimarySortBySetting';
@@ -56,6 +57,7 @@ export const LINE_CHART_SETTINGS: ChartSettingsGroup[] = [
AXIS_NAME_SETTING,
STACKED_LINES_SETTING,
DATA_LABELS_SETTING,
FORMAT_SETTING,
SHOW_LEGEND_SETTING,
],
},
@@ -7,6 +7,7 @@ import { DATA_LABELS_SETTING } from '@/side-panel/pages/page-layout/constants/se
import { DATE_GRANULARITY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularitySetting';
import { EACH_SLICE_REPRESENTS_SETTING } from '@/side-panel/pages/page-layout/constants/settings/EachSliceRepresentsSetting';
import { FILTER_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FilterSetting';
import { FORMAT_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FormatSetting';
import { HIDE_EMPTY_CATEGORY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/HideEmptyCategorySetting';
import { PRIMARY_SORT_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/PrimarySortBySetting';
import { SHOW_LEGEND_SETTING } from '@/side-panel/pages/page-layout/constants/settings/ShowLegendSetting';
@@ -32,6 +33,7 @@ export const PIE_CHART_SETTINGS: ChartSettingsGroup[] = [
items: [
COLORS_SETTING,
DATA_LABELS_SETTING,
FORMAT_SETTING,
SHOW_LEGEND_SETTING,
CENTER_METRIC_SETTING,
],
@@ -11,6 +11,7 @@ import {
AxisNameDisplay,
BarChartGroupMode,
BarChartLayout,
ChartNumberFormat,
FieldMetadataType,
GraphOrderBy,
RelationType,
@@ -676,4 +677,32 @@ describe('useChartSettingsValues', () => {
});
});
});
describe('Number format setting', () => {
it('should return the Full label for FORMAT when numberFormat is set on a bar chart', () => {
const config = buildBarChartConfiguration({
numberFormat: ChartNumberFormat.FULL,
});
const { result } = renderUseChartSettingsValues(config);
const value = result.current.getChartSettingsValues(
CHART_CONFIGURATION_SETTING_IDS.FORMAT,
);
expect(value).toBe('Full');
});
it('should return the default format label for FORMAT when numberFormat is not set', () => {
const { result } = renderUseChartSettingsValues(
buildBarChartConfiguration({}),
);
const value = result.current.getChartSettingsValues(
CHART_CONFIGURATION_SETTING_IDS.FORMAT,
);
expect(value).toBe('Short');
});
});
});
@@ -1,3 +1,4 @@
import { CHART_NUMBER_FORMAT_DEFAULT } from '@/page-layout/widgets/graph/constants/ChartNumberFormatDefault';
import { useGraphGroupBySortOptionLabels } from '@/side-panel/pages/page-layout/hooks/useGraphGroupBySortOptionLabels';
import { useGraphXSortOptionLabels } from '@/side-panel/pages/page-layout/hooks/useGraphXSortOptionLabels';
import { type ChartConfiguration } from '@/side-panel/pages/page-layout/types/ChartConfiguration';
@@ -300,6 +301,12 @@ export const useChartSettingsValues = ({
? (configuration.displayLegend ?? true)
: true;
case CHART_CONFIGURATION_SETTING_IDS.FORMAT:
if (isBarOrLineChart || isPieChart) {
return getChartNumberFormatLabel(
configuration.numberFormat ?? CHART_NUMBER_FORMAT_DEFAULT,
);
}
return configuration.__typename === 'AggregateChartConfiguration' &&
isDefined(configuration.numberFormat)
? getChartNumberFormatLabel(configuration.numberFormat)
@@ -9,6 +9,7 @@ import { DATA_LABELS_SETTING } from '@/side-panel/pages/page-layout/constants/se
import { DATE_GRANULARITY_X_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularityXSetting';
import { DATE_GRANULARITY_Y_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularityYSetting';
import { FILTER_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FilterSetting';
import { FORMAT_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FormatSetting';
import { GROUP_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/GroupBySetting';
import { OMIT_NULL_VALUES_SETTING } from '@/side-panel/pages/page-layout/constants/settings/OmitNullValuesSetting';
import { PRIMARY_SORT_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/PrimarySortBySetting';
@@ -156,6 +157,7 @@ describe('getBarChartSettings', () => {
AXIS_NAME_SETTING,
STACKED_BARS_SETTING,
DATA_LABELS_SETTING,
FORMAT_SETTING,
SHOW_LEGEND_SETTING,
]);
});
@@ -9,6 +9,7 @@ import { DATA_LABELS_SETTING } from '@/side-panel/pages/page-layout/constants/se
import { DATE_GRANULARITY_X_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularityXSetting';
import { DATE_GRANULARITY_Y_SETTING } from '@/side-panel/pages/page-layout/constants/settings/DateGranularityYSetting';
import { FILTER_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FilterSetting';
import { FORMAT_SETTING } from '@/side-panel/pages/page-layout/constants/settings/FormatSetting';
import { GROUP_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/GroupBySetting';
import { OMIT_NULL_VALUES_SETTING } from '@/side-panel/pages/page-layout/constants/settings/OmitNullValuesSetting';
import { PRIMARY_SORT_BY_SETTING } from '@/side-panel/pages/page-layout/constants/settings/PrimarySortBySetting';
@@ -73,6 +74,7 @@ export const getBarChartSettings = (
AXIS_NAME_SETTING,
STACKED_BARS_SETTING,
DATA_LABELS_SETTING,
FORMAT_SETTING,
SHOW_LEGEND_SETTING,
],
},
@@ -28,6 +28,7 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/
import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum';
import { BarChartGroupMode } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-group-mode.enum';
import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum';
import { ChartNumberFormat } from 'src/engine/metadata-modules/page-layout-widget/enums/chart-number-format.enum';
import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum';
import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
@@ -135,6 +136,11 @@ export class BarChartConfigurationDTO implements BarChartConfiguration {
@IsOptional()
displayLegend?: boolean;
@Field(() => ChartNumberFormat, { nullable: true })
@IsEnum(ChartNumberFormat)
@IsOptional()
numberFormat?: ChartNumberFormat;
@Field(() => Number, { nullable: true })
@IsNumber()
@IsOptional()
@@ -26,6 +26,7 @@ import {
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum';
import { ChartNumberFormat } from 'src/engine/metadata-modules/page-layout-widget/enums/chart-number-format.enum';
import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum';
import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
@@ -136,6 +137,11 @@ export class LineChartConfigurationDTO implements LineChartConfiguration {
@IsOptional()
displayLegend?: boolean;
@Field(() => ChartNumberFormat, { nullable: true })
@IsEnum(ChartNumberFormat)
@IsOptional()
numberFormat?: ChartNumberFormat;
@Field(() => Number, { nullable: true })
@IsNumber()
@IsOptional()
@@ -24,6 +24,7 @@ import {
} from 'twenty-shared/types';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { ChartNumberFormat } from 'src/engine/metadata-modules/page-layout-widget/enums/chart-number-format.enum';
import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum';
import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
@@ -97,6 +98,11 @@ export class PieChartConfigurationDTO implements PieChartConfiguration {
@IsOptional()
hideEmptyCategory?: boolean;
@Field(() => ChartNumberFormat, { nullable: true })
@IsEnum(ChartNumberFormat)
@IsOptional()
numberFormat?: ChartNumberFormat;
@Field(() => Boolean, { nullable: true, defaultValue: true })
@IsBoolean()
@IsOptional()
@@ -165,6 +165,12 @@ export const chartFilterSchema = z
const displayDataLabelSchema = z.boolean().optional();
const displayLegendSchema = z.boolean().optional();
const chartNumberFormatSchema = z
.enum(CHART_NUMBER_FORMAT_OPTIONS)
.optional()
.describe(
'Display format for data label values: SHORT abbreviates large numbers (1.3m), FULL shows the complete number (1,300,090). Tooltips always show the full value.',
);
const showCenterMetricSchema = z
.boolean()
.optional()
@@ -391,6 +397,7 @@ const barChartConfigSchemaCore = z.object({
.describe('Which axis labels to show'),
displayDataLabel: displayDataLabelSchema,
displayLegend: displayLegendSchema,
numberFormat: chartNumberFormatSchema,
groupMode: z
.enum(BAR_CHART_GROUP_MODE_OPTIONS)
.optional()
@@ -482,6 +489,7 @@ const lineChartConfigSchemaCore = z.object({
.describe('Which axis labels to show'),
displayDataLabel: displayDataLabelSchema,
displayLegend: displayLegendSchema,
numberFormat: chartNumberFormatSchema,
isStacked: z.boolean().optional().describe('Stack multiple lines'),
isCumulative: z.boolean().optional().describe('Show running totals'),
rangeMin: z.number().optional().describe('Y axis minimum value'),
@@ -541,6 +549,7 @@ const pieChartConfigSchemaCore = z.object({
color: z.enum(CHART_COLORS).optional().describe('Chart color theme'),
displayDataLabel: displayDataLabelSchema,
displayLegend: displayLegendSchema,
numberFormat: chartNumberFormatSchema,
showCenterMetric: showCenterMetricSchema,
hideEmptyCategory: hideEmptyCategorySchema,
filter: chartFilterSchema.optional(),
@@ -82,6 +82,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"groupMode": null,
"isCumulative": null,
"layout": "HORIZONTAL",
"numberFormat": "SHORT",
"omitNullValues": true,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -130,6 +131,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"groupMode": null,
"isCumulative": null,
"layout": "HORIZONTAL",
"numberFormat": null,
"omitNullValues": null,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -223,6 +225,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"firstDayOfTheWeek": 1,
"isCumulative": null,
"isStacked": null,
"numberFormat": "SHORT",
"omitNullValues": false,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -270,6 +273,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"firstDayOfTheWeek": 1,
"isCumulative": null,
"isStacked": null,
"numberFormat": null,
"omitNullValues": null,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -318,6 +322,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"groupByFieldMetadataId": Any<String>,
"groupBySubFieldName": null,
"manualSortOrder": null,
"numberFormat": "SHORT",
"orderBy": "VALUE_DESC",
"showCenterMetric": true,
"timezone": "UTC",
@@ -355,6 +360,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"groupByFieldMetadataId": Any<String>,
"groupBySubFieldName": null,
"manualSortOrder": null,
"numberFormat": null,
"orderBy": "FIELD_ASC",
"showCenterMetric": true,
"timezone": "UTC",
@@ -444,6 +450,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"groupMode": null,
"isCumulative": null,
"layout": "VERTICAL",
"numberFormat": "SHORT",
"omitNullValues": true,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -492,6 +499,7 @@ exports[`Page layout widget creation should succeed should create a page layout
"groupMode": null,
"isCumulative": null,
"layout": "VERTICAL",
"numberFormat": null,
"omitNullValues": null,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -49,6 +49,7 @@ exports[`Page layout widget update should succeed from a GRAPH widget should upd
"groupMode": null,
"isCumulative": null,
"layout": "HORIZONTAL",
"numberFormat": null,
"omitNullValues": true,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -96,6 +97,7 @@ exports[`Page layout widget update should succeed from a GRAPH widget should upd
"firstDayOfTheWeek": 1,
"isCumulative": null,
"isStacked": null,
"numberFormat": null,
"omitNullValues": false,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -144,6 +146,7 @@ exports[`Page layout widget update should succeed from a GRAPH widget should upd
"groupByFieldMetadataId": Any<String>,
"groupBySubFieldName": null,
"manualSortOrder": null,
"numberFormat": null,
"orderBy": "VALUE_DESC",
"showCenterMetric": true,
"timezone": "UTC",
@@ -181,6 +184,7 @@ exports[`Page layout widget update should succeed from a GRAPH widget should upd
"groupMode": null,
"isCumulative": null,
"layout": "VERTICAL",
"numberFormat": null,
"omitNullValues": true,
"primaryAxisDateGranularity": "DAY",
"primaryAxisGroupByFieldMetadataId": Any<String>,
@@ -17,6 +17,7 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = `
axisNameDisplay
displayDataLabel
displayLegend
numberFormat
rangeMin
rangeMax
color
@@ -46,6 +47,7 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = `
axisNameDisplay
displayDataLabel
displayLegend
numberFormat
rangeMin
rangeMax
color
@@ -68,6 +70,7 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = `
displayDataLabel
showCenterMetric
displayLegend
numberFormat
color
description
filter
@@ -157,6 +157,7 @@ describe('Page layout widget creation should succeed', () => {
primaryAxisGroupByFieldMetadataId: testSetup.fieldMetadataId2,
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: true,
numberFormat: ChartNumberFormat.SHORT,
axisNameDisplay: AxisNameDisplay.NONE,
color: 'red',
description: 'Monthly revenue breakdown',
@@ -196,6 +197,7 @@ describe('Page layout widget creation should succeed', () => {
primaryAxisGroupByFieldMetadataId: testSetup.fieldMetadataId2,
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: true,
numberFormat: ChartNumberFormat.SHORT,
axisNameDisplay: AxisNameDisplay.NONE,
color: 'blue',
description: 'Horizontal revenue breakdown',
@@ -234,6 +236,7 @@ describe('Page layout widget creation should succeed', () => {
orderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: true,
displayLegend: true,
numberFormat: ChartNumberFormat.SHORT,
showCenterMetric: true,
color: 'yellow',
description: 'Distribution by category',
@@ -269,6 +272,7 @@ describe('Page layout widget creation should succeed', () => {
testSetup.fieldMetadataId3SubFieldName,
secondaryAxisOrderBy: GraphOrderBy.FIELD_DESC,
displayDataLabel: true,
numberFormat: ChartNumberFormat.SHORT,
axisNameDisplay: AxisNameDisplay.NONE,
color: 'cyan',
description: 'Trend over time',
@@ -36,6 +36,7 @@ exports[`Page layout with tabs creation via update should succeed should create
"groupByFieldMetadataId": Any<String>,
"groupBySubFieldName": null,
"manualSortOrder": null,
"numberFormat": null,
"orderBy": "VALUE_DESC",
"showCenterMetric": true,
"timezone": "UTC",
@@ -36,6 +36,7 @@ exports[`Page layout with tabs update should succeed should update page layout w
"groupByFieldMetadataId": Any<String>,
"groupBySubFieldName": null,
"manualSortOrder": null,
"numberFormat": null,
"orderBy": "VALUE_DESC",
"showCenterMetric": true,
"timezone": "UTC",
@@ -133,6 +134,7 @@ exports[`Page layout with tabs update should succeed should update page layout w
"groupByFieldMetadataId": Any<String>,
"groupBySubFieldName": null,
"manualSortOrder": null,
"numberFormat": null,
"orderBy": "VALUE_DESC",
"showCenterMetric": true,
"timezone": "UTC",
@@ -35,6 +35,7 @@ export type PieChartConfiguration = BaseChartConfiguration & {
showCenterMetric?: boolean;
displayLegend?: boolean;
hideEmptyCategory?: boolean;
numberFormat?: string;
splitMultiValueFields?: boolean;
};
@@ -54,6 +55,7 @@ export type BarChartConfiguration = BaseChartConfiguration & {
splitMultiValueFields?: boolean;
axisNameDisplay?: string;
displayLegend?: boolean;
numberFormat?: string;
rangeMin?: number;
rangeMax?: number;
groupMode?: string;
@@ -77,6 +79,7 @@ export type LineChartConfiguration = BaseChartConfiguration & {
splitMultiValueFields?: boolean;
axisNameDisplay?: string;
displayLegend?: boolean;
numberFormat?: string;
rangeMin?: number;
rangeMax?: number;
isStacked?: boolean;