@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,341 @@
1
+ import { describe, test, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { MarkdownUI } from './markdown-ui'
4
+
5
+ describe('MarkdownUI', () => {
6
+ test('renders plain text without markdown', () => {
7
+ render(<MarkdownUI>Plain text content</MarkdownUI>)
8
+ expect(screen.getByText('Plain text content')).toBeTruthy()
9
+ })
10
+
11
+ test('renders empty string', () => {
12
+ const { container } = render(<MarkdownUI>{''}</MarkdownUI>)
13
+ expect(container).toBeTruthy()
14
+ })
15
+
16
+ test('renders h1 as Typography variant h4', () => {
17
+ render(<MarkdownUI># Heading 1</MarkdownUI>)
18
+ const heading = screen.getByText('Heading 1')
19
+ expect(heading.tagName).toBe('H4')
20
+ })
21
+
22
+ test('renders h2 as Typography variant h5', () => {
23
+ render(<MarkdownUI>## Heading 2</MarkdownUI>)
24
+ const heading = screen.getByText('Heading 2')
25
+ expect(heading.tagName).toBe('H5')
26
+ })
27
+
28
+ test('renders h3 as Typography variant h6', () => {
29
+ render(<MarkdownUI>### Heading 3</MarkdownUI>)
30
+ const heading = screen.getByText('Heading 3')
31
+ expect(heading.tagName).toBe('H6')
32
+ })
33
+
34
+ test('renders multiple headings of different levels', () => {
35
+ render(
36
+ <MarkdownUI>
37
+ {`# H1 Heading
38
+ ## H2 Heading
39
+ ### H3 Heading`}
40
+ </MarkdownUI>,
41
+ )
42
+ expect(screen.getByText('H1 Heading')).toBeTruthy()
43
+ expect(screen.getByText('H2 Heading')).toBeTruthy()
44
+ expect(screen.getByText('H3 Heading')).toBeTruthy()
45
+ })
46
+
47
+ test('renders paragraph as Typography', () => {
48
+ render(<MarkdownUI>This is a paragraph.</MarkdownUI>)
49
+ expect(screen.getByText('This is a paragraph.')).toBeTruthy()
50
+ })
51
+
52
+ test('renders multiple paragraphs', () => {
53
+ render(
54
+ <MarkdownUI>
55
+ {`First paragraph.
56
+
57
+ Second paragraph.
58
+
59
+ Third paragraph.`}
60
+ </MarkdownUI>,
61
+ )
62
+ expect(screen.getByText('First paragraph.')).toBeTruthy()
63
+ expect(screen.getByText('Second paragraph.')).toBeTruthy()
64
+ expect(screen.getByText('Third paragraph.')).toBeTruthy()
65
+ })
66
+
67
+ test('renders link with href attribute', () => {
68
+ render(<MarkdownUI>[Link text](https://example.com)</MarkdownUI>)
69
+ const link = screen.getByText('Link text')
70
+ expect(link.tagName).toBe('A')
71
+ expect(link.getAttribute('href')).toBe('https://example.com')
72
+ })
73
+
74
+ test('renders link with target="_blank" by default', () => {
75
+ render(<MarkdownUI>[External link](https://example.com)</MarkdownUI>)
76
+ const link = screen.getByText('External link')
77
+ expect(link.getAttribute('target')).toBe('_blank')
78
+ })
79
+
80
+ test('renders link with rel="noopener noreferrer" by default', () => {
81
+ render(<MarkdownUI>[Secure link](https://example.com)</MarkdownUI>)
82
+ const link = screen.getByText('Secure link')
83
+ expect(link.getAttribute('rel')).toBe('noopener noreferrer')
84
+ })
85
+
86
+ test('renders multiple links', () => {
87
+ render(
88
+ <MarkdownUI>
89
+ [Link 1](https://example1.com) and [Link 2](https://example2.com)
90
+ </MarkdownUI>,
91
+ )
92
+ expect(screen.getByText('Link 1')).toBeTruthy()
93
+ expect(screen.getByText('Link 2')).toBeTruthy()
94
+ })
95
+
96
+ test('renders unordered list', () => {
97
+ render(
98
+ <MarkdownUI>
99
+ {`- Item 1
100
+ - Item 2
101
+ - Item 3`}
102
+ </MarkdownUI>,
103
+ )
104
+ expect(screen.getByText('Item 1')).toBeTruthy()
105
+ expect(screen.getByText('Item 2')).toBeTruthy()
106
+ expect(screen.getByText('Item 3')).toBeTruthy()
107
+ })
108
+
109
+ test('renders ordered list', () => {
110
+ render(
111
+ <MarkdownUI>
112
+ {`1. First item
113
+ 2. Second item
114
+ 3. Third item`}
115
+ </MarkdownUI>,
116
+ )
117
+ expect(screen.getByText('First item')).toBeTruthy()
118
+ expect(screen.getByText('Second item')).toBeTruthy()
119
+ expect(screen.getByText('Third item')).toBeTruthy()
120
+ })
121
+
122
+ test('renders nested lists', () => {
123
+ render(
124
+ <MarkdownUI>
125
+ {`- Parent 1
126
+ - Child 1
127
+ - Child 2
128
+ - Parent 2`}
129
+ </MarkdownUI>,
130
+ )
131
+ expect(screen.getByText('Parent 1')).toBeTruthy()
132
+ expect(screen.getByText('Child 1')).toBeTruthy()
133
+ expect(screen.getByText('Child 2')).toBeTruthy()
134
+ expect(screen.getByText('Parent 2')).toBeTruthy()
135
+ })
136
+
137
+ test('renders list items as MUI ListItem components', () => {
138
+ render(<MarkdownUI>{`- List item`}</MarkdownUI>)
139
+ const listItem = screen.getByText('List item')
140
+ expect(listItem.classList.contains('MuiListItem-root')).toBe(true)
141
+ })
142
+
143
+ test('renders complex markdown with mixed elements', () => {
144
+ render(
145
+ <MarkdownUI>
146
+ {`# Main Title
147
+
148
+ This is a paragraph with [a link](https://example.com).
149
+
150
+ ## Subsection
151
+
152
+ - Bullet point 1
153
+ - Bullet point 2
154
+
155
+ ### Details
156
+
157
+ Another paragraph.`}
158
+ </MarkdownUI>,
159
+ )
160
+ expect(screen.getByText('Main Title')).toBeTruthy()
161
+ expect(screen.getByText(/This is a paragraph/)).toBeTruthy()
162
+ expect(screen.getByText('a link')).toBeTruthy()
163
+ expect(screen.getByText('Subsection')).toBeTruthy()
164
+ expect(screen.getByText('Bullet point 1')).toBeTruthy()
165
+ expect(screen.getByText('Bullet point 2')).toBeTruthy()
166
+ expect(screen.getByText('Details')).toBeTruthy()
167
+ expect(screen.getByText('Another paragraph.')).toBeTruthy()
168
+ })
169
+
170
+ test('renders markdown with special characters', () => {
171
+ render(<MarkdownUI>Text with `code` and **bold** and *italic*</MarkdownUI>)
172
+ expect(screen.getByText('code')).toBeTruthy()
173
+ expect(screen.getByText('bold')).toBeTruthy()
174
+ expect(screen.getByText('italic')).toBeTruthy()
175
+ })
176
+
177
+ test('handles long markdown content', () => {
178
+ const longContent = `# Long Content
179
+
180
+ ${'Paragraph. '.repeat(100)}
181
+
182
+ ## Section 2
183
+
184
+ ${'- Item\n'.repeat(50)}`
185
+ render(<MarkdownUI>{longContent}</MarkdownUI>)
186
+ expect(screen.getByText('Long Content')).toBeTruthy()
187
+ expect(screen.getByText('Section 2')).toBeTruthy()
188
+ })
189
+
190
+ test('renders markdown with line breaks', () => {
191
+ render(
192
+ <MarkdownUI>
193
+ {`Line 1
194
+
195
+ Line 2
196
+
197
+
198
+ Line 3`}
199
+ </MarkdownUI>,
200
+ )
201
+ expect(screen.getByText('Line 1')).toBeTruthy()
202
+ expect(screen.getByText('Line 2')).toBeTruthy()
203
+ expect(screen.getByText('Line 3')).toBeTruthy()
204
+ })
205
+
206
+ test('renders markdown with inline code', () => {
207
+ render(<MarkdownUI>Use `npm install` to install dependencies.</MarkdownUI>)
208
+ expect(screen.getByText('npm install')).toBeTruthy()
209
+ expect(screen.getByText(/Use/)).toBeTruthy()
210
+ expect(screen.getByText(/to install dependencies/)).toBeTruthy()
211
+ })
212
+
213
+ test('renders markdown with emphasis', () => {
214
+ render(<MarkdownUI>This is *emphasized* text</MarkdownUI>)
215
+ expect(screen.getByText('emphasized')).toBeTruthy()
216
+ })
217
+
218
+ test('renders markdown with strong emphasis', () => {
219
+ render(<MarkdownUI>This is **strong** text</MarkdownUI>)
220
+ expect(screen.getByText('strong')).toBeTruthy()
221
+ })
222
+
223
+ test('renders only specified heading levels (h1-h3)', () => {
224
+ render(
225
+ <MarkdownUI>
226
+ {`# H1
227
+ ## H2
228
+ ### H3
229
+ #### H4
230
+ ##### H5
231
+ ###### H6`}
232
+ </MarkdownUI>,
233
+ )
234
+ expect(screen.getByText('H1')).toBeTruthy()
235
+ expect(screen.getByText('H2')).toBeTruthy()
236
+ expect(screen.getByText('H3')).toBeTruthy()
237
+ expect(screen.getByText('H4')).toBeTruthy()
238
+ expect(screen.getByText('H5')).toBeTruthy()
239
+ expect(screen.getByText('H6')).toBeTruthy()
240
+ })
241
+
242
+ test('handles markdown with escaped characters', () => {
243
+ render(<MarkdownUI>Escaped \\* asterisk and \\# hash</MarkdownUI>)
244
+ expect(screen.getByText(/asterisk/)).toBeTruthy()
245
+ expect(screen.getByText(/hash/)).toBeTruthy()
246
+ })
247
+
248
+ test('renders markdown with numeric content', () => {
249
+ render(
250
+ <MarkdownUI>
251
+ {`1. First: 100
252
+ 2. Second: 200
253
+ 3. Third: 300`}
254
+ </MarkdownUI>,
255
+ )
256
+ expect(screen.getByText(/First: 100/)).toBeTruthy()
257
+ expect(screen.getByText(/Second: 200/)).toBeTruthy()
258
+ expect(screen.getByText(/Third: 300/)).toBeTruthy()
259
+ })
260
+
261
+ test('preserves whitespace in code elements', () => {
262
+ render(<MarkdownUI>`code with spaces`</MarkdownUI>)
263
+ expect(screen.getByText(/code with.*spaces/)).toBeTruthy()
264
+ })
265
+
266
+ test('renders multiple consecutive headings', () => {
267
+ render(
268
+ <MarkdownUI>
269
+ {`# Heading 1
270
+ ## Heading 2
271
+ ### Heading 3`}
272
+ </MarkdownUI>,
273
+ )
274
+ expect(screen.getByText('Heading 1').tagName).toBe('H4')
275
+ expect(screen.getByText('Heading 2').tagName).toBe('H5')
276
+ expect(screen.getByText('Heading 3').tagName).toBe('H6')
277
+ })
278
+
279
+ test('renders links within paragraphs', () => {
280
+ render(
281
+ <MarkdownUI>
282
+ This paragraph contains [a link](https://example.com) in the middle.
283
+ </MarkdownUI>,
284
+ )
285
+ const link = screen.getByText('a link')
286
+ expect(link.tagName).toBe('A')
287
+ expect(screen.getByText(/This paragraph contains/)).toBeTruthy()
288
+ })
289
+
290
+ test('renders mixed list types', () => {
291
+ render(
292
+ <MarkdownUI>
293
+ {`Unordered:
294
+ - Item A
295
+ - Item B
296
+
297
+ Ordered:
298
+ 1. First
299
+ 2. Second`}
300
+ </MarkdownUI>,
301
+ )
302
+ expect(screen.getByText('Item A')).toBeTruthy()
303
+ expect(screen.getByText('Item B')).toBeTruthy()
304
+ expect(screen.getByText('First')).toBeTruthy()
305
+ expect(screen.getByText('Second')).toBeTruthy()
306
+ })
307
+
308
+ test('renders content with all supported markdown features', () => {
309
+ render(
310
+ <MarkdownUI>
311
+ {`# Title
312
+
313
+ Paragraph with **bold**, *italic*, and \`code\`.
314
+
315
+ ## List Section
316
+
317
+ - Item 1 with [link](https://example.com)
318
+ - Item 2
319
+
320
+ ### Ordered
321
+
322
+ 1. First
323
+ 2. Second
324
+
325
+ End paragraph.`}
326
+ </MarkdownUI>,
327
+ )
328
+ expect(screen.getByText('Title')).toBeTruthy()
329
+ expect(screen.getByText('bold')).toBeTruthy()
330
+ expect(screen.getByText('italic')).toBeTruthy()
331
+ expect(screen.getByText('code')).toBeTruthy()
332
+ expect(screen.getByText('List Section')).toBeTruthy()
333
+ expect(screen.getByText(/Item 1/)).toBeTruthy()
334
+ expect(screen.getByText('link')).toBeTruthy()
335
+ expect(screen.getByText('Item 2')).toBeTruthy()
336
+ expect(screen.getByText('Ordered')).toBeTruthy()
337
+ expect(screen.getByText('First')).toBeTruthy()
338
+ expect(screen.getByText('Second')).toBeTruthy()
339
+ expect(screen.getByText('End paragraph.')).toBeTruthy()
340
+ })
341
+ })
@@ -0,0 +1,52 @@
1
+ import { Link, List, ListItem, Typography } from '@mui/material'
2
+ import ReactMarkdown, { type Components } from 'react-markdown'
3
+ import { styles } from '../markdown/style'
4
+ import type { MarkdownComponentProps } from './types'
5
+
6
+ const COMPONENTS: Components = {
7
+ h1: ({ children }) => (
8
+ <Typography variant='h4' gutterBottom color='inherit'>
9
+ {children}
10
+ </Typography>
11
+ ),
12
+ h2: ({ children }) => (
13
+ <Typography variant='h5' gutterBottom color='inherit'>
14
+ {children}
15
+ </Typography>
16
+ ),
17
+ h3: ({ children }) => (
18
+ <Typography variant='h6' gutterBottom color='inherit'>
19
+ {children}
20
+ </Typography>
21
+ ),
22
+ p: ({ children }) => (
23
+ <Typography paragraph color='inherit'>
24
+ {children}
25
+ </Typography>
26
+ ),
27
+ a: ({ children, href, target = '_blank', rel = 'noopener noreferrer' }) => (
28
+ <Link href={href} target={target} rel={rel}>
29
+ {children}
30
+ </Link>
31
+ ),
32
+ ul: ({ children }) => <List sx={styles.ul}>{children}</List>,
33
+ ol: ({ children }) => (
34
+ <List component='ol' sx={styles.ol}>
35
+ {children}
36
+ </List>
37
+ ),
38
+ li: ({ children }) => <ListItem sx={styles.li}>{children}</ListItem>,
39
+ }
40
+
41
+ export function MarkdownUI({ children, overrides }: MarkdownComponentProps) {
42
+ return (
43
+ <ReactMarkdown
44
+ components={{
45
+ ...COMPONENTS,
46
+ ...overrides,
47
+ }}
48
+ >
49
+ {children}
50
+ </ReactMarkdown>
51
+ )
52
+ }
@@ -0,0 +1,20 @@
1
+ import type { MarkdownUIProps } from './types'
2
+ import { useWidgetStore } from '../stores/widget-store'
3
+ import type { MarkdownWidgetData } from './types'
4
+ import { MarkdownUI } from '.'
5
+ import { useShallow } from 'zustand/shallow'
6
+
7
+ export function Markdown({ id }: MarkdownUIProps) {
8
+ const content = useWidgetStore(
9
+ useShallow(
10
+ (state) =>
11
+ (state.getWidget(id)?.data as MarkdownWidgetData | undefined)?.content,
12
+ ),
13
+ )
14
+
15
+ if (!content) {
16
+ return null
17
+ }
18
+
19
+ return <MarkdownUI>{content}</MarkdownUI>
20
+ }
@@ -0,0 +1,12 @@
1
+ import { Box, Skeleton } from '@mui/material'
2
+
3
+ export function MarkdownSkeleton() {
4
+ return (
5
+ <Box aria-label='Markdown skeleton'>
6
+ <Skeleton width='80%' height={24} sx={{ mb: 1 }} />
7
+ <Skeleton width='90%' height={20} sx={{ mb: 0.5 }} />
8
+ <Skeleton width='85%' height={20} sx={{ mb: 0.5 }} />
9
+ <Skeleton width='75%' height={20} />
10
+ </Box>
11
+ )
12
+ }
@@ -0,0 +1,28 @@
1
+ import type { SxProps, Theme } from '@mui/material'
2
+
3
+ export const styles: Record<string, SxProps<Theme>> = {
4
+ container: {
5
+ display: 'flex',
6
+ flexDirection: 'column',
7
+ gap: ({ spacing }) => spacing(2),
8
+ },
9
+ markdown: {
10
+ overflow: 'hidden',
11
+ typography: ({ typography }) => ({
12
+ ...typography.body1,
13
+ }),
14
+ },
15
+ li: {
16
+ display: 'list-item',
17
+ paddingTop: 0,
18
+ paddingBottom: 0,
19
+ },
20
+ ul: {
21
+ listStyleType: 'disc',
22
+ paddingLeft: ({ spacing }) => spacing(2),
23
+ },
24
+ ol: {
25
+ listStyleType: 'decimal',
26
+ paddingLeft: ({ spacing }) => spacing(2),
27
+ },
28
+ }
@@ -0,0 +1,28 @@
1
+ import type { BaseWidgetState, WidgetsStoreProps } from '../stores/types'
2
+ import type { WrapperState } from '../wrapper/types'
3
+ import type { DownloadItem } from '../actions/download/types'
4
+ import type { Components } from 'react-markdown'
5
+
6
+ // Base Markdown component props (used by Markdown component)
7
+ export interface MarkdownComponentProps {
8
+ children: string
9
+ overrides?: Components
10
+ }
11
+
12
+ export interface MarkdownUIProps {
13
+ id: WidgetsStoreProps['id']
14
+ }
15
+
16
+ export interface MarkdownDataItem {
17
+ content: string
18
+ }
19
+
20
+ export type MarkdownWidgetData = MarkdownDataItem
21
+
22
+ export type MarkdownWidgetState = BaseWidgetState<
23
+ WrapperState<MarkdownWidgetConfig>
24
+ >
25
+
26
+ export type MarkdownWidgetConfig = Record<string, unknown>
27
+
28
+ export type MarkdownDownloadConfig = DownloadItem<MarkdownWidgetData>[]
@@ -0,0 +1,2 @@
1
+ export { WidgetNoData } from './no-data'
2
+ export type { WidgetNoDataProps } from './types'