@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,691 @@
1
+ /**
2
+ * Component Serialization Integration Tests
3
+ *
4
+ * Comprehensive integration testing for the entire serialization system
5
+ * covering complex scenarios, nested components, and real-world usage patterns.
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
+
14
+ // Mock ErrorBoundary for testing unknown component handling
15
+ class MockErrorBoundary extends React.Component<
16
+ { children: React.ReactNode; onError?: (error: Error) => void },
17
+ { hasError: boolean; error?: Error }
18
+ > {
19
+ constructor(props: any) {
20
+ super(props);
21
+ this.state = { hasError: false };
22
+ }
23
+
24
+ static getDerivedStateFromError(error: Error) {
25
+ return { hasError: true, error };
26
+ }
27
+
28
+ componentDidCatch(error: Error) {
29
+ this.props.onError?.(error);
30
+ }
31
+
32
+ render() {
33
+ if (this.state.hasError) {
34
+ return React.createElement('div', {
35
+ 'data-testid': 'error-boundary',
36
+ 'data-error': this.state.error?.message
37
+ }, 'Component Error');
38
+ }
39
+ return this.props.children;
40
+ }
41
+ }
42
+
43
+ // Complex Mock Components for Integration Testing
44
+ class MockButton implements Serializable {
45
+ constructor(public props: {
46
+ label?: string;
47
+ variant?: 'primary' | 'secondary' | 'danger';
48
+ onClick?: () => void;
49
+ disabled?: boolean;
50
+ children?: any;
51
+ }) {}
52
+
53
+ static fromJson(jsonData: any): ReactElement {
54
+ return React.createElement('button', {
55
+ className: `btn btn-${jsonData.variant || 'primary'}`,
56
+ disabled: jsonData.disabled,
57
+ onClick: jsonData.onClick,
58
+ 'data-testid': 'mock-button'
59
+ }, jsonData.label || jsonData.children || 'Button');
60
+ }
61
+
62
+ toJson(): any {
63
+ return {
64
+ label: this.props.label,
65
+ variant: this.props.variant,
66
+ disabled: this.props.disabled,
67
+ children: this.props.children,
68
+ onClick: this.props.onClick ? 'function' : undefined
69
+ };
70
+ }
71
+ }
72
+
73
+ class MockSection implements Serializable {
74
+ constructor(public props: {
75
+ title?: string;
76
+ className?: string;
77
+ children?: any;
78
+ background?: string;
79
+ }) {}
80
+
81
+ static fromJson(jsonData: any): ReactElement {
82
+ // Section components often need to deserialize their children
83
+ const children = jsonData.children ?
84
+ ComponentTransformer.deserialize(jsonData.children) : null;
85
+
86
+ return React.createElement('section', {
87
+ className: jsonData.className || 'section',
88
+ style: jsonData.background ? { backgroundColor: jsonData.background } : undefined,
89
+ 'data-testid': 'mock-section'
90
+ }, [
91
+ jsonData.title ? React.createElement('h2', { key: 'title' }, jsonData.title) : null,
92
+ children ? React.createElement('div', { key: 'content', className: 'content' }, children) : null
93
+ ].filter(Boolean));
94
+ }
95
+
96
+ toJson(): any {
97
+ return {
98
+ title: this.props.title,
99
+ className: this.props.className,
100
+ background: this.props.background,
101
+ children: this.props.children ? ComponentTransformer.serialize(this.props.children) : null
102
+ };
103
+ }
104
+ }
105
+
106
+ class MockCard implements Serializable {
107
+ constructor(public props: {
108
+ title?: string;
109
+ content?: string;
110
+ image?: string;
111
+ actions?: any[];
112
+ metadata?: Record<string, any>;
113
+ }) {}
114
+
115
+ static fromJson(jsonData: any): ReactElement {
116
+ const actions = jsonData.actions ?
117
+ ComponentTransformer.deserialize(jsonData.actions) : null;
118
+
119
+ return React.createElement('div', {
120
+ className: 'card',
121
+ 'data-testid': 'mock-card'
122
+ }, [
123
+ jsonData.image ? React.createElement('img', {
124
+ key: 'image',
125
+ src: jsonData.image,
126
+ alt: jsonData.title || 'Card image'
127
+ }) : null,
128
+ React.createElement('div', { key: 'body', className: 'card-body' }, [
129
+ jsonData.title ? React.createElement('h3', { key: 'title' }, jsonData.title) : null,
130
+ jsonData.content ? React.createElement('p', { key: 'content' }, jsonData.content) : null,
131
+ jsonData.metadata ? React.createElement('pre', {
132
+ key: 'metadata',
133
+ className: 'metadata'
134
+ }, JSON.stringify(jsonData.metadata, null, 2)) : null,
135
+ actions ? React.createElement('div', { key: 'actions', className: 'actions' }, actions) : null
136
+ ].filter(Boolean))
137
+ ].filter(Boolean));
138
+ }
139
+
140
+ toJson(): any {
141
+ return {
142
+ title: this.props.title,
143
+ content: this.props.content,
144
+ image: this.props.image,
145
+ metadata: this.props.metadata,
146
+ actions: this.props.actions ? ComponentTransformer.serialize(this.props.actions) : null
147
+ };
148
+ }
149
+ }
150
+
151
+ class MockCode implements Serializable {
152
+ constructor(public props: {
153
+ code?: string;
154
+ language?: string;
155
+ showLineNumbers?: boolean;
156
+ highlightLines?: number[];
157
+ }) {}
158
+
159
+ static fromJson(jsonData: any): ReactElement {
160
+ return React.createElement('pre', {
161
+ className: `code-block language-${jsonData.language || 'text'}`,
162
+ 'data-testid': 'mock-code',
163
+ 'data-language': jsonData.language,
164
+ 'data-line-numbers': jsonData.showLineNumbers
165
+ }, React.createElement('code', {}, jsonData.code || ''));
166
+ }
167
+
168
+ toJson(): any {
169
+ return {
170
+ code: this.props.code,
171
+ language: this.props.language,
172
+ showLineNumbers: this.props.showLineNumbers,
173
+ highlightLines: this.props.highlightLines
174
+ };
175
+ }
176
+ }
177
+
178
+ describe('Component Serialization Integration Tests', () => {
179
+ beforeEach(() => {
180
+ ComponentTransformer.clearRegistry();
181
+ ComponentTransformer.registerComponent('Button', MockButton as SerializableConstructor);
182
+ ComponentTransformer.registerComponent('Section', MockSection as SerializableConstructor);
183
+ ComponentTransformer.registerComponent('Card', MockCard as SerializableConstructor);
184
+ ComponentTransformer.registerComponent('Code', MockCode as SerializableConstructor);
185
+ });
186
+
187
+ afterEach(() => {
188
+ ComponentTransformer.clearRegistry();
189
+ });
190
+
191
+ describe('Multi-Component Serialization', () => {
192
+ it('should serialize and deserialize array of different component types', () => {
193
+ const components = [
194
+ {
195
+ tag: 'Button',
196
+ version: '1.0.0',
197
+ data: { label: 'Click Me', variant: 'primary' }
198
+ },
199
+ {
200
+ tag: 'Card',
201
+ version: '1.0.0',
202
+ data: {
203
+ title: 'Test Card',
204
+ content: 'Card content',
205
+ metadata: { author: 'Test User', date: '2025-01-01' }
206
+ }
207
+ },
208
+ {
209
+ tag: 'Code',
210
+ version: '1.0.0',
211
+ data: {
212
+ code: 'const x = 1;',
213
+ language: 'javascript',
214
+ showLineNumbers: true
215
+ }
216
+ }
217
+ ];
218
+
219
+ const result = ComponentTransformer.deserialize(components);
220
+ expect(Array.isArray(result)).toBe(true);
221
+
222
+ const elements = result as ReactElement[];
223
+ expect(elements).toHaveLength(3);
224
+
225
+ // Verify each component type
226
+ expect(React.isValidElement(elements[0])).toBe(true);
227
+ expect(elements[0].props['data-testid']).toBe('mock-button');
228
+ expect(elements[0].props.className).toBe('btn btn-primary');
229
+
230
+ expect(React.isValidElement(elements[1])).toBe(true);
231
+ expect(elements[1].props['data-testid']).toBe('mock-card');
232
+
233
+ expect(React.isValidElement(elements[2])).toBe(true);
234
+ expect(elements[2].props['data-testid']).toBe('mock-code');
235
+ expect(elements[2].props['data-language']).toBe('javascript');
236
+ });
237
+
238
+ it('should handle mixed content arrays with primitives and components', () => {
239
+ const mixedContent = [
240
+ 'Plain text content',
241
+ {
242
+ tag: 'Button',
243
+ version: '1.0.0',
244
+ data: { label: 'Action Button' }
245
+ },
246
+ 42,
247
+ {
248
+ tag: 'Code',
249
+ version: '1.0.0',
250
+ data: { code: 'console.log("Hello");', language: 'javascript' }
251
+ },
252
+ null,
253
+ true
254
+ ];
255
+
256
+ const result = ComponentTransformer.deserialize(mixedContent);
257
+ expect(Array.isArray(result)).toBe(true);
258
+
259
+ const elements = result as any[];
260
+ expect(elements).toHaveLength(6);
261
+ expect(elements[0]).toBe('Plain text content');
262
+ expect(React.isValidElement(elements[1])).toBe(true);
263
+ expect(elements[2]).toBe(42);
264
+ expect(React.isValidElement(elements[3])).toBe(true);
265
+ expect(elements[4]).toBeNull();
266
+ expect(elements[5]).toBe(true);
267
+ });
268
+ });
269
+
270
+ describe('Nested Component Scenarios', () => {
271
+ it('should handle deeply nested component structures', () => {
272
+ const nestedStructure = {
273
+ tag: 'Section',
274
+ version: '1.0.0',
275
+ data: {
276
+ title: 'Main Section',
277
+ className: 'hero-section',
278
+ background: '#f0f0f0',
279
+ children: [
280
+ {
281
+ tag: 'Card',
282
+ version: '1.0.0',
283
+ data: {
284
+ title: 'Feature Card',
285
+ content: 'This card contains actions',
286
+ actions: [
287
+ {
288
+ tag: 'Button',
289
+ version: '1.0.0',
290
+ data: { label: 'Learn More', variant: 'primary' }
291
+ },
292
+ {
293
+ tag: 'Button',
294
+ version: '1.0.0',
295
+ data: { label: 'Try Now', variant: 'secondary' }
296
+ }
297
+ ]
298
+ }
299
+ },
300
+ {
301
+ tag: 'Code',
302
+ version: '1.0.0',
303
+ data: {
304
+ code: 'function example() {\n return "nested code";\n}',
305
+ language: 'javascript',
306
+ showLineNumbers: true
307
+ }
308
+ }
309
+ ]
310
+ }
311
+ };
312
+
313
+ const result = ComponentTransformer.deserialize(nestedStructure);
314
+ expect(React.isValidElement(result)).toBe(true);
315
+
316
+ const element = result as ReactElement;
317
+ expect(element.props['data-testid']).toBe('mock-section');
318
+ expect(element.props.className).toBe('hero-section');
319
+ expect(element.props.style?.backgroundColor).toBe('#f0f0f0');
320
+
321
+ // Verify nested structure exists
322
+ expect(Array.isArray(element.props.children)).toBe(true);
323
+ const sectionChildren = element.props.children.filter(Boolean);
324
+ expect(sectionChildren).toHaveLength(2); // title + content
325
+ });
326
+
327
+ it('should preserve component hierarchy through roundtrip serialization', () => {
328
+ const originalData = {
329
+ tag: 'Section',
330
+ version: '1.0.0',
331
+ data: {
332
+ title: 'Test Section',
333
+ children: [
334
+ {
335
+ tag: 'Card',
336
+ version: '1.0.0',
337
+ data: {
338
+ title: 'Nested Card',
339
+ actions: [
340
+ {
341
+ tag: 'Button',
342
+ version: '1.0.0',
343
+ data: { label: 'Action 1' }
344
+ }
345
+ ]
346
+ }
347
+ }
348
+ ]
349
+ }
350
+ };
351
+
352
+ // Deserialize to React elements
353
+ const reactElements = ComponentTransformer.deserialize(originalData);
354
+
355
+ // Serialize back to JSON
356
+ const serialized = ComponentTransformer.serialize(reactElements);
357
+ const parsedSerialized = JSON.parse(serialized);
358
+
359
+ // Deserialize again
360
+ const finalResult = ComponentTransformer.deserialize(parsedSerialized);
361
+
362
+ expect(React.isValidElement(finalResult)).toBe(true);
363
+ const element = finalResult as ReactElement;
364
+ expect(element.props['data-testid']).toBe('mock-section');
365
+ });
366
+
367
+ it('should handle circular delegation correctly', () => {
368
+ // This tests the scenario where components call ComponentTransformer.deserialize
369
+ // for their children, ensuring no infinite loops or stack overflows
370
+ const circularTestData = {
371
+ tag: 'Section',
372
+ version: '1.0.0',
373
+ data: {
374
+ children: [
375
+ {
376
+ tag: 'Section',
377
+ version: '1.0.0',
378
+ data: {
379
+ children: [
380
+ {
381
+ tag: 'Button',
382
+ version: '1.0.0',
383
+ data: { label: 'Deep Button' }
384
+ }
385
+ ]
386
+ }
387
+ }
388
+ ]
389
+ }
390
+ };
391
+
392
+ expect(() => {
393
+ const result = ComponentTransformer.deserialize(circularTestData);
394
+ expect(React.isValidElement(result)).toBe(true);
395
+ }).not.toThrow();
396
+ });
397
+ });
398
+
399
+ describe('Real-World Page Structures', () => {
400
+ it('should handle complex page layout with mixed components', () => {
401
+ const pageStructure = [
402
+ {
403
+ tag: 'Section',
404
+ version: '1.0.0',
405
+ data: {
406
+ title: 'Header Section',
407
+ className: 'header',
408
+ children: [
409
+ {
410
+ tag: 'Button',
411
+ version: '1.0.0',
412
+ data: { label: 'Get Started', variant: 'primary' }
413
+ }
414
+ ]
415
+ }
416
+ },
417
+ {
418
+ tag: 'Section',
419
+ version: '1.0.0',
420
+ data: {
421
+ title: 'Features',
422
+ className: 'features',
423
+ children: [
424
+ {
425
+ tag: 'Card',
426
+ version: '1.0.0',
427
+ data: {
428
+ title: 'Fast Performance',
429
+ content: 'Optimized for speed',
430
+ actions: [
431
+ {
432
+ tag: 'Button',
433
+ version: '1.0.0',
434
+ data: { label: 'Learn More' }
435
+ }
436
+ ]
437
+ }
438
+ },
439
+ {
440
+ tag: 'Card',
441
+ version: '1.0.0',
442
+ data: {
443
+ title: 'Easy Integration',
444
+ content: 'Simple to implement',
445
+ actions: [
446
+ {
447
+ tag: 'Code',
448
+ version: '1.0.0',
449
+ data: {
450
+ code: 'npm install @qwickapps/react-framework',
451
+ language: 'bash'
452
+ }
453
+ }
454
+ ]
455
+ }
456
+ }
457
+ ]
458
+ }
459
+ }
460
+ ];
461
+
462
+ const result = ComponentTransformer.deserialize(pageStructure);
463
+ expect(Array.isArray(result)).toBe(true);
464
+
465
+ const sections = result as ReactElement[];
466
+ expect(sections).toHaveLength(2);
467
+
468
+ // Verify first section (header)
469
+ expect(sections[0].props['data-testid']).toBe('mock-section');
470
+ expect(sections[0].props.className).toBe('header');
471
+
472
+ // Verify second section (features)
473
+ expect(sections[1].props['data-testid']).toBe('mock-section');
474
+ expect(sections[1].props.className).toBe('features');
475
+ });
476
+
477
+ it('should handle empty and null children gracefully', () => {
478
+ const structureWithEmpties = {
479
+ tag: 'Section',
480
+ version: '1.0.0',
481
+ data: {
482
+ title: 'Test Section',
483
+ children: [
484
+ null,
485
+ {
486
+ tag: 'Card',
487
+ version: '1.0.0',
488
+ data: {
489
+ title: 'Valid Card',
490
+ actions: []
491
+ }
492
+ },
493
+ undefined,
494
+ {
495
+ tag: 'Section',
496
+ version: '1.0.0',
497
+ data: {
498
+ children: null
499
+ }
500
+ }
501
+ ]
502
+ }
503
+ };
504
+
505
+ expect(() => {
506
+ const result = ComponentTransformer.deserialize(structureWithEmpties);
507
+ expect(React.isValidElement(result)).toBe(true);
508
+ }).not.toThrow();
509
+ });
510
+ });
511
+
512
+ describe('Error Boundary Integration', () => {
513
+ it('should trigger error boundary for unknown components', () => {
514
+ const unknownComponent = {
515
+ tag: 'UnknownWidget',
516
+ version: '1.0.0',
517
+ data: { prop: 'value' }
518
+ };
519
+
520
+ let capturedError: Error | null = null;
521
+ const onError = (error: Error) => {
522
+ capturedError = error;
523
+ };
524
+
525
+ // Wrap in error boundary and attempt deserialization
526
+ expect(() => {
527
+ try {
528
+ ComponentTransformer.deserialize(unknownComponent);
529
+ } catch (error) {
530
+ capturedError = error as Error;
531
+ throw error;
532
+ }
533
+ }).toThrow('Unknown component: UnknownWidget');
534
+
535
+ expect(capturedError?.message).toContain('Unknown component: UnknownWidget');
536
+ });
537
+
538
+ it('should handle malformed component data gracefully', () => {
539
+ const malformedComponents = [
540
+ { tag: 'Button' }, // Missing version and data
541
+ { tag: 'Button', version: '1.0.0' }, // Missing data
542
+ { tag: 'Button', version: '1.0.0', data: null }, // Null data
543
+ ];
544
+
545
+ malformedComponents.forEach((malformed, index) => {
546
+ expect(() => {
547
+ ComponentTransformer.deserialize(malformed);
548
+ }).toThrow(/Malformed component data/);
549
+ });
550
+ });
551
+ });
552
+
553
+ describe('Performance Scenarios', () => {
554
+ it('should handle large component trees efficiently', () => {
555
+ const startTime = performance.now();
556
+
557
+ // Generate large component tree (100 components)
558
+ const largeTree = Array.from({ length: 100 }, (_, i) => ({
559
+ tag: 'Card',
560
+ version: '1.0.0',
561
+ data: {
562
+ title: `Card ${i}`,
563
+ content: `Content for card ${i}`,
564
+ metadata: { index: i, timestamp: Date.now() },
565
+ actions: [
566
+ {
567
+ tag: 'Button',
568
+ version: '1.0.0',
569
+ data: { label: `Button ${i}` }
570
+ }
571
+ ]
572
+ }
573
+ }));
574
+
575
+ const result = ComponentTransformer.deserialize(largeTree);
576
+ const endTime = performance.now();
577
+
578
+ expect(Array.isArray(result)).toBe(true);
579
+ expect(result).toHaveLength(100);
580
+
581
+ // Performance assertion - should complete within reasonable time (< 100ms)
582
+ expect(endTime - startTime).toBeLessThan(100);
583
+ });
584
+
585
+ it('should handle deeply nested structures without stack overflow', () => {
586
+ // Create deeply nested structure (20 levels)
587
+ let nestedStructure: any = {
588
+ tag: 'Button',
589
+ version: '1.0.0',
590
+ data: { label: 'Deep Button' }
591
+ };
592
+
593
+ for (let i = 0; i < 20; i++) {
594
+ nestedStructure = {
595
+ tag: 'Section',
596
+ version: '1.0.0',
597
+ data: {
598
+ title: `Level ${i}`,
599
+ children: [nestedStructure]
600
+ }
601
+ };
602
+ }
603
+
604
+ expect(() => {
605
+ const result = ComponentTransformer.deserialize(nestedStructure);
606
+ expect(React.isValidElement(result)).toBe(true);
607
+ }).not.toThrow();
608
+ });
609
+ });
610
+
611
+ describe('Edge Cases and Data Integrity', () => {
612
+ it('should preserve all props through complex roundtrip', () => {
613
+ const complexComponent = {
614
+ tag: 'Card',
615
+ version: '1.0.0',
616
+ data: {
617
+ title: 'Complex Card',
618
+ content: 'Content with special chars: @#$%^&*()_+{}[]|\\:";\'<>?,./`~',
619
+ image: 'https://example.com/image.jpg',
620
+ metadata: {
621
+ tags: ['react', 'serialization', 'testing'],
622
+ ratings: { stars: 4.5, count: 123 },
623
+ nested: { deep: { value: 'preserved' } }
624
+ },
625
+ actions: [
626
+ {
627
+ tag: 'Button',
628
+ version: '1.0.0',
629
+ data: {
630
+ label: 'Special Button',
631
+ variant: 'danger',
632
+ disabled: true
633
+ }
634
+ }
635
+ ]
636
+ }
637
+ };
638
+
639
+ // Deserialize
640
+ const reactElement = ComponentTransformer.deserialize(complexComponent);
641
+ expect(React.isValidElement(reactElement)).toBe(true);
642
+
643
+ // Serialize back
644
+ const serialized = ComponentTransformer.serialize(reactElement);
645
+ const parsed = JSON.parse(serialized);
646
+
647
+ // Verify data preservation
648
+ expect(parsed.tag).toBe('Card');
649
+ expect(parsed.data.title).toBe('Complex Card');
650
+ expect(parsed.data.content).toContain('special chars');
651
+ expect(parsed.data.metadata.nested.deep.value).toBe('preserved');
652
+ });
653
+
654
+ it('should handle Unicode and special characters correctly', () => {
655
+ const unicodeData = {
656
+ tag: 'Code',
657
+ version: '1.0.0',
658
+ data: {
659
+ code: 'const πŸš€ = "rocket";\nconst δΈ­ζ–‡ = "Chinese";\nconst Ξ¨ = "psi";',
660
+ language: 'javascript'
661
+ }
662
+ };
663
+
664
+ const result = ComponentTransformer.deserialize(unicodeData);
665
+ expect(React.isValidElement(result)).toBe(true);
666
+
667
+ const element = result as ReactElement;
668
+ expect(element.props.children.props.children).toContain('πŸš€');
669
+ expect(element.props.children.props.children).toContain('δΈ­ζ–‡');
670
+ expect(element.props.children.props.children).toContain('Ξ¨');
671
+ });
672
+
673
+ it('should handle very large text content', () => {
674
+ const largeText = 'Lorem ipsum '.repeat(1000); // ~11KB of text
675
+
676
+ const largeContentComponent = {
677
+ tag: 'Card',
678
+ version: '1.0.0',
679
+ data: {
680
+ title: 'Large Content Card',
681
+ content: largeText
682
+ }
683
+ };
684
+
685
+ expect(() => {
686
+ const result = ComponentTransformer.deserialize(largeContentComponent);
687
+ expect(React.isValidElement(result)).toBe(true);
688
+ }).not.toThrow();
689
+ });
690
+ });
691
+ });