@carto/ps-react-ui 4.3.2 → 4.3.4

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