@carto/ps-react-ui 4.3.3 → 4.3.5

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 (301) hide show
  1. package/dist/components.js +3 -3
  2. package/dist/components.js.map +1 -1
  3. package/dist/{lasso-tool-BwRzEW7k.js → lasso-tool-wFqOD6wk.js} +13 -13
  4. package/dist/lasso-tool-wFqOD6wk.js.map +1 -0
  5. package/dist/types/components/common-types.d.ts +41 -0
  6. package/dist/types/components/types.d.ts +1 -1
  7. package/dist/types/widgets/echart/echart-ui.d.ts +1 -1
  8. package/dist/types/widgets/echart/types.d.ts +4 -0
  9. package/dist/widgets/actions.js +1 -1
  10. package/dist/widgets/bar.js +1 -1
  11. package/dist/widgets/category.js +1 -1
  12. package/dist/widgets/echart.js +96 -85
  13. package/dist/widgets/echart.js.map +1 -1
  14. package/dist/widgets/formula.js +1 -1
  15. package/dist/widgets/histogram.js +1 -1
  16. package/dist/widgets/markdown.js +1 -1
  17. package/dist/widgets/pie.js +1 -1
  18. package/dist/widgets/scatterplot.js +1 -1
  19. package/dist/widgets/spread.js +1 -1
  20. package/dist/widgets/table.js +1 -1
  21. package/dist/widgets/timeseries.js +1 -1
  22. package/dist/widgets/toolbar-actions.js.map +1 -1
  23. package/dist/widgets/wrapper.js +1 -1
  24. package/package.json +8 -3
  25. package/src/components/basemaps/basemaps.test.tsx +196 -0
  26. package/src/components/basemaps/basemaps.tsx +128 -0
  27. package/src/components/basemaps/const.ts +13 -0
  28. package/src/components/basemaps/group-wrapper.test.tsx +38 -0
  29. package/src/components/basemaps/group-wrapper.tsx +28 -0
  30. package/src/components/basemaps/group.test.tsx +52 -0
  31. package/src/components/basemaps/group.tsx +42 -0
  32. package/src/components/basemaps/header.test.tsx +54 -0
  33. package/src/components/basemaps/header.tsx +36 -0
  34. package/src/components/basemaps/styles.ts +76 -0
  35. package/src/components/basemaps/types.ts +30 -0
  36. package/src/components/common-types.ts +1 -0
  37. package/src/components/geolocation-controls/const.ts +6 -0
  38. package/src/components/geolocation-controls/geolocation-controls.test.tsx +133 -0
  39. package/src/components/geolocation-controls/geolocation-controls.tsx +95 -0
  40. package/src/components/geolocation-controls/types.ts +17 -0
  41. package/src/components/index.ts +64 -0
  42. package/src/components/lasso-tool/chip.tsx +37 -0
  43. package/src/components/lasso-tool/const.tsx +70 -0
  44. package/src/components/lasso-tool/icons.tsx +91 -0
  45. package/src/components/lasso-tool/lasso-tool-inline.test.tsx +168 -0
  46. package/src/components/lasso-tool/lasso-tool-inline.tsx +245 -0
  47. package/src/components/lasso-tool/lasso-tool.test.tsx +212 -0
  48. package/src/components/lasso-tool/lasso-tool.tsx +479 -0
  49. package/src/components/lasso-tool/styles.ts +143 -0
  50. package/src/components/lasso-tool/types.ts +114 -0
  51. package/src/components/list-data/list-data-skeleton.test.tsx +10 -0
  52. package/src/components/list-data/list-data-skeleton.tsx +40 -0
  53. package/src/components/list-data/list-data.test.tsx +94 -0
  54. package/src/components/list-data/list-data.tsx +106 -0
  55. package/src/components/list-data/styles.ts +37 -0
  56. package/src/components/list-data/types.ts +25 -0
  57. package/src/components/measurement-tools/const.tsx +108 -0
  58. package/src/components/measurement-tools/icons.tsx +54 -0
  59. package/src/components/measurement-tools/measurement-tools.test.tsx +165 -0
  60. package/src/components/measurement-tools/measurement-tools.tsx +443 -0
  61. package/src/components/measurement-tools/styles.ts +91 -0
  62. package/src/components/measurement-tools/types.ts +77 -0
  63. package/src/components/no-data-alert/no-data-alert.test.tsx +31 -0
  64. package/src/components/no-data-alert/no-data-alert.tsx +59 -0
  65. package/src/components/responsive-drawer/responsive-drawer.test.tsx +91 -0
  66. package/src/components/responsive-drawer/responsive-drawer.tsx +53 -0
  67. package/src/components/smart-tooltip/smart-tooltip.test.tsx +168 -0
  68. package/src/components/smart-tooltip/smart-tooltip.tsx +40 -0
  69. package/src/components/tooltip/tooltip.test.tsx +86 -0
  70. package/src/components/tooltip/tooltip.tsx +30 -0
  71. package/src/components/types.ts +1 -0
  72. package/src/components/zoom-controls/styles.ts +27 -0
  73. package/src/components/zoom-controls/types.ts +19 -0
  74. package/src/components/zoom-controls/zoom-controls.test.tsx +101 -0
  75. package/src/components/zoom-controls/zoom-controls.tsx +114 -0
  76. package/src/hooks/index.ts +2 -0
  77. package/src/hooks/use-debounce.ts +55 -0
  78. package/src/hooks/use-skeleton.test.tsx +32 -0
  79. package/src/hooks/use-skeleton.ts +19 -0
  80. package/src/hooks/use-widget-ref.ts +33 -0
  81. package/src/widgets/README.md +277 -0
  82. package/src/widgets/_shared/chart-config/config-factory.ts +67 -0
  83. package/src/widgets/_shared/chart-config/csv-modifiers.ts +56 -0
  84. package/src/widgets/_shared/chart-config/index.ts +21 -0
  85. package/src/widgets/_shared/chart-config/option-builders.ts +203 -0
  86. package/src/widgets/_shared/skeleton/index.ts +5 -0
  87. package/src/widgets/_shared/skeleton/styles.ts +20 -0
  88. package/src/widgets/actions/change-column/change-column-icon.tsx +10 -0
  89. package/src/widgets/actions/change-column/change-column.test.tsx +163 -0
  90. package/src/widgets/actions/change-column/change-column.tsx +141 -0
  91. package/src/widgets/actions/change-column/sortable-column-item.tsx +49 -0
  92. package/src/widgets/actions/change-column/types.ts +20 -0
  93. package/src/widgets/actions/download/download.test.tsx +322 -0
  94. package/src/widgets/actions/download/download.tsx +118 -0
  95. package/src/widgets/actions/download/exports.test.tsx +275 -0
  96. package/src/widgets/actions/download/exports.tsx +103 -0
  97. package/src/widgets/actions/download/types.ts +21 -0
  98. package/src/widgets/actions/fullscreen/fullscreen.test.tsx +269 -0
  99. package/src/widgets/actions/fullscreen/fullscreen.tsx +82 -0
  100. package/src/widgets/actions/fullscreen/styles.ts +17 -0
  101. package/src/widgets/actions/fullscreen/types.ts +27 -0
  102. package/src/widgets/actions/index.ts +51 -0
  103. package/src/widgets/actions/lock-selection/lock-selection.test.tsx +186 -0
  104. package/src/widgets/actions/lock-selection/lock-selection.tsx +133 -0
  105. package/src/widgets/actions/lock-selection/types.ts +41 -0
  106. package/src/widgets/actions/relative-data/relative-data.test.tsx +267 -0
  107. package/src/widgets/actions/relative-data/relative-data.tsx +133 -0
  108. package/src/widgets/actions/relative-data/style.ts +9 -0
  109. package/src/widgets/actions/relative-data/types.ts +31 -0
  110. package/src/widgets/actions/relative-data/utils.test.ts +223 -0
  111. package/src/widgets/actions/relative-data/utils.ts +58 -0
  112. package/src/widgets/actions/searcher/searcher-toggle.test.tsx +354 -0
  113. package/src/widgets/actions/searcher/searcher-toggle.tsx +73 -0
  114. package/src/widgets/actions/searcher/searcher.tsx +205 -0
  115. package/src/widgets/actions/searcher/types.ts +72 -0
  116. package/src/widgets/actions/shared/styles.ts +12 -0
  117. package/src/widgets/actions/stack-toggle/grouped-bar-chart-icon.tsx +14 -0
  118. package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +270 -0
  119. package/src/widgets/actions/stack-toggle/stack-toggle.tsx +146 -0
  120. package/src/widgets/actions/stack-toggle/types.ts +29 -0
  121. package/src/widgets/actions/zoom-toggle/index.ts +2 -0
  122. package/src/widgets/actions/zoom-toggle/style.ts +14 -0
  123. package/src/widgets/actions/zoom-toggle/types.ts +44 -0
  124. package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +186 -0
  125. package/src/widgets/bar/config.ts +122 -0
  126. package/src/widgets/bar/index.ts +9 -0
  127. package/src/widgets/bar/skeleton.tsx +60 -0
  128. package/src/widgets/bar/style.ts +33 -0
  129. package/src/widgets/bar/types.ts +16 -0
  130. package/src/widgets/category/category-ui.test.tsx +399 -0
  131. package/src/widgets/category/category-ui.tsx +156 -0
  132. package/src/widgets/category/components/category-bar.tsx +28 -0
  133. package/src/widgets/category/components/category-legend.tsx +30 -0
  134. package/src/widgets/category/components/category-row-multi.tsx +50 -0
  135. package/src/widgets/category/components/category-row-other.tsx +23 -0
  136. package/src/widgets/category/components/category-row-single.tsx +47 -0
  137. package/src/widgets/category/components/index.ts +14 -0
  138. package/src/widgets/category/config.ts +85 -0
  139. package/src/widgets/category/index.ts +30 -0
  140. package/src/widgets/category/skeleton.tsx +24 -0
  141. package/src/widgets/category/style.ts +133 -0
  142. package/src/widgets/category/types.ts +40 -0
  143. package/src/widgets/echart/const.ts +1 -0
  144. package/src/widgets/echart/echart-ui.test.tsx +537 -0
  145. package/src/widgets/echart/echart-ui.tsx +92 -0
  146. package/src/widgets/echart/echart.test.tsx +562 -0
  147. package/src/widgets/echart/echart.tsx +68 -0
  148. package/src/widgets/echart/index.ts +16 -0
  149. package/src/widgets/echart/options.ts +53 -0
  150. package/src/widgets/echart/types.ts +45 -0
  151. package/src/widgets/echart/utils.ts +169 -0
  152. package/src/widgets/error/error.test.tsx +331 -0
  153. package/src/widgets/error/error.tsx +40 -0
  154. package/src/widgets/error/index.ts +2 -0
  155. package/src/widgets/error/types.ts +14 -0
  156. package/src/widgets/formula/components/item.test.tsx +249 -0
  157. package/src/widgets/formula/components/item.tsx +18 -0
  158. package/src/widgets/formula/components/prefix.test.tsx +341 -0
  159. package/src/widgets/formula/components/prefix.tsx +18 -0
  160. package/src/widgets/formula/components/row.test.tsx +364 -0
  161. package/src/widgets/formula/components/row.tsx +21 -0
  162. package/src/widgets/formula/components/series.tsx +34 -0
  163. package/src/widgets/formula/components/suffix.test.tsx +383 -0
  164. package/src/widgets/formula/components/suffix.tsx +28 -0
  165. package/src/widgets/formula/components/value.test.tsx +329 -0
  166. package/src/widgets/formula/components/value.tsx +29 -0
  167. package/src/widgets/formula/config.ts +27 -0
  168. package/src/widgets/formula/formula-ui.test.tsx +399 -0
  169. package/src/widgets/formula/formula-ui.tsx +27 -0
  170. package/src/widgets/formula/index.ts +24 -0
  171. package/src/widgets/formula/serializer.test.tsx +144 -0
  172. package/src/widgets/formula/serializer.ts +28 -0
  173. package/src/widgets/formula/skeleton.tsx +10 -0
  174. package/src/widgets/formula/style.ts +23 -0
  175. package/src/widgets/formula/types.ts +50 -0
  176. package/src/widgets/histogram/config.ts +143 -0
  177. package/src/widgets/histogram/index.ts +8 -0
  178. package/src/widgets/histogram/skeleton.tsx +52 -0
  179. package/src/widgets/histogram/style.ts +8 -0
  180. package/src/widgets/histogram/types.ts +17 -0
  181. package/src/widgets/index.ts +25 -0
  182. package/src/widgets/loader/index.ts +4 -0
  183. package/src/widgets/loader/loader.tsx +70 -0
  184. package/src/widgets/loader/types.ts +11 -0
  185. package/src/widgets/loader/utils.test.ts +112 -0
  186. package/src/widgets/loader/utils.ts +35 -0
  187. package/src/widgets/markdown/config.ts +18 -0
  188. package/src/widgets/markdown/index.ts +14 -0
  189. package/src/widgets/markdown/markdown-ui.test.tsx +341 -0
  190. package/src/widgets/markdown/markdown-ui.tsx +52 -0
  191. package/src/widgets/markdown/markdown.tsx +20 -0
  192. package/src/widgets/markdown/skeleton.tsx +12 -0
  193. package/src/widgets/markdown/style.ts +28 -0
  194. package/src/widgets/markdown/types.ts +28 -0
  195. package/src/widgets/no-data/index.ts +2 -0
  196. package/src/widgets/no-data/no-data.test.tsx +447 -0
  197. package/src/widgets/no-data/no-data.tsx +116 -0
  198. package/src/widgets/no-data/style.ts +18 -0
  199. package/src/widgets/no-data/types.ts +72 -0
  200. package/src/widgets/note/index.ts +2 -0
  201. package/src/widgets/note/note.test.tsx +391 -0
  202. package/src/widgets/note/note.tsx +114 -0
  203. package/src/widgets/note/style.ts +29 -0
  204. package/src/widgets/note/types.ts +9 -0
  205. package/src/widgets/pie/config.ts +177 -0
  206. package/src/widgets/pie/index.ts +8 -0
  207. package/src/widgets/pie/skeleton.tsx +70 -0
  208. package/src/widgets/pie/style.ts +8 -0
  209. package/src/widgets/pie/types.ts +16 -0
  210. package/src/widgets/range/components/range-item.tsx +213 -0
  211. package/src/widgets/range/config.ts +10 -0
  212. package/src/widgets/range/index.ts +16 -0
  213. package/src/widgets/range/range-ui.test.tsx +203 -0
  214. package/src/widgets/range/range-ui.tsx +11 -0
  215. package/src/widgets/range/serializer.test.ts +70 -0
  216. package/src/widgets/range/serializer.ts +27 -0
  217. package/src/widgets/range/skeleton.tsx +14 -0
  218. package/src/widgets/range/style.ts +37 -0
  219. package/src/widgets/range/types.ts +39 -0
  220. package/src/widgets/scatterplot/config.ts +138 -0
  221. package/src/widgets/scatterplot/index.ts +8 -0
  222. package/src/widgets/scatterplot/skeleton.tsx +59 -0
  223. package/src/widgets/scatterplot/style.ts +21 -0
  224. package/src/widgets/scatterplot/types.ts +17 -0
  225. package/src/widgets/selection-summary/index.ts +6 -0
  226. package/src/widgets/selection-summary/selection-summary.tsx +46 -0
  227. package/src/widgets/selection-summary/style.ts +10 -0
  228. package/src/widgets/selection-summary/types.ts +14 -0
  229. package/src/widgets/skeleton-loader/index.ts +2 -0
  230. package/src/widgets/skeleton-loader/skeleton-loader.test.tsx +139 -0
  231. package/src/widgets/skeleton-loader/skeleton-loader.tsx +28 -0
  232. package/src/widgets/skeleton-loader/types.ts +8 -0
  233. package/src/widgets/spread/components/max-value.tsx +29 -0
  234. package/src/widgets/spread/components/min-value.tsx +29 -0
  235. package/src/widgets/spread/components/separator.tsx +6 -0
  236. package/src/widgets/spread/config.ts +34 -0
  237. package/src/widgets/spread/index.ts +23 -0
  238. package/src/widgets/spread/skeleton.tsx +10 -0
  239. package/src/widgets/spread/spread-ui.test.tsx +368 -0
  240. package/src/widgets/spread/spread-ui.tsx +29 -0
  241. package/src/widgets/spread/style.ts +22 -0
  242. package/src/widgets/spread/types.ts +47 -0
  243. package/src/widgets/stores/index.ts +9 -0
  244. package/src/widgets/stores/types.ts +192 -0
  245. package/src/widgets/stores/widget-store.test.ts +601 -0
  246. package/src/widgets/stores/widget-store.ts +239 -0
  247. package/src/widgets/subheader/index.ts +3 -0
  248. package/src/widgets/subheader/style.ts +20 -0
  249. package/src/widgets/subheader/subheader.test.tsx +45 -0
  250. package/src/widgets/subheader/subheader.tsx +16 -0
  251. package/src/widgets/subheader/types.ts +11 -0
  252. package/src/widgets/table/components/cell-header.tsx +58 -0
  253. package/src/widgets/table/components/cell.tsx +80 -0
  254. package/src/widgets/table/components/index.ts +4 -0
  255. package/src/widgets/table/components/pagination-actions.tsx +67 -0
  256. package/src/widgets/table/components/pagination.tsx +41 -0
  257. package/src/widgets/table/components/row.tsx +60 -0
  258. package/src/widgets/table/config.ts +71 -0
  259. package/src/widgets/table/helpers.test.ts +244 -0
  260. package/src/widgets/table/helpers.ts +107 -0
  261. package/src/widgets/table/hooks/index.ts +7 -0
  262. package/src/widgets/table/hooks/use-pagination.test.ts +294 -0
  263. package/src/widgets/table/hooks/use-pagination.ts +155 -0
  264. package/src/widgets/table/hooks/use-selection.test.ts +504 -0
  265. package/src/widgets/table/hooks/use-selection.ts +189 -0
  266. package/src/widgets/table/hooks/use-sort.test.ts +296 -0
  267. package/src/widgets/table/hooks/use-sort.ts +138 -0
  268. package/src/widgets/table/index.ts +53 -0
  269. package/src/widgets/table/serializer.ts +54 -0
  270. package/src/widgets/table/skeleton.tsx +48 -0
  271. package/src/widgets/table/style.ts +34 -0
  272. package/src/widgets/table/table-ui.tsx +64 -0
  273. package/src/widgets/table/types.ts +223 -0
  274. package/src/widgets/timeseries/config.ts +135 -0
  275. package/src/widgets/timeseries/index.ts +8 -0
  276. package/src/widgets/timeseries/skeleton.tsx +55 -0
  277. package/src/widgets/timeseries/style.ts +36 -0
  278. package/src/widgets/timeseries/types.ts +17 -0
  279. package/src/widgets/toolbar-actions/index.ts +6 -0
  280. package/src/widgets/toolbar-actions/styles.ts +38 -0
  281. package/src/widgets/toolbar-actions/toolbar-actions.test.tsx +691 -0
  282. package/src/widgets/toolbar-actions/toolbar-actions.tsx +145 -0
  283. package/src/widgets/toolbar-actions/types.ts +60 -0
  284. package/src/widgets/wrapper/components/actions.test.tsx +101 -0
  285. package/src/widgets/wrapper/components/actions.tsx +30 -0
  286. package/src/widgets/wrapper/components/options.test.tsx +323 -0
  287. package/src/widgets/wrapper/components/options.tsx +73 -0
  288. package/src/widgets/wrapper/components/title.test.tsx +126 -0
  289. package/src/widgets/wrapper/components/title.tsx +32 -0
  290. package/src/widgets/wrapper/index.ts +16 -0
  291. package/src/widgets/wrapper/styles.ts +98 -0
  292. package/src/widgets/wrapper/types.ts +55 -0
  293. package/src/widgets/wrapper/wrapper-ui.test.tsx +232 -0
  294. package/src/widgets/wrapper/wrapper-ui.tsx +57 -0
  295. package/src/widgets/wrapper/wrapper.test.tsx +365 -0
  296. package/src/widgets/wrapper/wrapper.tsx +50 -0
  297. package/dist/lasso-tool-BwRzEW7k.js.map +0 -1
  298. package/dist/types/common/common.d.ts +0 -3
  299. package/dist/types/common/index.d.ts +0 -26
  300. package/dist/types/common/lasso-tools.d.ts +0 -36
  301. package/dist/types/common/measurement-tools.d.ts +0 -65
@@ -0,0 +1,562 @@
1
+ import { describe, test, expect, beforeEach, vi } from 'vitest'
2
+ import { render } from '@testing-library/react'
3
+ import { Echart } from './echart'
4
+ import { useWidgetStore } from '../stores/widget-store'
5
+ import type { EchartWidgetState } from './types'
6
+ import type { EChartsOption } from 'echarts'
7
+ import * as echarts from 'echarts'
8
+
9
+ // Mock echarts module
10
+ vi.mock('echarts', () => {
11
+ const mockChart = {
12
+ setOption: vi.fn(),
13
+ dispose: vi.fn(),
14
+ resize: vi.fn(),
15
+ on: vi.fn(),
16
+ off: vi.fn(),
17
+ getZr: vi.fn(() => ({
18
+ setCursorStyle: vi.fn(),
19
+ })),
20
+ }
21
+
22
+ return {
23
+ init: vi.fn(() => mockChart),
24
+ }
25
+ })
26
+
27
+ // Mock MUI theme
28
+ vi.mock('@mui/material', async () => {
29
+ const actual = await vi.importActual('@mui/material')
30
+ return {
31
+ ...actual,
32
+ useTheme: () => ({
33
+ spacing: (value: number) => `${value * 8}px`,
34
+ palette: {
35
+ secondary: {
36
+ main: '#47db99',
37
+ },
38
+ grey: {
39
+ 400: '#bdbdbd',
40
+ 900: '#212121',
41
+ },
42
+ common: {
43
+ white: '#ffffff',
44
+ black: '#000000',
45
+ },
46
+ qualitative: {
47
+ bold: {
48
+ 1: '#7F3C8D',
49
+ 2: '#11A579',
50
+ 3: '#3969AC',
51
+ 4: '#F2B701',
52
+ 5: '#E73F74',
53
+ 6: '#80BA5A',
54
+ 7: '#E68310',
55
+ 8: '#008695',
56
+ 9: '#CF1C90',
57
+ 10: '#F97B72',
58
+ },
59
+ },
60
+ },
61
+ typography: {
62
+ caption: {
63
+ fontFamily: 'Roboto, sans-serif',
64
+ },
65
+ },
66
+ }),
67
+ }
68
+ })
69
+
70
+ describe('Echart', () => {
71
+ let mockChart: {
72
+ setOption: ReturnType<typeof vi.fn>
73
+ dispose: ReturnType<typeof vi.fn>
74
+ resize: ReturnType<typeof vi.fn>
75
+ on: ReturnType<typeof vi.fn>
76
+ off: ReturnType<typeof vi.fn>
77
+ getZr: ReturnType<typeof vi.fn>
78
+ }
79
+ let mockResizeObserver: {
80
+ observe: ReturnType<typeof vi.fn>
81
+ disconnect: ReturnType<typeof vi.fn>
82
+ unobserve: ReturnType<typeof vi.fn>
83
+ }
84
+
85
+ const basicOption: EChartsOption = {
86
+ title: {
87
+ text: 'Test Chart',
88
+ },
89
+ series: [
90
+ {
91
+ type: 'bar',
92
+ data: [1, 2, 3, 4, 5],
93
+ },
94
+ ],
95
+ }
96
+
97
+ beforeEach(() => {
98
+ vi.clearAllMocks()
99
+ useWidgetStore.getState().clearWidgets()
100
+
101
+ // Create a fresh mock chart instance for each test
102
+ mockChart = {
103
+ setOption: vi.fn(),
104
+ dispose: vi.fn(),
105
+ resize: vi.fn(),
106
+ on: vi.fn(),
107
+ off: vi.fn(),
108
+ getZr: vi.fn(() => ({
109
+ setCursorStyle: vi.fn(),
110
+ })),
111
+ }
112
+
113
+ // Make echarts.init return our mock chart
114
+ vi.mocked(echarts.init).mockReturnValue(
115
+ mockChart as unknown as echarts.ECharts,
116
+ )
117
+
118
+ // Mock ResizeObserver
119
+ mockResizeObserver = {
120
+ observe: vi.fn(),
121
+ disconnect: vi.fn(),
122
+ unobserve: vi.fn(),
123
+ }
124
+
125
+ global.ResizeObserver = class {
126
+ observe = mockResizeObserver.observe
127
+ disconnect = mockResizeObserver.disconnect
128
+ unobserve = mockResizeObserver.unobserve
129
+ } as unknown as typeof ResizeObserver
130
+ })
131
+
132
+ test('returns null when widget does not exist in store', () => {
133
+ const { container } = render(<Echart id='non-existent-widget' />)
134
+ expect(container.firstChild).toBeNull()
135
+ })
136
+
137
+ test('renders EchartUI when widget exists in store', () => {
138
+ // Set up widget in store
139
+ useWidgetStore.getState().setWidget('test-echart', {
140
+ id: 'test-echart',
141
+ type: 'echart',
142
+ option: basicOption,
143
+ } as EchartWidgetState)
144
+
145
+ const { container } = render(<Echart id='test-echart' />)
146
+
147
+ const div = container.querySelector('div')
148
+ expect(div).toBeTruthy()
149
+ expect(echarts.init).toHaveBeenCalled()
150
+ })
151
+
152
+ test('passes option from widget data to EchartUI', () => {
153
+ const customOption: EChartsOption = {
154
+ title: {
155
+ text: 'Custom Chart',
156
+ },
157
+ series: [
158
+ {
159
+ type: 'line',
160
+ data: [10, 20, 30],
161
+ },
162
+ ],
163
+ }
164
+
165
+ useWidgetStore.getState().setWidget('test-echart', {
166
+ id: 'test-echart',
167
+ type: 'echart',
168
+ option: customOption,
169
+ } as EchartWidgetState)
170
+
171
+ render(<Echart id='test-echart' />)
172
+
173
+ expect(mockChart.setOption).toHaveBeenCalledWith(
174
+ expect.objectContaining({
175
+ title: {
176
+ text: 'Custom Chart',
177
+ },
178
+ series: [
179
+ {
180
+ type: 'line',
181
+ data: [10, 20, 30],
182
+ },
183
+ ],
184
+ }),
185
+ {
186
+ lazyUpdate: true,
187
+ replaceMerge: ['dataset', 'series'],
188
+ },
189
+ )
190
+ })
191
+
192
+ test('passes onEvents from widget to EchartUI', () => {
193
+ const clickHandler = vi.fn()
194
+ const hoverHandler = vi.fn()
195
+
196
+ useWidgetStore.getState().setWidget<EchartWidgetState>('test-echart', {
197
+ type: 'echart',
198
+ option: basicOption,
199
+ onEvents: {
200
+ click: clickHandler,
201
+ mouseover: hoverHandler,
202
+ },
203
+ })
204
+
205
+ render(<Echart id='test-echart' />)
206
+
207
+ expect(mockChart.on).toHaveBeenCalledWith('click', clickHandler)
208
+ expect(mockChart.on).toHaveBeenCalledWith('mouseover', hoverHandler)
209
+ })
210
+
211
+ test('passes init options from widget to EchartUI', () => {
212
+ useWidgetStore.getState().setWidget('test-echart', {
213
+ id: 'test-echart',
214
+ type: 'echart',
215
+ option: basicOption,
216
+ init: {
217
+ renderer: 'canvas',
218
+ height: 500,
219
+ width: 800,
220
+ },
221
+ } as EchartWidgetState)
222
+
223
+ render(<Echart id='test-echart' />)
224
+
225
+ expect(echarts.init).toHaveBeenCalledWith(
226
+ expect.any(HTMLDivElement),
227
+ null,
228
+ expect.objectContaining({
229
+ renderer: 'canvas',
230
+ height: 500,
231
+ width: 800,
232
+ }),
233
+ )
234
+ })
235
+
236
+ test('handles widget with undefined onEvents', () => {
237
+ useWidgetStore.getState().setWidget('test-echart', {
238
+ id: 'test-echart',
239
+ type: 'echart',
240
+ option: basicOption,
241
+ onEvents: undefined,
242
+ } as EchartWidgetState)
243
+
244
+ expect(() => {
245
+ render(<Echart id='test-echart' />)
246
+ }).not.toThrow()
247
+ })
248
+
249
+ test('handles widget with undefined init', () => {
250
+ useWidgetStore.getState().setWidget('test-echart', {
251
+ id: 'test-echart',
252
+ type: 'echart',
253
+ option: basicOption,
254
+ init: undefined,
255
+ } as EchartWidgetState)
256
+
257
+ render(<Echart id='test-echart' />)
258
+
259
+ expect(echarts.init).toHaveBeenCalledWith(
260
+ expect.any(HTMLDivElement),
261
+ null,
262
+ expect.objectContaining({
263
+ renderer: 'svg',
264
+ height: 304,
265
+ }),
266
+ )
267
+ })
268
+
269
+ test('re-renders when widget data changes', () => {
270
+ useWidgetStore.getState().setWidget('test-echart', {
271
+ id: 'test-echart',
272
+ type: 'echart',
273
+ option: basicOption,
274
+ } as EchartWidgetState)
275
+
276
+ const { rerender } = render(<Echart id='test-echart' />)
277
+
278
+ expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
279
+ lazyUpdate: true,
280
+ replaceMerge: ['dataset', 'series'],
281
+ })
282
+
283
+ const newOption: EChartsOption = {
284
+ series: [
285
+ {
286
+ type: 'pie',
287
+ data: [{ value: 100 }, { value: 200 }],
288
+ },
289
+ ],
290
+ }
291
+
292
+ useWidgetStore.getState().setWidget('test-echart', {
293
+ id: 'test-echart',
294
+ type: 'echart',
295
+ option: newOption,
296
+ } as EchartWidgetState)
297
+
298
+ rerender(<Echart id='test-echart' />)
299
+
300
+ expect(mockChart.setOption).toHaveBeenCalledWith(newOption, {
301
+ lazyUpdate: true,
302
+ replaceMerge: ['dataset', 'series'],
303
+ })
304
+ })
305
+
306
+ test('returns null when widget is removed from store', () => {
307
+ useWidgetStore.getState().setWidget('test-echart', {
308
+ id: 'test-echart',
309
+ type: 'echart',
310
+ option: basicOption,
311
+ } as EchartWidgetState)
312
+
313
+ const { container, rerender } = render(<Echart id='test-echart' />)
314
+
315
+ expect(container.querySelector('div')).toBeTruthy()
316
+
317
+ useWidgetStore.getState().removeWidget('test-echart')
318
+ rerender(<Echart id='test-echart' />)
319
+
320
+ expect(container.firstChild).toBeNull()
321
+ })
322
+
323
+ test('handles complex chart options with multiple series', () => {
324
+ const complexOption: EChartsOption = {
325
+ title: {
326
+ text: 'Multi-Series Chart',
327
+ },
328
+ tooltip: {
329
+ trigger: 'axis',
330
+ },
331
+ legend: {
332
+ data: ['Series 1', 'Series 2', 'Series 3'],
333
+ },
334
+ xAxis: {
335
+ type: 'category',
336
+ data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
337
+ },
338
+ yAxis: {
339
+ type: 'value',
340
+ },
341
+ series: [
342
+ {
343
+ name: 'Series 1',
344
+ type: 'line',
345
+ data: [10, 20, 30, 40, 50],
346
+ },
347
+ {
348
+ name: 'Series 2',
349
+ type: 'bar',
350
+ data: [15, 25, 35, 45, 55],
351
+ },
352
+ {
353
+ name: 'Series 3',
354
+ type: 'scatter',
355
+ data: [5, 15, 25, 35, 45],
356
+ },
357
+ ],
358
+ }
359
+
360
+ useWidgetStore.getState().setWidget('test-echart', {
361
+ id: 'test-echart',
362
+ type: 'echart',
363
+ option: complexOption,
364
+ } as EchartWidgetState)
365
+
366
+ render(<Echart id='test-echart' />)
367
+
368
+ expect(mockChart.setOption).toHaveBeenCalledWith(complexOption, {
369
+ lazyUpdate: true,
370
+ replaceMerge: ['dataset', 'series'],
371
+ })
372
+ })
373
+
374
+ test('handles widget with all optional properties', () => {
375
+ const clickHandler = vi.fn()
376
+ const fullWidget: EchartWidgetState = {
377
+ id: 'test-echart',
378
+ type: 'echart',
379
+ data: [],
380
+ isFetching: false,
381
+ option: basicOption,
382
+ isLoading: false,
383
+ visible: true,
384
+ onEvents: {
385
+ click: clickHandler,
386
+ },
387
+ init: {
388
+ renderer: 'canvas',
389
+ height: 600,
390
+ },
391
+ }
392
+
393
+ useWidgetStore.getState().setWidget('test-echart', fullWidget)
394
+
395
+ render(<Echart id='test-echart' />)
396
+
397
+ expect(echarts.init).toHaveBeenCalledWith(
398
+ expect.any(HTMLDivElement),
399
+ null,
400
+ expect.objectContaining({
401
+ renderer: 'canvas',
402
+ height: 600,
403
+ }),
404
+ )
405
+ expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
406
+ lazyUpdate: true,
407
+ replaceMerge: ['dataset', 'series'],
408
+ })
409
+ expect(mockChart.on).toHaveBeenCalledWith('click', clickHandler)
410
+ })
411
+
412
+ test('handles multiple Echart instances with different IDs', () => {
413
+ useWidgetStore.getState().setWidget('chart-1', {
414
+ id: 'chart-1',
415
+ type: 'echart',
416
+ option: basicOption,
417
+ } as EchartWidgetState)
418
+
419
+ useWidgetStore.getState().setWidget('chart-2', {
420
+ id: 'chart-2',
421
+ type: 'echart',
422
+ option: {
423
+ series: [{ type: 'line', data: [5, 10, 15] }],
424
+ },
425
+ } as EchartWidgetState)
426
+
427
+ const { container: container1 } = render(<Echart id='chart-1' />)
428
+ const { container: container2 } = render(<Echart id='chart-2' />)
429
+
430
+ expect(container1.querySelector('div')).toBeTruthy()
431
+ expect(container2.querySelector('div')).toBeTruthy()
432
+ expect(echarts.init).toHaveBeenCalledTimes(2)
433
+ })
434
+
435
+ test('handles empty series data', () => {
436
+ const emptyOption: EChartsOption = {
437
+ series: [],
438
+ }
439
+
440
+ useWidgetStore.getState().setWidget('test-echart', {
441
+ id: 'test-echart',
442
+ type: 'echart',
443
+ option: emptyOption,
444
+ } as EchartWidgetState)
445
+
446
+ render(<Echart id='test-echart' />)
447
+
448
+ expect(mockChart.setOption).toHaveBeenCalledWith(emptyOption, {
449
+ lazyUpdate: true,
450
+ replaceMerge: ['dataset', 'series'],
451
+ })
452
+ })
453
+
454
+ test('handles widget data as unknown type (runtime typing)', () => {
455
+ // widget.data is typed as unknown at compile time but EChartsOption at runtime
456
+ const widgetData: unknown = basicOption
457
+
458
+ useWidgetStore.getState().setWidget('test-echart', {
459
+ id: 'test-echart',
460
+ type: 'echart',
461
+ option: widgetData as EChartsOption,
462
+ } as EchartWidgetState)
463
+
464
+ render(<Echart id='test-echart' />)
465
+
466
+ expect(mockChart.setOption).toHaveBeenCalledWith(
467
+ expect.objectContaining({
468
+ title: basicOption.title,
469
+ series: basicOption.series,
470
+ }),
471
+ {
472
+ lazyUpdate: true,
473
+ replaceMerge: ['dataset', 'series'],
474
+ },
475
+ )
476
+ })
477
+
478
+ test('passes through all event handlers from widget', () => {
479
+ const handlers = {
480
+ click: vi.fn(),
481
+ dblclick: vi.fn(),
482
+ mousedown: vi.fn(),
483
+ mouseup: vi.fn(),
484
+ mouseover: vi.fn(),
485
+ mouseout: vi.fn(),
486
+ mousemove: vi.fn(),
487
+ }
488
+
489
+ useWidgetStore.getState().setWidget<EchartWidgetState>('test-echart', {
490
+ type: 'echart',
491
+ option: basicOption,
492
+ onEvents: handlers,
493
+ })
494
+
495
+ render(<Echart id='test-echart' />)
496
+
497
+ Object.entries(handlers).forEach(([eventName, handler]) => {
498
+ expect(mockChart.on).toHaveBeenCalledWith(eventName, handler)
499
+ })
500
+ })
501
+
502
+ test('component updates when switching between different widget IDs', () => {
503
+ useWidgetStore.getState().setWidget('chart-a', {
504
+ id: 'chart-a',
505
+ type: 'echart',
506
+ option: basicOption,
507
+ } as EchartWidgetState)
508
+
509
+ useWidgetStore.getState().setWidget('chart-b', {
510
+ id: 'chart-b',
511
+ type: 'echart',
512
+ option: { series: [{ type: 'line', data: [1, 2, 3] }] },
513
+ } as EchartWidgetState)
514
+
515
+ const { rerender } = render(<Echart id='chart-a' />)
516
+
517
+ expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
518
+ lazyUpdate: true,
519
+ replaceMerge: ['dataset', 'series'],
520
+ })
521
+
522
+ rerender(<Echart id='chart-b' />)
523
+
524
+ expect(mockChart.setOption).toHaveBeenCalledWith(
525
+ { series: [{ type: 'line', data: [1, 2, 3] }] },
526
+ {
527
+ lazyUpdate: true,
528
+ replaceMerge: ['dataset', 'series'],
529
+ },
530
+ )
531
+ })
532
+
533
+ test('passes replaceMerge from widget to EchartUI', () => {
534
+ useWidgetStore.getState().setWidget('test-echart', {
535
+ id: 'test-echart',
536
+ type: 'echart',
537
+ option: basicOption,
538
+ replaceMerge: ['series'],
539
+ } as EchartWidgetState)
540
+
541
+ render(<Echart id='test-echart' />)
542
+
543
+ expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
544
+ lazyUpdate: true,
545
+ replaceMerge: ['series'],
546
+ })
547
+ })
548
+
549
+ test('disposes chart when component unmounts', () => {
550
+ useWidgetStore.getState().setWidget('test-echart', {
551
+ id: 'test-echart',
552
+ type: 'echart',
553
+ option: basicOption,
554
+ } as EchartWidgetState)
555
+
556
+ const { unmount } = render(<Echart id='test-echart' />)
557
+
558
+ unmount()
559
+
560
+ expect(mockChart.dispose).toHaveBeenCalled()
561
+ })
562
+ })
@@ -0,0 +1,68 @@
1
+ import type {
2
+ EchartProps,
3
+ EchartWidgetState,
4
+ EchartWidgetData,
5
+ EchartOptionsProps,
6
+ } from './types'
7
+ import { EchartUI } from './echart-ui'
8
+ import { useWidgetStore } from '../stores/widget-store'
9
+ import { useShallow } from 'zustand/shallow'
10
+ import { useMemo } from 'react'
11
+
12
+ export function Echart(props: EchartProps) {
13
+ const widget = useWidgetStore(
14
+ useShallow((state) => {
15
+ const widget = state.getWidget<EchartWidgetState>(props.id)
16
+ return {
17
+ id: widget?.id,
18
+ data: widget?.data as EchartWidgetData | undefined,
19
+ option: widget?.option,
20
+ onEvents: widget?.onEvents,
21
+ init: widget?.init,
22
+ replaceMerge: widget?.replaceMerge,
23
+ }
24
+ }),
25
+ )
26
+
27
+ // Memoize dataset transformation to avoid re-computing on every render
28
+ const dataset = useMemo(() => buildDataset(widget.data), [widget.data])
29
+
30
+ if (!widget.id) {
31
+ return null
32
+ }
33
+ const option: EchartOptionsProps = {
34
+ ...widget.option,
35
+ ...(dataset && { dataset }),
36
+ }
37
+
38
+ const onEvents = widget.onEvents
39
+ const init = widget.init
40
+ const replaceMerge = widget.replaceMerge
41
+
42
+ return (
43
+ <EchartUI
44
+ id={props.id}
45
+ option={option}
46
+ onEvents={onEvents}
47
+ init={init}
48
+ replaceMerge={replaceMerge}
49
+ />
50
+ )
51
+ }
52
+
53
+ /**
54
+ * Builds the dataset configuration from widget data
55
+ * @param data - The widget data array
56
+ * @returns The dataset configuration for ECharts
57
+ */
58
+ function buildDataset(
59
+ data: EchartWidgetData | undefined,
60
+ ): EchartOptionsProps['dataset'] {
61
+ if (!data || data.length === 0) {
62
+ return undefined
63
+ }
64
+
65
+ return data.map((d) => ({
66
+ source: d,
67
+ }))
68
+ }
@@ -0,0 +1,16 @@
1
+ export { Echart } from './echart'
2
+ export { EchartUI } from './echart-ui'
3
+ export type {
4
+ EchartProps,
5
+ EchartOptionsProps,
6
+ EchartUIProps,
7
+ EchartWidgetData,
8
+ EchartWidgetState,
9
+ } from './types'
10
+ export { getCommonOptions } from './options'
11
+ export {
12
+ mergeEchartWidgetConfig,
13
+ getEChartBrushConfig,
14
+ getEChartStackConfig,
15
+ getEChartZoomConfig,
16
+ } from './utils'
@@ -0,0 +1,53 @@
1
+ import type { EchartOptionsProps, EchartWidgetOptionProps } from './types'
2
+
3
+ export function getCommonOptions({
4
+ theme,
5
+ }: EchartWidgetOptionProps<unknown>): EchartOptionsProps {
6
+ return {
7
+ grid: {
8
+ left: parseInt(theme.spacing(1)),
9
+ top: parseInt(theme.spacing(3)),
10
+ right: parseInt(theme.spacing(1)),
11
+ bottom: parseInt(theme.spacing(4)),
12
+ containLabel: true,
13
+ },
14
+ toolbox: {
15
+ show: false,
16
+ },
17
+ tooltip: {
18
+ axisPointer: {
19
+ type: 'shadow',
20
+ shadowStyle: {
21
+ color: 'rgba(44,48,50, 0.08)',
22
+ },
23
+ },
24
+ backgroundColor: theme.palette.grey[900],
25
+ borderWidth: 0,
26
+ padding: [parseInt(theme.spacing(1)), parseInt(theme.spacing(1))],
27
+ textStyle: {
28
+ color: theme.palette.common.white,
29
+ fontSize: 11,
30
+ fontFamily: theme.typography.caption.fontFamily,
31
+ },
32
+ trigger: 'axis',
33
+ },
34
+ legend: {
35
+ type: 'scroll',
36
+ bottom: 0,
37
+ },
38
+ axisPointer: {
39
+ lineStyle: {
40
+ color: theme.palette.grey[400],
41
+ },
42
+ },
43
+ xAxis: {},
44
+ yAxis: {},
45
+ color: [
46
+ theme.palette.secondary.main,
47
+ ...Object.values(
48
+ (theme.palette as { qualitative?: { bold?: Record<string, string> } })
49
+ .qualitative?.bold ?? {},
50
+ ),
51
+ ],
52
+ }
53
+ }