@oanda/labs-crowd-view-widget 1.0.54 → 1.0.56

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 (193) hide show
  1. package/CHANGELOG.md +452 -0
  2. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +42 -58
  3. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +33 -8
  5. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js +15 -9
  7. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +28 -14
  9. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
  10. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +55 -15
  11. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
  12. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +171 -0
  13. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -0
  14. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js +7 -14
  15. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js.map +1 -1
  16. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js +20 -2
  17. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js.map +1 -1
  18. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js +13 -5
  19. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js.map +1 -1
  20. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +51 -5
  21. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
  22. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +30 -7
  23. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
  24. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +27 -29
  25. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
  26. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/index.js +11 -0
  27. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/index.js.map +1 -1
  28. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js +18 -0
  29. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js.map +1 -0
  30. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js +6 -5
  31. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js.map +1 -1
  32. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js +1 -20
  33. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js.map +1 -1
  34. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +91 -21
  35. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
  36. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js +59 -0
  37. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js.map +1 -0
  38. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js +34 -0
  39. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js.map +1 -0
  40. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js +44 -0
  41. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
  42. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js +11 -0
  43. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js.map +1 -0
  44. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js +19 -0
  45. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js.map +1 -0
  46. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/index.js +11 -0
  47. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/index.js.map +1 -1
  48. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js +1 -1
  49. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js.map +1 -1
  50. package/dist/main/CrowdViewWidget/components/Chart/getOption.js +41 -23
  51. package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
  52. package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
  53. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +7 -4
  54. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  55. package/dist/main/CrowdViewWidget/components/Chart/useResizeObserver.js +47 -0
  56. package/dist/main/CrowdViewWidget/components/Chart/useResizeObserver.js.map +1 -0
  57. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +1 -1
  58. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  59. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +11 -1
  60. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  61. package/dist/main/CrowdViewWidget/constants.js +31 -10
  62. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  63. package/dist/main/CrowdViewWidget/selectConfig.js +3 -9
  64. package/dist/main/CrowdViewWidget/selectConfig.js.map +1 -1
  65. package/dist/main/translations/sources/en.json +10 -8
  66. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +43 -58
  67. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  68. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +32 -7
  69. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  70. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js +15 -9
  71. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js.map +1 -1
  72. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +28 -14
  73. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
  74. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +55 -15
  75. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
  76. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +163 -0
  77. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -0
  78. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js +7 -14
  79. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js.map +1 -1
  80. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js +20 -2
  81. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js.map +1 -1
  82. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js +13 -5
  83. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js.map +1 -1
  84. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +50 -5
  85. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
  86. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +31 -8
  87. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
  88. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +28 -30
  89. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
  90. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/index.js +1 -0
  91. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/index.js.map +1 -1
  92. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js +11 -0
  93. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js.map +1 -0
  94. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js +6 -5
  95. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js.map +1 -1
  96. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js +1 -20
  97. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js.map +1 -1
  98. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +91 -21
  99. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
  100. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js +52 -0
  101. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js.map +1 -0
  102. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js +27 -0
  103. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js.map +1 -0
  104. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js +4 -0
  105. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
  106. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js +4 -0
  107. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js.map +1 -0
  108. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js +12 -0
  109. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js.map +1 -0
  110. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/index.js +1 -0
  111. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/index.js.map +1 -1
  112. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js +1 -1
  113. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js.map +1 -1
  114. package/dist/module/CrowdViewWidget/components/Chart/getOption.js +42 -24
  115. package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
  116. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  117. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +8 -5
  118. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  119. package/dist/module/CrowdViewWidget/components/Chart/useResizeObserver.js +40 -0
  120. package/dist/module/CrowdViewWidget/components/Chart/useResizeObserver.js.map +1 -0
  121. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +1 -1
  122. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  123. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +11 -1
  124. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  125. package/dist/module/CrowdViewWidget/constants.js +31 -10
  126. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  127. package/dist/module/CrowdViewWidget/selectConfig.js +3 -9
  128. package/dist/module/CrowdViewWidget/selectConfig.js.map +1 -1
  129. package/dist/module/translations/sources/en.json +10 -8
  130. package/dist/types/CrowdViewWidget/components/Chart/Chart.d.ts +1 -1
  131. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.d.ts +5 -1
  132. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getGridLines.d.ts +2 -1
  133. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.d.ts +8 -0
  134. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.d.ts +2 -3
  135. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.d.ts +2 -1
  136. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.d.ts +2 -1
  137. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.d.ts +6 -1
  138. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.d.ts +2 -1
  139. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.d.ts +1 -2
  140. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/index.d.ts +1 -0
  141. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.d.ts +1 -0
  142. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.d.ts +1 -1
  143. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/getLabelData.d.ts +1 -16
  144. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.d.ts +6 -1
  145. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.d.ts +4 -0
  146. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.d.ts +3 -0
  147. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/index.d.ts +4 -0
  148. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.d.ts +1 -0
  149. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.d.ts +2 -0
  150. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/index.d.ts +1 -0
  151. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +6 -0
  152. package/dist/types/CrowdViewWidget/components/Chart/useResizeObserver.d.ts +5 -0
  153. package/dist/types/CrowdViewWidget/constants.d.ts +28 -15
  154. package/package.json +3 -3
  155. package/src/CrowdViewWidget/components/Chart/Chart.tsx +78 -80
  156. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +51 -14
  157. package/src/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.ts +11 -2
  158. package/src/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.ts +34 -19
  159. package/src/CrowdViewWidget/components/Chart/chartOptions/getGridLines.ts +71 -8
  160. package/src/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.ts +207 -0
  161. package/src/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.ts +7 -15
  162. package/src/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.ts +22 -1
  163. package/src/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.ts +13 -4
  164. package/src/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.ts +91 -37
  165. package/src/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.ts +62 -30
  166. package/src/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.ts +22 -33
  167. package/src/CrowdViewWidget/components/Chart/chartOptions/index.ts +1 -0
  168. package/src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.ts +14 -0
  169. package/src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.ts +6 -4
  170. package/src/CrowdViewWidget/components/Chart/chartUtils/getLabelData.ts +11 -35
  171. package/src/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.ts +157 -31
  172. package/src/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.ts +80 -0
  173. package/src/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.ts +37 -0
  174. package/src/CrowdViewWidget/components/Chart/chartUtils/index.ts +4 -0
  175. package/src/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.ts +3 -0
  176. package/src/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.ts +13 -0
  177. package/src/CrowdViewWidget/components/Chart/dataUtils/index.ts +1 -0
  178. package/src/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.ts +1 -1
  179. package/src/CrowdViewWidget/components/Chart/getOption.ts +47 -17
  180. package/src/CrowdViewWidget/components/Chart/types.ts +6 -0
  181. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +12 -5
  182. package/src/CrowdViewWidget/components/Chart/useResizeObserver.ts +51 -0
  183. package/src/CrowdViewWidget/components/Legend/Legend.tsx +1 -1
  184. package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +18 -1
  185. package/src/CrowdViewWidget/constants.ts +34 -7
  186. package/src/CrowdViewWidget/selectConfig.ts +4 -12
  187. package/src/translations/sources/en.json +10 -8
  188. package/test/components/Chart/utils/chartUtils.test.ts +29 -6
  189. package/test/components/Chart/utils/handleLabelUpdate.test.ts +435 -0
  190. package/test/components/Chart/utils/handleTooltipUpdate.test.ts +140 -0
  191. package/test/components/Legend.test.tsx +3 -3
  192. package/test/components/LegendBar.test.tsx +22 -20
  193. package/test/utils/processPriceCandles.test.ts +4 -4
@@ -0,0 +1,435 @@
1
+ import type { EChartsType } from 'echarts';
2
+
3
+ import { formatXAxisAdditionalLabel } from '../../../../src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel';
4
+ import { formatXAxisLabel } from '../../../../src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel';
5
+ import { getLabelData } from '../../../../src/CrowdViewWidget/components/Chart/chartUtils/getLabelData';
6
+ import { handleLabelUpdate } from '../../../../src/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate';
7
+ import { isDifferenceGreaterThanTwoWeeks } from '../../../../src/CrowdViewWidget/components/Chart/chartUtils/isDifferenceGreaterThanTwoWeeks';
8
+ import type { ChartProps } from '../../../../src/CrowdViewWidget/components/Chart/types';
9
+
10
+ // Mock dependencies
11
+ jest.mock(
12
+ '../../../../src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel'
13
+ );
14
+ jest.mock(
15
+ '../../../../src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel'
16
+ );
17
+ jest.mock(
18
+ '../../../../src/CrowdViewWidget/components/Chart/chartUtils/getLabelData'
19
+ );
20
+ jest.mock(
21
+ '../../../../src/CrowdViewWidget/components/Chart/chartUtils/isDifferenceGreaterThanTwoWeeks'
22
+ );
23
+
24
+ const mockFormatXAxisLabel = formatXAxisLabel as jest.MockedFunction<
25
+ typeof formatXAxisLabel
26
+ >;
27
+ const mockFormatXAxisAdditionalLabel =
28
+ formatXAxisAdditionalLabel as jest.MockedFunction<
29
+ typeof formatXAxisAdditionalLabel
30
+ >;
31
+ const mockGetLabelData = getLabelData as jest.MockedFunction<
32
+ typeof getLabelData
33
+ >;
34
+ const mockIsDifferenceGreaterThanTwoWeeks =
35
+ isDifferenceGreaterThanTwoWeeks as jest.MockedFunction<
36
+ typeof isDifferenceGreaterThanTwoWeeks
37
+ >;
38
+
39
+ describe('handleLabelUpdate', () => {
40
+ let mockInstance: jest.Mocked<EChartsType>;
41
+ let labelTimerRef: { current: NodeJS.Timeout | null };
42
+ let isGreaterThanTwoWeeksRef: { current: boolean | null };
43
+ let mainData: ChartProps['mainData'];
44
+
45
+ beforeEach(() => {
46
+ jest.useFakeTimers();
47
+ jest.clearAllMocks();
48
+
49
+ mockInstance = {
50
+ getOption: jest.fn(),
51
+ setOption: jest.fn(),
52
+ } as unknown as jest.Mocked<EChartsType>;
53
+
54
+ labelTimerRef = { current: null };
55
+ isGreaterThanTwoWeeksRef = { current: null };
56
+
57
+ mainData = {
58
+ dates: [
59
+ '2025-01-01T00:00:00Z',
60
+ '2025-01-02T00:00:00Z',
61
+ '2025-01-03T00:00:00Z',
62
+ '2025-01-04T00:00:00Z',
63
+ '2025-01-05T00:00:00Z',
64
+ ],
65
+ candlesOpen: [1.1, 1.2, 1.3, 1.4, 1.5],
66
+ candlesClose: [1.15, 1.25, 1.35, 1.45, 1.55],
67
+ candlesLow: [1.05, 1.15, 1.25, 1.35, 1.45],
68
+ candlesHigh: [1.2, 1.3, 1.4, 1.5, 1.6],
69
+ sentimentShorts: [10, 20, 30, 40, 50],
70
+ sentimentLongs: [90, 80, 70, 60, 50],
71
+ };
72
+
73
+ mockFormatXAxisLabel.mockReturnValue('formatted-label');
74
+ mockFormatXAxisAdditionalLabel.mockReturnValue('formatted-additional');
75
+ mockGetLabelData.mockReturnValue([
76
+ '2025-01-02T00:00:00Z',
77
+ '2025-01-04T00:00:00Z',
78
+ ]);
79
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(false);
80
+ });
81
+
82
+ afterEach(() => {
83
+ jest.runOnlyPendingTimers();
84
+ jest.useRealTimers();
85
+ });
86
+
87
+ describe('when dataZoom is invalid', () => {
88
+ it('should return early if dataZoom is not an array', () => {
89
+ mockInstance.getOption.mockReturnValue({
90
+ dataZoom: null,
91
+ xAxis: [],
92
+ });
93
+
94
+ handleLabelUpdate(
95
+ mockInstance,
96
+ mainData,
97
+ labelTimerRef,
98
+ isGreaterThanTwoWeeksRef,
99
+ 'en-US'
100
+ );
101
+
102
+ jest.advanceTimersByTime(50);
103
+
104
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
105
+ });
106
+
107
+ it('should return early if dataZoom array is empty', () => {
108
+ mockInstance.getOption.mockReturnValue({
109
+ dataZoom: [],
110
+ xAxis: [],
111
+ });
112
+
113
+ handleLabelUpdate(
114
+ mockInstance,
115
+ mainData,
116
+ labelTimerRef,
117
+ isGreaterThanTwoWeeksRef,
118
+ 'en-US'
119
+ );
120
+
121
+ jest.advanceTimersByTime(50);
122
+
123
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
124
+ });
125
+
126
+ it('should return early if dataZoom[0] is null', () => {
127
+ mockInstance.getOption.mockReturnValue({
128
+ dataZoom: [null],
129
+ xAxis: [],
130
+ });
131
+
132
+ handleLabelUpdate(
133
+ mockInstance,
134
+ mainData,
135
+ labelTimerRef,
136
+ isGreaterThanTwoWeeksRef,
137
+ 'en-US'
138
+ );
139
+
140
+ jest.advanceTimersByTime(50);
141
+
142
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
143
+ });
144
+
145
+ it('should return early if startValue is not a number', () => {
146
+ mockInstance.getOption.mockReturnValue({
147
+ dataZoom: [{ startValue: 'invalid', endValue: 3 }],
148
+ xAxis: [],
149
+ });
150
+
151
+ handleLabelUpdate(
152
+ mockInstance,
153
+ mainData,
154
+ labelTimerRef,
155
+ isGreaterThanTwoWeeksRef,
156
+ 'en-US'
157
+ );
158
+
159
+ jest.advanceTimersByTime(50);
160
+
161
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
162
+ });
163
+
164
+ it('should return early if endValue is not a number', () => {
165
+ mockInstance.getOption.mockReturnValue({
166
+ dataZoom: [{ startValue: 0, endValue: 'invalid' }],
167
+ xAxis: [],
168
+ });
169
+
170
+ handleLabelUpdate(
171
+ mockInstance,
172
+ mainData,
173
+ labelTimerRef,
174
+ isGreaterThanTwoWeeksRef,
175
+ 'en-US'
176
+ );
177
+
178
+ jest.advanceTimersByTime(50);
179
+
180
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
181
+ });
182
+ });
183
+
184
+ describe('when dates are invalid', () => {
185
+ it('should return early if startValue date is missing', () => {
186
+ mockInstance.getOption.mockReturnValue({
187
+ dataZoom: [{ startValue: 10, endValue: 3 }],
188
+ xAxis: [],
189
+ });
190
+
191
+ handleLabelUpdate(
192
+ mockInstance,
193
+ mainData,
194
+ labelTimerRef,
195
+ isGreaterThanTwoWeeksRef,
196
+ 'en-US'
197
+ );
198
+
199
+ jest.advanceTimersByTime(50);
200
+
201
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
202
+ });
203
+
204
+ it('should return early if endValue date is missing', () => {
205
+ mockInstance.getOption.mockReturnValue({
206
+ dataZoom: [{ startValue: 0, endValue: 10 }],
207
+ xAxis: [],
208
+ });
209
+
210
+ handleLabelUpdate(
211
+ mockInstance,
212
+ mainData,
213
+ labelTimerRef,
214
+ isGreaterThanTwoWeeksRef,
215
+ 'en-US'
216
+ );
217
+
218
+ jest.advanceTimersByTime(50);
219
+
220
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
221
+ });
222
+ });
223
+
224
+ describe('when state changes', () => {
225
+ it('should update xAxis when isGreaterThanTwoWeeks changes from null to true', () => {
226
+ isGreaterThanTwoWeeksRef.current = null;
227
+ mockInstance.getOption.mockReturnValue({
228
+ dataZoom: [{ startValue: 0, endValue: 3 }],
229
+ xAxis: [],
230
+ });
231
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(true);
232
+
233
+ handleLabelUpdate(
234
+ mockInstance,
235
+ mainData,
236
+ labelTimerRef,
237
+ isGreaterThanTwoWeeksRef,
238
+ 'en-US'
239
+ );
240
+
241
+ jest.advanceTimersByTime(50);
242
+
243
+ expect(mockInstance.setOption).toHaveBeenCalledWith({
244
+ xAxis: [
245
+ {
246
+ id: 'main-xAxis',
247
+ axisLabel: {
248
+ formatter: expect.any(Function),
249
+ },
250
+ },
251
+ {
252
+ id: 'additional-xAxis',
253
+ axisLabel: {
254
+ customValues: ['2025-01-02T00:00:00Z', '2025-01-04T00:00:00Z'],
255
+ formatter: expect.any(Function),
256
+ },
257
+ },
258
+ ],
259
+ });
260
+ expect(isGreaterThanTwoWeeksRef.current).toBe(true);
261
+ });
262
+
263
+ it('should update xAxis when isGreaterThanTwoWeeks changes from false to true', () => {
264
+ isGreaterThanTwoWeeksRef.current = false;
265
+ mockInstance.getOption.mockReturnValue({
266
+ dataZoom: [{ startValue: 0, endValue: 3 }],
267
+ xAxis: [],
268
+ });
269
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(true);
270
+
271
+ handleLabelUpdate(
272
+ mockInstance,
273
+ mainData,
274
+ labelTimerRef,
275
+ isGreaterThanTwoWeeksRef,
276
+ 'en-US'
277
+ );
278
+
279
+ jest.advanceTimersByTime(50);
280
+
281
+ expect(mockInstance.setOption).toHaveBeenCalled();
282
+ expect(isGreaterThanTwoWeeksRef.current).toBe(true);
283
+ });
284
+
285
+ it('should not update xAxis when isGreaterThanTwoWeeks does not change', () => {
286
+ isGreaterThanTwoWeeksRef.current = false;
287
+ mockInstance.getOption.mockReturnValue({
288
+ dataZoom: [{ startValue: 0, endValue: 3 }],
289
+ xAxis: [],
290
+ });
291
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(false);
292
+
293
+ handleLabelUpdate(
294
+ mockInstance,
295
+ mainData,
296
+ labelTimerRef,
297
+ isGreaterThanTwoWeeksRef,
298
+ 'en-US'
299
+ );
300
+
301
+ jest.advanceTimersByTime(50);
302
+
303
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
304
+ expect(isGreaterThanTwoWeeksRef.current).toBe(false);
305
+ });
306
+ });
307
+
308
+ describe('timer management', () => {
309
+ it('should clear existing timeout before setting new one', () => {
310
+ const existingTimeout = setTimeout(() => {}, 1000);
311
+ labelTimerRef.current = existingTimeout;
312
+ const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
313
+
314
+ mockInstance.getOption.mockReturnValue({
315
+ dataZoom: [{ startValue: 0, endValue: 3 }],
316
+ xAxis: [],
317
+ });
318
+
319
+ handleLabelUpdate(
320
+ mockInstance,
321
+ mainData,
322
+ labelTimerRef,
323
+ isGreaterThanTwoWeeksRef,
324
+ 'en-US'
325
+ );
326
+
327
+ expect(clearTimeoutSpy).toHaveBeenCalledWith(existingTimeout);
328
+ clearTimeoutSpy.mockRestore();
329
+ });
330
+
331
+ it('should delay execution by 50ms', () => {
332
+ mockInstance.getOption.mockReturnValue({
333
+ dataZoom: [{ startValue: 0, endValue: 3 }],
334
+ xAxis: [],
335
+ });
336
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(true);
337
+ isGreaterThanTwoWeeksRef.current = false;
338
+
339
+ handleLabelUpdate(
340
+ mockInstance,
341
+ mainData,
342
+ labelTimerRef,
343
+ isGreaterThanTwoWeeksRef,
344
+ 'en-US'
345
+ );
346
+
347
+ // Should not be called before timeout
348
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
349
+
350
+ // Fast-forward 49ms
351
+ jest.advanceTimersByTime(49);
352
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
353
+
354
+ // Fast-forward 1ms more
355
+ jest.advanceTimersByTime(1);
356
+ expect(mockInstance.setOption).toHaveBeenCalled();
357
+ });
358
+ });
359
+
360
+ describe('formatter functions', () => {
361
+ it('should call formatXAxisLabel with correct parameters', () => {
362
+ isGreaterThanTwoWeeksRef.current = false;
363
+ mockInstance.getOption.mockReturnValue({
364
+ dataZoom: [{ startValue: 0, endValue: 3 }],
365
+ xAxis: [],
366
+ });
367
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(true);
368
+
369
+ handleLabelUpdate(
370
+ mockInstance,
371
+ mainData,
372
+ labelTimerRef,
373
+ isGreaterThanTwoWeeksRef,
374
+ 'en-US'
375
+ );
376
+
377
+ jest.advanceTimersByTime(50);
378
+
379
+ const setOptionCall = mockInstance.setOption.mock.calls[0][0] as {
380
+ xAxis: Array<{
381
+ id: string;
382
+ axisLabel: {
383
+ formatter: (value: string) => string;
384
+ };
385
+ }>;
386
+ };
387
+ const mainAxisFormatter = setOptionCall.xAxis[0].axisLabel.formatter;
388
+ mainAxisFormatter('2025-01-01T00:00:00Z');
389
+
390
+ expect(mockFormatXAxisLabel).toHaveBeenCalledWith(
391
+ '2025-01-01T00:00:00Z',
392
+ true,
393
+ 'en-US'
394
+ );
395
+ });
396
+
397
+ it('should call formatXAxisAdditionalLabel with correct parameters', () => {
398
+ isGreaterThanTwoWeeksRef.current = false;
399
+ mockInstance.getOption.mockReturnValue({
400
+ dataZoom: [{ startValue: 0, endValue: 3 }],
401
+ xAxis: [],
402
+ });
403
+ mockIsDifferenceGreaterThanTwoWeeks.mockReturnValue(true);
404
+
405
+ handleLabelUpdate(
406
+ mockInstance,
407
+ mainData,
408
+ labelTimerRef,
409
+ isGreaterThanTwoWeeksRef,
410
+ 'en-US'
411
+ );
412
+
413
+ jest.advanceTimersByTime(50);
414
+
415
+ const setOptionCall = mockInstance.setOption.mock.calls[0][0] as {
416
+ xAxis: Array<{
417
+ id: string;
418
+ axisLabel: {
419
+ formatter: (value: unknown) => string;
420
+ customValues?: string[];
421
+ };
422
+ }>;
423
+ };
424
+ const additionalAxisFormatter =
425
+ setOptionCall.xAxis[1].axisLabel.formatter;
426
+ additionalAxisFormatter('2025-01-02T00:00:00Z');
427
+
428
+ expect(mockFormatXAxisAdditionalLabel).toHaveBeenCalledWith(
429
+ '2025-01-02T00:00:00Z',
430
+ true,
431
+ 'en-US'
432
+ );
433
+ });
434
+ });
435
+ });
@@ -0,0 +1,140 @@
1
+ import type { EChartsType } from 'echarts';
2
+
3
+ import { handleTooltipUpdate } from '../../../../src/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate';
4
+
5
+ describe('handleTooltipUpdate', () => {
6
+ let mockInstance: jest.Mocked<EChartsType>;
7
+ let zoomTimerRef: { current: NodeJS.Timeout | null };
8
+ let tooltipVisibleRef: { current: boolean };
9
+
10
+ beforeEach(() => {
11
+ jest.useFakeTimers();
12
+ jest.clearAllMocks();
13
+
14
+ mockInstance = {
15
+ setOption: jest.fn(),
16
+ } as unknown as jest.Mocked<EChartsType>;
17
+
18
+ zoomTimerRef = { current: null };
19
+ tooltipVisibleRef = { current: true };
20
+ });
21
+
22
+ afterEach(() => {
23
+ jest.runOnlyPendingTimers();
24
+ jest.useRealTimers();
25
+ });
26
+
27
+ describe('when canHover is false', () => {
28
+ it('should not hide tooltip on touch devices', () => {
29
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, false);
30
+
31
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
32
+ expect(tooltipVisibleRef.current).toBe(true);
33
+ });
34
+ });
35
+
36
+ describe('when canHover is true', () => {
37
+ it('should hide tooltip immediately when visible', () => {
38
+ tooltipVisibleRef.current = true;
39
+
40
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
41
+
42
+ expect(mockInstance.setOption).toHaveBeenCalledWith({
43
+ tooltip: {
44
+ show: false,
45
+ },
46
+ });
47
+ expect(tooltipVisibleRef.current).toBe(false);
48
+ });
49
+
50
+ it('should not call setOption when tooltip is already hidden', () => {
51
+ tooltipVisibleRef.current = false;
52
+
53
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
54
+
55
+ expect(mockInstance.setOption).not.toHaveBeenCalled();
56
+ });
57
+
58
+ it('should clear existing timeout before setting new one', () => {
59
+ const existingTimeout = setTimeout(() => {}, 1000);
60
+ zoomTimerRef.current = existingTimeout;
61
+ const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
62
+
63
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
64
+
65
+ expect(clearTimeoutSpy).toHaveBeenCalledWith(existingTimeout);
66
+ clearTimeoutSpy.mockRestore();
67
+ });
68
+
69
+ it('should show tooltip after 100ms delay', () => {
70
+ tooltipVisibleRef.current = true;
71
+
72
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
73
+
74
+ // Tooltip should be hidden immediately
75
+ expect(tooltipVisibleRef.current).toBe(false);
76
+ expect(mockInstance.setOption).toHaveBeenCalledWith({
77
+ tooltip: {
78
+ show: false,
79
+ },
80
+ });
81
+
82
+ // Fast-forward time
83
+ jest.advanceTimersByTime(100);
84
+
85
+ // Tooltip should be shown again
86
+ expect(mockInstance.setOption).toHaveBeenCalledWith({
87
+ tooltip: {
88
+ show: true,
89
+ },
90
+ });
91
+ expect(tooltipVisibleRef.current).toBe(true);
92
+ });
93
+
94
+ it('should not show tooltip if it was already shown during timeout', () => {
95
+ tooltipVisibleRef.current = true;
96
+
97
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
98
+
99
+ // Manually set tooltip to visible before timeout
100
+ tooltipVisibleRef.current = true;
101
+
102
+ jest.advanceTimersByTime(100);
103
+
104
+ // Should not call setOption to show tooltip since it's already visible
105
+ const { calls } = mockInstance.setOption.mock;
106
+ expect(calls.length).toBe(1); // Only the hide call
107
+ expect(calls[0][0]).toEqual({
108
+ tooltip: {
109
+ show: false,
110
+ },
111
+ });
112
+ });
113
+
114
+ it('should reset timeout on subsequent calls', () => {
115
+ tooltipVisibleRef.current = true;
116
+
117
+ // First call
118
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
119
+ const firstTimeout = zoomTimerRef.current;
120
+
121
+ // Second call before timeout
122
+ jest.advanceTimersByTime(50);
123
+ handleTooltipUpdate(mockInstance, zoomTimerRef, tooltipVisibleRef, true);
124
+ const secondTimeout = zoomTimerRef.current;
125
+
126
+ // Timeouts should be different
127
+ expect(firstTimeout).not.toBe(secondTimeout);
128
+
129
+ // Fast-forward 100ms from second call (150ms total)
130
+ jest.advanceTimersByTime(100);
131
+
132
+ // Should only show tooltip once (from second call)
133
+ const showCalls = mockInstance.setOption.mock.calls.filter(
134
+ (call) =>
135
+ (call[0] as { tooltip?: { show: boolean } }).tooltip?.show === true
136
+ );
137
+ expect(showCalls.length).toBe(1);
138
+ });
139
+ });
140
+ });
@@ -21,7 +21,7 @@ describe('Crowd View Widget', () => {
21
21
  );
22
22
 
23
23
  const segments = getAllByTestId('legend-bar-segment');
24
- expect(segments.length).toBe(2);
24
+ expect(segments.length).toBe(4);
25
25
  });
26
26
 
27
27
  it('passes isDark prop to LegendBar components in light mode', () => {
@@ -31,7 +31,7 @@ describe('Crowd View Widget', () => {
31
31
  </MockLayoutProvider>
32
32
  );
33
33
  const segments = getAllByTestId('legend-bar-segment');
34
- expect(segments.length).toBe(2);
34
+ expect(segments.length).toBe(4);
35
35
  // Check that segments use light mode colors (either long or short)
36
36
  const styles = segments.map((segment) => segment.getAttribute('style'));
37
37
  const hasLightColors = styles.some(
@@ -49,7 +49,7 @@ describe('Crowd View Widget', () => {
49
49
  </MockLayoutProvider>
50
50
  );
51
51
  const segments = getAllByTestId('legend-bar-segment');
52
- expect(segments.length).toBe(2);
52
+ expect(segments.length).toBe(4);
53
53
  // Check that segments use dark mode colors (either long or short)
54
54
  const styles = segments.map((segment) => segment.getAttribute('style'));
55
55
  const hasDarkColors = styles.some(
@@ -11,58 +11,60 @@ import { COLOR_MAP } from '../../src/CrowdViewWidget/constants';
11
11
  describe('Crowd View Widget', () => {
12
12
  describe('components', () => {
13
13
  describe('<LegendBar />', () => {
14
- it('renders exactly 1 segment', () => {
14
+ it('renders exactly 2 segments', () => {
15
15
  const { getAllByTestId } = render(
16
16
  <LegendBar isDark={false} type="short" />
17
17
  );
18
18
  const segments = getAllByTestId('legend-bar-segment');
19
- expect(segments.length).toBe(1);
19
+ expect(segments.length).toBe(2);
20
20
  });
21
21
 
22
22
  it('uses light mode colors when isDark is false for long type', () => {
23
- const { getByTestId } = render(
23
+ const { getAllByTestId } = render(
24
24
  <LegendBar isDark={false} type="long" />
25
25
  );
26
- const segment = getByTestId('legend-bar-segment');
27
- const style = segment.getAttribute('style');
26
+ const segments = getAllByTestId('legend-bar-segment');
27
+ const style = segments[0].getAttribute('style');
28
28
  expect(style).toContain(COLOR_MAP.light.long[0]);
29
29
  expect(style).toContain(COLOR_MAP.light.long[1]);
30
30
  });
31
31
 
32
32
  it('uses dark mode colors when isDark is true for long type', () => {
33
- const { getByTestId } = render(<LegendBar isDark={true} type="long" />);
34
- const segment = getByTestId('legend-bar-segment');
35
- const style = segment.getAttribute('style');
33
+ const { getAllByTestId } = render(
34
+ <LegendBar isDark={true} type="long" />
35
+ );
36
+ const segments = getAllByTestId('legend-bar-segment');
37
+ const style = segments[0].getAttribute('style');
36
38
  expect(style).toContain(COLOR_MAP.dark.long[0]);
37
39
  expect(style).toContain(COLOR_MAP.dark.long[1]);
38
40
  });
39
41
 
40
42
  it('uses light mode short colors when isDark is false', () => {
41
- const { getByTestId } = render(
43
+ const { getAllByTestId } = render(
42
44
  <LegendBar isDark={false} type="short" />
43
45
  );
44
- const segment = getByTestId('legend-bar-segment');
45
- const style = segment.getAttribute('style');
46
+ const segments = getAllByTestId('legend-bar-segment');
47
+ const style = segments[0].getAttribute('style');
46
48
  expect(style).toContain(COLOR_MAP.light.short[0]);
47
49
  expect(style).toContain(COLOR_MAP.light.short[1]);
48
50
  });
49
51
 
50
52
  it('uses dark mode short colors when isDark is true', () => {
51
- const { getByTestId } = render(
53
+ const { getAllByTestId } = render(
52
54
  <LegendBar isDark={true} type="short" />
53
55
  );
54
- const segment = getByTestId('legend-bar-segment');
55
- const style = segment.getAttribute('style');
56
+ const segments = getAllByTestId('legend-bar-segment');
57
+ const style = segments[0].getAttribute('style');
56
58
  expect(style).toContain(COLOR_MAP.dark.short[0]);
57
59
  expect(style).toContain(COLOR_MAP.dark.short[1]);
58
60
  });
59
61
 
60
62
  it('renders gradient with correct direction for long type', () => {
61
- const { getByTestId } = render(
63
+ const { getAllByTestId } = render(
62
64
  <LegendBar isDark={false} type="long" />
63
65
  );
64
- const segment = getByTestId('legend-bar-segment');
65
- const style = segment.getAttribute('style');
66
+ const segments = getAllByTestId('legend-bar-segment');
67
+ const style = segments[0].getAttribute('style');
66
68
  // Long type should use long[1] as start and long[0] as end
67
69
  expect(style).toMatch(/linear-gradient\(90deg/);
68
70
  expect(style).toContain(COLOR_MAP.light.long[1]);
@@ -70,11 +72,11 @@ describe('Crowd View Widget', () => {
70
72
  });
71
73
 
72
74
  it('renders gradient with correct direction for short type', () => {
73
- const { getByTestId } = render(
75
+ const { getAllByTestId } = render(
74
76
  <LegendBar isDark={false} type="short" />
75
77
  );
76
- const segment = getByTestId('legend-bar-segment');
77
- const style = segment.getAttribute('style');
78
+ const segments = getAllByTestId('legend-bar-segment');
79
+ const style = segments[0].getAttribute('style');
78
80
  // Short type should use short[0] as start and short[1] as end
79
81
  expect(style).toMatch(/linear-gradient\(90deg/);
80
82
  expect(style).toContain(COLOR_MAP.light.short[0]);