@thoughtspot/ts-chart-sdk 0.0.2-alpha.24 → 0.0.2-alpha.26

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 (51) hide show
  1. package/dist/ts-chart-sdk.d.ts +63 -3
  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/visual-prop.types.d.ts +40 -0
  14. package/lib/types/visual-prop.types.d.ts.map +1 -1
  15. package/lib/types/visual-prop.types.js +32 -1
  16. package/lib/types/visual-prop.types.js.map +1 -1
  17. package/lib/utils/globalize-Initializer/globalize-utils.d.ts +16 -0
  18. package/lib/utils/globalize-Initializer/globalize-utils.d.ts.map +1 -0
  19. package/lib/utils/globalize-Initializer/globalize-utils.js +101 -0
  20. package/lib/utils/globalize-Initializer/globalize-utils.js.map +1 -0
  21. package/lib/utils/globalize-Initializer/globalize-utils.spec.d.ts +2 -0
  22. package/lib/utils/globalize-Initializer/globalize-utils.spec.d.ts.map +1 -0
  23. package/lib/utils/globalize-Initializer/globalize-utils.spec.js +149 -0
  24. package/lib/utils/globalize-Initializer/globalize-utils.spec.js.map +1 -0
  25. package/lib/utils/number-formatting/number-formatting-utils.d.ts +20 -0
  26. package/lib/utils/number-formatting/number-formatting-utils.d.ts.map +1 -0
  27. package/lib/utils/number-formatting/number-formatting-utils.js +159 -0
  28. package/lib/utils/number-formatting/number-formatting-utils.js.map +1 -0
  29. package/lib/utils/number-formatting/number-formatting-utils.spec.d.ts +2 -0
  30. package/lib/utils/number-formatting/number-formatting-utils.spec.d.ts.map +1 -0
  31. package/lib/utils/number-formatting/number-formatting-utils.spec.js +221 -0
  32. package/lib/utils/number-formatting/number-formatting-utils.spec.js.map +1 -0
  33. package/lib/utils/number-formatting/number-formatting.d.ts +5 -0
  34. package/lib/utils/number-formatting/number-formatting.d.ts.map +1 -0
  35. package/lib/utils/number-formatting/number-formatting.js +128 -0
  36. package/lib/utils/number-formatting/number-formatting.js.map +1 -0
  37. package/lib/utils/number-formatting/number-formatting.spec.d.ts +2 -0
  38. package/lib/utils/number-formatting/number-formatting.spec.d.ts.map +1 -0
  39. package/lib/utils/number-formatting/number-formatting.spec.js +159 -0
  40. package/lib/utils/number-formatting/number-formatting.spec.js.map +1 -0
  41. package/package.json +4 -1
  42. package/src/index.ts +2 -0
  43. package/src/main/custom-chart-context.ts +4 -1
  44. package/src/types/answer-column.types.ts +3 -3
  45. package/src/types/visual-prop.types.ts +86 -0
  46. package/src/utils/globalize-Initializer/globalize-utils.spec.ts +192 -0
  47. package/src/utils/globalize-Initializer/globalize-utils.ts +216 -0
  48. package/src/utils/number-formatting/number-formatting-utils.spec.ts +296 -0
  49. package/src/utils/number-formatting/number-formatting-utils.ts +260 -0
  50. package/src/utils/number-formatting/number-formatting.spec.ts +243 -0
  51. package/src/utils/number-formatting/number-formatting.ts +264 -0
@@ -0,0 +1,192 @@
1
+ import Globalize from 'globalize';
2
+ import {
3
+ formatNumberSafely,
4
+ getCountryCode,
5
+ getCurrentCurrencyFormat,
6
+ getDefaultCurrencyCode,
7
+ getGlobalizeLocale,
8
+ globalizeCurrencyFormatter,
9
+ globalizeNumberFormatter,
10
+ initGlobalize,
11
+ loadCurrencyData,
12
+ loadGlobalizeData,
13
+ sanitizeFormat,
14
+ setCurrentCurrencyFormat,
15
+ setGlobalizeLocale,
16
+ validateNumberFormat,
17
+ } from './globalize-utils';
18
+
19
+ describe('Initialize Globalize', () => {
20
+ beforeEach(() => {
21
+ jest.clearAllMocks();
22
+ initGlobalize('en-US');
23
+ });
24
+
25
+ describe('getCountryCode', () => {
26
+ it('should extract country code from locale with underscore', () => {
27
+ expect(getCountryCode('en_US')).toBe('US');
28
+ });
29
+
30
+ it('should extract country code from locale with hyphen', () => {
31
+ expect(getCountryCode('en-US')).toBe('US');
32
+ });
33
+
34
+ it('should return the input locale if no delimiter is found', () => {
35
+ expect(getCountryCode('en')).toBe('en');
36
+ });
37
+ });
38
+
39
+ describe('getDefaultCurrencyCode', () => {
40
+ it('should return the current currency format if set', () => {
41
+ setCurrentCurrencyFormat('EUR');
42
+ expect(getDefaultCurrencyCode()).toBe('EUR');
43
+ });
44
+
45
+ it('should return GBP if no currency data is found', () => {
46
+ setCurrentCurrencyFormat(null);
47
+ loadCurrencyData({});
48
+ expect(getDefaultCurrencyCode()).toBe('GBP');
49
+ });
50
+
51
+ it('should retrieve the default currency code from supplemental data', () => {
52
+ loadCurrencyData({
53
+ supplemental: {
54
+ currencyData: {
55
+ region: {
56
+ US: [{ USD: {} }],
57
+ },
58
+ },
59
+ },
60
+ });
61
+ setGlobalizeLocale('en-US');
62
+ expect(getDefaultCurrencyCode()).toBe('USD');
63
+ });
64
+ });
65
+
66
+ describe('setGlobalizeLocale', () => {
67
+ it('should set the Globalize locale', () => {
68
+ setGlobalizeLocale('fr-FR');
69
+ expect(getGlobalizeLocale()).toBe('fr-FR');
70
+ });
71
+ });
72
+
73
+ describe('getGlobalizeLocale', () => {
74
+ it('should return the current locale', () => {
75
+ expect(getGlobalizeLocale()).toBe('en-US');
76
+ });
77
+ });
78
+
79
+ describe('setCurrentCurrencyFormat', () => {
80
+ it('should set the current currency format', () => {
81
+ setCurrentCurrencyFormat('JPY');
82
+ expect(getCurrentCurrencyFormat()).toBe('JPY');
83
+ });
84
+ });
85
+
86
+ describe('loadCurrencyData', () => {
87
+ it('should load supplemental currency data', () => {
88
+ const data = {
89
+ supplemental: {
90
+ currencyData: {
91
+ region: {
92
+ US: [{ USD: {} }],
93
+ },
94
+ },
95
+ },
96
+ };
97
+ setCurrentCurrencyFormat(null);
98
+ loadCurrencyData(data);
99
+ expect(getDefaultCurrencyCode()).toBe('USD');
100
+ });
101
+ });
102
+
103
+ describe('loadGlobalizeData', () => {
104
+ it('should load CLDR data', () => {
105
+ jest.spyOn(Globalize, 'load').mockImplementationOnce((data) => {
106
+ return data;
107
+ });
108
+ const data = { key: 'value' };
109
+ loadGlobalizeData(data);
110
+ expect(Globalize.load).toHaveBeenCalledWith(data);
111
+ jest.clearAllMocks();
112
+ });
113
+ });
114
+
115
+ describe('globalizeNumberFormatter', () => {
116
+ it('should create a number formatter', () => {
117
+ const formatter = globalizeNumberFormatter({
118
+ minimumFractionDigits: 2,
119
+ });
120
+ expect(typeof formatter).toBe('function');
121
+ });
122
+ });
123
+
124
+ describe('globalizeCurrencyFormatter', () => {
125
+ it('should create a currency formatter', () => {
126
+ const formatter = globalizeCurrencyFormatter('USD', {
127
+ style: 'symbol',
128
+ });
129
+ expect(typeof formatter).toBe('function');
130
+ });
131
+ });
132
+
133
+ describe('formatNumberSafely', () => {
134
+ it('should format numbers safely', () => {
135
+ const formattedNumber = formatNumberSafely(
136
+ { maximumFractionDigits: 2, minimumFractionDigits: 2 },
137
+ 123.456,
138
+ );
139
+ expect(formattedNumber).toBe('123.46');
140
+ });
141
+
142
+ it('should return 0 for very small numbers', () => {
143
+ jest.spyOn(Globalize, 'numberFormatter').mockImplementationOnce(
144
+ () => {
145
+ throw new Error('Test Error');
146
+ },
147
+ );
148
+ const formattedNumber = formatNumberSafely({}, 1e-8);
149
+ expect(formattedNumber).toBe('0');
150
+ jest.clearAllMocks();
151
+ });
152
+
153
+ it('should return the string representation for errors', () => {
154
+ jest.spyOn(Globalize, 'numberFormatter').mockImplementationOnce(
155
+ () => {
156
+ throw new Error('Test Error');
157
+ },
158
+ );
159
+ const formattedNumber = formatNumberSafely({}, 123);
160
+ expect(formattedNumber).toBe('123');
161
+ jest.clearAllMocks();
162
+ });
163
+ });
164
+
165
+ describe('sanitizeFormat', () => {
166
+ it('should add zero before decimal point', () => {
167
+ expect(sanitizeFormat('#.##')).toBe('0.##');
168
+ });
169
+
170
+ it('should add zero at the end if no decimal point', () => {
171
+ expect(sanitizeFormat('#%')).toBe('0%');
172
+ });
173
+
174
+ it('should return the same format if no changes are needed', () => {
175
+ expect(sanitizeFormat('0.##')).toBe('0.##');
176
+ });
177
+ });
178
+
179
+ describe('validateNumberFormat', () => {
180
+ it('should return true for valid number formats', () => {
181
+ expect(validateNumberFormat('#.##')).toBe(true);
182
+ });
183
+
184
+ it('should return false for invalid number formats', () => {
185
+ jest.spyOn(Globalize, 'numberFormatter').mockImplementation(() => {
186
+ throw new Error('Test Error');
187
+ });
188
+ expect(validateNumberFormat('#.#')).toBe(false);
189
+ jest.clearAllMocks();
190
+ });
191
+ });
192
+ });
@@ -0,0 +1,216 @@
1
+ /**
2
+ * @file: Initialize Globalize
3
+ *
4
+ * @author Yashvardhan Nehra <yashvardhan.nehra@thoughtspot.com>
5
+ *
6
+ * Copyright: ThoughtSpot Inc. 2024
7
+ */
8
+
9
+ /* eslint-disable import/no-extraneous-dependencies */
10
+ import enCaGregorian from 'cldr-data/main/en/ca-gregorian.json';
11
+ import enCurrencies from 'cldr-data/main/en/currencies.json';
12
+ import enNumbers from 'cldr-data/main/en/numbers.json';
13
+ import currencyData from 'cldr-data/supplemental/currencyData.json';
14
+ import supplemental from 'cldr-data/supplemental/likelySubtags.json';
15
+ import enpluralJson from 'cldr-data/supplemental/plurals.json';
16
+ import Globalize from 'globalize';
17
+ import _ from 'lodash';
18
+ import { create } from '../../main/logger';
19
+
20
+ const logger = create('globalize-initializer');
21
+
22
+ let currentLocale: string;
23
+ let currentCurrencyFormat: any;
24
+ let supplementalCurrencyDataJson: any;
25
+
26
+ /**
27
+ * Extracts the country code from a locale string.
28
+ *
29
+ * @param locale - The locale string (e.g., 'en-US', 'en_US').
30
+ * @returns The country code (e.g., 'US') or the input locale if no delimiter is found.
31
+ */
32
+ export const getCountryCode = (locale: string): string => {
33
+ let parts = locale.split('_'); // // Split by '_'
34
+ if (parts.length === 2) {
35
+ return parts[1].toUpperCase();
36
+ }
37
+ parts = locale.split('-'); // // Split by '-'
38
+ if (parts.length === 2) {
39
+ return parts[1].toUpperCase();
40
+ }
41
+ return locale;
42
+ };
43
+
44
+ /**
45
+ * Retrieves the default currency code for the current locale.
46
+ *
47
+ * @returns The default currency code (e.g., 'USD') or GBP if not found.
48
+ */
49
+ export const getDefaultCurrencyCode = (): string => {
50
+ if (currentCurrencyFormat) {
51
+ return currentCurrencyFormat;
52
+ }
53
+
54
+ const countryCode = getCountryCode(currentLocale);
55
+ const regionData =
56
+ supplementalCurrencyDataJson?.supplemental?.currencyData?.region[
57
+ countryCode.toUpperCase()
58
+ ];
59
+ if (!regionData || regionData.length === 0) {
60
+ logger.warn('No currency data found for country:', countryCode);
61
+ return 'GBP';
62
+ }
63
+ return Object.keys(regionData[regionData.length - 1])[0];
64
+ };
65
+
66
+ /**
67
+ * Sets the current locale for Globalize and updates the global state.
68
+ *
69
+ * @param locale - The locale string to set (e.g., 'en-US').
70
+ */
71
+ export const setGlobalizeLocale = (locale: string): void => {
72
+ Globalize.locale(locale);
73
+ currentLocale = locale;
74
+ };
75
+
76
+ /**
77
+ * Retrieves the current locale set in Globalize.
78
+ *
79
+ * @returns The current locale string (e.g., 'en-US').
80
+ */
81
+ export const getGlobalizeLocale = () => currentLocale;
82
+
83
+ /**
84
+ * Updates the current currency format.
85
+ *
86
+ * @param currencyFormat - The currency format to set.
87
+ */
88
+ export const setCurrentCurrencyFormat = (currencyFormat: any): void => {
89
+ currentCurrencyFormat = currencyFormat;
90
+ };
91
+
92
+ /**
93
+ * Retrieves the current currency format.
94
+ *
95
+ * @returns The current currency format.
96
+ */
97
+ export const getCurrentCurrencyFormat = () => currentCurrencyFormat;
98
+
99
+ /**
100
+ * Loads supplemental currency data for Globalize.
101
+ *
102
+ * @param data - The supplemental currency data to load.
103
+ */
104
+ export const loadCurrencyData = (data: any) => {
105
+ supplementalCurrencyDataJson = data;
106
+ };
107
+
108
+ /**
109
+ * Loads CLDR data into Globalize.
110
+ *
111
+ * @param data - The CLDR data to load.
112
+ */
113
+ export const loadGlobalizeData = (data: any) => {
114
+ Globalize.load(data);
115
+ };
116
+
117
+ /**
118
+ * Initializes Globalize with CLDR data and sets the default locale.
119
+ *
120
+ * @param locale - The locale to initialize Globalize with (default: 'en-GB').
121
+ */
122
+ export const initGlobalize = (locale = 'en-GB') => {
123
+ loadGlobalizeData(enNumbers);
124
+ loadGlobalizeData(enCaGregorian);
125
+ loadGlobalizeData(supplemental);
126
+ loadGlobalizeData(currencyData);
127
+ loadGlobalizeData(enpluralJson);
128
+ loadGlobalizeData(enCurrencies);
129
+
130
+ loadCurrencyData(currencyData);
131
+
132
+ setGlobalizeLocale(locale);
133
+ };
134
+
135
+ /**
136
+ * Creates a number formatter with the given options.
137
+ *
138
+ * @param format - The Globalize number formatter options.
139
+ * @returns A formatter function for numbers.
140
+ */
141
+ export function globalizeNumberFormatter(
142
+ format: Globalize.NumberFormatterOptions,
143
+ ): (num: number) => string {
144
+ return Globalize.numberFormatter(format);
145
+ }
146
+
147
+ /**
148
+ * Creates a currency formatter with the given options.
149
+ *
150
+ * @param currencyCode - The ISO currency code (e.g., 'USD').
151
+ * @param format - The Globalize currency formatter options.
152
+ * @returns A formatter function for currency values.
153
+ */
154
+ export function globalizeCurrencyFormatter(
155
+ currencyCode: string,
156
+ format: Globalize.CurrencyFormatterOptions,
157
+ ): (num: number) => string {
158
+ return Globalize.currencyFormatter(currencyCode, format);
159
+ }
160
+
161
+ /**
162
+ * Formats a number using Globalize, handling errors gracefully.
163
+ *
164
+ * @param format - Globalize number formatter options.
165
+ * @param num - The number to format.
166
+ * @returns The formatted number as a string.
167
+ */
168
+ export function formatNumberSafely<
169
+ FormatOptions extends Globalize.NumberFormatterOptions,
170
+ >(format: FormatOptions, num: number): string {
171
+ try {
172
+ const formatter = globalizeNumberFormatter(format);
173
+ const formattedNumber = formatter(num);
174
+ return formattedNumber;
175
+ } catch (e) {
176
+ logger.error('Error formatting pattern: ', format, num, e);
177
+ if (Math.abs(num) < 1e-7) {
178
+ return '0';
179
+ }
180
+ return String(num);
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Sanitizes a number format to ensure compatibility with Globalize.
186
+ *
187
+ * @param format - The raw format string (e.g., '#.##').
188
+ * @returns A sanitized format string (e.g., '0.##').
189
+ */
190
+ export const sanitizeFormat = (format: string): string => {
191
+ // Globalize needs to have a zero before the decimal point
192
+ // or at the end of format if no decimal point
193
+ let sanitizedFormat = format.replace(/#\./, '0.');
194
+ if (!sanitizedFormat.includes('.')) {
195
+ sanitizedFormat = sanitizedFormat.replace(/#(%?)$/, '0$1');
196
+ }
197
+ return sanitizedFormat;
198
+ };
199
+
200
+ /**
201
+ * Validates if a given number format is compatible with Globalize.
202
+ *
203
+ * @param format - The raw format string.
204
+ * @returns True if the format is valid; otherwise, false.
205
+ */
206
+ export const validateNumberFormat = (format: string): boolean => {
207
+ try {
208
+ Globalize.numberFormatter({
209
+ ...({ raw: sanitizeFormat(format) } as any),
210
+ })(123); // Test the formatter with a dummy value
211
+ } catch (e) {
212
+ logger.error('Invalid number format:', format, e);
213
+ return false;
214
+ }
215
+ return true;
216
+ };
@@ -0,0 +1,296 @@
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
+ setLocaleBasedStringFormats,
24
+ UNITS_TO_DIVIDING_FACTOR,
25
+ } from './number-formatting-utils';
26
+
27
+ describe('formatNegativeValue', () => {
28
+ it('should format with prefix dash', () => {
29
+ const result = formatNegativeValue(
30
+ '100',
31
+ NegativeValueFormat.PrefixDash,
32
+ );
33
+ expect(result).toBe('-100');
34
+ });
35
+
36
+ it('should format with suffix dash', () => {
37
+ const result = formatNegativeValue(
38
+ '100',
39
+ NegativeValueFormat.SuffixDash,
40
+ );
41
+ expect(result).toBe('100-');
42
+ });
43
+
44
+ it('should format with braces without dash', () => {
45
+ const result = formatNegativeValue(
46
+ '100',
47
+ NegativeValueFormat.BracesNodash,
48
+ );
49
+ expect(result).toBe('(100)');
50
+ });
51
+
52
+ it('should default to prefix dash if no format is provided', () => {
53
+ const result = formatNegativeValue('100');
54
+ expect(result).toBe('-100');
55
+ });
56
+ });
57
+
58
+ describe('getAutoUnit', () => {
59
+ it('should return Trillion for values above 1 Trillion', () => {
60
+ const result = getAutoUnit(1e12);
61
+ expect(result).toBe(Unit.Trillion);
62
+ });
63
+
64
+ it('should return Billion for values above 1 Billion', () => {
65
+ const result = getAutoUnit(1e9);
66
+ expect(result).toBe(Unit.Billion);
67
+ });
68
+
69
+ it('should return Million for values above 1 Million', () => {
70
+ const result = getAutoUnit(1e6);
71
+ expect(result).toBe(Unit.Million);
72
+ });
73
+
74
+ it('should return Thousands for values above 1 Thousand', () => {
75
+ const result = getAutoUnit(1e3);
76
+ expect(result).toBe(Unit.Thousands);
77
+ });
78
+
79
+ it('should return None for values below 1000', () => {
80
+ const result = getAutoUnit(500);
81
+ expect(result).toBe(Unit.None);
82
+ });
83
+ });
84
+
85
+ describe('getLocaleName', () => {
86
+ it('should return default locale if currency format is not ISO_CODE', () => {
87
+ jest.spyOn(
88
+ globalizeUtils,
89
+ 'getDefaultCurrencyCode',
90
+ ).mockReturnValueOnce('GBP');
91
+ const currencyFormat: CurrencyFormat = {
92
+ type: CurrencyFormatType.USER_LOCALE,
93
+ column: '',
94
+ isoCode: '',
95
+ };
96
+ const result = getLocaleName(currencyFormat);
97
+ expect(result).toBe('GBP'); // Default locale
98
+ });
99
+
100
+ it('should return ISO code if currency format is ISO_CODE', () => {
101
+ const currencyFormat: CurrencyFormat = {
102
+ type: CurrencyFormatType.ISO_CODE,
103
+ column: '',
104
+ isoCode: 'USD',
105
+ };
106
+ const result = getLocaleName(currencyFormat);
107
+ expect(result).toBe('USD');
108
+ });
109
+ });
110
+
111
+ describe('defaultFormatConfig', () => {
112
+ it('should return custom format config for a pattern', () => {
113
+ const columnFormatConfig: ColumnFormat = {
114
+ type: FormatType.PATTERN,
115
+ pattern: '###,###',
116
+ };
117
+ const result = defaultFormatConfig(columnFormatConfig);
118
+ expect(result.category).toBe(CategoryType.Custom);
119
+ expect(result.customFormatConfig?.format).toBe('###,###');
120
+ });
121
+
122
+ it('should return currency format config if currencyFormat is defined', () => {
123
+ const columnFormatConfig: ColumnFormat = {
124
+ type: FormatType.CURRENCY,
125
+ currencyFormat: {
126
+ type: CurrencyFormatType.ISO_CODE,
127
+ column: '',
128
+ isoCode: 'USD',
129
+ },
130
+ };
131
+ const result = defaultFormatConfig(columnFormatConfig);
132
+ expect(result.category).toBe(CategoryType.Currency);
133
+ expect(result.currencyFormatConfig?.locale).toBe('USD');
134
+ });
135
+
136
+ it('should return number format config by default', () => {
137
+ const result = defaultFormatConfig();
138
+ expect(result.category).toBe(CategoryType.Number);
139
+ expect(result.numberFormatConfig?.decimals).toBe(0);
140
+ });
141
+ });
142
+
143
+ describe('mapFormatterConfig', () => {
144
+ it('should return correct formatter config for a number format', () => {
145
+ const absFloatValue = 1000;
146
+ const configDetails: NumberFormatConfig = {
147
+ decimals: 2,
148
+ negativeValueFormat: NegativeValueFormat.PrefixDash,
149
+ removeTrailingZeroes: false,
150
+ toSeparateThousands: true,
151
+ unit: Unit.Million,
152
+ };
153
+
154
+ const result = mapFormatterConfig(absFloatValue, configDetails);
155
+ expect(result.unitDetails).toBe(Unit.Million); // Unit: Million
156
+ expect(result.decimalDetails).toBe(2);
157
+ expect(result.shouldRemoveTrailingZeros).toBe(false);
158
+ });
159
+
160
+ it('should return auto unit and decimal precision for large value', () => {
161
+ const absFloatValue = 1e9; // 1 Billion
162
+ const configDetails: NumberFormatConfig = {
163
+ decimals: 0,
164
+ negativeValueFormat: NegativeValueFormat.PrefixDash,
165
+ removeTrailingZeroes: false,
166
+ toSeparateThousands: true,
167
+ unit: Unit.Auto, // Auto
168
+ };
169
+
170
+ const result = mapFormatterConfig(absFloatValue, configDetails);
171
+ expect(result.unitDetails).toBe(Unit.Billion);
172
+ expect(result.decimalDetails).toBe(2); // Default decimal precision for auto
173
+ });
174
+
175
+ it('should return correct config for currency formatting', () => {
176
+ const absFloatValue = 1000;
177
+ const configDetails: CurrencyFormatConfig = {
178
+ decimals: 2,
179
+ locale: 'en',
180
+ removeTrailingZeroes: false,
181
+ toSeparateThousands: true,
182
+ unit: Unit.Auto,
183
+ };
184
+
185
+ const result = mapFormatterConfig(absFloatValue, configDetails);
186
+ expect(result.unitDetails).toBe(Unit.Thousands); // Automatically chosen unit
187
+ expect(result.decimalDetails).toBe(2);
188
+ });
189
+
190
+ it('should correctly map valid unit numbers to the corresponding unit', () => {
191
+ const input = { unit: Unit.None };
192
+ const result = mapFormatterConfig(1000, input);
193
+
194
+ // Assert that the unit has been correctly mapped to the corresponding
195
+ // value in PROTO_TO_UNITS
196
+ expect(result.unitDetails).toBe(Unit.None);
197
+ });
198
+ });
199
+
200
+ describe('formatSpecialDataValue', () => {
201
+ it('should return {Null} for {Null} values', () => {
202
+ const result = formatSpecialDataValue('{Null}');
203
+ expect(result).toBe('{Null}');
204
+ });
205
+
206
+ it('should return {Null} for null values', () => {
207
+ const result = formatSpecialDataValue(null);
208
+ expect(result).toBe('{Null}');
209
+ });
210
+
211
+ it('should return {Empty} for empty string', () => {
212
+ const result = formatSpecialDataValue('');
213
+ expect(result).toBe('{Empty}');
214
+ });
215
+
216
+ it('should return {Null} for undefined values', () => {
217
+ const result = formatSpecialDataValue(undefined);
218
+ expect(result).toBe('{Null}');
219
+ });
220
+
221
+ it('should return NaN for string "NaN"', () => {
222
+ const result = formatSpecialDataValue(['NaN']);
223
+ expect(result).toBe('NaN');
224
+ });
225
+
226
+ it('should return {Null} for empty array', () => {
227
+ const result = formatSpecialDataValue([]);
228
+ expect(result).toBe('{Null}');
229
+ });
230
+
231
+ it('should return Null for array', () => {
232
+ const result = formatSpecialDataValue(['Test']);
233
+ expect(result).toBe(null);
234
+ });
235
+
236
+ it('should return Infinity for string "Infinity"', () => {
237
+ const result = formatSpecialDataValue(['Infinity']);
238
+ expect(result).toBe('Infinity');
239
+ });
240
+
241
+ it('should return null for non-special values', () => {
242
+ const result = formatSpecialDataValue('Test');
243
+ expect(result).toBeNull();
244
+ });
245
+
246
+ it('should return Infinity for Infinity', () => {
247
+ const result = formatSpecialDataValue(Infinity);
248
+ expect(result).toBe('Infinity');
249
+ });
250
+ });
251
+
252
+ describe('Unit constants', () => {
253
+ it('should map unit to correct dividing factor', () => {
254
+ expect(UNITS_TO_DIVIDING_FACTOR[Unit.Thousands]).toBe(1000);
255
+ expect(UNITS_TO_DIVIDING_FACTOR[Unit.Million]).toBe(1000000);
256
+ expect(UNITS_TO_DIVIDING_FACTOR[Unit.Billion]).toBe(1000000000);
257
+ });
258
+ });
259
+
260
+ describe('Locale String Formats', () => {
261
+ const strings: Record<string, string> = {
262
+ NULL_VALUE_PLACEHOLDER_LABEL: '{Null}',
263
+ EMPTY_VALUE_PLACEHOLDER_LABEL: '{Empty}',
264
+ };
265
+
266
+ it('should return an default object if setLocaleBasedStringFormats is not called', () => {
267
+ const result = getLocaleBasedStringFormats();
268
+ expect(result).toEqual(strings);
269
+ });
270
+
271
+ it('should set and get locale-based string formats correctly', () => {
272
+ const testLocaleStrings = {
273
+ greeting: 'Hello',
274
+ farewell: 'Goodbye',
275
+ };
276
+
277
+ setLocaleBasedStringFormats(testLocaleStrings);
278
+
279
+ const result = getLocaleBasedStringFormats();
280
+
281
+ expect(result).toEqual(testLocaleStrings);
282
+ });
283
+
284
+ it('should update the string formats when setLocaleBasedStringFormats is called multiple times', () => {
285
+ const firstSet = { greeting: 'Hola' };
286
+ const secondSet = { greeting: 'Bonjour' };
287
+
288
+ // Set first locale-based string formats
289
+ setLocaleBasedStringFormats(firstSet);
290
+ expect(getLocaleBasedStringFormats()).toEqual(firstSet);
291
+
292
+ // Set second locale-based string formats
293
+ setLocaleBasedStringFormats(secondSet);
294
+ expect(getLocaleBasedStringFormats()).toEqual(secondSet);
295
+ });
296
+ });