@qwickapps/react-framework 1.3.5 → 1.4.1

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 (320) hide show
  1. package/README.md +1691 -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/CoverImageHeader.d.ts.map +1 -1
  28. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  30. package/dist/components/blocks/Footer.d.ts.map +1 -1
  31. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  32. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  33. package/dist/components/blocks/Image.d.ts +41 -0
  34. package/dist/components/blocks/Image.d.ts.map +1 -0
  35. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  36. package/dist/components/blocks/Section.d.ts +16 -2
  37. package/dist/components/blocks/Section.d.ts.map +1 -1
  38. package/dist/components/blocks/Text.d.ts +41 -0
  39. package/dist/components/blocks/Text.d.ts.map +1 -0
  40. package/dist/components/blocks/index.d.ts +4 -0
  41. package/dist/components/blocks/index.d.ts.map +1 -1
  42. package/dist/components/buttons/Button.d.ts +23 -7
  43. package/dist/components/buttons/Button.d.ts.map +1 -1
  44. package/dist/components/forms/FormBlock.d.ts +19 -13
  45. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  46. package/dist/components/index.d.ts +4 -0
  47. package/dist/components/index.d.ts.map +1 -1
  48. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  49. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  50. package/dist/components/input/HtmlInputField.d.ts +17 -11
  51. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  52. package/dist/components/input/SelectInputField.d.ts +16 -10
  53. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  54. package/dist/components/input/SwitchInputField.d.ts +16 -10
  55. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  56. package/dist/components/input/TextField.d.ts.map +1 -1
  57. package/dist/components/input/TextInputField.d.ts +16 -11
  58. package/dist/components/input/TextInputField.d.ts.map +1 -1
  59. package/dist/components/layout/GridCell.d.ts +23 -6
  60. package/dist/components/layout/GridCell.d.ts.map +1 -1
  61. package/dist/components/layout/GridLayout.d.ts +24 -23
  62. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  63. package/dist/components/pages/FormPage.d.ts.map +1 -1
  64. package/dist/components/pages/Page.d.ts +49 -87
  65. package/dist/components/pages/Page.d.ts.map +1 -1
  66. package/dist/components/pages/index.d.ts +2 -2
  67. package/dist/components/pages/index.d.ts.map +1 -1
  68. package/dist/config/AppConfig.d.ts +49 -0
  69. package/dist/config/AppConfig.d.ts.map +1 -0
  70. package/dist/config/AppConfigBuilder.d.ts +75 -0
  71. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  72. package/dist/config/index.d.ts +13 -0
  73. package/dist/config/index.d.ts.map +1 -0
  74. package/dist/config/types.d.ts +130 -0
  75. package/dist/config/types.d.ts.map +1 -0
  76. package/dist/config.d.ts +15 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.esm.js +451 -0
  79. package/dist/config.js +455 -0
  80. package/dist/contexts/PrintModeContext.d.ts +27 -0
  81. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  82. package/dist/contexts/QwickAppContext.d.ts +2 -2
  83. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  84. package/dist/contexts/index.d.ts +2 -0
  85. package/dist/contexts/index.d.ts.map +1 -1
  86. package/dist/hooks/index.d.ts +2 -0
  87. package/dist/hooks/index.d.ts.map +1 -1
  88. package/dist/hooks/usePrintMode.d.ts +39 -0
  89. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  90. package/dist/index.css +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.esm.css +1 -1
  94. package/dist/index.esm.js +10951 -6238
  95. package/dist/index.js +11014 -6287
  96. package/dist/schemas/CodeSchema.d.ts +2 -1
  97. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  98. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  99. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  100. package/dist/schemas/ContentSchema.d.ts +2 -1
  101. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  102. package/dist/schemas/GridCellSchema.d.ts +25 -0
  103. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  104. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  105. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  106. package/dist/schemas/HtmlSchema.d.ts +14 -0
  107. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  108. package/dist/schemas/ImageSchema.d.ts +32 -0
  109. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  110. package/dist/schemas/LogoSchema.d.ts +35 -0
  111. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  112. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  113. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  114. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  115. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  116. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  117. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  118. package/dist/schemas/SectionSchema.d.ts +2 -1
  119. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  120. package/dist/schemas/TextSchema.d.ts +37 -0
  121. package/dist/schemas/TextSchema.d.ts.map +1 -0
  122. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  123. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  124. package/dist/schemas/index.d.ts +15 -1
  125. package/dist/schemas/index.d.ts.map +1 -1
  126. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  127. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  128. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  129. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  130. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  131. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  132. package/dist/schemas/transformers/registry.d.ts +15 -0
  133. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  134. package/dist/schemas/types/Serializable.d.ts +46 -0
  135. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  136. package/dist/utils/htmlTransform.d.ts.map +1 -1
  137. package/dist/utils/reactUtils.d.ts +12 -3
  138. package/dist/utils/reactUtils.d.ts.map +1 -1
  139. package/package.json +17 -3
  140. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  141. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  142. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  143. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  145. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  148. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  150. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  152. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  154. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  155. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  156. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  158. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  159. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  160. package/src/__tests__/components/Logo.test.js +3 -3
  161. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  162. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  163. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  165. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  166. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  167. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  168. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  169. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  172. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  173. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  174. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  175. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  176. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  177. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  178. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  179. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  180. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  181. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  182. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  183. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  184. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  185. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  186. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  187. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  188. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  189. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  190. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  191. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  192. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  193. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  194. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  195. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  196. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  197. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  198. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  199. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  200. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  201. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  202. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  203. package/src/components/ErrorBoundary.tsx +8 -8
  204. package/src/components/Html.tsx +147 -44
  205. package/src/components/Logo.tsx +198 -100
  206. package/src/components/Markdown.tsx +125 -16
  207. package/src/components/QwickApp.tsx +64 -31
  208. package/src/components/QwickIcon.tsx +59 -0
  209. package/src/components/SafeSpan.tsx +65 -10
  210. package/src/components/Scaffold.tsx +2 -8
  211. package/src/components/base/ModelView.tsx +199 -0
  212. package/src/components/base/index.ts +11 -0
  213. package/src/components/blocks/Article.tsx +57 -18
  214. package/src/components/blocks/Code.md +529 -0
  215. package/src/components/blocks/Code.tsx +102 -15
  216. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  217. package/src/components/blocks/FeatureCard.tsx +1 -2
  218. package/src/components/blocks/FeatureGrid.tsx +19 -1
  219. package/src/components/blocks/Footer.tsx +13 -1
  220. package/src/components/blocks/HeroBlock.tsx +87 -20
  221. package/src/components/blocks/Image.tsx +395 -0
  222. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  223. package/src/components/blocks/ProductCard.tsx +1 -1
  224. package/src/components/blocks/Section.tsx +113 -8
  225. package/src/components/blocks/Text.tsx +285 -0
  226. package/src/components/blocks/index.ts +4 -0
  227. package/src/components/buttons/Button.tsx +184 -15
  228. package/src/components/forms/FormBlock.tsx +70 -17
  229. package/src/components/index.ts +5 -0
  230. package/src/components/input/ChoiceInputField.tsx +48 -18
  231. package/src/components/input/HtmlInputField.tsx +48 -18
  232. package/src/components/input/SelectInputField.tsx +48 -16
  233. package/src/components/input/SwitchInputField.tsx +48 -17
  234. package/src/components/input/TextField.tsx +41 -1
  235. package/src/components/input/TextInputField.tsx +52 -18
  236. package/src/components/layout/GridCell.tsx +118 -9
  237. package/src/components/layout/GridLayout.tsx +125 -24
  238. package/src/components/pages/FormPage.tsx +0 -1
  239. package/src/components/pages/Page.css +304 -332
  240. package/src/components/pages/Page.tsx +307 -255
  241. package/src/components/pages/index.ts +2 -2
  242. package/src/config/AppConfig.ts +133 -0
  243. package/src/config/AppConfigBuilder.ts +421 -0
  244. package/src/config/__tests__/AppConfig.test.ts +385 -0
  245. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  246. package/src/config/index.ts +24 -0
  247. package/src/config/types.ts +170 -0
  248. package/src/config.ts +25 -0
  249. package/src/contexts/PrintModeContext.tsx +332 -0
  250. package/src/contexts/QwickAppContext.tsx +2 -2
  251. package/src/contexts/index.ts +2 -0
  252. package/src/hooks/index.ts +5 -1
  253. package/src/hooks/usePrintMode.ts +73 -0
  254. package/src/index.ts +3 -0
  255. package/src/schemas/CodeSchema.ts +3 -3
  256. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  257. package/src/schemas/ContentSchema.ts +2 -1
  258. package/src/schemas/GridCellSchema.ts +164 -0
  259. package/src/schemas/GridLayoutSchema.ts +133 -0
  260. package/src/schemas/HtmlSchema.ts +47 -0
  261. package/src/schemas/ImageSchema.ts +235 -0
  262. package/src/schemas/LogoSchema.ts +241 -0
  263. package/src/schemas/MarkdownSchema.ts +47 -0
  264. package/src/schemas/PageTemplateSchema.ts +186 -0
  265. package/src/schemas/PrintConfigSchema.ts +207 -0
  266. package/src/schemas/README.md +661 -0
  267. package/src/schemas/SectionSchema.ts +2 -1
  268. package/src/schemas/TextSchema.ts +329 -0
  269. package/src/schemas/ViewModelSchema.ts +115 -0
  270. package/src/schemas/index.ts +21 -2
  271. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  272. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  273. package/src/schemas/transformers/registry.ts +72 -0
  274. package/src/schemas/types/Serializable.ts +51 -0
  275. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  276. package/src/stories/Article.stories.tsx +433 -433
  277. package/src/stories/Button.stories.tsx +1 -1
  278. package/src/stories/CardListGrid.stories.tsx +451 -451
  279. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  280. package/src/stories/Code.stories.tsx +1 -1
  281. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  282. package/src/stories/Content.stories.tsx +393 -393
  283. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  284. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  285. package/src/stories/DataProvider.stories.tsx +1192 -1192
  286. package/src/stories/FeatureCard.stories.tsx +557 -557
  287. package/src/stories/FeatureGrid.stories.tsx +594 -594
  288. package/src/stories/Footer.stories.tsx +640 -640
  289. package/src/stories/FormBlock.stories.tsx +760 -760
  290. package/src/stories/FormComponents.stories.tsx +349 -541
  291. package/src/stories/GridCell.stories.tsx +417 -0
  292. package/src/stories/GridLayout.stories.tsx +353 -0
  293. package/src/stories/HeroBlock.stories.tsx +862 -373
  294. package/src/stories/HtmlInputField.stories.tsx +474 -474
  295. package/src/stories/Image.stories.tsx +819 -0
  296. package/src/stories/Introduction.stories.tsx +667 -667
  297. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  298. package/src/stories/Logo.stories.tsx +165 -6
  299. package/src/stories/Markdown.stories.tsx +137 -137
  300. package/src/stories/ModelView.stories.tsx +477 -0
  301. package/src/stories/Page.stories.tsx +688 -688
  302. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  303. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  304. package/src/stories/ProductCard.stories.tsx +424 -424
  305. package/src/stories/QwickApp.stories.tsx +368 -368
  306. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  307. package/src/stories/SafeSpan.stories.tsx +531 -531
  308. package/src/stories/Section.stories.tsx +90 -2
  309. package/src/stories/SelectInputField.stories.tsx +524 -524
  310. package/src/stories/Text.stories.tsx +560 -0
  311. package/src/stories/TextInputField.stories.tsx +443 -443
  312. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  313. package/src/utils/htmlTransform.tsx +74 -53
  314. package/src/utils/reactUtils.tsx +57 -6
  315. package/dist/index.bundled.css +0 -12
  316. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  317. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  318. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  319. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  320. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -0,0 +1,1165 @@
1
+ /**
2
+ * Real-World Component Serialization Scenario Tests
3
+ *
4
+ * Tests actual usage patterns and scenarios that the serialization system
5
+ * will encounter in production environments, including API data transformation,
6
+ * form submissions, page builders, and content management systems.
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+
11
+ import React, { ReactElement } from 'react';
12
+ import { ComponentTransformer } from '../ComponentTransformer';
13
+ import { Serializable, SerializableConstructor } from '../../types/Serializable';
14
+
15
+ // Realistic component implementations based on common patterns
16
+ class RealButton implements Serializable {
17
+ constructor(public props: {
18
+ id?: string;
19
+ label?: string;
20
+ variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
21
+ size?: 'sm' | 'md' | 'lg';
22
+ disabled?: boolean;
23
+ loading?: boolean;
24
+ icon?: string;
25
+ href?: string;
26
+ target?: '_blank' | '_self';
27
+ onClick?: string; // Event handler name/reference
28
+ 'data-testid'?: string;
29
+ 'aria-label'?: string;
30
+ className?: string;
31
+ }) {}
32
+
33
+ static fromJson(jsonData: any): ReactElement {
34
+ const isLink = !!jsonData.href;
35
+ const element = isLink ? 'a' : 'button';
36
+
37
+ const props: any = {
38
+ id: jsonData.id,
39
+ className: [
40
+ 'btn',
41
+ `btn-${jsonData.variant || 'primary'}`,
42
+ `btn-${jsonData.size || 'md'}`,
43
+ jsonData.loading ? 'btn-loading' : '',
44
+ jsonData.className || ''
45
+ ].filter(Boolean).join(' '),
46
+ disabled: jsonData.disabled || jsonData.loading,
47
+ 'data-testid': jsonData['data-testid'],
48
+ 'aria-label': jsonData['aria-label'] || jsonData.label
49
+ };
50
+
51
+ if (isLink) {
52
+ props.href = jsonData.href;
53
+ props.target = jsonData.target || '_self';
54
+ } else {
55
+ props.type = 'button';
56
+ if (jsonData.onClick) {
57
+ props['data-onclick'] = jsonData.onClick;
58
+ }
59
+ }
60
+
61
+ const children = [
62
+ jsonData.icon ? React.createElement('i', {
63
+ key: 'icon',
64
+ className: `icon icon-${jsonData.icon}`
65
+ }) : null,
66
+ jsonData.loading ? React.createElement('span', {
67
+ key: 'spinner',
68
+ className: 'spinner'
69
+ }) : null,
70
+ jsonData.label ? React.createElement('span', {
71
+ key: 'label'
72
+ }, jsonData.label) : null
73
+ ].filter(Boolean);
74
+
75
+ return React.createElement(element, props, children);
76
+ }
77
+
78
+ toJson(): any {
79
+ return {
80
+ id: this.props.id,
81
+ label: this.props.label,
82
+ variant: this.props.variant,
83
+ size: this.props.size,
84
+ disabled: this.props.disabled,
85
+ loading: this.props.loading,
86
+ icon: this.props.icon,
87
+ href: this.props.href,
88
+ target: this.props.target,
89
+ onClick: this.props.onClick,
90
+ 'data-testid': this.props['data-testid'],
91
+ 'aria-label': this.props['aria-label'],
92
+ className: this.props.className
93
+ };
94
+ }
95
+ }
96
+
97
+ class RealCard implements Serializable {
98
+ constructor(public props: {
99
+ id?: string;
100
+ title?: string;
101
+ subtitle?: string;
102
+ content?: string;
103
+ image?: {
104
+ src: string;
105
+ alt?: string;
106
+ width?: number;
107
+ height?: number;
108
+ };
109
+ metadata?: {
110
+ author?: string;
111
+ date?: string;
112
+ tags?: string[];
113
+ category?: string;
114
+ readTime?: string;
115
+ };
116
+ actions?: any[];
117
+ variant?: 'default' | 'outlined' | 'elevated';
118
+ clickable?: boolean;
119
+ href?: string;
120
+ className?: string;
121
+ }) {}
122
+
123
+ static fromJson(jsonData: any): ReactElement {
124
+ const actions = jsonData.actions ? ComponentTransformer.deserialize(jsonData.actions) : null;
125
+ const isClickable = jsonData.clickable || jsonData.href;
126
+
127
+ const cardContent = [
128
+ jsonData.image ? React.createElement('div', {
129
+ key: 'image',
130
+ className: 'card-image'
131
+ }, React.createElement('img', {
132
+ src: jsonData.image.src,
133
+ alt: jsonData.image.alt || jsonData.title || 'Card image',
134
+ width: jsonData.image.width,
135
+ height: jsonData.image.height,
136
+ loading: 'lazy'
137
+ })) : null,
138
+
139
+ React.createElement('div', {
140
+ key: 'body',
141
+ className: 'card-body'
142
+ }, [
143
+ jsonData.title ? React.createElement('h3', {
144
+ key: 'title',
145
+ className: 'card-title'
146
+ }, jsonData.title) : null,
147
+
148
+ jsonData.subtitle ? React.createElement('p', {
149
+ key: 'subtitle',
150
+ className: 'card-subtitle'
151
+ }, jsonData.subtitle) : null,
152
+
153
+ jsonData.content ? React.createElement('div', {
154
+ key: 'content',
155
+ className: 'card-content',
156
+ dangerouslySetInnerHTML: { __html: jsonData.content }
157
+ }) : null,
158
+
159
+ jsonData.metadata ? React.createElement('div', {
160
+ key: 'metadata',
161
+ className: 'card-metadata'
162
+ }, [
163
+ jsonData.metadata.author ? React.createElement('span', {
164
+ key: 'author',
165
+ className: 'card-author'
166
+ }, `By ${jsonData.metadata.author}`) : null,
167
+
168
+ jsonData.metadata.date ? React.createElement('time', {
169
+ key: 'date',
170
+ className: 'card-date',
171
+ dateTime: jsonData.metadata.date
172
+ }, jsonData.metadata.date) : null,
173
+
174
+ jsonData.metadata.readTime ? React.createElement('span', {
175
+ key: 'readTime',
176
+ className: 'card-read-time'
177
+ }, jsonData.metadata.readTime) : null,
178
+
179
+ jsonData.metadata.tags ? React.createElement('div', {
180
+ key: 'tags',
181
+ className: 'card-tags'
182
+ }, jsonData.metadata.tags.map((tag: string, index: number) =>
183
+ React.createElement('span', {
184
+ key: index,
185
+ className: 'card-tag'
186
+ }, tag)
187
+ )) : null
188
+ ].filter(Boolean)) : null,
189
+
190
+ actions ? React.createElement('div', {
191
+ key: 'actions',
192
+ className: 'card-actions'
193
+ }, actions) : null
194
+ ].filter(Boolean))
195
+ ].filter(Boolean);
196
+
197
+ const props: any = {
198
+ id: jsonData.id,
199
+ className: [
200
+ 'card',
201
+ `card-${jsonData.variant || 'default'}`,
202
+ isClickable ? 'card-clickable' : '',
203
+ jsonData.className || ''
204
+ ].filter(Boolean).join(' '),
205
+ 'data-testid': 'real-card'
206
+ };
207
+
208
+ if (jsonData.href) {
209
+ return React.createElement('a', {
210
+ ...props,
211
+ href: jsonData.href,
212
+ className: `${props.className} card-link`
213
+ }, cardContent);
214
+ }
215
+
216
+ return React.createElement('div', props, cardContent);
217
+ }
218
+
219
+ toJson(): any {
220
+ return {
221
+ id: this.props.id,
222
+ title: this.props.title,
223
+ subtitle: this.props.subtitle,
224
+ content: this.props.content,
225
+ image: this.props.image,
226
+ metadata: this.props.metadata,
227
+ actions: this.props.actions ? ComponentTransformer.serialize(this.props.actions) : null,
228
+ variant: this.props.variant,
229
+ clickable: this.props.clickable,
230
+ href: this.props.href,
231
+ className: this.props.className
232
+ };
233
+ }
234
+ }
235
+
236
+ class RealSection implements Serializable {
237
+ constructor(public props: {
238
+ id?: string;
239
+ title?: string;
240
+ subtitle?: string;
241
+ children?: any;
242
+ layout?: 'container' | 'full-width' | 'grid';
243
+ columns?: number;
244
+ gap?: string;
245
+ padding?: string;
246
+ backgroundColor?: string;
247
+ backgroundImage?: string;
248
+ textAlign?: 'left' | 'center' | 'right';
249
+ className?: string;
250
+ 'data-section'?: string;
251
+ }) {}
252
+
253
+ static fromJson(jsonData: any): ReactElement {
254
+ const children = jsonData.children ? ComponentTransformer.deserialize(jsonData.children) : null;
255
+
256
+ const sectionStyle: React.CSSProperties = {
257
+ backgroundColor: jsonData.backgroundColor,
258
+ backgroundImage: jsonData.backgroundImage ? `url(${jsonData.backgroundImage})` : undefined,
259
+ padding: jsonData.padding,
260
+ textAlign: jsonData.textAlign as any,
261
+ gap: jsonData.gap
262
+ };
263
+
264
+ const contentProps: any = {
265
+ className: 'section-content'
266
+ };
267
+
268
+ if (jsonData.layout === 'grid' && jsonData.columns) {
269
+ contentProps.style = {
270
+ display: 'grid',
271
+ gridTemplateColumns: `repeat(${jsonData.columns}, 1fr)`,
272
+ gap: jsonData.gap || '1rem'
273
+ };
274
+ } else if (jsonData.layout === 'container') {
275
+ contentProps.className += ' container';
276
+ }
277
+
278
+ return React.createElement('section', {
279
+ id: jsonData.id,
280
+ className: [
281
+ 'section',
282
+ `section-${jsonData.layout || 'container'}`,
283
+ jsonData.className || ''
284
+ ].filter(Boolean).join(' '),
285
+ style: sectionStyle,
286
+ 'data-section': jsonData['data-section'],
287
+ 'data-testid': 'real-section'
288
+ }, [
289
+ jsonData.title || jsonData.subtitle ? React.createElement('header', {
290
+ key: 'header',
291
+ className: 'section-header'
292
+ }, [
293
+ jsonData.title ? React.createElement('h2', {
294
+ key: 'title',
295
+ className: 'section-title'
296
+ }, jsonData.title) : null,
297
+
298
+ jsonData.subtitle ? React.createElement('p', {
299
+ key: 'subtitle',
300
+ className: 'section-subtitle'
301
+ }, jsonData.subtitle) : null
302
+ ].filter(Boolean)) : null,
303
+
304
+ children ? React.createElement('div', contentProps, children) : null
305
+ ].filter(Boolean));
306
+ }
307
+
308
+ toJson(): any {
309
+ return {
310
+ id: this.props.id,
311
+ title: this.props.title,
312
+ subtitle: this.props.subtitle,
313
+ children: this.props.children ? ComponentTransformer.serialize(this.props.children) : null,
314
+ layout: this.props.layout,
315
+ columns: this.props.columns,
316
+ gap: this.props.gap,
317
+ padding: this.props.padding,
318
+ backgroundColor: this.props.backgroundColor,
319
+ backgroundImage: this.props.backgroundImage,
320
+ textAlign: this.props.textAlign,
321
+ className: this.props.className,
322
+ 'data-section': this.props['data-section']
323
+ };
324
+ }
325
+ }
326
+
327
+ class RealFormField implements Serializable {
328
+ constructor(public props: {
329
+ id?: string;
330
+ name: string;
331
+ label?: string;
332
+ type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'textarea' | 'select';
333
+ value?: any;
334
+ placeholder?: string;
335
+ required?: boolean;
336
+ disabled?: boolean;
337
+ readOnly?: boolean;
338
+ validation?: {
339
+ pattern?: string;
340
+ minLength?: number;
341
+ maxLength?: number;
342
+ min?: number;
343
+ max?: number;
344
+ };
345
+ options?: { value: string; label: string }[]; // For select fields
346
+ rows?: number; // For textarea
347
+ helperText?: string;
348
+ error?: string;
349
+ className?: string;
350
+ }) {}
351
+
352
+ static fromJson(jsonData: any): ReactElement {
353
+ const fieldId = jsonData.id || `field-${jsonData.name}`;
354
+ const hasError = !!jsonData.error;
355
+
356
+ let inputElement: ReactElement;
357
+
358
+ if (jsonData.type === 'textarea') {
359
+ inputElement = React.createElement('textarea', {
360
+ id: fieldId,
361
+ name: jsonData.name,
362
+ value: jsonData.value || '',
363
+ placeholder: jsonData.placeholder,
364
+ required: jsonData.required,
365
+ disabled: jsonData.disabled,
366
+ readOnly: jsonData.readOnly,
367
+ rows: jsonData.rows || 3,
368
+ minLength: jsonData.validation?.minLength,
369
+ maxLength: jsonData.validation?.maxLength,
370
+ className: `form-control ${hasError ? 'is-invalid' : ''}`,
371
+ 'aria-describedby': jsonData.helperText || jsonData.error ? `${fieldId}-help` : undefined
372
+ });
373
+ } else if (jsonData.type === 'select') {
374
+ inputElement = React.createElement('select', {
375
+ id: fieldId,
376
+ name: jsonData.name,
377
+ value: jsonData.value || '',
378
+ required: jsonData.required,
379
+ disabled: jsonData.disabled,
380
+ className: `form-control ${hasError ? 'is-invalid' : ''}`,
381
+ 'aria-describedby': jsonData.helperText || jsonData.error ? `${fieldId}-help` : undefined
382
+ }, [
383
+ React.createElement('option', { key: 'empty', value: '' }, 'Select an option'),
384
+ ...(jsonData.options || []).map((option: any, index: number) =>
385
+ React.createElement('option', {
386
+ key: index,
387
+ value: option.value
388
+ }, option.label)
389
+ )
390
+ ]);
391
+ } else {
392
+ inputElement = React.createElement('input', {
393
+ id: fieldId,
394
+ name: jsonData.name,
395
+ type: jsonData.type || 'text',
396
+ value: jsonData.value || '',
397
+ placeholder: jsonData.placeholder,
398
+ required: jsonData.required,
399
+ disabled: jsonData.disabled,
400
+ readOnly: jsonData.readOnly,
401
+ pattern: jsonData.validation?.pattern,
402
+ minLength: jsonData.validation?.minLength,
403
+ maxLength: jsonData.validation?.maxLength,
404
+ min: jsonData.validation?.min,
405
+ max: jsonData.validation?.max,
406
+ className: `form-control ${hasError ? 'is-invalid' : ''}`,
407
+ 'aria-describedby': jsonData.helperText || jsonData.error ? `${fieldId}-help` : undefined
408
+ });
409
+ }
410
+
411
+ return React.createElement('div', {
412
+ className: [
413
+ 'form-field',
414
+ hasError ? 'form-field-error' : '',
415
+ jsonData.className || ''
416
+ ].filter(Boolean).join(' '),
417
+ 'data-testid': 'real-form-field'
418
+ }, [
419
+ jsonData.label ? React.createElement('label', {
420
+ key: 'label',
421
+ htmlFor: fieldId,
422
+ className: 'form-label'
423
+ }, [
424
+ jsonData.label,
425
+ jsonData.required ? React.createElement('span', {
426
+ key: 'required',
427
+ className: 'required',
428
+ 'aria-label': 'required'
429
+ }, ' *') : null
430
+ ]) : null,
431
+
432
+ inputElement,
433
+
434
+ (jsonData.helperText || jsonData.error) ? React.createElement('div', {
435
+ key: 'help',
436
+ id: `${fieldId}-help`,
437
+ className: hasError ? 'form-error' : 'form-help'
438
+ }, jsonData.error || jsonData.helperText) : null
439
+ ].filter(Boolean));
440
+ }
441
+
442
+ toJson(): any {
443
+ return {
444
+ id: this.props.id,
445
+ name: this.props.name,
446
+ label: this.props.label,
447
+ type: this.props.type,
448
+ value: this.props.value,
449
+ placeholder: this.props.placeholder,
450
+ required: this.props.required,
451
+ disabled: this.props.disabled,
452
+ readOnly: this.props.readOnly,
453
+ validation: this.props.validation,
454
+ options: this.props.options,
455
+ rows: this.props.rows,
456
+ helperText: this.props.helperText,
457
+ error: this.props.error,
458
+ className: this.props.className
459
+ };
460
+ }
461
+ }
462
+
463
+ describe('Real-World Component Serialization Scenarios', () => {
464
+ beforeEach(() => {
465
+ ComponentTransformer.clearRegistry();
466
+ ComponentTransformer.registerComponent('RealButton', RealButton as SerializableConstructor);
467
+ ComponentTransformer.registerComponent('RealCard', RealCard as SerializableConstructor);
468
+ ComponentTransformer.registerComponent('RealSection', RealSection as SerializableConstructor);
469
+ ComponentTransformer.registerComponent('RealFormField', RealFormField as SerializableConstructor);
470
+ });
471
+
472
+ afterEach(() => {
473
+ ComponentTransformer.clearRegistry();
474
+ });
475
+
476
+ describe('API Data Transformation', () => {
477
+ it('should handle blog post API data transformation', () => {
478
+ // Simulate API response for a blog post
479
+ const blogPostApiData = {
480
+ id: 'post-123',
481
+ title: 'Building Scalable React Applications',
482
+ subtitle: 'A comprehensive guide to modern React development',
483
+ author: 'Jane Developer',
484
+ publishedAt: '2025-01-01T10:00:00Z',
485
+ readTime: '8 min read',
486
+ tags: ['React', 'JavaScript', 'Web Development'],
487
+ coverImage: {
488
+ url: 'https://example.com/cover.jpg',
489
+ width: 1200,
490
+ height: 630,
491
+ alt: 'React application architecture diagram'
492
+ },
493
+ content: '<p>React applications can be complex...</p><p>In this article, we\'ll explore...</p>',
494
+ category: 'Technology'
495
+ };
496
+
497
+ // Transform to component structure
498
+ const componentStructure = {
499
+ tag: 'RealCard',
500
+ version: '1.0.0',
501
+ data: {
502
+ id: blogPostApiData.id,
503
+ title: blogPostApiData.title,
504
+ subtitle: blogPostApiData.subtitle,
505
+ content: blogPostApiData.content,
506
+ image: {
507
+ src: blogPostApiData.coverImage.url,
508
+ alt: blogPostApiData.coverImage.alt,
509
+ width: blogPostApiData.coverImage.width,
510
+ height: blogPostApiData.coverImage.height
511
+ },
512
+ metadata: {
513
+ author: blogPostApiData.author,
514
+ date: new Date(blogPostApiData.publishedAt).toLocaleDateString(),
515
+ tags: blogPostApiData.tags,
516
+ category: blogPostApiData.category,
517
+ readTime: blogPostApiData.readTime
518
+ },
519
+ href: `/blog/${blogPostApiData.id}`,
520
+ variant: 'elevated'
521
+ }
522
+ };
523
+
524
+ const result = ComponentTransformer.deserialize(componentStructure);
525
+ expect(React.isValidElement(result)).toBe(true);
526
+
527
+ const element = result as ReactElement;
528
+ expect(element.props['data-testid']).toBe('real-card');
529
+ expect(element.props.href).toBe('/blog/post-123');
530
+ expect(element.props.className).toContain('card-elevated');
531
+ });
532
+
533
+ it('should handle e-commerce product listing API data', () => {
534
+ const productListingApiData = [
535
+ {
536
+ id: 'prod-1',
537
+ name: 'Wireless Headphones',
538
+ price: 199.99,
539
+ originalPrice: 249.99,
540
+ rating: 4.5,
541
+ reviewCount: 128,
542
+ image: 'https://example.com/headphones.jpg',
543
+ inStock: true,
544
+ category: 'Electronics'
545
+ },
546
+ {
547
+ id: 'prod-2',
548
+ name: 'Smart Watch',
549
+ price: 299.99,
550
+ rating: 4.8,
551
+ reviewCount: 89,
552
+ image: 'https://example.com/watch.jpg',
553
+ inStock: false,
554
+ category: 'Electronics'
555
+ }
556
+ ];
557
+
558
+ const productGrid = {
559
+ tag: 'RealSection',
560
+ version: '1.0.0',
561
+ data: {
562
+ title: 'Featured Products',
563
+ layout: 'grid',
564
+ columns: 2,
565
+ gap: '2rem',
566
+ className: 'product-grid',
567
+ children: productListingApiData.map(product => ({
568
+ tag: 'RealCard',
569
+ version: '1.0.0',
570
+ data: {
571
+ id: product.id,
572
+ title: product.name,
573
+ content: `$${product.price}${product.originalPrice ? ` <del>$${product.originalPrice}</del>` : ''}`,
574
+ image: {
575
+ src: product.image,
576
+ alt: product.name
577
+ },
578
+ metadata: {
579
+ category: product.category,
580
+ tags: [`⭐ ${product.rating} (${product.reviewCount} reviews)`]
581
+ },
582
+ actions: [
583
+ {
584
+ tag: 'RealButton',
585
+ version: '1.0.0',
586
+ data: {
587
+ label: product.inStock ? 'Add to Cart' : 'Out of Stock',
588
+ variant: product.inStock ? 'primary' : 'secondary',
589
+ disabled: !product.inStock,
590
+ icon: product.inStock ? 'cart' : 'clock'
591
+ }
592
+ }
593
+ ]
594
+ }
595
+ }))
596
+ }
597
+ };
598
+
599
+ const result = ComponentTransformer.deserialize(productGrid);
600
+ expect(React.isValidElement(result)).toBe(true);
601
+
602
+ const section = result as ReactElement;
603
+ expect(section.props['data-testid']).toBe('real-section');
604
+ expect(section.props.className).toContain('product-grid');
605
+ });
606
+ });
607
+
608
+ describe('Form Submission and Processing', () => {
609
+ it('should handle contact form structure', () => {
610
+ const contactFormStructure = {
611
+ tag: 'RealSection',
612
+ version: '1.0.0',
613
+ data: {
614
+ id: 'contact-form',
615
+ title: 'Get In Touch',
616
+ subtitle: 'We\'d love to hear from you',
617
+ className: 'contact-section',
618
+ children: [
619
+ {
620
+ tag: 'RealFormField',
621
+ version: '1.0.0',
622
+ data: {
623
+ name: 'name',
624
+ label: 'Full Name',
625
+ type: 'text',
626
+ required: true,
627
+ placeholder: 'Enter your full name',
628
+ validation: {
629
+ minLength: 2,
630
+ maxLength: 100
631
+ }
632
+ }
633
+ },
634
+ {
635
+ tag: 'RealFormField',
636
+ version: '1.0.0',
637
+ data: {
638
+ name: 'email',
639
+ label: 'Email Address',
640
+ type: 'email',
641
+ required: true,
642
+ placeholder: 'your@email.com',
643
+ validation: {
644
+ pattern: '^[^@]+@[^@]+\\.[^@]+$'
645
+ }
646
+ }
647
+ },
648
+ {
649
+ tag: 'RealFormField',
650
+ version: '1.0.0',
651
+ data: {
652
+ name: 'subject',
653
+ label: 'Subject',
654
+ type: 'select',
655
+ required: true,
656
+ options: [
657
+ { value: 'general', label: 'General Inquiry' },
658
+ { value: 'support', label: 'Technical Support' },
659
+ { value: 'sales', label: 'Sales Question' },
660
+ { value: 'partnership', label: 'Partnership Opportunity' }
661
+ ]
662
+ }
663
+ },
664
+ {
665
+ tag: 'RealFormField',
666
+ version: '1.0.0',
667
+ data: {
668
+ name: 'message',
669
+ label: 'Message',
670
+ type: 'textarea',
671
+ required: true,
672
+ placeholder: 'Tell us about your inquiry...',
673
+ rows: 6,
674
+ validation: {
675
+ minLength: 10,
676
+ maxLength: 1000
677
+ },
678
+ helperText: 'Please provide detailed information about your request'
679
+ }
680
+ },
681
+ {
682
+ tag: 'RealButton',
683
+ version: '1.0.0',
684
+ data: {
685
+ label: 'Send Message',
686
+ variant: 'primary',
687
+ size: 'lg',
688
+ icon: 'send',
689
+ onClick: 'submitContactForm',
690
+ 'data-testid': 'submit-button'
691
+ }
692
+ }
693
+ ]
694
+ }
695
+ };
696
+
697
+ const result = ComponentTransformer.deserialize(contactFormStructure);
698
+ expect(React.isValidElement(result)).toBe(true);
699
+
700
+ // Verify form structure
701
+ const section = result as ReactElement;
702
+ expect(section.props['data-testid']).toBe('real-section');
703
+ expect(section.props.id).toBe('contact-form');
704
+ });
705
+
706
+ it('should handle form with validation errors', () => {
707
+ const formWithErrors = [
708
+ {
709
+ tag: 'RealFormField',
710
+ version: '1.0.0',
711
+ data: {
712
+ name: 'email',
713
+ label: 'Email Address',
714
+ type: 'email',
715
+ value: 'invalid-email',
716
+ error: 'Please enter a valid email address',
717
+ required: true
718
+ }
719
+ },
720
+ {
721
+ tag: 'RealFormField',
722
+ version: '1.0.0',
723
+ data: {
724
+ name: 'password',
725
+ label: 'Password',
726
+ type: 'password',
727
+ value: '123',
728
+ error: 'Password must be at least 8 characters long',
729
+ required: true,
730
+ validation: {
731
+ minLength: 8
732
+ }
733
+ }
734
+ }
735
+ ];
736
+
737
+ const result = ComponentTransformer.deserialize(formWithErrors);
738
+ expect(Array.isArray(result)).toBe(true);
739
+
740
+ const fields = result as ReactElement[];
741
+ expect(fields).toHaveLength(2);
742
+
743
+ // Check error states
744
+ fields.forEach(field => {
745
+ expect(field.props.className).toContain('form-field-error');
746
+ });
747
+ });
748
+ });
749
+
750
+ describe('Page Builder Scenarios', () => {
751
+ it('should handle complex landing page structure', () => {
752
+ const landingPageStructure = [
753
+ // Hero Section
754
+ {
755
+ tag: 'RealSection',
756
+ version: '1.0.0',
757
+ data: {
758
+ id: 'hero',
759
+ title: 'Build Amazing Web Applications',
760
+ subtitle: 'The fastest way to create modern, responsive web apps',
761
+ layout: 'container',
762
+ textAlign: 'center',
763
+ backgroundColor: '#f8f9fa',
764
+ padding: '4rem 2rem',
765
+ children: [
766
+ {
767
+ tag: 'RealButton',
768
+ version: '1.0.0',
769
+ data: {
770
+ label: 'Get Started Free',
771
+ variant: 'primary',
772
+ size: 'lg',
773
+ icon: 'rocket',
774
+ href: '/signup'
775
+ }
776
+ },
777
+ {
778
+ tag: 'RealButton',
779
+ version: '1.0.0',
780
+ data: {
781
+ label: 'Watch Demo',
782
+ variant: 'secondary',
783
+ size: 'lg',
784
+ icon: 'play',
785
+ href: '/demo',
786
+ className: 'ml-3'
787
+ }
788
+ }
789
+ ]
790
+ }
791
+ },
792
+ // Features Section
793
+ {
794
+ tag: 'RealSection',
795
+ version: '1.0.0',
796
+ data: {
797
+ id: 'features',
798
+ title: 'Why Choose Our Platform?',
799
+ layout: 'grid',
800
+ columns: 3,
801
+ gap: '2rem',
802
+ padding: '4rem 2rem',
803
+ children: [
804
+ {
805
+ tag: 'RealCard',
806
+ version: '1.0.0',
807
+ data: {
808
+ title: 'Lightning Fast',
809
+ content: 'Built with performance in mind. Load times under 100ms.',
810
+ image: {
811
+ src: 'https://example.com/speed-icon.svg',
812
+ alt: 'Speed icon'
813
+ },
814
+ variant: 'outlined'
815
+ }
816
+ },
817
+ {
818
+ tag: 'RealCard',
819
+ version: '1.0.0',
820
+ data: {
821
+ title: 'Fully Responsive',
822
+ content: 'Works perfectly on desktop, tablet, and mobile devices.',
823
+ image: {
824
+ src: 'https://example.com/responsive-icon.svg',
825
+ alt: 'Responsive icon'
826
+ },
827
+ variant: 'outlined'
828
+ }
829
+ },
830
+ {
831
+ tag: 'RealCard',
832
+ version: '1.0.0',
833
+ data: {
834
+ title: 'SEO Optimized',
835
+ content: 'Built-in SEO tools to help your site rank higher.',
836
+ image: {
837
+ src: 'https://example.com/seo-icon.svg',
838
+ alt: 'SEO icon'
839
+ },
840
+ variant: 'outlined'
841
+ }
842
+ }
843
+ ]
844
+ }
845
+ },
846
+ // CTA Section
847
+ {
848
+ tag: 'RealSection',
849
+ version: '1.0.0',
850
+ data: {
851
+ id: 'cta',
852
+ title: 'Ready to Get Started?',
853
+ subtitle: 'Join thousands of developers building with our platform',
854
+ layout: 'container',
855
+ textAlign: 'center',
856
+ backgroundColor: '#007bff',
857
+ padding: '3rem 2rem',
858
+ className: 'text-white',
859
+ children: [
860
+ {
861
+ tag: 'RealButton',
862
+ version: '1.0.0',
863
+ data: {
864
+ label: 'Start Building Now',
865
+ variant: 'success',
866
+ size: 'lg',
867
+ icon: 'arrow-right',
868
+ href: '/signup'
869
+ }
870
+ }
871
+ ]
872
+ }
873
+ }
874
+ ];
875
+
876
+ const result = ComponentTransformer.deserialize(landingPageStructure);
877
+ expect(Array.isArray(result)).toBe(true);
878
+
879
+ const sections = result as ReactElement[];
880
+ expect(sections).toHaveLength(3);
881
+
882
+ // Verify each section
883
+ expect(sections[0].props.id).toBe('hero');
884
+ expect(sections[1].props.id).toBe('features');
885
+ expect(sections[2].props.id).toBe('cta');
886
+ });
887
+
888
+ it('should handle dynamic content variations', () => {
889
+ // Simulate A/B test variations
890
+ const variations = {
891
+ control: {
892
+ tag: 'RealButton',
893
+ version: '1.0.0',
894
+ data: {
895
+ label: 'Sign Up',
896
+ variant: 'primary'
897
+ }
898
+ },
899
+ variation_a: {
900
+ tag: 'RealButton',
901
+ version: '1.0.0',
902
+ data: {
903
+ label: 'Join Now - Free!',
904
+ variant: 'success',
905
+ icon: 'star'
906
+ }
907
+ },
908
+ variation_b: {
909
+ tag: 'RealButton',
910
+ version: '1.0.0',
911
+ data: {
912
+ label: 'Get Started Today',
913
+ variant: 'primary',
914
+ size: 'lg',
915
+ icon: 'rocket'
916
+ }
917
+ }
918
+ };
919
+
920
+ Object.entries(variations).forEach(([name, variation]) => {
921
+ const result = ComponentTransformer.deserialize(variation);
922
+ expect(React.isValidElement(result)).toBe(true);
923
+
924
+ const button = result as ReactElement;
925
+ expect(button.type).toBe('button');
926
+ expect(button.props.className).toContain('btn');
927
+ });
928
+ });
929
+ });
930
+
931
+ describe('Content Management System Integration', () => {
932
+ it('should handle rich content with mixed components', () => {
933
+ const richContentStructure = {
934
+ tag: 'RealSection',
935
+ version: '1.0.0',
936
+ data: {
937
+ id: 'article-content',
938
+ className: 'article-body',
939
+ children: [
940
+ // Text content
941
+ {
942
+ tag: 'RealCard',
943
+ version: '1.0.0',
944
+ data: {
945
+ content: '<p>This article explores the latest trends in web development...</p>',
946
+ variant: 'default'
947
+ }
948
+ },
949
+ // Embedded image
950
+ {
951
+ tag: 'RealCard',
952
+ version: '1.0.0',
953
+ data: {
954
+ image: {
955
+ src: 'https://example.com/diagram.jpg',
956
+ alt: 'Web development architecture diagram',
957
+ width: 800,
958
+ height: 400
959
+ },
960
+ subtitle: 'Figure 1: Modern web application architecture'
961
+ }
962
+ },
963
+ // Call-to-action
964
+ {
965
+ tag: 'RealCard',
966
+ version: '1.0.0',
967
+ data: {
968
+ title: 'Want to Learn More?',
969
+ content: 'Subscribe to our newsletter for the latest web development insights.',
970
+ variant: 'outlined',
971
+ actions: [
972
+ {
973
+ tag: 'RealButton',
974
+ version: '1.0.0',
975
+ data: {
976
+ label: 'Subscribe Now',
977
+ variant: 'primary',
978
+ icon: 'email'
979
+ }
980
+ }
981
+ ]
982
+ }
983
+ },
984
+ // Related articles
985
+ {
986
+ tag: 'RealSection',
987
+ version: '1.0.0',
988
+ data: {
989
+ title: 'Related Articles',
990
+ layout: 'grid',
991
+ columns: 2,
992
+ gap: '1rem',
993
+ children: [
994
+ {
995
+ tag: 'RealCard',
996
+ version: '1.0.0',
997
+ data: {
998
+ title: 'React Best Practices',
999
+ subtitle: '10 min read',
1000
+ href: '/articles/react-best-practices'
1001
+ }
1002
+ },
1003
+ {
1004
+ tag: 'RealCard',
1005
+ version: '1.0.0',
1006
+ data: {
1007
+ title: 'State Management Guide',
1008
+ subtitle: '15 min read',
1009
+ href: '/articles/state-management'
1010
+ }
1011
+ }
1012
+ ]
1013
+ }
1014
+ }
1015
+ ]
1016
+ }
1017
+ };
1018
+
1019
+ const result = ComponentTransformer.deserialize(richContentStructure);
1020
+ expect(React.isValidElement(result)).toBe(true);
1021
+
1022
+ const article = result as ReactElement;
1023
+ expect(article.props.id).toBe('article-content');
1024
+ expect(article.props.className).toContain('article-body');
1025
+ });
1026
+
1027
+ it('should handle user-generated content with safety considerations', () => {
1028
+ const userGeneratedContent = {
1029
+ tag: 'RealCard',
1030
+ version: '1.0.0',
1031
+ data: {
1032
+ title: 'User Comment',
1033
+ content: 'This is a great article! <script>alert("xss")</script> Thanks for sharing.',
1034
+ metadata: {
1035
+ author: 'JohnDoe123',
1036
+ date: '2025-01-01',
1037
+ tags: ['comment', 'feedback']
1038
+ },
1039
+ actions: [
1040
+ {
1041
+ tag: 'RealButton',
1042
+ version: '1.0.0',
1043
+ data: {
1044
+ label: 'Reply',
1045
+ variant: 'secondary',
1046
+ size: 'sm'
1047
+ }
1048
+ },
1049
+ {
1050
+ tag: 'RealButton',
1051
+ version: '1.0.0',
1052
+ data: {
1053
+ label: 'Like',
1054
+ variant: 'secondary',
1055
+ size: 'sm',
1056
+ icon: 'heart'
1057
+ }
1058
+ }
1059
+ ]
1060
+ }
1061
+ };
1062
+
1063
+ const result = ComponentTransformer.deserialize(userGeneratedContent);
1064
+ expect(React.isValidElement(result)).toBe(true);
1065
+
1066
+ // Note: In a real implementation, the content should be sanitized
1067
+ // before reaching the component serialization system
1068
+ const card = result as ReactElement;
1069
+ expect(card.props['data-testid']).toBe('real-card');
1070
+ });
1071
+ });
1072
+
1073
+ describe('Performance and Scale Testing', () => {
1074
+ it('should handle large-scale content efficiently', () => {
1075
+ const startTime = performance.now();
1076
+
1077
+ // Generate 1000 components
1078
+ const largeContentStructure = {
1079
+ tag: 'RealSection',
1080
+ version: '1.0.0',
1081
+ data: {
1082
+ title: 'Large Content Section',
1083
+ layout: 'grid',
1084
+ columns: 4,
1085
+ children: Array.from({ length: 1000 }, (_, i) => ({
1086
+ tag: 'RealCard',
1087
+ version: '1.0.0',
1088
+ data: {
1089
+ id: `item-${i}`,
1090
+ title: `Item ${i}`,
1091
+ content: `Content for item ${i}`,
1092
+ metadata: {
1093
+ index: i,
1094
+ category: `category-${i % 10}`,
1095
+ tags: [`tag-${i % 5}`]
1096
+ },
1097
+ actions: [
1098
+ {
1099
+ tag: 'RealButton',
1100
+ version: '1.0.0',
1101
+ data: {
1102
+ label: `Action ${i}`,
1103
+ variant: i % 2 === 0 ? 'primary' : 'secondary'
1104
+ }
1105
+ }
1106
+ ]
1107
+ }
1108
+ }))
1109
+ }
1110
+ };
1111
+
1112
+ const result = ComponentTransformer.deserialize(largeContentStructure);
1113
+ const endTime = performance.now();
1114
+
1115
+ expect(React.isValidElement(result)).toBe(true);
1116
+ expect(endTime - startTime).toBeLessThan(500); // Should complete within 500ms
1117
+
1118
+ console.log(`Large content deserialization took ${(endTime - startTime).toFixed(2)}ms`);
1119
+ });
1120
+
1121
+ it('should handle complex nested structures in real-world scenarios', () => {
1122
+ // Simulate a complex dashboard
1123
+ const dashboardStructure = {
1124
+ tag: 'RealSection',
1125
+ version: '1.0.0',
1126
+ data: {
1127
+ id: 'dashboard',
1128
+ title: 'Analytics Dashboard',
1129
+ children: Array.from({ length: 20 }, (_, i) => ({
1130
+ tag: 'RealSection',
1131
+ version: '1.0.0',
1132
+ data: {
1133
+ title: `Widget ${i}`,
1134
+ layout: 'grid',
1135
+ columns: 2,
1136
+ children: Array.from({ length: 10 }, (_, j) => ({
1137
+ tag: 'RealCard',
1138
+ version: '1.0.0',
1139
+ data: {
1140
+ title: `Metric ${i}-${j}`,
1141
+ content: `Value: ${Math.random() * 1000}`,
1142
+ actions: [
1143
+ {
1144
+ tag: 'RealButton',
1145
+ version: '1.0.0',
1146
+ data: {
1147
+ label: 'View Details',
1148
+ size: 'sm'
1149
+ }
1150
+ }
1151
+ ]
1152
+ }
1153
+ }))
1154
+ }
1155
+ }))
1156
+ }
1157
+ };
1158
+
1159
+ expect(() => {
1160
+ const result = ComponentTransformer.deserialize(dashboardStructure);
1161
+ expect(React.isValidElement(result)).toBe(true);
1162
+ }).not.toThrow();
1163
+ });
1164
+ });
1165
+ });