@thoughtspot/ts-chart-sdk 0.0.2-alpha.23 → 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 +82 -6
- 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 +13 -9
- package/lib/main/custom-chart-context.js.map +1 -1
- package/lib/main/logger.d.ts +13 -0
- package/lib/main/logger.d.ts.map +1 -0
- package/lib/main/logger.js +64 -0
- package/lib/main/logger.js.map +1 -0
- package/lib/main/logger.spec.d.ts +2 -0
- package/lib/main/logger.spec.d.ts.map +1 -0
- package/lib/main/logger.spec.js +88 -0
- package/lib/main/logger.spec.js.map +1 -0
- package/lib/main/util.d.ts +2 -0
- package/lib/main/util.d.ts.map +1 -1
- package/lib/main/util.js +11 -0
- package/lib/main/util.js.map +1 -1
- package/lib/main/util.spec.js +43 -1
- package/lib/main/util.spec.js.map +1 -1
- package/lib/react/use-custom-chart-context.d.ts.map +1 -1
- package/lib/react/use-custom-chart-context.js +3 -1
- package/lib/react/use-custom-chart-context.js.map +1 -1
- package/lib/react/use-custom-chart-context.util.d.ts.map +1 -1
- package/lib/react/use-custom-chart-context.util.js +5 -3
- package/lib/react/use-custom-chart-context.util.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/common.types.d.ts +6 -0
- package/lib/types/common.types.d.ts.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/ts-to-chart-event.types.d.ts.map +1 -1
- package/lib/types/ts-to-chart-event.types.js.map +1 -1
- package/lib/types/visual-prop.types.d.ts +51 -1
- 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/date-formatting.d.ts.map +1 -1
- package/lib/utils/date-formatting.js +4 -2
- package/lib/utils/date-formatting.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 +14 -10
- package/src/main/logger.spec.ts +114 -0
- package/src/main/logger.ts +97 -0
- package/src/main/util.spec.ts +54 -1
- package/src/main/util.ts +20 -0
- package/src/react/use-custom-chart-context.tsx +4 -1
- package/src/react/use-custom-chart-context.util.ts +6 -3
- package/src/types/answer-column.types.ts +3 -3
- package/src/types/common.types.ts +7 -0
- package/src/types/number-formatting.types.ts +2 -2
- package/src/types/ts-to-chart-event.types.ts +0 -1
- package/src/types/visual-prop.types.ts +129 -2
- package/src/utils/date-formatting.ts +5 -2
- 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
|
@@ -11,6 +11,43 @@
|
|
|
11
11
|
import type { CustomChartContext } from '../main/custom-chart-context';
|
|
12
12
|
import { ColumnType } from './answer-column.types';
|
|
13
13
|
import { ChartModel } from './common.types';
|
|
14
|
+
|
|
15
|
+
export type TSTooltipConfig = {
|
|
16
|
+
columnIds: Array<string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export enum VisualPropComponentTranslationKeys {
|
|
20
|
+
SHOW_ALL_LABELS = 'SHOW_ALL_LABELS',
|
|
21
|
+
TOO_MANY_LABELS = 'TOO_MANY_LABELS',
|
|
22
|
+
MAP_TILE_LABEL = 'MAP_TILE_LABEL',
|
|
23
|
+
ENABLE_MARKERS = 'ENABLE_MARKERS',
|
|
24
|
+
SHOW_REGRESSION_LINE = 'SHOW_REGRESSION_LINE',
|
|
25
|
+
X_AXIS_GRID_LINE = 'X_AXIS_GRID_LINE',
|
|
26
|
+
Y_AXIS_GRID_LINE = 'Y_AXIS_GRID_LINE',
|
|
27
|
+
MAX_DATA_POINTS = 'MAX_DATA_POINTS',
|
|
28
|
+
HIGH_CARDINALITY_BATCH_SIZE_DISABLED = 'highCardinalityBatchSizeDisabled',
|
|
29
|
+
HIGH_CARDINALITY_BATCH_SIZE_LIMIT = 'highCardinalityBatchSizeLimit',
|
|
30
|
+
CHART_CUSTOMIZE = 'CHART_CUSTOMIZE',
|
|
31
|
+
SELECT_AN_AREA = 'chartConfigurator.SELECT_AN_AREA',
|
|
32
|
+
RESET_ZOOM = 'chartConfigurator.RESET_ZOOM',
|
|
33
|
+
EDIT_TOOLTIP = 'EDIT_TOOLTIP',
|
|
34
|
+
DONT_SHOW = 'DONT_SHOW',
|
|
35
|
+
SHOW_GAP = 'SHOW_GAP',
|
|
36
|
+
SHOW_AS_ZERO = 'SHOW_AS_ZERO',
|
|
37
|
+
HANDLE_MISSING_VALUES = 'HANDLE_MISSING_VALUES',
|
|
38
|
+
SHOW_NULL_AS_ZERO = 'SHOW_NULL_AS_ZERO',
|
|
39
|
+
EXCLUDE_NULL_VALUES = 'EXCLUDE_NULL_VALUES',
|
|
40
|
+
COLUMN_CUSTOMIZE = 'COLUMN_CUSTOMIZE',
|
|
41
|
+
SHOW_TOTAL_LABELS = 'SHOW_TOTAL_LABELS',
|
|
42
|
+
SHOW_DETAILED_LABELS = 'SHOW_DETAILED_LABELS',
|
|
43
|
+
SHOW_DATA_LABELS = 'SHOW_DATA_LABELS',
|
|
44
|
+
SHOW_AXIS_AS_PERCENT = 'SHOW_AXIS_AS_PERCENT',
|
|
45
|
+
RIGHT_LEGEND = 'RIGHT_LEGEND',
|
|
46
|
+
LEFT_LEGEND = 'LEFT_LEGEND',
|
|
47
|
+
TOP_LEGEND = 'TOP_LEGEND',
|
|
48
|
+
BOTTOM_LEGEND = 'BOTTOM_LEGEND',
|
|
49
|
+
}
|
|
50
|
+
|
|
14
51
|
/**
|
|
15
52
|
* Configuration for input validation rules
|
|
16
53
|
*/
|
|
@@ -122,6 +159,12 @@ export interface TextInputFormDetail {
|
|
|
122
159
|
* @version SDK: 0.0.2-alpha.13 | ThoughtSpot:
|
|
123
160
|
*/
|
|
124
161
|
disabled?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Translation key for the label
|
|
164
|
+
*
|
|
165
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
166
|
+
*/
|
|
167
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
125
168
|
}
|
|
126
169
|
|
|
127
170
|
/**
|
|
@@ -161,6 +204,12 @@ export interface NumberInputFormDetail {
|
|
|
161
204
|
* @version SDK: 0.0.2-alpha.13 | ThoughtSpot:
|
|
162
205
|
*/
|
|
163
206
|
disabled?: boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Translation key for the label
|
|
209
|
+
*
|
|
210
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
211
|
+
*/
|
|
212
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
164
213
|
}
|
|
165
214
|
|
|
166
215
|
/**
|
|
@@ -195,6 +244,12 @@ export interface ColorPickerFormDetail {
|
|
|
195
244
|
* @version SDK: 0.0.1-alpha.7 | ThoughtSpot:
|
|
196
245
|
*/
|
|
197
246
|
defaultValue?: string;
|
|
247
|
+
/**
|
|
248
|
+
* Translation key for the label
|
|
249
|
+
*
|
|
250
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
251
|
+
*/
|
|
252
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
198
253
|
}
|
|
199
254
|
|
|
200
255
|
/**
|
|
@@ -228,6 +283,12 @@ export interface ToggleFormDetail {
|
|
|
228
283
|
* @version SDK: 0.0.2-alpha.13 | ThoughtSpot:
|
|
229
284
|
*/
|
|
230
285
|
disabled?: boolean;
|
|
286
|
+
/**
|
|
287
|
+
* Translation key for the label
|
|
288
|
+
*
|
|
289
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
290
|
+
*/
|
|
291
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
231
292
|
}
|
|
232
293
|
|
|
233
294
|
/**
|
|
@@ -261,6 +322,12 @@ export interface CheckboxFormDetail {
|
|
|
261
322
|
* @version SDK: 0.0.2-alpha.13 | ThoughtSpot:
|
|
262
323
|
*/
|
|
263
324
|
disabled?: boolean;
|
|
325
|
+
/**
|
|
326
|
+
* Translation key for the label
|
|
327
|
+
*
|
|
328
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
329
|
+
*/
|
|
330
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
264
331
|
}
|
|
265
332
|
|
|
266
333
|
/**
|
|
@@ -300,6 +367,12 @@ export interface RadioButtonFormDetail {
|
|
|
300
367
|
* @version SDK: 0.0.2-3 | ThoughtSpot:
|
|
301
368
|
*/
|
|
302
369
|
disabled?: boolean;
|
|
370
|
+
/**
|
|
371
|
+
* Translation key for the label
|
|
372
|
+
*
|
|
373
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
374
|
+
*/
|
|
375
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
303
376
|
}
|
|
304
377
|
|
|
305
378
|
/**
|
|
@@ -339,6 +412,12 @@ export interface DropDownFormDetail {
|
|
|
339
412
|
* @version SDK: 0.0.2-alpha.13 | ThoughtSpot:
|
|
340
413
|
*/
|
|
341
414
|
disabled?: boolean;
|
|
415
|
+
/**
|
|
416
|
+
* Translation key for the label
|
|
417
|
+
*
|
|
418
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
419
|
+
*/
|
|
420
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
342
421
|
}
|
|
343
422
|
|
|
344
423
|
/**
|
|
@@ -386,11 +465,58 @@ export interface Section {
|
|
|
386
465
|
*/
|
|
387
466
|
disabled?: boolean;
|
|
388
467
|
/* Optional property to make the accordian expanded by default. If
|
|
389
|
-
* not passed the accordian will remain closed by default. Only works with layout type
|
|
468
|
+
* not passed the accordian will remain closed by default. Only works with layout type
|
|
469
|
+
* 'accordian'
|
|
390
470
|
*
|
|
391
471
|
* @version SDK: 0.0.2-alpha.19 | ThoughtSpot:
|
|
392
472
|
*/
|
|
393
473
|
isAccordianExpanded?: boolean;
|
|
474
|
+
/**
|
|
475
|
+
* Translation key for the label
|
|
476
|
+
*
|
|
477
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
478
|
+
*/
|
|
479
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Native charts edit tool tip component defined for regular charts in TS Advance Chart Settings
|
|
484
|
+
*
|
|
485
|
+
* @group Visual Properties Editor
|
|
486
|
+
*/
|
|
487
|
+
export interface NativeEditToolTip {
|
|
488
|
+
type: 'tooltipconfig';
|
|
489
|
+
/**
|
|
490
|
+
* Key to store the value
|
|
491
|
+
*
|
|
492
|
+
* @version SDK: 0.1 | ThoughtSpot:
|
|
493
|
+
*/
|
|
494
|
+
key: string;
|
|
495
|
+
|
|
496
|
+
/*
|
|
497
|
+
List of column ids that are present in ToolTipConfig by default
|
|
498
|
+
*/
|
|
499
|
+
|
|
500
|
+
defaultValue?: TSTooltipConfig;
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* I18n'ed string to show on the form label
|
|
504
|
+
*
|
|
505
|
+
* @version SDK: 0.1 | ThoughtSpot:
|
|
506
|
+
*/
|
|
507
|
+
label?: string;
|
|
508
|
+
/**
|
|
509
|
+
* Determines whether it should be disabled or not
|
|
510
|
+
*
|
|
511
|
+
* @version SDK: 0.0.2-alpha.13 | ThoughtSpot:
|
|
512
|
+
*/
|
|
513
|
+
disabled?: boolean;
|
|
514
|
+
/**
|
|
515
|
+
* Translation key for the label
|
|
516
|
+
*
|
|
517
|
+
* @version SDK: 0.2 | ThoughtSpot:
|
|
518
|
+
*/
|
|
519
|
+
labelTranslation?: VisualPropComponentTranslationKeys;
|
|
394
520
|
}
|
|
395
521
|
|
|
396
522
|
/**
|
|
@@ -406,7 +532,8 @@ export type PropElement =
|
|
|
406
532
|
| ToggleFormDetail
|
|
407
533
|
| CheckboxFormDetail
|
|
408
534
|
| RadioButtonFormDetail
|
|
409
|
-
| DropDownFormDetail
|
|
535
|
+
| DropDownFormDetail
|
|
536
|
+
| NativeEditToolTip;
|
|
410
537
|
|
|
411
538
|
/**
|
|
412
539
|
* Define Column settings, based on column type, settings needs to be defined in
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import _ from 'lodash';
|
|
10
10
|
import { DateTime } from 'luxon';
|
|
11
|
+
import { create } from '../main/logger';
|
|
11
12
|
import {
|
|
12
13
|
ChartColumn,
|
|
13
14
|
ColumnTimeBucket,
|
|
@@ -15,6 +16,8 @@ import {
|
|
|
15
16
|
DataType,
|
|
16
17
|
} from '../types/answer-column.types';
|
|
17
18
|
|
|
19
|
+
const logger = create('DateFormattingUtilsSdk');
|
|
20
|
+
|
|
18
21
|
export interface CustomCalendarDate {
|
|
19
22
|
v: {
|
|
20
23
|
s: number;
|
|
@@ -615,7 +618,7 @@ export function formatDateNum(
|
|
|
615
618
|
case dateNumTypes.DATE_NUM_HOUR_IN_DAY:
|
|
616
619
|
return `${value}`;
|
|
617
620
|
default:
|
|
618
|
-
|
|
621
|
+
logger.log(
|
|
619
622
|
'unknown effectiveDataType for date num',
|
|
620
623
|
effectiveDataType,
|
|
621
624
|
);
|
|
@@ -664,7 +667,7 @@ export function formatDate(
|
|
|
664
667
|
epochMillis = newInputDate.getTime();
|
|
665
668
|
}
|
|
666
669
|
if (!_.isNumber(epochMillis)) {
|
|
667
|
-
|
|
670
|
+
logger.log(
|
|
668
671
|
'formatDate could not convert input date to a timestamp',
|
|
669
672
|
inputDate,
|
|
670
673
|
);
|
|
@@ -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
|
+
};
|