@qwickapps/react-framework 1.3.4 → 1.4.0

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 (325) hide show
  1. package/README.md +1688 -2
  2. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
  3. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
  4. package/dist/components/ErrorBoundary.d.ts +7 -0
  5. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  6. package/dist/components/Html.d.ts +28 -18
  7. package/dist/components/Html.d.ts.map +1 -1
  8. package/dist/components/Logo.d.ts +12 -35
  9. package/dist/components/Logo.d.ts.map +1 -1
  10. package/dist/components/Markdown.d.ts +18 -13
  11. package/dist/components/Markdown.d.ts.map +1 -1
  12. package/dist/components/QwickApp.d.ts +16 -3
  13. package/dist/components/QwickApp.d.ts.map +1 -1
  14. package/dist/components/QwickIcon.d.ts +23 -0
  15. package/dist/components/QwickIcon.d.ts.map +1 -0
  16. package/dist/components/SafeSpan.d.ts +12 -5
  17. package/dist/components/SafeSpan.d.ts.map +1 -1
  18. package/dist/components/Scaffold.d.ts.map +1 -1
  19. package/dist/components/base/ModelView.d.ts +101 -0
  20. package/dist/components/base/ModelView.d.ts.map +1 -0
  21. package/dist/components/base/index.d.ts +11 -0
  22. package/dist/components/base/index.d.ts.map +1 -0
  23. package/dist/components/blocks/Article.d.ts +12 -2
  24. package/dist/components/blocks/Article.d.ts.map +1 -1
  25. package/dist/components/blocks/Code.d.ts +13 -2
  26. package/dist/components/blocks/Code.d.ts.map +1 -1
  27. package/dist/components/blocks/Content.d.ts.map +1 -1
  28. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  30. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  31. package/dist/components/blocks/Footer.d.ts.map +1 -1
  32. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  33. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  34. package/dist/components/blocks/Image.d.ts +41 -0
  35. package/dist/components/blocks/Image.d.ts.map +1 -0
  36. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  37. package/dist/components/blocks/ProductCard.d.ts.map +1 -1
  38. package/dist/components/blocks/Section.d.ts +16 -2
  39. package/dist/components/blocks/Section.d.ts.map +1 -1
  40. package/dist/components/blocks/Text.d.ts +41 -0
  41. package/dist/components/blocks/Text.d.ts.map +1 -0
  42. package/dist/components/blocks/index.d.ts +4 -0
  43. package/dist/components/blocks/index.d.ts.map +1 -1
  44. package/dist/components/buttons/Button.d.ts +23 -7
  45. package/dist/components/buttons/Button.d.ts.map +1 -1
  46. package/dist/components/forms/FormBlock.d.ts +19 -13
  47. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  48. package/dist/components/index.d.ts +4 -0
  49. package/dist/components/index.d.ts.map +1 -1
  50. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  51. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  52. package/dist/components/input/HtmlInputField.d.ts +17 -11
  53. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  54. package/dist/components/input/SelectInputField.d.ts +16 -10
  55. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  56. package/dist/components/input/SwitchInputField.d.ts +16 -10
  57. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  58. package/dist/components/input/TextField.d.ts.map +1 -1
  59. package/dist/components/input/TextInputField.d.ts +16 -11
  60. package/dist/components/input/TextInputField.d.ts.map +1 -1
  61. package/dist/components/layout/GridCell.d.ts +23 -6
  62. package/dist/components/layout/GridCell.d.ts.map +1 -1
  63. package/dist/components/layout/GridLayout.d.ts +24 -23
  64. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  65. package/dist/components/pages/FormPage.d.ts.map +1 -1
  66. package/dist/components/pages/Page.d.ts +49 -87
  67. package/dist/components/pages/Page.d.ts.map +1 -1
  68. package/dist/components/pages/index.d.ts +2 -2
  69. package/dist/components/pages/index.d.ts.map +1 -1
  70. package/dist/config/AppConfig.d.ts +49 -0
  71. package/dist/config/AppConfig.d.ts.map +1 -0
  72. package/dist/config/AppConfigBuilder.d.ts +75 -0
  73. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  74. package/dist/config/index.d.ts +13 -0
  75. package/dist/config/index.d.ts.map +1 -0
  76. package/dist/config/types.d.ts +130 -0
  77. package/dist/config/types.d.ts.map +1 -0
  78. package/dist/config.d.ts +15 -0
  79. package/dist/config.d.ts.map +1 -0
  80. package/dist/config.esm.js +451 -0
  81. package/dist/config.js +455 -0
  82. package/dist/contexts/PrintModeContext.d.ts +27 -0
  83. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  84. package/dist/contexts/QwickAppContext.d.ts +2 -2
  85. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  86. package/dist/contexts/ThemeContext.d.ts.map +1 -1
  87. package/dist/contexts/index.d.ts +2 -0
  88. package/dist/contexts/index.d.ts.map +1 -1
  89. package/dist/hooks/index.d.ts +2 -0
  90. package/dist/hooks/index.d.ts.map +1 -1
  91. package/dist/hooks/usePrintMode.d.ts +39 -0
  92. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  93. package/dist/index.css +1 -1
  94. package/dist/index.d.ts +1 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.esm.css +1 -1
  97. package/dist/index.esm.js +20722 -16021
  98. package/dist/index.js +20725 -16010
  99. package/dist/schemas/CodeSchema.d.ts +2 -1
  100. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  101. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  102. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  103. package/dist/schemas/ContentSchema.d.ts +2 -1
  104. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  105. package/dist/schemas/GridCellSchema.d.ts +25 -0
  106. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  107. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  108. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  109. package/dist/schemas/HtmlSchema.d.ts +14 -0
  110. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  111. package/dist/schemas/ImageSchema.d.ts +32 -0
  112. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  113. package/dist/schemas/LogoSchema.d.ts +35 -0
  114. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  115. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  116. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  117. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  118. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  119. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  120. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  121. package/dist/schemas/SectionSchema.d.ts +2 -1
  122. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  123. package/dist/schemas/TextSchema.d.ts +37 -0
  124. package/dist/schemas/TextSchema.d.ts.map +1 -0
  125. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  126. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  127. package/dist/schemas/index.d.ts +15 -1
  128. package/dist/schemas/index.d.ts.map +1 -1
  129. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  130. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  131. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  132. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  133. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  134. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  135. package/dist/schemas/transformers/registry.d.ts +15 -0
  136. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  137. package/dist/schemas/types/Serializable.d.ts +46 -0
  138. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  139. package/dist/utils/htmlTransform.d.ts.map +1 -1
  140. package/dist/utils/reactUtils.d.ts +12 -3
  141. package/dist/utils/reactUtils.d.ts.map +1 -1
  142. package/package.json +17 -3
  143. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  145. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  148. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  150. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  152. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  154. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  155. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  156. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  158. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  159. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  160. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  161. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  162. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  163. package/src/__tests__/components/Logo.test.js +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  165. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  166. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  167. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  168. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  169. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  172. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  173. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  174. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  175. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  176. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  177. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  178. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  179. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  180. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  181. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  182. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  183. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  184. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  185. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  186. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  187. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  188. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  189. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  190. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  191. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  192. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  193. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  194. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  195. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  196. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  197. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  198. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  199. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  200. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  201. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  202. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  203. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  204. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  205. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  206. package/src/components/ErrorBoundary.tsx +8 -8
  207. package/src/components/Html.tsx +147 -44
  208. package/src/components/Logo.tsx +198 -100
  209. package/src/components/Markdown.tsx +125 -16
  210. package/src/components/QwickApp.tsx +64 -31
  211. package/src/components/QwickIcon.tsx +59 -0
  212. package/src/components/SafeSpan.tsx +65 -10
  213. package/src/components/Scaffold.tsx +2 -8
  214. package/src/components/base/ModelView.tsx +199 -0
  215. package/src/components/base/index.ts +11 -0
  216. package/src/components/blocks/Article.tsx +57 -18
  217. package/src/components/blocks/Code.md +529 -0
  218. package/src/components/blocks/Code.tsx +102 -15
  219. package/src/components/blocks/Content.tsx +25 -77
  220. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  221. package/src/components/blocks/FeatureCard.tsx +1 -2
  222. package/src/components/blocks/FeatureGrid.tsx +19 -1
  223. package/src/components/blocks/Footer.tsx +13 -1
  224. package/src/components/blocks/HeroBlock.tsx +87 -20
  225. package/src/components/blocks/Image.tsx +395 -0
  226. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  227. package/src/components/blocks/ProductCard.tsx +51 -52
  228. package/src/components/blocks/Section.tsx +113 -8
  229. package/src/components/blocks/Text.tsx +285 -0
  230. package/src/components/blocks/index.ts +4 -0
  231. package/src/components/buttons/Button.tsx +184 -15
  232. package/src/components/forms/FormBlock.tsx +70 -17
  233. package/src/components/index.ts +5 -0
  234. package/src/components/input/ChoiceInputField.tsx +48 -18
  235. package/src/components/input/HtmlInputField.tsx +48 -18
  236. package/src/components/input/SelectInputField.tsx +48 -16
  237. package/src/components/input/SwitchInputField.tsx +48 -17
  238. package/src/components/input/TextField.tsx +41 -1
  239. package/src/components/input/TextInputField.tsx +52 -18
  240. package/src/components/layout/GridCell.tsx +118 -9
  241. package/src/components/layout/GridLayout.tsx +125 -24
  242. package/src/components/pages/FormPage.tsx +0 -1
  243. package/src/components/pages/Page.css +304 -332
  244. package/src/components/pages/Page.tsx +307 -255
  245. package/src/components/pages/index.ts +2 -2
  246. package/src/config/AppConfig.ts +133 -0
  247. package/src/config/AppConfigBuilder.ts +421 -0
  248. package/src/config/__tests__/AppConfig.test.ts +385 -0
  249. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  250. package/src/config/index.ts +24 -0
  251. package/src/config/types.ts +170 -0
  252. package/src/config.ts +25 -0
  253. package/src/contexts/PrintModeContext.tsx +332 -0
  254. package/src/contexts/QwickAppContext.tsx +2 -2
  255. package/src/contexts/ThemeContext.tsx +1 -2
  256. package/src/contexts/index.ts +2 -0
  257. package/src/hooks/index.ts +5 -1
  258. package/src/hooks/usePrintMode.ts +73 -0
  259. package/src/index.ts +3 -0
  260. package/src/schemas/CodeSchema.ts +3 -3
  261. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  262. package/src/schemas/ContentSchema.ts +2 -1
  263. package/src/schemas/GridCellSchema.ts +164 -0
  264. package/src/schemas/GridLayoutSchema.ts +133 -0
  265. package/src/schemas/HtmlSchema.ts +47 -0
  266. package/src/schemas/ImageSchema.ts +235 -0
  267. package/src/schemas/LogoSchema.ts +241 -0
  268. package/src/schemas/MarkdownSchema.ts +47 -0
  269. package/src/schemas/PageTemplateSchema.ts +186 -0
  270. package/src/schemas/PrintConfigSchema.ts +207 -0
  271. package/src/schemas/README.md +661 -0
  272. package/src/schemas/SectionSchema.ts +2 -1
  273. package/src/schemas/TextSchema.ts +329 -0
  274. package/src/schemas/ViewModelSchema.ts +115 -0
  275. package/src/schemas/index.ts +21 -2
  276. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  277. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  278. package/src/schemas/transformers/registry.ts +72 -0
  279. package/src/schemas/types/Serializable.ts +51 -0
  280. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  281. package/src/stories/Article.stories.tsx +433 -433
  282. package/src/stories/Button.stories.tsx +1 -1
  283. package/src/stories/CardListGrid.stories.tsx +451 -451
  284. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  285. package/src/stories/Code.stories.tsx +1 -1
  286. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  287. package/src/stories/Content.stories.tsx +393 -393
  288. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  289. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  290. package/src/stories/DataProvider.stories.tsx +1192 -1192
  291. package/src/stories/FeatureCard.stories.tsx +557 -557
  292. package/src/stories/FeatureGrid.stories.tsx +594 -594
  293. package/src/stories/Footer.stories.tsx +640 -640
  294. package/src/stories/FormBlock.stories.tsx +760 -760
  295. package/src/stories/FormComponents.stories.tsx +349 -541
  296. package/src/stories/GridCell.stories.tsx +417 -0
  297. package/src/stories/GridLayout.stories.tsx +353 -0
  298. package/src/stories/HeroBlock.stories.tsx +862 -373
  299. package/src/stories/HtmlInputField.stories.tsx +474 -474
  300. package/src/stories/Image.stories.tsx +819 -0
  301. package/src/stories/Introduction.stories.tsx +667 -667
  302. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  303. package/src/stories/Logo.stories.tsx +165 -6
  304. package/src/stories/Markdown.stories.tsx +137 -137
  305. package/src/stories/ModelView.stories.tsx +477 -0
  306. package/src/stories/Page.stories.tsx +688 -688
  307. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  308. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  309. package/src/stories/ProductCard.stories.tsx +424 -424
  310. package/src/stories/QwickApp.stories.tsx +368 -368
  311. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  312. package/src/stories/SafeSpan.stories.tsx +531 -531
  313. package/src/stories/Section.stories.tsx +90 -2
  314. package/src/stories/SelectInputField.stories.tsx +524 -524
  315. package/src/stories/Text.stories.tsx +560 -0
  316. package/src/stories/TextInputField.stories.tsx +443 -443
  317. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  318. package/src/utils/htmlTransform.tsx +74 -53
  319. package/src/utils/reactUtils.tsx +57 -6
  320. package/dist/index.bundled.css +0 -12
  321. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  322. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  323. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  324. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  325. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -0,0 +1,521 @@
1
+ /**
2
+ * ComponentTransformer Tests - Comprehensive test suite
3
+ *
4
+ * Tests the core component serialization system functionality
5
+ * including registration, serialization, deserialization, and error handling.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import React, { ReactElement } from 'react';
11
+ import { ComponentTransformer } from '../ComponentTransformer';
12
+ import { Serializable, SerializableConstructor } from '../../types/Serializable';
13
+ import {
14
+ MockSerializableComponentClass,
15
+ AlternativeMockComponentClass,
16
+ InvalidMockComponentClass
17
+ } from './MockSerializableComponent';
18
+
19
+ // Legacy mock components for testing (updated with self-declaration)
20
+ class MockButton implements Serializable {
21
+ static readonly tagName = 'Button';
22
+ static readonly version = '1.0.0';
23
+
24
+ constructor(public props: { label?: string; variant?: string; onClick?: () => void }) {}
25
+
26
+ static fromJson(jsonData: any): ReactElement {
27
+ return React.createElement('button', {
28
+ className: `btn-${jsonData.variant || 'default'}`,
29
+ onClick: jsonData.onClick
30
+ }, jsonData.label || 'Button');
31
+ }
32
+
33
+ toJson(): any {
34
+ return {
35
+ label: this.props.label,
36
+ variant: this.props.variant,
37
+ onClick: this.props.onClick ? 'function' : undefined
38
+ };
39
+ }
40
+ }
41
+
42
+ class MockCard implements Serializable {
43
+ static readonly tagName = 'Card';
44
+ static readonly version = '1.0.0';
45
+
46
+ constructor(public props: { title?: string; content?: string; children?: any }) {}
47
+
48
+ static fromJson(jsonData: any): ReactElement {
49
+ return React.createElement('div', {
50
+ className: 'card'
51
+ }, [
52
+ React.createElement('h3', { key: 'title' }, jsonData.title),
53
+ React.createElement('p', { key: 'content' }, jsonData.content)
54
+ ]);
55
+ }
56
+
57
+ toJson(): any {
58
+ return {
59
+ title: this.props.title,
60
+ content: this.props.content
61
+ };
62
+ }
63
+ }
64
+
65
+ describe('ComponentTransformer', () => {
66
+ beforeEach(() => {
67
+ // Clear registry before each test
68
+ ComponentTransformer.clearRegistry();
69
+ });
70
+
71
+ afterEach(() => {
72
+ // Clean up after each test
73
+ ComponentTransformer.clearRegistry();
74
+ });
75
+
76
+ describe('Component Registration', () => {
77
+ it('should register a component successfully', () => {
78
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
79
+
80
+ const registered = ComponentTransformer.getRegisteredComponents();
81
+ expect(registered).toContain('Button');
82
+ expect(registered).toHaveLength(1);
83
+ });
84
+
85
+ it('should register multiple components', () => {
86
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
87
+ ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
88
+
89
+ const registered = ComponentTransformer.getRegisteredComponents();
90
+ expect(registered).toContain('Button');
91
+ expect(registered).toContain('Card');
92
+ expect(registered).toHaveLength(2);
93
+ });
94
+
95
+ it('should overwrite existing component registration with same tagName', () => {
96
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
97
+ ComponentTransformer.registerComponent(AlternativeMockComponentClass);
98
+
99
+ const registered = ComponentTransformer.getRegisteredComponents();
100
+ expect(registered).toContain('Button');
101
+ expect(registered).toContain('AlternativeComponent');
102
+ expect(registered).toHaveLength(2);
103
+ });
104
+
105
+ it('should clear registry completely', () => {
106
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
107
+ ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
108
+
109
+ ComponentTransformer.clearRegistry();
110
+
111
+ const registered = ComponentTransformer.getRegisteredComponents();
112
+ expect(registered).toHaveLength(0);
113
+ });
114
+ });
115
+
116
+ describe('Serialization', () => {
117
+ beforeEach(() => {
118
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
119
+ });
120
+
121
+ it('should serialize null to null', () => {
122
+ const result = ComponentTransformer.serialize(null);
123
+ expect(result).toBe('null');
124
+ expect(JSON.parse(result)).toBeNull();
125
+ });
126
+
127
+ it('should serialize undefined to null', () => {
128
+ const result = ComponentTransformer.serialize(undefined);
129
+ expect(result).toBe('null');
130
+ expect(JSON.parse(result)).toBeNull();
131
+ });
132
+
133
+ it('should serialize primitive values', () => {
134
+ expect(JSON.parse(ComponentTransformer.serialize('hello'))).toBe('hello');
135
+ expect(JSON.parse(ComponentTransformer.serialize(42))).toBe(42);
136
+ expect(JSON.parse(ComponentTransformer.serialize(true))).toBe(true);
137
+ expect(JSON.parse(ComponentTransformer.serialize(false))).toBe(false);
138
+ });
139
+
140
+ it('should serialize array of primitives', () => {
141
+ const input = ['hello', 42, true, null];
142
+ const result = ComponentTransformer.serialize(input);
143
+ expect(JSON.parse(result)).toEqual(input);
144
+ });
145
+
146
+ it('should serialize array of mixed content', () => {
147
+ const input = ['text', 123, null, undefined];
148
+ const result = ComponentTransformer.serialize(input);
149
+ const parsed = JSON.parse(result);
150
+ expect(parsed).toEqual(['text', 123, null, null]);
151
+ });
152
+ });
153
+
154
+ describe('Deserialization', () => {
155
+ beforeEach(() => {
156
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
157
+ ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
158
+ });
159
+
160
+ it('should deserialize null correctly', () => {
161
+ expect(ComponentTransformer.deserialize('null')).toBeNull();
162
+ expect(ComponentTransformer.deserialize(null)).toBeNull();
163
+ });
164
+
165
+ it('should deserialize primitive values from strings', () => {
166
+ expect(ComponentTransformer.deserialize('"hello"')).toBe('hello');
167
+ expect(ComponentTransformer.deserialize('42')).toBe(42);
168
+ expect(ComponentTransformer.deserialize('true')).toBe(true);
169
+ expect(ComponentTransformer.deserialize('false')).toBe(false);
170
+ });
171
+
172
+ it('should deserialize primitive values from objects', () => {
173
+ expect(ComponentTransformer.deserialize('hello')).toBe('hello');
174
+ expect(ComponentTransformer.deserialize(42)).toBe(42);
175
+ expect(ComponentTransformer.deserialize(true)).toBe(true);
176
+ expect(ComponentTransformer.deserialize(false)).toBe(false);
177
+ });
178
+
179
+ it('should deserialize arrays', () => {
180
+ const input = ['hello', 42, true, null];
181
+ const result = ComponentTransformer.deserialize(input);
182
+ expect(result).toEqual(input);
183
+ });
184
+
185
+ it('should deserialize registered component from object', () => {
186
+ const componentData = {
187
+ tag: 'Button',
188
+ version: '1.0.0',
189
+ data: {
190
+ label: 'Click Me',
191
+ variant: 'primary'
192
+ }
193
+ };
194
+
195
+ const result = ComponentTransformer.deserialize(componentData);
196
+ expect(React.isValidElement(result)).toBe(true);
197
+
198
+ const element = result as ReactElement;
199
+ expect(element.type).toBe('button');
200
+ expect(element.props.className).toBe('btn-primary');
201
+ expect(element.props.children).toBe('Click Me');
202
+ });
203
+
204
+ it('should deserialize registered component from JSON string', () => {
205
+ const componentData = {
206
+ tag: 'Card',
207
+ version: '1.0.0',
208
+ data: {
209
+ title: 'Test Card',
210
+ content: 'Test content'
211
+ }
212
+ };
213
+
214
+ const jsonString = JSON.stringify(componentData);
215
+ const result = ComponentTransformer.deserialize(jsonString);
216
+ expect(React.isValidElement(result)).toBe(true);
217
+
218
+ const element = result as ReactElement;
219
+ expect(element.type).toBe('div');
220
+ expect(element.props.className).toBe('card');
221
+ expect(Array.isArray(element.props.children)).toBe(true);
222
+ });
223
+
224
+ it('should handle array of components', () => {
225
+ const input = [
226
+ {
227
+ tag: 'Button',
228
+ version: '1.0.0',
229
+ data: { label: 'First Button' }
230
+ },
231
+ {
232
+ tag: 'Button',
233
+ version: '1.0.0',
234
+ data: { label: 'Second Button' }
235
+ }
236
+ ];
237
+
238
+ const result = ComponentTransformer.deserialize(input);
239
+ expect(Array.isArray(result)).toBe(true);
240
+
241
+ const elements = result as ReactElement[];
242
+ expect(elements).toHaveLength(2);
243
+ expect(React.isValidElement(elements[0])).toBe(true);
244
+ expect(React.isValidElement(elements[1])).toBe(true);
245
+ });
246
+ });
247
+
248
+ describe('Error Handling', () => {
249
+ it('should throw error for invalid JSON string', () => {
250
+ expect(() => {
251
+ ComponentTransformer.deserialize('invalid json {');
252
+ }).toThrow('Invalid JSON input');
253
+ });
254
+
255
+ it('should throw error for unknown component', () => {
256
+ const componentData = {
257
+ tag: 'UnknownComponent',
258
+ version: '1.0.0',
259
+ data: {}
260
+ };
261
+
262
+ // With the new fallback system, unknown components should not throw but use ReactNodeTransformer
263
+ const result = ComponentTransformer.deserialize(componentData);
264
+ // Should return a fallback React element rather than throwing
265
+ expect(result).toBeDefined();
266
+ });
267
+
268
+ it('should handle malformed component data gracefully', () => {
269
+ const malformedData = {
270
+ tag: 'Button',
271
+ // Missing version and data
272
+ };
273
+
274
+ // Register a button component first
275
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
276
+
277
+ expect(() => {
278
+ ComponentTransformer.deserialize(malformedData);
279
+ }).toThrow('Malformed component data: missing \'data\' property for component \'Button\'');
280
+ });
281
+ });
282
+
283
+ describe('String vs Object Input Handling', () => {
284
+ beforeEach(() => {
285
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
286
+ });
287
+
288
+ it('should handle string input correctly', () => {
289
+ const componentData = {
290
+ tag: 'Button',
291
+ version: '1.0.0',
292
+ data: { label: 'String Input Test' }
293
+ };
294
+
295
+ const jsonString = JSON.stringify(componentData);
296
+ const result = ComponentTransformer.deserialize(jsonString);
297
+
298
+ expect(React.isValidElement(result)).toBe(true);
299
+ const element = result as ReactElement;
300
+ expect(element.props.children).toBe('String Input Test');
301
+ });
302
+
303
+ it('should handle object input correctly', () => {
304
+ const componentData = {
305
+ tag: 'Button',
306
+ version: '1.0.0',
307
+ data: { label: 'Object Input Test' }
308
+ };
309
+
310
+ const result = ComponentTransformer.deserialize(componentData);
311
+
312
+ expect(React.isValidElement(result)).toBe(true);
313
+ const element = result as ReactElement;
314
+ expect(element.props.children).toBe('Object Input Test');
315
+ });
316
+
317
+ it('should handle array input correctly', () => {
318
+ const componentsData = [
319
+ {
320
+ tag: 'Button',
321
+ version: '1.0.0',
322
+ data: { label: 'Array Item 1' }
323
+ },
324
+ {
325
+ tag: 'Button',
326
+ version: '1.0.0',
327
+ data: { label: 'Array Item 2' }
328
+ }
329
+ ];
330
+
331
+ const result = ComponentTransformer.deserialize(componentsData);
332
+
333
+ expect(Array.isArray(result)).toBe(true);
334
+ const elements = result as ReactElement[];
335
+ expect(elements).toHaveLength(2);
336
+ expect(elements[0].props.children).toBe('Array Item 1');
337
+ expect(elements[1].props.children).toBe('Array Item 2');
338
+ });
339
+ });
340
+
341
+ describe('Nested Component Scenarios', () => {
342
+ beforeEach(() => {
343
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
344
+ ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
345
+ });
346
+
347
+ it('should handle nested primitive content', () => {
348
+ const input = ['text', 42, ['nested', 'array'], { key: 'value' }];
349
+ const serialized = ComponentTransformer.serialize(input);
350
+ const deserialized = ComponentTransformer.deserialize(serialized);
351
+
352
+ expect(deserialized).toEqual(['text', 42, ['nested', 'array'], { key: 'value' }]);
353
+ });
354
+
355
+ it('should handle mixed content arrays', () => {
356
+ const input = [
357
+ 'plain text',
358
+ {
359
+ tag: 'Button',
360
+ version: '1.0.0',
361
+ data: { label: 'Embedded Button' }
362
+ },
363
+ 42,
364
+ null
365
+ ];
366
+
367
+ const result = ComponentTransformer.deserialize(input);
368
+ expect(Array.isArray(result)).toBe(true);
369
+
370
+ const elements = result as any[];
371
+ expect(elements[0]).toBe('plain text');
372
+ expect(React.isValidElement(elements[1])).toBe(true);
373
+ expect(elements[2]).toBe(42);
374
+ expect(elements[3]).toBeNull();
375
+ });
376
+ });
377
+
378
+ describe('Data Structure Requirements', () => {
379
+ beforeEach(() => {
380
+ ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
381
+ });
382
+
383
+ it('should produce correct serialized structure format', () => {
384
+ // This test verifies the data structure requirement:
385
+ // { tag: "ComponentName", version: "1.0.0", data: {...} }
386
+
387
+ // Since we can't easily test serialization of actual React elements
388
+ // without a more complex setup, we'll test the deserialization format
389
+ const expectedStructure = {
390
+ tag: 'Button',
391
+ version: '1.0.0',
392
+ data: {
393
+ label: 'Test Button',
394
+ variant: 'primary'
395
+ }
396
+ };
397
+
398
+ const result = ComponentTransformer.deserialize(expectedStructure);
399
+ expect(React.isValidElement(result)).toBe(true);
400
+ });
401
+
402
+ it('should handle version information correctly', () => {
403
+ const componentWithVersion = {
404
+ tag: 'Button',
405
+ version: '2.1.0',
406
+ data: { label: 'Version Test' }
407
+ };
408
+
409
+ // Should not throw - version handling is delegated to component's fromJson
410
+ expect(() => {
411
+ ComponentTransformer.deserialize(componentWithVersion);
412
+ }).not.toThrow();
413
+ });
414
+ });
415
+
416
+ describe('New Architecture Features', () => {
417
+ it('should validate component self-declaration with tagName and version', () => {
418
+ ComponentTransformer.registerComponent(MockSerializableComponentClass);
419
+
420
+ const registered = ComponentTransformer.getRegisteredComponents();
421
+ expect(registered).toContain('MockComponent');
422
+ });
423
+
424
+ it('should throw error for components missing tagName', () => {
425
+ expect(() => {
426
+ ComponentTransformer.registerComponent(InvalidMockComponentClass);
427
+ }).toThrow("Component class must have a static 'tagName' property");
428
+ });
429
+
430
+ it('should use component declared version in serialization', () => {
431
+ ComponentTransformer.registerComponent(AlternativeMockComponentClass);
432
+
433
+ const componentData = {
434
+ tag: 'AlternativeComponent',
435
+ version: '2.1.0',
436
+ data: { label: 'Version Test', type: 'primary' }
437
+ };
438
+
439
+ const result = ComponentTransformer.deserialize(componentData);
440
+ expect(React.isValidElement(result)).toBe(true);
441
+ });
442
+
443
+ it('should handle unregistered React elements with ReactNodeTransformer fallback', () => {
444
+ const unregisteredElement = React.createElement('span', { className: 'test' }, 'Fallback Test');
445
+ const serialized = ComponentTransformer.serialize(unregisteredElement);
446
+ const parsed = JSON.parse(serialized);
447
+
448
+ // Should use __react_node__ tag for unregistered components
449
+ expect(parsed.tag).toBe('__react_node__');
450
+ expect(parsed.version).toBe('1.0.0');
451
+
452
+ const deserialized = ComponentTransformer.deserialize(parsed);
453
+ expect(deserialized).toBeDefined();
454
+ });
455
+
456
+ it('should handle mixed array of registered and unregistered components', () => {
457
+ ComponentTransformer.registerComponent(MockSerializableComponentClass);
458
+
459
+ const mixedArray = [
460
+ 'Text node',
461
+ {
462
+ tag: 'MockComponent',
463
+ version: '1.0.0',
464
+ data: { title: 'Registered Component' }
465
+ },
466
+ {
467
+ tag: '__react_node__',
468
+ version: '1.0.0',
469
+ data: {
470
+ type: 'react-element',
471
+ elementType: 'div',
472
+ props: { children: 'Unregistered Element' }
473
+ }
474
+ }
475
+ ];
476
+
477
+ const result = ComponentTransformer.deserialize(mixedArray);
478
+ expect(Array.isArray(result)).toBe(true);
479
+
480
+ const elements = result as any[];
481
+ expect(elements).toHaveLength(3);
482
+ expect(elements[0]).toBe('Text node');
483
+ expect(React.isValidElement(elements[1])).toBe(true);
484
+ expect(elements[2]).toBeDefined(); // Fallback content
485
+ });
486
+
487
+ it('should gracefully handle unknown registered components with fallback', () => {
488
+ const unknownComponent = {
489
+ tag: 'NonExistentComponent',
490
+ version: '1.0.0',
491
+ data: { some: 'data' }
492
+ };
493
+
494
+ // Should not throw, but use fallback
495
+ const result = ComponentTransformer.deserialize(unknownComponent);
496
+ expect(result).toBeDefined();
497
+ });
498
+
499
+ it('should preserve all functionality for correctly registered components', () => {
500
+ ComponentTransformer.registerComponent(MockSerializableComponentClass);
501
+
502
+ const componentData = {
503
+ tag: 'MockComponent',
504
+ version: '1.0.0',
505
+ data: {
506
+ title: 'Test Title',
507
+ content: 'Test Content',
508
+ variant: 'primary'
509
+ }
510
+ };
511
+
512
+ const result = ComponentTransformer.deserialize(componentData);
513
+ expect(React.isValidElement(result)).toBe(true);
514
+
515
+ const element = result as ReactElement;
516
+ expect(element.type).toBe('div');
517
+ expect(element.props.className).toBe('mock-component primary');
518
+ expect(element.props['data-testid']).toBe('mock-component');
519
+ });
520
+ });
521
+ });