@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.
Files changed (54) hide show
  1. package/dist/ts-chart-sdk.d.ts +65 -5
  2. package/lib/index.d.ts +2 -0
  3. package/lib/index.d.ts.map +1 -1
  4. package/lib/index.js +2 -0
  5. package/lib/index.js.map +1 -1
  6. package/lib/main/custom-chart-context.d.ts.map +1 -1
  7. package/lib/main/custom-chart-context.js +3 -1
  8. package/lib/main/custom-chart-context.js.map +1 -1
  9. package/lib/types/answer-column.types.d.ts +3 -3
  10. package/lib/types/answer-column.types.d.ts.map +1 -1
  11. package/lib/types/answer-column.types.js +3 -3
  12. package/lib/types/answer-column.types.js.map +1 -1
  13. package/lib/types/number-formatting.types.d.ts +2 -2
  14. package/lib/types/number-formatting.types.d.ts.map +1 -1
  15. package/lib/types/visual-prop.types.d.ts +40 -0
  16. package/lib/types/visual-prop.types.d.ts.map +1 -1
  17. package/lib/types/visual-prop.types.js +32 -1
  18. package/lib/types/visual-prop.types.js.map +1 -1
  19. package/lib/utils/globalize-Initializer/globalize-utils.d.ts +16 -0
  20. package/lib/utils/globalize-Initializer/globalize-utils.d.ts.map +1 -0
  21. package/lib/utils/globalize-Initializer/globalize-utils.js +101 -0
  22. package/lib/utils/globalize-Initializer/globalize-utils.js.map +1 -0
  23. package/lib/utils/globalize-Initializer/globalize-utils.spec.d.ts +2 -0
  24. package/lib/utils/globalize-Initializer/globalize-utils.spec.d.ts.map +1 -0
  25. package/lib/utils/globalize-Initializer/globalize-utils.spec.js +149 -0
  26. package/lib/utils/globalize-Initializer/globalize-utils.spec.js.map +1 -0
  27. package/lib/utils/number-formatting/number-formatting-utils.d.ts +33 -0
  28. package/lib/utils/number-formatting/number-formatting-utils.d.ts.map +1 -0
  29. package/lib/utils/number-formatting/number-formatting-utils.js +180 -0
  30. package/lib/utils/number-formatting/number-formatting-utils.js.map +1 -0
  31. package/lib/utils/number-formatting/number-formatting-utils.spec.d.ts +2 -0
  32. package/lib/utils/number-formatting/number-formatting-utils.spec.d.ts.map +1 -0
  33. package/lib/utils/number-formatting/number-formatting-utils.spec.js +235 -0
  34. package/lib/utils/number-formatting/number-formatting-utils.spec.js.map +1 -0
  35. package/lib/utils/number-formatting/number-formatting.d.ts +5 -0
  36. package/lib/utils/number-formatting/number-formatting.d.ts.map +1 -0
  37. package/lib/utils/number-formatting/number-formatting.js +131 -0
  38. package/lib/utils/number-formatting/number-formatting.js.map +1 -0
  39. package/lib/utils/number-formatting/number-formatting.spec.d.ts +2 -0
  40. package/lib/utils/number-formatting/number-formatting.spec.d.ts.map +1 -0
  41. package/lib/utils/number-formatting/number-formatting.spec.js +159 -0
  42. package/lib/utils/number-formatting/number-formatting.spec.js.map +1 -0
  43. package/package.json +4 -1
  44. package/src/index.ts +2 -0
  45. package/src/main/custom-chart-context.ts +4 -1
  46. package/src/types/answer-column.types.ts +3 -3
  47. package/src/types/number-formatting.types.ts +2 -2
  48. package/src/types/visual-prop.types.ts +86 -0
  49. package/src/utils/globalize-Initializer/globalize-utils.spec.ts +192 -0
  50. package/src/utils/globalize-Initializer/globalize-utils.ts +216 -0
  51. package/src/utils/number-formatting/number-formatting-utils.spec.ts +321 -0
  52. package/src/utils/number-formatting/number-formatting-utils.ts +288 -0
  53. package/src/utils/number-formatting/number-formatting.spec.ts +243 -0
  54. package/src/utils/number-formatting/number-formatting.ts +268 -0
@@ -0,0 +1,321 @@
1
+ import {
2
+ ColumnFormat,
3
+ CurrencyFormat,
4
+ CurrencyFormatType,
5
+ FormatType,
6
+ } from '../../types/answer-column.types';
7
+ import {
8
+ CategoryType,
9
+ CurrencyFormatConfig,
10
+ NegativeValueFormat,
11
+ NumberFormatConfig,
12
+ Unit,
13
+ } from '../../types/number-formatting.types';
14
+ import * as globalizeUtils from '../globalize-Initializer/globalize-utils';
15
+ import {
16
+ defaultFormatConfig,
17
+ formatNegativeValue,
18
+ formatSpecialDataValue,
19
+ getAutoUnit,
20
+ getLocaleBasedStringFormats,
21
+ getLocaleName,
22
+ mapFormatterConfig,
23
+ PROTO_TO_UNITS,
24
+ setLocaleBasedStringFormats,
25
+ UNITS_TO_DIVIDING_FACTOR,
26
+ } from './number-formatting-utils';
27
+
28
+ describe('formatNegativeValue', () => {
29
+ it('should format with prefix dash', () => {
30
+ const result = formatNegativeValue(
31
+ '100',
32
+ NegativeValueFormat.PrefixDash,
33
+ );
34
+ expect(result).toBe('-100');
35
+ });
36
+
37
+ it('should format with suffix dash', () => {
38
+ const result = formatNegativeValue(
39
+ '100',
40
+ NegativeValueFormat.SuffixDash,
41
+ );
42
+ expect(result).toBe('100-');
43
+ });
44
+
45
+ it('should format with braces without dash', () => {
46
+ const result = formatNegativeValue(
47
+ '100',
48
+ NegativeValueFormat.BracesNodash,
49
+ );
50
+ expect(result).toBe('(100)');
51
+ });
52
+
53
+ it('should default to prefix dash if no format is provided', () => {
54
+ const result = formatNegativeValue('100');
55
+ expect(result).toBe('-100');
56
+ });
57
+
58
+ it('should convert a numeric negative format to a string format', () => {
59
+ // Case where negativeFormat is a number
60
+ const negativeFormatNumber = 1; // For example, PrefixDash
61
+ const formattedValue = '100.25'; // some sample value to format
62
+
63
+ // Before mapping, negativeFormat should be a number
64
+ let result = formatNegativeValue(
65
+ formattedValue,
66
+ negativeFormatNumber as number,
67
+ );
68
+ expect(result).toBe('-100.25'); // PrefixDash adds a '-' before the value
69
+
70
+ // Case with a different number for negative format
71
+ const negativeFormatNumber2 = 2; // For example, SuffixDash
72
+ result = formatNegativeValue(formattedValue, negativeFormatNumber2);
73
+ expect(result).toBe('100.25-'); // SuffixDash adds a '-' after the value
74
+ });
75
+ });
76
+
77
+ describe('getAutoUnit', () => {
78
+ it('should return Trillion for values above 1 Trillion', () => {
79
+ const result = getAutoUnit(1e12);
80
+ expect(result).toBe(Unit.Trillion);
81
+ });
82
+
83
+ it('should return Billion for values above 1 Billion', () => {
84
+ const result = getAutoUnit(1e9);
85
+ expect(result).toBe(Unit.Billion);
86
+ });
87
+
88
+ it('should return Million for values above 1 Million', () => {
89
+ const result = getAutoUnit(1e6);
90
+ expect(result).toBe(Unit.Million);
91
+ });
92
+
93
+ it('should return Thousands for values above 1 Thousand', () => {
94
+ const result = getAutoUnit(1e3);
95
+ expect(result).toBe(Unit.Thousands);
96
+ });
97
+
98
+ it('should return None for values below 1000', () => {
99
+ const result = getAutoUnit(500);
100
+ expect(result).toBe(Unit.None);
101
+ });
102
+ });
103
+
104
+ describe('getLocaleName', () => {
105
+ it('should return default locale if currency format is not ISO_CODE', () => {
106
+ jest.spyOn(
107
+ globalizeUtils,
108
+ 'getDefaultCurrencyCode',
109
+ ).mockReturnValueOnce('GBP');
110
+ const currencyFormat: CurrencyFormat = {
111
+ type: CurrencyFormatType.USER_LOCALE,
112
+ column: '',
113
+ isoCode: '',
114
+ };
115
+ const result = getLocaleName(currencyFormat);
116
+ expect(result).toBe('GBP'); // Default locale
117
+ });
118
+
119
+ it('should return ISO code if currency format is ISO_CODE', () => {
120
+ const currencyFormat: CurrencyFormat = {
121
+ type: CurrencyFormatType.ISO_CODE,
122
+ column: '',
123
+ isoCode: 'USD',
124
+ };
125
+ const result = getLocaleName(currencyFormat);
126
+ expect(result).toBe('USD');
127
+ });
128
+ });
129
+
130
+ describe('defaultFormatConfig', () => {
131
+ it('should return custom format config for a pattern', () => {
132
+ const columnFormatConfig: ColumnFormat = {
133
+ type: FormatType.PATTERN,
134
+ pattern: '###,###',
135
+ };
136
+ const result = defaultFormatConfig(columnFormatConfig);
137
+ expect(result.category).toBe(CategoryType.Custom);
138
+ expect(result.customFormatConfig?.format).toBe('###,###');
139
+ });
140
+
141
+ it('should return currency format config if currencyFormat is defined', () => {
142
+ const columnFormatConfig: ColumnFormat = {
143
+ type: FormatType.CURRENCY,
144
+ currencyFormat: {
145
+ type: CurrencyFormatType.ISO_CODE,
146
+ column: '',
147
+ isoCode: 'USD',
148
+ },
149
+ };
150
+ const result = defaultFormatConfig(columnFormatConfig);
151
+ expect(result.category).toBe(CategoryType.Currency);
152
+ expect(result.currencyFormatConfig?.locale).toBe('USD');
153
+ });
154
+
155
+ it('should return number format config by default', () => {
156
+ const result = defaultFormatConfig();
157
+ expect(result.category).toBe(CategoryType.Number);
158
+ expect(result.numberFormatConfig?.decimals).toBe(0);
159
+ });
160
+ });
161
+
162
+ describe('mapFormatterConfig', () => {
163
+ it('should return correct formatter config for a number format', () => {
164
+ const absFloatValue = 1000;
165
+ const configDetails: NumberFormatConfig = {
166
+ decimals: 2,
167
+ negativeValueFormat: NegativeValueFormat.PrefixDash,
168
+ removeTrailingZeroes: false,
169
+ toSeparateThousands: true,
170
+ unit: Unit.Million,
171
+ };
172
+
173
+ const result = mapFormatterConfig(absFloatValue, configDetails);
174
+ expect(result.unitDetails).toBe(Unit.Million); // Unit: Million
175
+ expect(result.decimalDetails).toBe(2);
176
+ expect(result.shouldRemoveTrailingZeros).toBe(false);
177
+ });
178
+
179
+ it('should return auto unit and decimal precision for large value', () => {
180
+ const absFloatValue = 1e9; // 1 Billion
181
+ const configDetails: NumberFormatConfig = {
182
+ decimals: 0,
183
+ negativeValueFormat: NegativeValueFormat.PrefixDash,
184
+ removeTrailingZeroes: false,
185
+ toSeparateThousands: true,
186
+ unit: Unit.Auto, // Auto
187
+ };
188
+
189
+ const result = mapFormatterConfig(absFloatValue, configDetails);
190
+ expect(result.unitDetails).toBe(Unit.Billion);
191
+ expect(result.decimalDetails).toBe(2); // Default decimal precision for auto
192
+ });
193
+
194
+ it('should return correct config for currency formatting', () => {
195
+ const absFloatValue = 1000;
196
+ const configDetails: CurrencyFormatConfig = {
197
+ decimals: 2,
198
+ locale: 'en',
199
+ removeTrailingZeroes: false,
200
+ toSeparateThousands: true,
201
+ unit: Unit.Auto,
202
+ };
203
+
204
+ const result = mapFormatterConfig(absFloatValue, configDetails);
205
+ expect(result.unitDetails).toBe(Unit.Thousands); // Automatically chosen unit
206
+ expect(result.decimalDetails).toBe(2);
207
+ });
208
+
209
+ it('should correctly map valid unit numbers to the corresponding unit', () => {
210
+ const input = { unit: 1 };
211
+ const result = mapFormatterConfig(1000, input);
212
+
213
+ // Assert that the unit has been correctly mapped to the corresponding
214
+ // value in PROTO_TO_UNITS
215
+ expect(result.unitDetails).toBe(PROTO_TO_UNITS[1]);
216
+ });
217
+ });
218
+
219
+ describe('formatSpecialDataValue', () => {
220
+ it('should return {Null} for {Null} values', () => {
221
+ const result = formatSpecialDataValue('{Null}');
222
+ expect(result).toBe('{Null}');
223
+ });
224
+
225
+ it('should return {Null} for null values', () => {
226
+ const result = formatSpecialDataValue(null);
227
+ expect(result).toBe('{Null}');
228
+ });
229
+
230
+ it('should return {Empty} for empty string', () => {
231
+ const result = formatSpecialDataValue('');
232
+ expect(result).toBe('{Empty}');
233
+ });
234
+
235
+ it('should return {Null} for undefined values', () => {
236
+ const result = formatSpecialDataValue(undefined);
237
+ expect(result).toBe('{Null}');
238
+ });
239
+
240
+ it('should return NaN for string "NaN"', () => {
241
+ const result = formatSpecialDataValue(['NaN']);
242
+ expect(result).toBe('NaN');
243
+ });
244
+
245
+ it('should return {Null} for empty array', () => {
246
+ const result = formatSpecialDataValue([]);
247
+ expect(result).toBe('{Null}');
248
+ });
249
+
250
+ it('should return Null for array', () => {
251
+ const result = formatSpecialDataValue(['Test']);
252
+ expect(result).toBe(null);
253
+ });
254
+
255
+ it('should return Infinity for string "Infinity"', () => {
256
+ const result = formatSpecialDataValue(['Infinity']);
257
+ expect(result).toBe('Infinity');
258
+ });
259
+
260
+ it('should return null for non-special values', () => {
261
+ const result = formatSpecialDataValue('Test');
262
+ expect(result).toBeNull();
263
+ });
264
+
265
+ it('should return Infinity for Infinity', () => {
266
+ const result = formatSpecialDataValue(Infinity);
267
+ expect(result).toBe('Infinity');
268
+ });
269
+ });
270
+
271
+ describe('Unit constants', () => {
272
+ it('should map unit to correct dividing factor', () => {
273
+ expect(UNITS_TO_DIVIDING_FACTOR[Unit.Thousands]).toBe(1000);
274
+ expect(UNITS_TO_DIVIDING_FACTOR[Unit.Million]).toBe(1000000);
275
+ expect(UNITS_TO_DIVIDING_FACTOR[Unit.Billion]).toBe(1000000000);
276
+ });
277
+
278
+ it('should map proto to correct unit', () => {
279
+ expect(PROTO_TO_UNITS[1]).toBe(Unit.None);
280
+ expect(PROTO_TO_UNITS[2]).toBe(Unit.Thousands);
281
+ expect(PROTO_TO_UNITS[3]).toBe(Unit.Million);
282
+ });
283
+ });
284
+
285
+ describe('Locale String Formats', () => {
286
+ const strings: Record<string, string> = {
287
+ NULL_VALUE_PLACEHOLDER_LABEL: '{Null}',
288
+ EMPTY_VALUE_PLACEHOLDER_LABEL: '{Empty}',
289
+ };
290
+
291
+ it('should return an default object if setLocaleBasedStringFormats is not called', () => {
292
+ const result = getLocaleBasedStringFormats();
293
+ expect(result).toEqual(strings);
294
+ });
295
+
296
+ it('should set and get locale-based string formats correctly', () => {
297
+ const testLocaleStrings = {
298
+ greeting: 'Hello',
299
+ farewell: 'Goodbye',
300
+ };
301
+
302
+ setLocaleBasedStringFormats(testLocaleStrings);
303
+
304
+ const result = getLocaleBasedStringFormats();
305
+
306
+ expect(result).toEqual(testLocaleStrings);
307
+ });
308
+
309
+ it('should update the string formats when setLocaleBasedStringFormats is called multiple times', () => {
310
+ const firstSet = { greeting: 'Hola' };
311
+ const secondSet = { greeting: 'Bonjour' };
312
+
313
+ // Set first locale-based string formats
314
+ setLocaleBasedStringFormats(firstSet);
315
+ expect(getLocaleBasedStringFormats()).toEqual(firstSet);
316
+
317
+ // Set second locale-based string formats
318
+ setLocaleBasedStringFormats(secondSet);
319
+ expect(getLocaleBasedStringFormats()).toEqual(secondSet);
320
+ });
321
+ });
@@ -0,0 +1,288 @@
1
+ /**
2
+ * @file: Formatting Utils
3
+ *
4
+ * @author Yashvardhan Nehra <yashvardhan.nehra@thoughtspot.com>
5
+ *
6
+ * Copyright: ThoughtSpot Inc. 2024
7
+ */
8
+
9
+ import _ from 'lodash';
10
+ import {
11
+ ColumnFormat,
12
+ CurrencyFormat,
13
+ CurrencyFormatType,
14
+ } from '../../types/answer-column.types';
15
+ import { Maybe } from '../../types/common.types';
16
+ import {
17
+ CategoryType,
18
+ CurrencyFormatConfig,
19
+ FormatConfig,
20
+ NegativeValueFormat,
21
+ NumberFormatConfig,
22
+ Unit,
23
+ } from '../../types/number-formatting.types';
24
+ import { getDefaultCurrencyCode } from '../globalize-Initializer/globalize-utils';
25
+
26
+ interface FormatterConfig {
27
+ unitDetails: Unit;
28
+ decimalDetails: number;
29
+ shouldRemoveTrailingZeros: boolean;
30
+ }
31
+
32
+ export const PROTO_TO_NEGATIVE_VALUE_FORMAT = {
33
+ 1: NegativeValueFormat.PrefixDash,
34
+ 2: NegativeValueFormat.SuffixDash,
35
+ 3: NegativeValueFormat.BracesNodash,
36
+ };
37
+
38
+ /**
39
+ * Constants for unit conversions and suffixes.
40
+ */
41
+ export const UNITS_TO_DIVIDING_FACTOR: Record<Unit, number> = {
42
+ [Unit.None]: 1,
43
+ [Unit.Thousands]: 1000,
44
+ [Unit.Million]: 1000 * 1000,
45
+ [Unit.Billion]: 1000 * 1000 * 1000,
46
+ [Unit.Trillion]: 1000 * 1000 * 1000 * 1000,
47
+ [Unit.Auto]: 1,
48
+ };
49
+
50
+ export const PROTO_TO_UNITS = {
51
+ 1: Unit.None,
52
+ 2: Unit.Thousands,
53
+ 3: Unit.Million,
54
+ 4: Unit.Billion,
55
+ 5: Unit.Trillion,
56
+ 6: Unit.Auto,
57
+ };
58
+
59
+ /**
60
+ * Default strings for placeholders.
61
+ */
62
+ let strings: Record<string, string> = {
63
+ NULL_VALUE_PLACEHOLDER_LABEL: '{Null}',
64
+ EMPTY_VALUE_PLACEHOLDER_LABEL: '{Empty}',
65
+ };
66
+
67
+ export const setLocaleBasedStringFormats = (
68
+ tsLocaleBasedStringsFormats?: Record<string, string>,
69
+ ) => {
70
+ if (tsLocaleBasedStringsFormats) {
71
+ strings = tsLocaleBasedStringsFormats;
72
+ }
73
+ };
74
+
75
+ export const getLocaleBasedStringFormats = () => strings;
76
+
77
+ export const UNITS_TO_SUFFIX: Record<Unit, string> = {
78
+ [Unit.None]: '',
79
+ [Unit.Thousands]: 'K',
80
+ [Unit.Million]: 'M',
81
+ [Unit.Billion]: 'B',
82
+ [Unit.Trillion]: 'T',
83
+ [Unit.Auto]: '',
84
+ };
85
+
86
+ const DEFAULT_DECIMAL_PRECISION = 2;
87
+
88
+ /**
89
+ * Formats negative values according to the specified format.
90
+ *
91
+ * @param formattedValue - The formatted value to modify.
92
+ * @param negativeFormat - The desired negative value format.
93
+ * @returns The formatted negative value.
94
+ */
95
+ export const formatNegativeValue = (
96
+ formattedValue: string,
97
+ negativeFormat?: Maybe<NegativeValueFormat> | number,
98
+ ): string => {
99
+ if (typeof negativeFormat === 'number') {
100
+ // eslint-disable-next-line no-param-reassign
101
+ negativeFormat =
102
+ PROTO_TO_NEGATIVE_VALUE_FORMAT[
103
+ negativeFormat as keyof typeof PROTO_TO_NEGATIVE_VALUE_FORMAT
104
+ ];
105
+ }
106
+ switch (negativeFormat) {
107
+ case NegativeValueFormat.PrefixDash:
108
+ return `-${formattedValue}`;
109
+ case NegativeValueFormat.SuffixDash:
110
+ return `${formattedValue}-`;
111
+ case NegativeValueFormat.BracesNodash:
112
+ return `(${formattedValue})`;
113
+ default:
114
+ return `-${formattedValue}`;
115
+ }
116
+ };
117
+
118
+ /**
119
+ * Determines the appropriate unit for auto-scaling values.
120
+ *
121
+ * @param value - The numeric value to evaluate.
122
+ * @returns The appropriate unit (e.g., Thousand, Million, etc.).
123
+ */
124
+ export const getAutoUnit = (value: number): Unit => {
125
+ if (value >= UNITS_TO_DIVIDING_FACTOR[Unit.Trillion]) {
126
+ return Unit.Trillion;
127
+ }
128
+ if (value >= UNITS_TO_DIVIDING_FACTOR[Unit.Billion]) {
129
+ return Unit.Billion;
130
+ }
131
+ if (value >= UNITS_TO_DIVIDING_FACTOR[Unit.Million]) {
132
+ return Unit.Million;
133
+ }
134
+ if (value >= UNITS_TO_DIVIDING_FACTOR[Unit.Thousands]) {
135
+ return Unit.Thousands;
136
+ }
137
+ return Unit.None;
138
+ };
139
+
140
+ /**
141
+ * Retrieves the locale name based on the provided currency format.
142
+ *
143
+ * @param currencyFormat - The currency format configuration.
144
+ * @returns The locale name as a string.
145
+ */
146
+ export const getLocaleName = (currencyFormat: CurrencyFormat): string => {
147
+ if (currencyFormat.type === CurrencyFormatType.ISO_CODE) {
148
+ return currencyFormat.isoCode;
149
+ }
150
+
151
+ const locale = getDefaultCurrencyCode();
152
+ return locale;
153
+ };
154
+
155
+ /**
156
+ * Generates a default format configuration based on the column settings.
157
+ *
158
+ * @param columnFormatConfig - The column-specific formatting settings.
159
+ * @returns The default format configuration.
160
+ */
161
+ export const defaultFormatConfig = (
162
+ columnFormatConfig?: ColumnFormat,
163
+ ): FormatConfig => {
164
+ if (columnFormatConfig?.pattern) {
165
+ return {
166
+ __typename: 'FormatConfig',
167
+ category: CategoryType.Custom,
168
+ isCategoryEditable: true,
169
+ customFormatConfig: {
170
+ __typename: 'CustomFormatConfig',
171
+ format: columnFormatConfig?.pattern,
172
+ },
173
+ };
174
+ }
175
+ if (columnFormatConfig?.currencyFormat) {
176
+ const locale = getLocaleName(columnFormatConfig?.currencyFormat);
177
+ const currencyFormatConfig: CurrencyFormatConfig = {
178
+ __typename: 'CurrencyFormatConfig',
179
+ decimals: 0,
180
+ locale,
181
+ removeTrailingZeroes: false,
182
+ toSeparateThousands: true,
183
+ unit: Unit.Auto,
184
+ };
185
+ const formatConfig: FormatConfig = {
186
+ __typename: 'FormatConfig',
187
+ category: CategoryType.Currency,
188
+ isCategoryEditable: true,
189
+ currencyFormatConfig,
190
+ };
191
+ return formatConfig;
192
+ }
193
+ const numberFormatConfig: NumberFormatConfig = {
194
+ __typename: 'NumberFormatConfig',
195
+ decimals: 0,
196
+ negativeValueFormat: NegativeValueFormat.PrefixDash,
197
+ removeTrailingZeroes: false,
198
+ toSeparateThousands: true,
199
+ unit: Unit.Auto,
200
+ };
201
+ const formatConfig: FormatConfig = {
202
+ __typename: 'FormatConfig',
203
+ category: CategoryType.Number,
204
+ isCategoryEditable: true,
205
+ numberFormatConfig,
206
+ percentageFormatConfig: null,
207
+ };
208
+ return formatConfig;
209
+ };
210
+
211
+ /**
212
+ * Maps configuration details to a formatter configuration object.
213
+ *
214
+ * @param absFloatValue - The absolute value of the number being formatted.
215
+ * @param configDetails - The number or currency format configuration.
216
+ * @returns A mapped formatter configuration.
217
+ */
218
+ export const mapFormatterConfig = (
219
+ absFloatValue: number,
220
+ configDetails: NumberFormatConfig | CurrencyFormatConfig,
221
+ ): FormatterConfig => {
222
+ if (typeof configDetails.unit === 'number') {
223
+ // eslint-disable-next-line no-param-reassign
224
+ configDetails.unit =
225
+ PROTO_TO_UNITS[configDetails.unit as keyof typeof PROTO_TO_UNITS];
226
+ }
227
+ const isAutoFormatted = configDetails.unit === Unit.Auto;
228
+ let unitDetails = configDetails.unit || Unit.Auto;
229
+ let decimalDetails = configDetails.decimals || 0;
230
+
231
+ if (isAutoFormatted) {
232
+ unitDetails = getAutoUnit(absFloatValue);
233
+ if (unitDetails !== Unit.None) {
234
+ decimalDetails = DEFAULT_DECIMAL_PRECISION;
235
+ }
236
+ }
237
+ const shouldRemoveTrailingZeros =
238
+ configDetails.removeTrailingZeroes || isAutoFormatted;
239
+ return {
240
+ unitDetails,
241
+ decimalDetails,
242
+ shouldRemoveTrailingZeros,
243
+ };
244
+ };
245
+
246
+ /**
247
+ * Handles formatting of special data values (e.g., null, empty, NaN, Infinity).
248
+ *
249
+ * @param value - The value to check and format.
250
+ * @returns A formatted special value or null if not applicable.
251
+ */
252
+ export function formatSpecialDataValue(value: any) {
253
+ if (
254
+ value === strings.NULL_VALUE_PLACEHOLDER_LABEL ||
255
+ value === strings.EMPTY_VALUE_PLACEHOLDER_LABEL
256
+ ) {
257
+ return value;
258
+ }
259
+
260
+ if (value === null || value === undefined) {
261
+ return strings.NULL_VALUE_PLACEHOLDER_LABEL;
262
+ }
263
+
264
+ if (value === Infinity || value === -Infinity || _.isNaN(value)) {
265
+ return value.toString();
266
+ }
267
+ // {Empty} placeholder is set for empty string or no characters
268
+ // other than spaces.
269
+ if (value === '') {
270
+ return strings.EMPTY_VALUE_PLACEHOLDER_LABEL;
271
+ }
272
+ if (value instanceof Array) {
273
+ if (_.isEmpty(value) || _.isNil(value[0])) {
274
+ return strings.NULL_VALUE_PLACEHOLDER_LABEL;
275
+ }
276
+
277
+ switch (value[0]) {
278
+ case 'NaN':
279
+ return 'NaN';
280
+ case 'Infinity':
281
+ return 'Infinity';
282
+ default:
283
+ return null;
284
+ }
285
+ }
286
+
287
+ return null;
288
+ }