@thoughtspot/ts-chart-sdk 0.0.2-alpha.24 → 0.0.2-alpha.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ts-chart-sdk.d.ts +65 -5
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/main/custom-chart-context.d.ts.map +1 -1
- package/lib/main/custom-chart-context.js +3 -1
- package/lib/main/custom-chart-context.js.map +1 -1
- package/lib/types/answer-column.types.d.ts +3 -3
- package/lib/types/answer-column.types.d.ts.map +1 -1
- package/lib/types/answer-column.types.js +3 -3
- package/lib/types/answer-column.types.js.map +1 -1
- package/lib/types/number-formatting.types.d.ts +2 -2
- package/lib/types/number-formatting.types.d.ts.map +1 -1
- package/lib/types/visual-prop.types.d.ts +40 -0
- package/lib/types/visual-prop.types.d.ts.map +1 -1
- package/lib/types/visual-prop.types.js +32 -1
- package/lib/types/visual-prop.types.js.map +1 -1
- package/lib/utils/globalize-Initializer/globalize-utils.d.ts +16 -0
- package/lib/utils/globalize-Initializer/globalize-utils.d.ts.map +1 -0
- package/lib/utils/globalize-Initializer/globalize-utils.js +101 -0
- package/lib/utils/globalize-Initializer/globalize-utils.js.map +1 -0
- package/lib/utils/globalize-Initializer/globalize-utils.spec.d.ts +2 -0
- package/lib/utils/globalize-Initializer/globalize-utils.spec.d.ts.map +1 -0
- package/lib/utils/globalize-Initializer/globalize-utils.spec.js +149 -0
- package/lib/utils/globalize-Initializer/globalize-utils.spec.js.map +1 -0
- package/lib/utils/number-formatting/number-formatting-utils.d.ts +33 -0
- package/lib/utils/number-formatting/number-formatting-utils.d.ts.map +1 -0
- package/lib/utils/number-formatting/number-formatting-utils.js +180 -0
- package/lib/utils/number-formatting/number-formatting-utils.js.map +1 -0
- package/lib/utils/number-formatting/number-formatting-utils.spec.d.ts +2 -0
- package/lib/utils/number-formatting/number-formatting-utils.spec.d.ts.map +1 -0
- package/lib/utils/number-formatting/number-formatting-utils.spec.js +235 -0
- package/lib/utils/number-formatting/number-formatting-utils.spec.js.map +1 -0
- package/lib/utils/number-formatting/number-formatting.d.ts +5 -0
- package/lib/utils/number-formatting/number-formatting.d.ts.map +1 -0
- package/lib/utils/number-formatting/number-formatting.js +131 -0
- package/lib/utils/number-formatting/number-formatting.js.map +1 -0
- package/lib/utils/number-formatting/number-formatting.spec.d.ts +2 -0
- package/lib/utils/number-formatting/number-formatting.spec.d.ts.map +1 -0
- package/lib/utils/number-formatting/number-formatting.spec.js +159 -0
- package/lib/utils/number-formatting/number-formatting.spec.js.map +1 -0
- package/package.json +4 -1
- package/src/index.ts +2 -0
- package/src/main/custom-chart-context.ts +4 -1
- package/src/types/answer-column.types.ts +3 -3
- package/src/types/number-formatting.types.ts +2 -2
- package/src/types/visual-prop.types.ts +86 -0
- package/src/utils/globalize-Initializer/globalize-utils.spec.ts +192 -0
- package/src/utils/globalize-Initializer/globalize-utils.ts +216 -0
- package/src/utils/number-formatting/number-formatting-utils.spec.ts +321 -0
- package/src/utils/number-formatting/number-formatting-utils.ts +288 -0
- package/src/utils/number-formatting/number-formatting.spec.ts +243 -0
- package/src/utils/number-formatting/number-formatting.ts +268 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { create, LogLevel, logMethods } from '../../main/logger';
|
|
2
|
+
import {
|
|
3
|
+
ColumnFormat,
|
|
4
|
+
CurrencyFormatType,
|
|
5
|
+
FormatType,
|
|
6
|
+
} from '../../types/answer-column.types';
|
|
7
|
+
import {
|
|
8
|
+
CategoryType,
|
|
9
|
+
FormatConfig,
|
|
10
|
+
NegativeValueFormat,
|
|
11
|
+
Unit,
|
|
12
|
+
} from '../../types/number-formatting.types';
|
|
13
|
+
import { initGlobalize } from '../globalize-Initializer/globalize-utils';
|
|
14
|
+
import * as globalizeUtils from '../globalize-Initializer/globalize-utils';
|
|
15
|
+
import {
|
|
16
|
+
formatCurrencyWithCustomPattern,
|
|
17
|
+
getFormattedValue,
|
|
18
|
+
} from './number-formatting';
|
|
19
|
+
|
|
20
|
+
jest.mock('../../main/util', () => ({
|
|
21
|
+
getQueryParam: jest.fn().mockReturnValue('true'),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe('formatCurrencyWithCustomPattern', () => {
|
|
25
|
+
beforeAll(() => {
|
|
26
|
+
initGlobalize('en-US');
|
|
27
|
+
});
|
|
28
|
+
test('formats value with a custom pattern and currency symbol', () => {
|
|
29
|
+
const value = 12345.678;
|
|
30
|
+
const currencyCode = 'USD';
|
|
31
|
+
const formatPattern = '#,##0.00';
|
|
32
|
+
|
|
33
|
+
const result = formatCurrencyWithCustomPattern(
|
|
34
|
+
value,
|
|
35
|
+
currencyCode,
|
|
36
|
+
formatPattern,
|
|
37
|
+
);
|
|
38
|
+
expect(result).toBe('$12,345.68');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('getFormattedValue', () => {
|
|
43
|
+
beforeAll(() => {
|
|
44
|
+
initGlobalize('en-US');
|
|
45
|
+
});
|
|
46
|
+
const columnFormatConfig: ColumnFormat = {
|
|
47
|
+
type: FormatType.CURRENCY,
|
|
48
|
+
currencyFormat: {
|
|
49
|
+
type: CurrencyFormatType.ISO_CODE,
|
|
50
|
+
column: '',
|
|
51
|
+
isoCode: 'USD',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
jest.clearAllMocks();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('uses Number Fromatting as default configuration when formatConfigProp & columnFormatConfig are null', () => {
|
|
60
|
+
const result = getFormattedValue(12345, {}, {} as ColumnFormat);
|
|
61
|
+
expect(result).toBe('12.35K');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('use Custom Formatting as default configuration when only formatConfigProp is null', () => {
|
|
65
|
+
const result = getFormattedValue(12345, {}, columnFormatConfig);
|
|
66
|
+
expect(result).toBe('$12.35K');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('formats special values (NaN, Infinity)', () => {
|
|
70
|
+
expect(getFormattedValue(NaN, {}, {} as ColumnFormat)).toBe('NaN');
|
|
71
|
+
expect(getFormattedValue(Infinity, {}, {} as ColumnFormat)).toBe(
|
|
72
|
+
'Infinity',
|
|
73
|
+
);
|
|
74
|
+
expect(getFormattedValue(-Infinity, {}, {} as ColumnFormat)).toBe(
|
|
75
|
+
'-Infinity',
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('formats number category values with units and trailing zeroes', () => {
|
|
80
|
+
const formatConfig: FormatConfig = {
|
|
81
|
+
category: CategoryType.Number,
|
|
82
|
+
numberFormatConfig: {
|
|
83
|
+
unit: Unit.Thousands,
|
|
84
|
+
decimals: 2,
|
|
85
|
+
toSeparateThousands: true,
|
|
86
|
+
negativeValueFormat: NegativeValueFormat.PrefixDash,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const result = getFormattedValue(
|
|
90
|
+
-12345,
|
|
91
|
+
formatConfig,
|
|
92
|
+
{} as ColumnFormat,
|
|
93
|
+
);
|
|
94
|
+
expect(result).toBe('-12.35K'); // Update based on actual expected output
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('formats percentage values correctly', () => {
|
|
98
|
+
const formatConfig: FormatConfig = {
|
|
99
|
+
category: CategoryType.Percentage,
|
|
100
|
+
percentageFormatConfig: { decimals: 2, removeTrailingZeroes: true },
|
|
101
|
+
};
|
|
102
|
+
const result = getFormattedValue(
|
|
103
|
+
0.1234,
|
|
104
|
+
formatConfig,
|
|
105
|
+
{} as ColumnFormat,
|
|
106
|
+
);
|
|
107
|
+
expect(result).toBe('12.34%');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('formats currency values with compact units', () => {
|
|
111
|
+
const formatConfig: FormatConfig = {
|
|
112
|
+
category: CategoryType.Currency,
|
|
113
|
+
currencyFormatConfig: {
|
|
114
|
+
locale: 'USD',
|
|
115
|
+
toSeparateThousands: true,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
const result = getFormattedValue(
|
|
119
|
+
12345.678,
|
|
120
|
+
formatConfig,
|
|
121
|
+
{} as ColumnFormat,
|
|
122
|
+
);
|
|
123
|
+
// eslint-disable-next-line security/detect-unsafe-regex
|
|
124
|
+
expect(result).toMatch(/^\$\d+,\d+(\.\d+)?$/); // Update expected value
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('formats currency values with user Locale', () => {
|
|
128
|
+
jest.spyOn(
|
|
129
|
+
globalizeUtils,
|
|
130
|
+
'getDefaultCurrencyCode',
|
|
131
|
+
).mockImplementationOnce(() => 'INR');
|
|
132
|
+
const formatConfig: FormatConfig = {
|
|
133
|
+
category: CategoryType.Currency,
|
|
134
|
+
currencyFormatConfig: {
|
|
135
|
+
unit: Unit.Thousands,
|
|
136
|
+
decimals: 2,
|
|
137
|
+
locale: CurrencyFormatType.USER_LOCALE,
|
|
138
|
+
toSeparateThousands: true,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
const result = getFormattedValue(
|
|
142
|
+
12345.678,
|
|
143
|
+
formatConfig,
|
|
144
|
+
{} as ColumnFormat,
|
|
145
|
+
);
|
|
146
|
+
expect(result).toMatch('₹12.35K'); // Update expected value
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('should normalize category to CategoryType.Number when it is a number', () => {
|
|
150
|
+
// Mock formatConfig with category as a number
|
|
151
|
+
const formatConfig: FormatConfig = {
|
|
152
|
+
category: 12345 as any,
|
|
153
|
+
numberFormatConfig: {
|
|
154
|
+
unit: Unit.Thousands,
|
|
155
|
+
decimals: 2,
|
|
156
|
+
toSeparateThousands: true,
|
|
157
|
+
negativeValueFormat: NegativeValueFormat.PrefixDash,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const result = getFormattedValue(
|
|
162
|
+
1000,
|
|
163
|
+
formatConfig,
|
|
164
|
+
{} as ColumnFormat,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
expect(result).toBe('1.00K'); // Update based on actual expected output
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('falls back to default formatting for unsupported categories', () => {
|
|
171
|
+
const formatConfig: FormatConfig = { category: 'Unknown' as any };
|
|
172
|
+
const result = getFormattedValue(
|
|
173
|
+
12345.678,
|
|
174
|
+
formatConfig,
|
|
175
|
+
{} as ColumnFormat,
|
|
176
|
+
);
|
|
177
|
+
expect(result).toBe('12,345.678'); // Update based on fallback behavior
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('formats custom pattern correctly', () => {
|
|
181
|
+
const formatConfig: FormatConfig = {
|
|
182
|
+
category: CategoryType.Custom,
|
|
183
|
+
customFormatConfig: { format: '#,##0.00' },
|
|
184
|
+
};
|
|
185
|
+
const result = getFormattedValue(
|
|
186
|
+
12345.678,
|
|
187
|
+
formatConfig,
|
|
188
|
+
{} as ColumnFormat,
|
|
189
|
+
);
|
|
190
|
+
expect(result).toBe('12,345.68'); // Update based on actual output
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('formats custom pattern with valid customColumn format', () => {
|
|
194
|
+
const formatConfig: FormatConfig = {
|
|
195
|
+
category: CategoryType.Custom,
|
|
196
|
+
customFormatConfig: { format: '#,##0.00' },
|
|
197
|
+
};
|
|
198
|
+
const result = getFormattedValue(
|
|
199
|
+
12345.678,
|
|
200
|
+
formatConfig,
|
|
201
|
+
columnFormatConfig,
|
|
202
|
+
);
|
|
203
|
+
expect(result).toBe('$12,345.68'); // Update based on actual output
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('logs error for invalid custom format patterns', () => {
|
|
207
|
+
const formatConfig: FormatConfig = {
|
|
208
|
+
category: CategoryType.Custom,
|
|
209
|
+
customFormatConfig: { format: 'invalid-format' },
|
|
210
|
+
};
|
|
211
|
+
const error = jest.fn();
|
|
212
|
+
logMethods[LogLevel.ERROR] = error;
|
|
213
|
+
const logger = create('TestLogger');
|
|
214
|
+
logger.error = error;
|
|
215
|
+
|
|
216
|
+
getFormattedValue(12345.678, formatConfig, {} as ColumnFormat);
|
|
217
|
+
expect(logger.error).toHaveBeenCalled();
|
|
218
|
+
|
|
219
|
+
jest.restoreAllMocks();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('handles currency formatting failure', () => {
|
|
223
|
+
const formatConfig: FormatConfig = {
|
|
224
|
+
category: CategoryType.Currency,
|
|
225
|
+
};
|
|
226
|
+
jest.spyOn(
|
|
227
|
+
globalizeUtils,
|
|
228
|
+
'globalizeCurrencyFormatter',
|
|
229
|
+
).mockImplementationOnce(() => {
|
|
230
|
+
throw new Error('Currency format error');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const error = jest.fn();
|
|
234
|
+
logMethods[LogLevel.ERROR] = error;
|
|
235
|
+
const logger = create('TestLogger');
|
|
236
|
+
logger.error = error;
|
|
237
|
+
|
|
238
|
+
getFormattedValue(12345.678, formatConfig, {} as ColumnFormat);
|
|
239
|
+
expect(logger.error).toHaveBeenCalled();
|
|
240
|
+
|
|
241
|
+
jest.restoreAllMocks();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file: Number Formatting Utils
|
|
3
|
+
*
|
|
4
|
+
* @author Yashvardhan Nehra <yashvardhan.nehra@thoughtspot.com>
|
|
5
|
+
*
|
|
6
|
+
* Copyright: ThoughtSpot Inc. 2024
|
|
7
|
+
*/
|
|
8
|
+
import _ from 'lodash';
|
|
9
|
+
import { create } from '../../main/logger';
|
|
10
|
+
import {
|
|
11
|
+
ColumnFormat,
|
|
12
|
+
CurrencyFormat,
|
|
13
|
+
CurrencyFormatType,
|
|
14
|
+
} from '../../types/answer-column.types';
|
|
15
|
+
import {
|
|
16
|
+
CategoryType,
|
|
17
|
+
FormatConfig,
|
|
18
|
+
Unit,
|
|
19
|
+
} from '../../types/number-formatting.types';
|
|
20
|
+
import {
|
|
21
|
+
formatNumberSafely,
|
|
22
|
+
getDefaultCurrencyCode,
|
|
23
|
+
globalizeCurrencyFormatter,
|
|
24
|
+
globalizeNumberFormatter,
|
|
25
|
+
sanitizeFormat,
|
|
26
|
+
validateNumberFormat,
|
|
27
|
+
} from '../globalize-Initializer/globalize-utils';
|
|
28
|
+
import {
|
|
29
|
+
defaultFormatConfig,
|
|
30
|
+
formatNegativeValue,
|
|
31
|
+
formatSpecialDataValue,
|
|
32
|
+
getLocaleName,
|
|
33
|
+
mapFormatterConfig,
|
|
34
|
+
UNITS_TO_DIVIDING_FACTOR,
|
|
35
|
+
UNITS_TO_SUFFIX,
|
|
36
|
+
} from './number-formatting-utils';
|
|
37
|
+
|
|
38
|
+
const logger = create('number-formatting');
|
|
39
|
+
|
|
40
|
+
const CURRENCY_CODE_EXTRACTOR_REGEX = /[\d.,]+/g;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Formats a number with a custom pattern and combines it with a currency symbol.
|
|
44
|
+
*
|
|
45
|
+
* @param value - The numeric value to format.
|
|
46
|
+
* @param currencyCode - The ISO currency code (e.g., 'USD').
|
|
47
|
+
* @param formatPattern - The custom format pattern (e.g., '#,##0.00').
|
|
48
|
+
* @returns The formatted value with the currency symbol.
|
|
49
|
+
*/
|
|
50
|
+
export const formatCurrencyWithCustomPattern = (
|
|
51
|
+
value: number,
|
|
52
|
+
currencyCode: string,
|
|
53
|
+
formatPattern: string,
|
|
54
|
+
): string => {
|
|
55
|
+
// Sanitize the custom format pattern
|
|
56
|
+
const sanitizedPattern = sanitizeFormat(formatPattern);
|
|
57
|
+
|
|
58
|
+
// Create a custom number formatter
|
|
59
|
+
const customFormatter = globalizeNumberFormatter({
|
|
60
|
+
...({ raw: sanitizedPattern } as any),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const formattedValue = customFormatter(value);
|
|
64
|
+
|
|
65
|
+
const currencyFormatter = globalizeCurrencyFormatter(currencyCode, {
|
|
66
|
+
style: 'symbol',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const currencySymbol = currencyFormatter(0).replace(
|
|
70
|
+
CURRENCY_CODE_EXTRACTOR_REGEX,
|
|
71
|
+
'',
|
|
72
|
+
); // Extract the currency symbol
|
|
73
|
+
|
|
74
|
+
// Combine the custom formatted value with the currency symbol
|
|
75
|
+
return `${currencySymbol}${formattedValue}`;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Formats a value based on the provided format configuration and column settings.
|
|
80
|
+
*
|
|
81
|
+
* @param value - The value to format (can be a string or number).
|
|
82
|
+
* @param formatConfigProp - The format configuration for the value.
|
|
83
|
+
* @param columnFormatConfig - The column-specific formatting settings.
|
|
84
|
+
* @returns The formatted value as a string.
|
|
85
|
+
*/
|
|
86
|
+
export const getFormattedValue = (
|
|
87
|
+
value: string | number,
|
|
88
|
+
formatConfigProp: FormatConfig,
|
|
89
|
+
columnFormatConfig: ColumnFormat,
|
|
90
|
+
): string => {
|
|
91
|
+
let formatConfig = _.cloneDeep(formatConfigProp);
|
|
92
|
+
|
|
93
|
+
// Use default configuration if none is provided
|
|
94
|
+
if (_.isNil(formatConfig) || _.isEmpty(formatConfig)) {
|
|
95
|
+
formatConfig = defaultFormatConfig(columnFormatConfig);
|
|
96
|
+
}
|
|
97
|
+
// Normalize category to a proper type
|
|
98
|
+
if (typeof formatConfig.category === 'number') {
|
|
99
|
+
formatConfig.category = CategoryType.Number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Handle special data values (e.g., NaN, Infinity)
|
|
103
|
+
const specialVal = formatSpecialDataValue(value);
|
|
104
|
+
if (specialVal) {
|
|
105
|
+
return specialVal;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Convert value to a float
|
|
109
|
+
const floatValue = parseFloat(value.toString());
|
|
110
|
+
const absFloatValue = Math.abs(floatValue);
|
|
111
|
+
|
|
112
|
+
switch (formatConfig.category) {
|
|
113
|
+
case CategoryType.Number: {
|
|
114
|
+
const configDetails = formatConfig.numberFormatConfig || {};
|
|
115
|
+
const formatterConfigMap = mapFormatterConfig(
|
|
116
|
+
absFloatValue,
|
|
117
|
+
configDetails,
|
|
118
|
+
);
|
|
119
|
+
const compactValue =
|
|
120
|
+
absFloatValue /
|
|
121
|
+
UNITS_TO_DIVIDING_FACTOR[
|
|
122
|
+
formatterConfigMap.unitDetails as Unit
|
|
123
|
+
];
|
|
124
|
+
const suffix = UNITS_TO_SUFFIX[formatterConfigMap.unitDetails];
|
|
125
|
+
const formattedValue = formatNumberSafely(
|
|
126
|
+
{
|
|
127
|
+
style: 'decimal',
|
|
128
|
+
maximumFractionDigits: formatterConfigMap.decimalDetails,
|
|
129
|
+
minimumFractionDigits:
|
|
130
|
+
formatterConfigMap.shouldRemoveTrailingZeros
|
|
131
|
+
? 0
|
|
132
|
+
: formatterConfigMap.decimalDetails,
|
|
133
|
+
useGrouping: configDetails.toSeparateThousands || false,
|
|
134
|
+
},
|
|
135
|
+
compactValue,
|
|
136
|
+
);
|
|
137
|
+
const absFormattedValue = `${formattedValue}${suffix}`;
|
|
138
|
+
|
|
139
|
+
if (absFloatValue !== floatValue) {
|
|
140
|
+
return formatNegativeValue(
|
|
141
|
+
absFormattedValue,
|
|
142
|
+
configDetails.negativeValueFormat,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return absFormattedValue;
|
|
146
|
+
}
|
|
147
|
+
case CategoryType.Percentage: {
|
|
148
|
+
const configDetails = formatConfig.percentageFormatConfig;
|
|
149
|
+
const decimalDetails = configDetails?.decimals || 0;
|
|
150
|
+
return formatNumberSafely(
|
|
151
|
+
{
|
|
152
|
+
style: 'percent',
|
|
153
|
+
maximumFractionDigits: decimalDetails,
|
|
154
|
+
minimumFractionDigits: configDetails?.removeTrailingZeroes
|
|
155
|
+
? 0
|
|
156
|
+
: decimalDetails,
|
|
157
|
+
},
|
|
158
|
+
floatValue,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
case CategoryType.Currency: {
|
|
162
|
+
const configDetails = formatConfig.currencyFormatConfig || {};
|
|
163
|
+
const formatterConfigMap = mapFormatterConfig(
|
|
164
|
+
absFloatValue,
|
|
165
|
+
configDetails,
|
|
166
|
+
);
|
|
167
|
+
const compactValue =
|
|
168
|
+
floatValue /
|
|
169
|
+
UNITS_TO_DIVIDING_FACTOR[formatterConfigMap.unitDetails];
|
|
170
|
+
|
|
171
|
+
let locale = configDetails.locale || getDefaultCurrencyCode();
|
|
172
|
+
if (locale === CurrencyFormatType.USER_LOCALE) {
|
|
173
|
+
locale = getLocaleName({
|
|
174
|
+
type: locale,
|
|
175
|
+
} as CurrencyFormat);
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const formatter = globalizeCurrencyFormatter(locale, {
|
|
179
|
+
style: 'symbol',
|
|
180
|
+
maximumFractionDigits: formatterConfigMap.decimalDetails,
|
|
181
|
+
minimumFractionDigits:
|
|
182
|
+
formatterConfigMap.shouldRemoveTrailingZeros
|
|
183
|
+
? 0
|
|
184
|
+
: formatterConfigMap.decimalDetails,
|
|
185
|
+
useGrouping: configDetails.toSeparateThousands || false,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const formattedValue = formatter(compactValue);
|
|
189
|
+
const currencyCode = formattedValue.replace(
|
|
190
|
+
CURRENCY_CODE_EXTRACTOR_REGEX,
|
|
191
|
+
'',
|
|
192
|
+
);
|
|
193
|
+
/**
|
|
194
|
+
* When we format the value in Millions, Billions etc,
|
|
195
|
+
* we have to handle the scenario of the unit suffix, currencycode and the formatted
|
|
196
|
+
* value We test the presence of numeric character with the help of regex and then
|
|
197
|
+
* we append the apt suffix and currencyCode to the formatted value
|
|
198
|
+
*/
|
|
199
|
+
const suffix = !_.isNil(floatValue)
|
|
200
|
+
? UNITS_TO_SUFFIX[formatterConfigMap.unitDetails]
|
|
201
|
+
: '';
|
|
202
|
+
if (/[0-9]$/.test(formattedValue.charAt(0))) {
|
|
203
|
+
return `${formattedValue.replace(
|
|
204
|
+
currencyCode,
|
|
205
|
+
'',
|
|
206
|
+
)}${suffix}${currencyCode}`;
|
|
207
|
+
}
|
|
208
|
+
return `${formattedValue}${suffix}`;
|
|
209
|
+
} catch (e) {
|
|
210
|
+
logger.error(
|
|
211
|
+
'Corrupted format config passed, formatting using default config',
|
|
212
|
+
formatConfig,
|
|
213
|
+
e,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case CategoryType.Custom: {
|
|
219
|
+
const formatPattern = formatConfig.customFormatConfig?.format;
|
|
220
|
+
const currencyCode = columnFormatConfig.currencyFormat?.isoCode;
|
|
221
|
+
if (
|
|
222
|
+
!_.isNil(formatPattern) &&
|
|
223
|
+
validateNumberFormat(sanitizeFormat(formatPattern))
|
|
224
|
+
) {
|
|
225
|
+
const sanitizedPattern = sanitizeFormat(formatPattern);
|
|
226
|
+
if (!_.isNil(currencyCode)) {
|
|
227
|
+
/**
|
|
228
|
+
* Globalize does not consider format pattern while formatting the currency,
|
|
229
|
+
* only the currency specific rules are considered, so need to combine. That's
|
|
230
|
+
* what we do in this formatCurrency method
|
|
231
|
+
*/
|
|
232
|
+
const formattedValueWithLocaleAndPattern =
|
|
233
|
+
formatCurrencyWithCustomPattern(
|
|
234
|
+
floatValue,
|
|
235
|
+
currencyCode,
|
|
236
|
+
formatPattern,
|
|
237
|
+
);
|
|
238
|
+
return `${formattedValueWithLocaleAndPattern}`;
|
|
239
|
+
}
|
|
240
|
+
return formatNumberSafely(
|
|
241
|
+
{
|
|
242
|
+
style: 'decimal',
|
|
243
|
+
raw: sanitizedPattern,
|
|
244
|
+
},
|
|
245
|
+
floatValue as any,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
logger.error('Invalid custom format config passed:', formatConfig);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
default: {
|
|
252
|
+
return formatNumberSafely(
|
|
253
|
+
{
|
|
254
|
+
style: 'decimal',
|
|
255
|
+
},
|
|
256
|
+
floatValue as any,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Default fallback: Decimal formatting
|
|
262
|
+
return formatNumberSafely(
|
|
263
|
+
{
|
|
264
|
+
style: 'decimal',
|
|
265
|
+
},
|
|
266
|
+
floatValue as any,
|
|
267
|
+
);
|
|
268
|
+
};
|