@qwickapps/react-framework 1.3.5 → 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 (320) hide show
  1. package/README.md +1681 -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,432 @@
1
+ /**
2
+ * AppConfigBuilder Tests
3
+ *
4
+ * Comprehensive test suite for the AppConfigBuilder class.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ import { AppConfigBuilder } from '../AppConfigBuilder';
12
+ import { AppConfig } from '../AppConfig';
13
+
14
+ // Mock fs module for file system tests
15
+ jest.mock('fs');
16
+ const mockFs = fs as jest.Mocked<typeof fs>;
17
+
18
+ describe('AppConfigBuilder', () => {
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ });
22
+
23
+ describe('Builder Pattern', () => {
24
+ it('should create a new builder instance', () => {
25
+ const builder = AppConfigBuilder.create();
26
+ expect(builder).toBeInstanceOf(AppConfigBuilder);
27
+ });
28
+
29
+ it('should support fluent API chaining', () => {
30
+ const builder = AppConfigBuilder.create()
31
+ .withName('Test App')
32
+ .withId('test-app')
33
+ .withPort(3000)
34
+ .withThemeColor('#ff0000');
35
+
36
+ expect(builder).toBeInstanceOf(AppConfigBuilder);
37
+ });
38
+ });
39
+
40
+ describe('Required Fields Validation', () => {
41
+ it('should throw error when app name is missing', () => {
42
+ expect(() => {
43
+ AppConfigBuilder.create()
44
+ .withId('test-app')
45
+ .build();
46
+ }).toThrow('AppConfig validation failed');
47
+ });
48
+
49
+ it('should throw error when app ID is missing', () => {
50
+ expect(() => {
51
+ AppConfigBuilder.create()
52
+ .withName('Test App')
53
+ .build();
54
+ }).toThrow('AppConfig validation failed');
55
+ });
56
+
57
+ it('should build successfully with required fields', () => {
58
+ const config = AppConfigBuilder.create()
59
+ .withName('Test App')
60
+ .withId('test-app')
61
+ .build();
62
+
63
+ expect(config).toBeInstanceOf(AppConfig);
64
+ expect(config.app.name).toBe('Test App');
65
+ expect(config.app.id).toBe('test-app');
66
+ });
67
+ });
68
+
69
+ describe('Default Values', () => {
70
+ it('should apply default values for optional fields', () => {
71
+ const config = AppConfigBuilder.create()
72
+ .withName('Test App')
73
+ .withId('test-app')
74
+ .build();
75
+
76
+ // App defaults
77
+ expect(config.app.shortName).toBe('Test App'); // defaults to name
78
+ expect(config.app.description).toBe('Test App'); // defaults to name
79
+ expect(config.app.version).toBe('1.0.0');
80
+ expect(config.app.logo).toBe('/favicon.ico');
81
+
82
+ // Build defaults
83
+ expect(config.build.port).toBe(3000);
84
+ expect(config.build.previewPort).toBe(4173);
85
+ expect(config.build.outputDir).toBe('dist');
86
+ expect(config.build.publicPath).toBe('/');
87
+ expect(config.build.base).toBe('/');
88
+
89
+ // PWA defaults
90
+ expect(config.pwa.themeColor).toBe('#000000');
91
+ expect(config.pwa.backgroundColor).toBe('#ffffff');
92
+ expect(config.pwa.startUrl).toBe('/');
93
+ expect(config.pwa.scope).toBe('/');
94
+ expect(config.pwa.display).toBe('standalone');
95
+
96
+ // UI defaults
97
+ expect(config.ui.defaultTheme).toBe('light');
98
+ expect(config.ui.defaultPalette).toBe('default');
99
+ expect(config.ui.enableScaffolding).toBe(true);
100
+ expect(config.ui.showThemeSwitcher).toBe(false);
101
+ expect(config.ui.showPaletteSwitcher).toBe(false);
102
+ });
103
+
104
+ it('should override defaults with provided values', () => {
105
+ const config = AppConfigBuilder.create()
106
+ .withName('Custom App')
107
+ .withId('custom-app')
108
+ .withShortName('Custom')
109
+ .withDescription('A custom application')
110
+ .withVersion('2.0.0')
111
+ .withLogo('./custom-logo.svg')
112
+ .withPort(4000)
113
+ .withPreviewPort(5000)
114
+ .withThemeColor('#ff5722')
115
+ .withDefaultTheme('dark')
116
+ .withScaffolding(false)
117
+ .build();
118
+
119
+ expect(config.app.shortName).toBe('Custom');
120
+ expect(config.app.description).toBe('A custom application');
121
+ expect(config.app.version).toBe('2.0.0');
122
+ expect(config.app.logo).toBe('./custom-logo.svg');
123
+ expect(config.build.port).toBe(4000);
124
+ expect(config.build.previewPort).toBe(5000);
125
+ expect(config.pwa.themeColor).toBe('#ff5722');
126
+ expect(config.ui.defaultTheme).toBe('dark');
127
+ expect(config.ui.enableScaffolding).toBe(false);
128
+ });
129
+ });
130
+
131
+ describe('Port Validation', () => {
132
+ it('should accept valid port numbers', () => {
133
+ const config = AppConfigBuilder.create()
134
+ .withName('Test App')
135
+ .withId('test-app')
136
+ .withPort(3000)
137
+ .withPreviewPort(4000)
138
+ .build();
139
+
140
+ expect(config.build.port).toBe(3000);
141
+ expect(config.build.previewPort).toBe(4000);
142
+ });
143
+
144
+ it('should throw error for invalid port numbers', () => {
145
+ expect(() => {
146
+ AppConfigBuilder.create()
147
+ .withName('Test App')
148
+ .withId('test-app')
149
+ .withPort(0)
150
+ .build();
151
+ }).toThrow('Port must be a valid integer between 1 and 65535');
152
+
153
+ expect(() => {
154
+ AppConfigBuilder.create()
155
+ .withName('Test App')
156
+ .withId('test-app')
157
+ .withPort(70000)
158
+ .build();
159
+ }).toThrow('Port must be a valid integer between 1 and 65535');
160
+ });
161
+
162
+ it('should throw error for non-integer ports', () => {
163
+ expect(() => {
164
+ AppConfigBuilder.create()
165
+ .withName('Test App')
166
+ .withId('test-app')
167
+ .withPort(3000.5 as any)
168
+ .build();
169
+ }).toThrow('Port must be a valid integer between 1 and 65535');
170
+ });
171
+ });
172
+
173
+ describe('Logo File Validation', () => {
174
+ it.skip('should warn when logo file does not exist', () => {
175
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
176
+ mockFs.existsSync.mockReturnValue(false);
177
+
178
+ const config = AppConfigBuilder.create()
179
+ .withName('Test App')
180
+ .withId('test-app')
181
+ .withLogo('./missing-logo.svg')
182
+ .build();
183
+
184
+ expect(consoleWarn).toHaveBeenCalledWith(
185
+ '⚠️ AppConfig: Logo file not found: ./missing-logo.svg (will use default)'
186
+ );
187
+ expect(config.app.logo).toBe('./missing-logo.svg'); // Still uses provided path
188
+
189
+ consoleWarn.mockRestore();
190
+ });
191
+
192
+ it('should not warn when logo file exists', () => {
193
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
194
+ mockFs.existsSync.mockReturnValue(true);
195
+
196
+ const config = AppConfigBuilder.create()
197
+ .withName('Test App')
198
+ .withId('test-app')
199
+ .withLogo('./existing-logo.svg')
200
+ .build();
201
+
202
+ expect(consoleWarn).not.toHaveBeenCalled();
203
+ expect(config.app.logo).toBe('./existing-logo.svg');
204
+
205
+ consoleWarn.mockRestore();
206
+ });
207
+
208
+ it('should skip validation when skipValidation option is true', () => {
209
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
210
+
211
+ const config = AppConfigBuilder.create()
212
+ .withOptions({ skipValidation: true })
213
+ .withName('Test App')
214
+ .withId('test-app')
215
+ .withLogo('./missing-logo.svg')
216
+ .build();
217
+
218
+ expect(consoleWarn).not.toHaveBeenCalled();
219
+ expect(mockFs.existsSync).not.toHaveBeenCalled();
220
+
221
+ consoleWarn.mockRestore();
222
+ });
223
+ });
224
+
225
+ describe('Color Validation', () => {
226
+ it('should accept valid hex colors', () => {
227
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
228
+
229
+ const config = AppConfigBuilder.create()
230
+ .withName('Test App')
231
+ .withId('test-app')
232
+ .withThemeColor('#ff5722')
233
+ .withBackgroundColor('#ffffff')
234
+ .build();
235
+
236
+ expect(consoleWarn).not.toHaveBeenCalled();
237
+ expect(config.pwa.themeColor).toBe('#ff5722');
238
+ expect(config.pwa.backgroundColor).toBe('#ffffff');
239
+
240
+ consoleWarn.mockRestore();
241
+ });
242
+
243
+ it('should accept named colors', () => {
244
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
245
+
246
+ const config = AppConfigBuilder.create()
247
+ .withName('Test App')
248
+ .withId('test-app')
249
+ .withThemeColor('red')
250
+ .withBackgroundColor('white')
251
+ .build();
252
+
253
+ expect(consoleWarn).not.toHaveBeenCalled();
254
+
255
+ consoleWarn.mockRestore();
256
+ });
257
+
258
+ it('should warn for potentially invalid colors', () => {
259
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
260
+
261
+ const config = AppConfigBuilder.create()
262
+ .withName('Test App')
263
+ .withId('test-app')
264
+ .withThemeColor('invalid-color')
265
+ .build();
266
+
267
+ expect(consoleWarn).toHaveBeenCalledWith(
268
+ '⚠️ AppConfig: Theme color may not be valid: invalid-color'
269
+ );
270
+
271
+ consoleWarn.mockRestore();
272
+ });
273
+ });
274
+
275
+ describe('Config File Loading', () => {
276
+ it.skip('should load configuration from JSON file', () => {
277
+ const configData = {
278
+ app: {
279
+ name: 'Config App',
280
+ id: 'config-app',
281
+ version: '1.5.0'
282
+ },
283
+ build: {
284
+ port: 3500
285
+ },
286
+ pwa: {
287
+ themeColor: '#2196f3'
288
+ },
289
+ ui: {
290
+ defaultTheme: 'dark'
291
+ }
292
+ };
293
+
294
+ mockFs.readFileSync.mockReturnValue(JSON.stringify(configData));
295
+
296
+ const config = AppConfigBuilder.create()
297
+ .withConfig('./app.config.json')
298
+ .build();
299
+
300
+ expect(config.app.name).toBe('Config App');
301
+ expect(config.app.id).toBe('config-app');
302
+ expect(config.app.version).toBe('1.5.0');
303
+ expect(config.build.port).toBe(3500);
304
+ expect(config.pwa.themeColor).toBe('#2196f3');
305
+ expect(config.ui.defaultTheme).toBe('dark');
306
+ });
307
+
308
+ it.skip('should override config file values with builder methods', () => {
309
+ const configData = {
310
+ app: {
311
+ name: 'Config App',
312
+ id: 'config-app'
313
+ }
314
+ };
315
+
316
+ mockFs.readFileSync.mockReturnValue(JSON.stringify(configData));
317
+
318
+ const config = AppConfigBuilder.create()
319
+ .withConfig('./app.config.json')
320
+ .withName('Override App') // Override the config file value
321
+ .build();
322
+
323
+ expect(config.app.name).toBe('Override App');
324
+ expect(config.app.id).toBe('config-app');
325
+ });
326
+
327
+ it.skip('should warn when config file cannot be loaded', () => {
328
+ const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
329
+ mockFs.readFileSync.mockImplementation(() => {
330
+ throw new Error('File not found');
331
+ });
332
+
333
+ const config = AppConfigBuilder.create()
334
+ .withConfig('./missing.json')
335
+ .withName('Test App')
336
+ .withId('test-app')
337
+ .build();
338
+
339
+ expect(consoleWarn).toHaveBeenCalledWith(
340
+ 'Warning: Could not load config file: ./missing.json'
341
+ );
342
+
343
+ consoleWarn.mockRestore();
344
+ });
345
+ });
346
+
347
+ describe('Computed Properties', () => {
348
+ it('should generate vite configuration', () => {
349
+ const config = AppConfigBuilder.create()
350
+ .withName('Test App')
351
+ .withId('test-app')
352
+ .withPort(3001)
353
+ .withPreviewPort(4001)
354
+ .withOutputDir('build')
355
+ .withBase('/app/')
356
+ .build();
357
+
358
+ const viteConfig = config.viteConfig;
359
+
360
+ expect(viteConfig.server?.port).toBe(3001);
361
+ expect(viteConfig.preview?.port).toBe(4001);
362
+ expect(viteConfig.build?.outDir).toBe('build');
363
+ expect(viteConfig.base).toBe('/app/');
364
+ expect(viteConfig.define?.__APP_NAME__).toBe('"Test App"');
365
+ expect(viteConfig.define?.__APP_ID__).toBe('"test-app"');
366
+ });
367
+
368
+ it('should generate PWA manifest', () => {
369
+ const config = AppConfigBuilder.create()
370
+ .withName('PWA Test App')
371
+ .withId('pwa-test')
372
+ .withShortName('PWA Test')
373
+ .withDescription('A PWA test application')
374
+ .withThemeColor('#4caf50')
375
+ .withBackgroundColor('#fafafa')
376
+ .withDisplay('fullscreen')
377
+ .build();
378
+
379
+ const manifest = config.pwaManifest;
380
+
381
+ expect(manifest.name).toBe('PWA Test App');
382
+ expect(manifest.short_name).toBe('PWA Test');
383
+ expect(manifest.description).toBe('A PWA test application');
384
+ expect(manifest.theme_color).toBe('#4caf50');
385
+ expect(manifest.background_color).toBe('#fafafa');
386
+ expect(manifest.display).toBe('fullscreen');
387
+ expect(manifest.icons).toHaveLength(2);
388
+ });
389
+ });
390
+
391
+ describe('Error Handling', () => {
392
+ it('should collect multiple errors and throw once', () => {
393
+ expect(() => {
394
+ AppConfigBuilder.create()
395
+ .withPort(0) // Invalid port
396
+ .withPreviewPort(100000) // Invalid port
397
+ .build(); // Missing name and ID
398
+ }).toThrow(/AppConfig validation failed/);
399
+ });
400
+
401
+ it('should show all errors in the error message', () => {
402
+ try {
403
+ AppConfigBuilder.create()
404
+ .withPort(0)
405
+ .build();
406
+ } catch (error: any) {
407
+ const message = error.message;
408
+ expect(message).toContain('App name is required');
409
+ expect(message).toContain('App ID is required');
410
+ expect(message).toContain('Port must be a valid integer');
411
+ }
412
+ });
413
+ });
414
+
415
+ describe('Immutability', () => {
416
+ it('should create immutable configuration objects', () => {
417
+ const config = AppConfigBuilder.create()
418
+ .withName('Test App')
419
+ .withId('test-app')
420
+ .build();
421
+
422
+ // Attempt to modify the configuration should not work
423
+ expect(() => {
424
+ (config.app as any).name = 'Modified Name';
425
+ }).toThrow();
426
+
427
+ expect(() => {
428
+ (config as any).app = { name: 'New App' };
429
+ }).toThrow();
430
+ });
431
+ });
432
+ });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * QwickApps Configuration Module
3
+ *
4
+ * Exports configuration types and utilities for QwickApp applications.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ export { AppConfig } from './AppConfig';
10
+ export { AppConfigBuilder } from './AppConfigBuilder';
11
+ export type {
12
+ AppConfig as IAppConfig,
13
+ AppIdentity,
14
+ BuildConfig,
15
+ PWAConfig,
16
+ UIConfig,
17
+ CopyrightConfig,
18
+ AppConfigOptions,
19
+ ValidationResult,
20
+ } from './types';
21
+
22
+ // Re-export for convenience
23
+ import { AppConfigBuilder } from './AppConfigBuilder';
24
+ export const createAppConfig = AppConfigBuilder.create;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * QwickApps AppConfig Types
3
+ *
4
+ * Defines the configuration contract for QwickApp applications.
5
+ * Separates static app configuration from runtime settings.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import type { UserConfig as ViteUserConfig } from 'vite';
11
+
12
+ /**
13
+ * Static, immutable application configuration
14
+ */
15
+ export interface AppConfig {
16
+ /** Application identity and metadata */
17
+ readonly app: AppIdentity;
18
+
19
+ /** Build and development settings */
20
+ readonly build: BuildConfig;
21
+
22
+ /** Progressive Web App configuration */
23
+ readonly pwa: PWAConfig;
24
+
25
+ /** Default theme and UI settings */
26
+ readonly ui: UIConfig;
27
+
28
+ /** Copyright and legal information */
29
+ readonly copyright: CopyrightConfig;
30
+
31
+ /** Computed Vite configuration */
32
+ readonly viteConfig: ViteUserConfig;
33
+
34
+ /** Computed PWA manifest */
35
+ readonly pwaManifest: Record<string, any>;
36
+ }
37
+
38
+ /**
39
+ * Application identity and basic metadata
40
+ */
41
+ export interface AppIdentity {
42
+ /** Unique application identifier */
43
+ readonly id: string;
44
+
45
+ /** Full application name */
46
+ readonly name: string;
47
+
48
+ /** Short name for PWA and limited spaces */
49
+ readonly shortName: string;
50
+
51
+ /** Application description */
52
+ readonly description: string;
53
+
54
+ /** Application version */
55
+ readonly version: string;
56
+
57
+ /** Path to application logo/icon */
58
+ readonly logo: string;
59
+
60
+ /** Application author/organization */
61
+ readonly author?: string;
62
+
63
+ /** Application homepage URL */
64
+ readonly homepage?: string;
65
+ }
66
+
67
+ /**
68
+ * Build and development configuration
69
+ */
70
+ export interface BuildConfig {
71
+ /** Development server port */
72
+ readonly port: number;
73
+
74
+ /** Preview server port */
75
+ readonly previewPort: number;
76
+
77
+ /** Build output directory */
78
+ readonly outputDir: string;
79
+
80
+ /** Public path for assets */
81
+ readonly publicPath: string;
82
+
83
+ /** Base URL for the application */
84
+ readonly base: string;
85
+ }
86
+
87
+ /**
88
+ * Progressive Web App configuration
89
+ */
90
+ export interface PWAConfig {
91
+ /** PWA theme color */
92
+ readonly themeColor: string;
93
+
94
+ /** PWA background color */
95
+ readonly backgroundColor: string;
96
+
97
+ /** PWA start URL */
98
+ readonly startUrl: string;
99
+
100
+ /** PWA scope */
101
+ readonly scope: string;
102
+
103
+ /** PWA display mode */
104
+ readonly display: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';
105
+
106
+ /** PWA orientation */
107
+ readonly orientation?: 'portrait' | 'landscape' | 'any';
108
+ }
109
+
110
+ /**
111
+ * Default UI and theme configuration
112
+ */
113
+ export interface UIConfig {
114
+ /** Default theme mode */
115
+ readonly defaultTheme: 'light' | 'dark' | 'system';
116
+
117
+ /** Default color palette */
118
+ readonly defaultPalette: string;
119
+
120
+ /** Enable scaffolding (navigation, layout) */
121
+ readonly enableScaffolding: boolean;
122
+
123
+ /** Show theme switcher */
124
+ readonly showThemeSwitcher: boolean;
125
+
126
+ /** Show palette switcher */
127
+ readonly showPaletteSwitcher: boolean;
128
+ }
129
+
130
+ /**
131
+ * Copyright and legal information
132
+ */
133
+ export interface CopyrightConfig {
134
+ /** Copyright year ('auto' for current year or specific year) */
135
+ readonly year: 'auto' | number;
136
+
137
+ /** Copyright holder/owner */
138
+ readonly author: string;
139
+
140
+ /** Additional legal text */
141
+ readonly text?: string;
142
+ }
143
+
144
+ /**
145
+ * Configuration builder options
146
+ */
147
+ export interface AppConfigOptions {
148
+ /** Base directory for resolving relative paths */
149
+ baseDir?: string;
150
+
151
+ /** Environment (development, production, test) */
152
+ env?: string;
153
+
154
+ /** Skip file existence validation */
155
+ skipValidation?: boolean;
156
+ }
157
+
158
+ /**
159
+ * Validation result
160
+ */
161
+ export interface ValidationResult {
162
+ /** Validation warnings */
163
+ warnings: string[];
164
+
165
+ /** Validation errors */
166
+ errors: string[];
167
+
168
+ /** Whether configuration is valid */
169
+ isValid: boolean;
170
+ }
package/src/config.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * QwickApps Configuration Module Entry
3
+ *
4
+ * Lightweight export for config-only usage at build time.
5
+ * This file only exports configuration types and builders
6
+ * without pulling in React or MUI dependencies.
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+
11
+ export { AppConfig } from './config/AppConfig';
12
+ export { AppConfigBuilder } from './config/AppConfigBuilder';
13
+ export type {
14
+ AppConfig as IAppConfig,
15
+ AppIdentity,
16
+ BuildConfig,
17
+ PWAConfig,
18
+ UIConfig,
19
+ AppConfigOptions,
20
+ ValidationResult,
21
+ } from './config/types';
22
+
23
+ // Re-export for convenience
24
+ import { AppConfigBuilder } from './config/AppConfigBuilder';
25
+ export const createAppConfig = AppConfigBuilder.create;