@qwickapps/react-framework 1.3.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.
- package/LICENSE +44 -0
- package/README.md +794 -0
- package/dist/components/AccessibilityChecker.d.ts +12 -0
- package/dist/components/AccessibilityChecker.d.ts.map +1 -0
- package/dist/components/Html.d.ts +48 -0
- package/dist/components/Html.d.ts.map +1 -0
- package/dist/components/Logo.d.ts +79 -0
- package/dist/components/Logo.d.ts.map +1 -0
- package/dist/components/Markdown.d.ts +47 -0
- package/dist/components/Markdown.d.ts.map +1 -0
- package/dist/components/QwickApp.d.ts +56 -0
- package/dist/components/QwickApp.d.ts.map +1 -0
- package/dist/components/QwickAppsLogo.d.ts +25 -0
- package/dist/components/QwickAppsLogo.d.ts.map +1 -0
- package/dist/components/ResponsiveMenu.d.ts +38 -0
- package/dist/components/ResponsiveMenu.d.ts.map +1 -0
- package/dist/components/SafeSpan.d.ts +23 -0
- package/dist/components/SafeSpan.d.ts.map +1 -0
- package/dist/components/Scaffold.d.ts +57 -0
- package/dist/components/Scaffold.d.ts.map +1 -0
- package/dist/components/blocks/Article.d.ts +23 -0
- package/dist/components/blocks/Article.d.ts.map +1 -0
- package/dist/components/blocks/CardListGrid.d.ts +23 -0
- package/dist/components/blocks/CardListGrid.d.ts.map +1 -0
- package/dist/components/blocks/Code.d.ts +21 -0
- package/dist/components/blocks/Code.d.ts.map +1 -0
- package/dist/components/blocks/Content.d.ts +24 -0
- package/dist/components/blocks/Content.d.ts.map +1 -0
- package/dist/components/blocks/CoverImageHeader.d.ts +44 -0
- package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -0
- package/dist/components/blocks/FeatureCard.d.ts +66 -0
- package/dist/components/blocks/FeatureCard.d.ts.map +1 -0
- package/dist/components/blocks/FeatureGrid.d.ts +48 -0
- package/dist/components/blocks/FeatureGrid.d.ts.map +1 -0
- package/dist/components/blocks/Footer.d.ts +56 -0
- package/dist/components/blocks/Footer.d.ts.map +1 -0
- package/dist/components/blocks/HeroBlock.d.ts +33 -0
- package/dist/components/blocks/HeroBlock.d.ts.map +1 -0
- package/dist/components/blocks/PageBannerHeader.d.ts +30 -0
- package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -0
- package/dist/components/blocks/ProductCard.d.ts +57 -0
- package/dist/components/blocks/ProductCard.d.ts.map +1 -0
- package/dist/components/blocks/Section.d.ts +40 -0
- package/dist/components/blocks/Section.d.ts.map +1 -0
- package/dist/components/blocks/index.d.ts +37 -0
- package/dist/components/blocks/index.d.ts.map +1 -0
- package/dist/components/buttons/Button.d.ts +38 -0
- package/dist/components/buttons/Button.d.ts.map +1 -0
- package/dist/components/buttons/PaletteSwitcher.d.ts +24 -0
- package/dist/components/buttons/PaletteSwitcher.d.ts.map +1 -0
- package/dist/components/buttons/ThemeSwitcher.d.ts +24 -0
- package/dist/components/buttons/ThemeSwitcher.d.ts.map +1 -0
- package/dist/components/buttons/index.d.ts +11 -0
- package/dist/components/buttons/index.d.ts.map +1 -0
- package/dist/components/forms/FormBlock.d.ts +45 -0
- package/dist/components/forms/FormBlock.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +8 -0
- package/dist/components/forms/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +32 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/input/ChoiceInputField.d.ts +30 -0
- package/dist/components/input/ChoiceInputField.d.ts.map +1 -0
- package/dist/components/input/HtmlInputField.d.ts +29 -0
- package/dist/components/input/HtmlInputField.d.ts.map +1 -0
- package/dist/components/input/SelectInputField.d.ts +29 -0
- package/dist/components/input/SelectInputField.d.ts.map +1 -0
- package/dist/components/input/TextField.d.ts +18 -0
- package/dist/components/input/TextField.d.ts.map +1 -0
- package/dist/components/input/TextInputField.d.ts +32 -0
- package/dist/components/input/TextInputField.d.ts.map +1 -0
- package/dist/components/input/index.d.ts +17 -0
- package/dist/components/input/index.d.ts.map +1 -0
- package/dist/components/layout/GridCell.d.ts +16 -0
- package/dist/components/layout/GridCell.d.ts.map +1 -0
- package/dist/components/layout/GridCellWrapper.d.ts +46 -0
- package/dist/components/layout/GridCellWrapper.d.ts.map +1 -0
- package/dist/components/layout/GridLayout.d.ts +38 -0
- package/dist/components/layout/GridLayout.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +12 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/menu/Menu.d.ts +1 -0
- package/dist/components/menu/Menu.d.ts.map +1 -0
- package/dist/components/menu/MenuItem.d.ts +31 -0
- package/dist/components/menu/MenuItem.d.ts.map +1 -0
- package/dist/components/menu/index.d.ts +7 -0
- package/dist/components/menu/index.d.ts.map +1 -0
- package/dist/components/pages/FormPage.d.ts +66 -0
- package/dist/components/pages/FormPage.d.ts.map +1 -0
- package/dist/components/pages/Page.d.ts +124 -0
- package/dist/components/pages/Page.d.ts.map +1 -0
- package/dist/components/pages/index.d.ts +11 -0
- package/dist/components/pages/index.d.ts.map +1 -0
- package/dist/contexts/DataContext.d.ts +139 -0
- package/dist/contexts/DataContext.d.ts.map +1 -0
- package/dist/contexts/DimensionsContext.d.ts +42 -0
- package/dist/contexts/DimensionsContext.d.ts.map +1 -0
- package/dist/contexts/PaletteContext.d.ts +53 -0
- package/dist/contexts/PaletteContext.d.ts.map +1 -0
- package/dist/contexts/QwickAppContext.d.ts +71 -0
- package/dist/contexts/QwickAppContext.d.ts.map +1 -0
- package/dist/contexts/ThemeContext.d.ts +65 -0
- package/dist/contexts/ThemeContext.d.ts.map +1 -0
- package/dist/contexts/index.d.ts +9 -0
- package/dist/contexts/index.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/useBaseProps.d.ts +101 -0
- package/dist/hooks/useBaseProps.d.ts.map +1 -0
- package/dist/hooks/useDataBinding.d.ts +22 -0
- package/dist/hooks/useDataBinding.d.ts.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.css +1 -0
- package/dist/index.esm.js +24143 -0
- package/dist/index.js +24245 -0
- package/dist/palettes/PaletteAutumn.d.ts +10 -0
- package/dist/palettes/PaletteAutumn.d.ts.map +1 -0
- package/dist/palettes/PaletteCosmic.d.ts +10 -0
- package/dist/palettes/PaletteCosmic.d.ts.map +1 -0
- package/dist/palettes/PaletteDefault.d.ts +10 -0
- package/dist/palettes/PaletteDefault.d.ts.map +1 -0
- package/dist/palettes/PaletteOcean.d.ts +10 -0
- package/dist/palettes/PaletteOcean.d.ts.map +1 -0
- package/dist/palettes/PaletteSpring.d.ts +10 -0
- package/dist/palettes/PaletteSpring.d.ts.map +1 -0
- package/dist/palettes/PaletteWinter.d.ts +10 -0
- package/dist/palettes/PaletteWinter.d.ts.map +1 -0
- package/dist/palettes/index.d.ts +13 -0
- package/dist/palettes/index.d.ts.map +1 -0
- package/dist/schemas/ActionSchema.d.ts +21 -0
- package/dist/schemas/ActionSchema.d.ts.map +1 -0
- package/dist/schemas/ArticleSchema.d.ts +13 -0
- package/dist/schemas/ArticleSchema.d.ts.map +1 -0
- package/dist/schemas/Builders.d.ts +7 -0
- package/dist/schemas/Builders.d.ts.map +1 -0
- package/dist/schemas/ButtonSchema.d.ts +19 -0
- package/dist/schemas/ButtonSchema.d.ts.map +1 -0
- package/dist/schemas/CardListGridSchema.d.ts +17 -0
- package/dist/schemas/CardListGridSchema.d.ts.map +1 -0
- package/dist/schemas/ChoiceInputFieldSchema.d.ts +18 -0
- package/dist/schemas/ChoiceInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/CodeSchema.d.ts +18 -0
- package/dist/schemas/CodeSchema.d.ts.map +1 -0
- package/dist/schemas/ContentSchema.d.ts +20 -0
- package/dist/schemas/ContentSchema.d.ts.map +1 -0
- package/dist/schemas/CoverImageHeaderSchema.d.ts +28 -0
- package/dist/schemas/CoverImageHeaderSchema.d.ts.map +1 -0
- package/dist/schemas/FeatureCardSchema.d.ts +28 -0
- package/dist/schemas/FeatureCardSchema.d.ts.map +1 -0
- package/dist/schemas/FeatureGridSchema.d.ts +17 -0
- package/dist/schemas/FeatureGridSchema.d.ts.map +1 -0
- package/dist/schemas/FeatureItemSchema.d.ts +16 -0
- package/dist/schemas/FeatureItemSchema.d.ts.map +1 -0
- package/dist/schemas/FooterItemSchema.d.ts +15 -0
- package/dist/schemas/FooterItemSchema.d.ts.map +1 -0
- package/dist/schemas/FooterSchema.d.ts +20 -0
- package/dist/schemas/FooterSchema.d.ts.map +1 -0
- package/dist/schemas/FooterSectionSchema.d.ts +15 -0
- package/dist/schemas/FooterSectionSchema.d.ts.map +1 -0
- package/dist/schemas/FormBlockSchema.d.ts +19 -0
- package/dist/schemas/FormBlockSchema.d.ts.map +1 -0
- package/dist/schemas/HeaderActionSchema.d.ts +17 -0
- package/dist/schemas/HeaderActionSchema.d.ts.map +1 -0
- package/dist/schemas/HeroBlockSchema.d.ts +22 -0
- package/dist/schemas/HeroBlockSchema.d.ts.map +1 -0
- package/dist/schemas/HtmlInputFieldSchema.d.ts +18 -0
- package/dist/schemas/HtmlInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/MetadataItemSchema.d.ts +13 -0
- package/dist/schemas/MetadataItemSchema.d.ts.map +1 -0
- package/dist/schemas/PageBannerHeaderSchema.d.ts +28 -0
- package/dist/schemas/PageBannerHeaderSchema.d.ts.map +1 -0
- package/dist/schemas/PaletteSwitcherSchema.d.ts +16 -0
- package/dist/schemas/PaletteSwitcherSchema.d.ts.map +1 -0
- package/dist/schemas/ProductCardSchema.d.ts +39 -0
- package/dist/schemas/ProductCardSchema.d.ts.map +1 -0
- package/dist/schemas/SafeSpanSchema.d.ts +13 -0
- package/dist/schemas/SafeSpanSchema.d.ts.map +1 -0
- package/dist/schemas/SectionSchema.d.ts +17 -0
- package/dist/schemas/SectionSchema.d.ts.map +1 -0
- package/dist/schemas/SelectInputFieldSchema.d.ts +27 -0
- package/dist/schemas/SelectInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/TextInputFieldSchema.d.ts +22 -0
- package/dist/schemas/TextInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/ThemeSwitcherSchema.d.ts +19 -0
- package/dist/schemas/ThemeSwitcherSchema.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +33 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/types.d.ts +7 -0
- package/dist/schemas/types.d.ts.map +1 -0
- package/dist/templates/TemplateResolver.d.ts +52 -0
- package/dist/templates/TemplateResolver.d.ts.map +1 -0
- package/dist/templates/index.d.ts +7 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/tests/ConsoleWarningTest.d.ts +5 -0
- package/dist/tests/ConsoleWarningTest.d.ts.map +1 -0
- package/dist/tests/StorageKeyTest.d.ts +6 -0
- package/dist/tests/StorageKeyTest.d.ts.map +1 -0
- package/dist/tests/ThemeStorageKeyTest.d.ts +6 -0
- package/dist/tests/ThemeStorageKeyTest.d.ts.map +1 -0
- package/dist/types/CacheProvider.d.ts +18 -0
- package/dist/types/CacheProvider.d.ts.map +1 -0
- package/dist/types/ContentProxy.d.ts +47 -0
- package/dist/types/ContentProxy.d.ts.map +1 -0
- package/dist/types/DataBinding.d.ts +7 -0
- package/dist/types/DataBinding.d.ts.map +1 -0
- package/dist/types/DataProvider.d.ts +7 -0
- package/dist/types/DataProvider.d.ts.map +1 -0
- package/dist/types/DataTypes.d.ts +185 -0
- package/dist/types/DataTypes.d.ts.map +1 -0
- package/dist/types/TemplateProvider.d.ts +10 -0
- package/dist/types/TemplateProvider.d.ts.map +1 -0
- package/dist/types/TemplateResolver.d.ts +23 -0
- package/dist/types/TemplateResolver.d.ts.map +1 -0
- package/dist/types/index.d.ts +81 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/breakpoints.d.ts +35 -0
- package/dist/utils/breakpoints.d.ts.map +1 -0
- package/dist/utils/customPaletteManager.d.ts +8 -0
- package/dist/utils/customPaletteManager.d.ts.map +1 -0
- package/dist/utils/dimensions.d.ts +34 -0
- package/dist/utils/dimensions.d.ts.map +1 -0
- package/dist/utils/htmlTransform.d.ts +44 -0
- package/dist/utils/htmlTransform.d.ts.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/paletteUtils.d.ts +38 -0
- package/dist/utils/paletteUtils.d.ts.map +1 -0
- package/dist/utils/persistenceUtils.d.ts +31 -0
- package/dist/utils/persistenceUtils.d.ts.map +1 -0
- package/dist/utils/reactUtils.d.ts +24 -0
- package/dist/utils/reactUtils.d.ts.map +1 -0
- package/dist/utils/spacing.d.ts +34 -0
- package/dist/utils/spacing.d.ts.map +1 -0
- package/dist/utils/themePerformanceMonitor.d.ts +32 -0
- package/dist/utils/themePerformanceMonitor.d.ts.map +1 -0
- package/dist/utils/themeUtils.d.ts +27 -0
- package/dist/utils/themeUtils.d.ts.map +1 -0
- package/package.json +141 -0
- package/src/__tests__/components/Logo.test.js +172 -0
- package/src/__tests__/contexts/DataContext.test.js +505 -0
- package/src/__tests__/contexts/PaletteContext.test.js +115 -0
- package/src/__tests__/contexts/ThemeContext.test.js +123 -0
- package/src/__tests__/utils/paletteUtils.test.js +142 -0
- package/src/__tests__/utils/themeUtils.test.js +142 -0
- package/src/components/AccessibilityChecker.tsx +264 -0
- package/src/components/Html.tsx +191 -0
- package/src/components/Logo.css +217 -0
- package/src/components/Logo.tsx +370 -0
- package/src/components/Markdown.tsx +191 -0
- package/src/components/QwickApp.css +257 -0
- package/src/components/QwickApp.tsx +157 -0
- package/src/components/QwickAppsLogo.tsx +77 -0
- package/src/components/ResponsiveMenu.css +416 -0
- package/src/components/ResponsiveMenu.tsx +310 -0
- package/src/components/SafeSpan.tsx +128 -0
- package/src/components/Scaffold.css +541 -0
- package/src/components/Scaffold.tsx +463 -0
- package/src/components/__tests__/Article.test.tsx +419 -0
- package/src/components/__tests__/Button.test.tsx +702 -0
- package/src/components/__tests__/CardListGrid.test.tsx +478 -0
- package/src/components/__tests__/ChoiceInputField.test.tsx +864 -0
- package/src/components/__tests__/Code.test.tsx +595 -0
- package/src/components/__tests__/Content.integration.test.tsx +193 -0
- package/src/components/__tests__/Content.test.tsx +504 -0
- package/src/components/__tests__/CoverImageHeader.test.tsx +456 -0
- package/src/components/__tests__/FeatureCard.integration.test.tsx +384 -0
- package/src/components/__tests__/FeatureGrid.integration.test.tsx +364 -0
- package/src/components/__tests__/FeatureGrid.test.tsx +494 -0
- package/src/components/__tests__/Footer.test.tsx +544 -0
- package/src/components/__tests__/FormBlock.test.tsx +857 -0
- package/src/components/__tests__/HeroBlock.integration.test.tsx +272 -0
- package/src/components/__tests__/HeroBlock.test.tsx +463 -0
- package/src/components/__tests__/Html.test.tsx +174 -0
- package/src/components/__tests__/HtmlInputField.test.tsx +856 -0
- package/src/components/__tests__/Markdown.test.tsx +233 -0
- package/src/components/__tests__/PageBannerHeader.test.tsx +614 -0
- package/src/components/__tests__/PaletteSwitcher.test.tsx +864 -0
- package/src/components/__tests__/ProductCard.test.tsx +377 -0
- package/src/components/__tests__/SafeSpan.integration.test.tsx +123 -0
- package/src/components/__tests__/SafeSpan.simple.test.tsx +65 -0
- package/src/components/__tests__/SafeSpan.test.tsx +388 -0
- package/src/components/__tests__/Section.integration.test.tsx +288 -0
- package/src/components/__tests__/Section.test.tsx +494 -0
- package/src/components/__tests__/SelectInputField.test.tsx +886 -0
- package/src/components/__tests__/TextInputField.test.tsx +749 -0
- package/src/components/__tests__/ThemeSwitcher.test.tsx +777 -0
- package/src/components/blocks/Article.tsx +194 -0
- package/src/components/blocks/CardListGrid.tsx +132 -0
- package/src/components/blocks/Code.tsx +313 -0
- package/src/components/blocks/Content.tsx +265 -0
- package/src/components/blocks/CoverImageHeader.css +17 -0
- package/src/components/blocks/CoverImageHeader.tsx +435 -0
- package/src/components/blocks/FeatureCard.tsx +321 -0
- package/src/components/blocks/FeatureGrid.tsx +147 -0
- package/src/components/blocks/Footer.tsx +343 -0
- package/src/components/blocks/HeroBlock.tsx +280 -0
- package/src/components/blocks/PageBannerHeader.tsx +471 -0
- package/src/components/blocks/ProductCard.tsx +472 -0
- package/src/components/blocks/Section.tsx +209 -0
- package/src/components/blocks/index.ts +37 -0
- package/src/components/buttons/Button.tsx +233 -0
- package/src/components/buttons/PaletteSwitcher.tsx +268 -0
- package/src/components/buttons/ThemeSwitcher.tsx +283 -0
- package/src/components/buttons/index.ts +11 -0
- package/src/components/forms/FormBlock.tsx +291 -0
- package/src/components/forms/index.ts +7 -0
- package/src/components/index.ts +37 -0
- package/src/components/input/ChoiceInputField.tsx +188 -0
- package/src/components/input/HtmlInputField.tsx +326 -0
- package/src/components/input/SelectInputField.tsx +197 -0
- package/src/components/input/TextField.tsx +47 -0
- package/src/components/input/TextInputField.tsx +144 -0
- package/src/components/input/index.ts +17 -0
- package/src/components/layout/GridCell.tsx +46 -0
- package/src/components/layout/GridCellWrapper.tsx +87 -0
- package/src/components/layout/GridLayout.tsx +169 -0
- package/src/components/layout/index.ts +13 -0
- package/src/components/menu/Menu.tsx +0 -0
- package/src/components/menu/MenuItem.tsx +32 -0
- package/src/components/menu/index.ts +6 -0
- package/src/components/pages/FormPage.tsx +108 -0
- package/src/components/pages/Page.css +460 -0
- package/src/components/pages/Page.tsx +345 -0
- package/src/components/pages/index.ts +11 -0
- package/src/contexts/DataContext.tsx +355 -0
- package/src/contexts/DimensionsContext.tsx +154 -0
- package/src/contexts/PaletteContext.tsx +217 -0
- package/src/contexts/QwickAppContext.tsx +95 -0
- package/src/contexts/ThemeContext.tsx +376 -0
- package/src/contexts/index.ts +9 -0
- package/src/hooks/__tests__/useDataBinding.test.tsx.disabled +229 -0
- package/src/hooks/index.ts +11 -0
- package/src/hooks/useBaseProps.ts +267 -0
- package/src/hooks/useDataBinding.ts +77 -0
- package/src/index.ts +23 -0
- package/src/palettes/PaletteAutumn.css +172 -0
- package/src/palettes/PaletteAutumn.ts +16 -0
- package/src/palettes/PaletteCosmic.css +172 -0
- package/src/palettes/PaletteCosmic.ts +16 -0
- package/src/palettes/PaletteDefault.css +178 -0
- package/src/palettes/PaletteDefault.ts +17 -0
- package/src/palettes/PaletteOcean.css +172 -0
- package/src/palettes/PaletteOcean.ts +16 -0
- package/src/palettes/PaletteSpring.css +160 -0
- package/src/palettes/PaletteSpring.ts +16 -0
- package/src/palettes/PaletteWinter.css +172 -0
- package/src/palettes/PaletteWinter.ts +16 -0
- package/src/palettes/index.css +12 -0
- package/src/palettes/index.ts +29 -0
- package/src/schemas/ActionSchema.ts +140 -0
- package/src/schemas/ArticleSchema.ts +35 -0
- package/src/schemas/ButtonSchema.ts +99 -0
- package/src/schemas/CardListGridSchema.ts +102 -0
- package/src/schemas/ChoiceInputFieldSchema.ts +89 -0
- package/src/schemas/CodeSchema.ts +88 -0
- package/src/schemas/ContentSchema.ts +128 -0
- package/src/schemas/CoverImageHeaderSchema.ts +208 -0
- package/src/schemas/FeatureCardSchema.ts +161 -0
- package/src/schemas/FeatureGridSchema.ts +87 -0
- package/src/schemas/FeatureItemSchema.ts +68 -0
- package/src/schemas/FooterItemSchema.ts +57 -0
- package/src/schemas/FooterSchema.ts +116 -0
- package/src/schemas/FooterSectionSchema.ts +50 -0
- package/src/schemas/FormBlockSchema.ts +102 -0
- package/src/schemas/HeaderActionSchema.ts +83 -0
- package/src/schemas/HeroBlockSchema.ts +149 -0
- package/src/schemas/HtmlInputFieldSchema.ts +88 -0
- package/src/schemas/MetadataItemSchema.ts +35 -0
- package/src/schemas/PageBannerHeaderSchema.ts +206 -0
- package/src/schemas/PaletteSwitcherSchema.ts +66 -0
- package/src/schemas/ProductCardSchema.ts +264 -0
- package/src/schemas/SafeSpanSchema.ts +36 -0
- package/src/schemas/SectionSchema.ts +106 -0
- package/src/schemas/SelectInputFieldSchema.ts +137 -0
- package/src/schemas/TextInputFieldSchema.ts +129 -0
- package/src/schemas/ThemeSwitcherSchema.ts +97 -0
- package/src/schemas/__tests__/builders.test.ts +313 -0
- package/src/schemas/index.ts +34 -0
- package/src/setupTests.js +60 -0
- package/src/stories/Article.stories.tsx +549 -0
- package/src/stories/Button.stories.tsx +498 -0
- package/src/stories/CardListGrid.stories.tsx +539 -0
- package/src/stories/ChoiceInputField.stories.tsx +591 -0
- package/src/stories/Code.stories.tsx +711 -0
- package/src/stories/Content.stories.tsx +463 -0
- package/src/stories/CoverImageHeader.stories.tsx +794 -0
- package/src/stories/DataBinding.advanced.stories.tsx +548 -0
- package/src/stories/DataBinding.stories.tsx +452 -0
- package/src/stories/DataProvider.stories.tsx +1361 -0
- package/src/stories/FeatureCard.stories.tsx +642 -0
- package/src/stories/FeatureGrid.stories.tsx +669 -0
- package/src/stories/Footer.stories.tsx +724 -0
- package/src/stories/FormBlock.stories.tsx +834 -0
- package/src/stories/HeroBlock.stories.tsx +442 -0
- package/src/stories/Html.stories.tsx +264 -0
- package/src/stories/HtmlInputField.stories.tsx +558 -0
- package/src/stories/Introduction.stories.tsx +721 -0
- package/src/stories/LayoutBlocks.stories.tsx +382 -0
- package/src/stories/LayoutSystem.stories.tsx +253 -0
- package/src/stories/Logo.stories.tsx +400 -0
- package/src/stories/Markdown.stories.tsx +349 -0
- package/src/stories/Page.stories.tsx +762 -0
- package/src/stories/PageBannerHeader.stories.tsx +949 -0
- package/src/stories/PaletteSwitcher.stories.tsx +156 -0
- package/src/stories/ProductCard.stories.tsx +504 -0
- package/src/stories/QwickApp.stories.tsx +461 -0
- package/src/stories/ResponsiveMenu.stories.tsx +299 -0
- package/src/stories/SafeSpan.stories.tsx +612 -0
- package/src/stories/Section.stories.tsx +613 -0
- package/src/stories/SelectInputField.stories.tsx +605 -0
- package/src/stories/TextInputField.stories.tsx +526 -0
- package/src/stories/ThemeSwitcher.stories.tsx +170 -0
- package/src/stories/form/FormComponents.stories.tsx +588 -0
- package/src/templates/TemplateResolver.ts +156 -0
- package/src/templates/index.ts +6 -0
- package/src/tests/ConsoleWarningTest.tsx +30 -0
- package/src/tests/StorageKeyTest.tsx +110 -0
- package/src/tests/ThemeStorageKeyTest.tsx +114 -0
- package/src/types/CacheProvider.ts +14 -0
- package/src/types/ContentProxy.ts +99 -0
- package/src/types/DataTypes.ts +196 -0
- package/src/types/TemplateProvider.ts +9 -0
- package/src/types/TemplateResolver.ts +26 -0
- package/src/types/index.ts +99 -0
- package/src/utils/__tests__/createDataDrivenComponent.test.tsx.disabled +193 -0
- package/src/utils/__tests__/htmlTransform.test.tsx +255 -0
- package/src/utils/breakpoints.ts +87 -0
- package/src/utils/customPaletteManager.js +214 -0
- package/src/utils/dimensions.ts +147 -0
- package/src/utils/htmlTransform.tsx +323 -0
- package/src/utils/index.ts +16 -0
- package/src/utils/logger.ts +28 -0
- package/src/utils/paletteUtils.ts +78 -0
- package/src/utils/persistenceUtils.ts +107 -0
- package/src/utils/reactUtils.tsx +37 -0
- package/src/utils/spacing.ts +155 -0
- package/src/utils/themePerformanceMonitor.js +113 -0
- package/src/utils/themeUtils.ts +67 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Context - Manages theme state and switching
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
|
8
|
+
import { createTheme, Theme, ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
|
|
9
|
+
import { useMediaQuery } from '@mui/material';
|
|
10
|
+
import { setTheme as setThemeAttribute } from '../utils/themeUtils';
|
|
11
|
+
import { PaletteProvider } from './PaletteContext';
|
|
12
|
+
import {
|
|
13
|
+
loadUserThemePreference,
|
|
14
|
+
saveUserThemePreference,
|
|
15
|
+
clearUserThemePreference
|
|
16
|
+
} from '../utils/persistenceUtils';
|
|
17
|
+
|
|
18
|
+
// Type definitions
|
|
19
|
+
export type ThemeMode = 'light' | 'dark' | 'system';
|
|
20
|
+
export type ActualThemeMode = 'light' | 'dark';
|
|
21
|
+
|
|
22
|
+
export interface ThemeContextValue {
|
|
23
|
+
/** The theme currently being applied to the UI (resolved from preferred/system/default) */
|
|
24
|
+
currentTheme: ThemeMode;
|
|
25
|
+
/** The user's preferred theme setting, or null if not set */
|
|
26
|
+
preferredTheme: ThemeMode | null;
|
|
27
|
+
/** The resolved theme mode ('light' or 'dark') after system detection */
|
|
28
|
+
actualThemeMode: ActualThemeMode;
|
|
29
|
+
/** The MUI theme object */
|
|
30
|
+
theme: Theme;
|
|
31
|
+
/** Whether the current theme is dark mode */
|
|
32
|
+
isDark: boolean;
|
|
33
|
+
/** Whether preferences are persisted to localStorage */
|
|
34
|
+
isPersistent: boolean;
|
|
35
|
+
|
|
36
|
+
// Theme preference management
|
|
37
|
+
/** Set user's preferred theme (saves to localStorage if persistent) */
|
|
38
|
+
setPreferredTheme: (theme: ThemeMode) => void;
|
|
39
|
+
/** Clear user's preferred theme (removes from localStorage) */
|
|
40
|
+
clearPreferredTheme: () => void;
|
|
41
|
+
/** Get user's preferred theme */
|
|
42
|
+
getPreferredTheme: () => ThemeMode | null;
|
|
43
|
+
|
|
44
|
+
// Current theme management (for admin/preview overrides)
|
|
45
|
+
/** Get currently applied theme */
|
|
46
|
+
getCurrentTheme: () => ThemeMode;
|
|
47
|
+
/** Temporarily override current theme (does not affect user preference) */
|
|
48
|
+
setCurrentTheme: (theme: ThemeMode) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ThemeProviderProps {
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
/**
|
|
54
|
+
* App identifier for generating unique storage keys, or persistence strategy.
|
|
55
|
+
* - string: Use as app identifier to generate keys 'appId.theme' and 'appId.palette'
|
|
56
|
+
* - true (default): Use default keys 'qwickapps-react-framework-theme' and 'qwickapps-react-framework-palette'
|
|
57
|
+
* - false: Disable persistence (session-only)
|
|
58
|
+
* @example 'com.mycompany.myapp' | true | false
|
|
59
|
+
*/
|
|
60
|
+
appId?: string | true | false;
|
|
61
|
+
/**
|
|
62
|
+
* Whether to automatically include PaletteProvider. Default: true
|
|
63
|
+
* Set to false if you want to use ThemeProvider without palette support
|
|
64
|
+
*/
|
|
65
|
+
includePalette?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Default theme for the application when no user preference exists. The default is 'system' which follows the user's system preference.
|
|
68
|
+
* This can be overridden by user settings or host device settings.
|
|
69
|
+
* @example 'light' | 'dark' | 'system'
|
|
70
|
+
*/
|
|
71
|
+
defaultTheme?: ThemeMode;
|
|
72
|
+
/**
|
|
73
|
+
* Default palette for the application when no user preference exists. The default is 'default'.
|
|
74
|
+
* This can be overridden by user settings.
|
|
75
|
+
* This is only used when includePalette is true.
|
|
76
|
+
* @example 'ocean' | 'autumn' | 'spring' | 'winter' | 'default'
|
|
77
|
+
*/
|
|
78
|
+
defaultPalette?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create context with proper typing
|
|
82
|
+
const ThemeContext = createContext<ThemeContextValue | null>(null);
|
|
83
|
+
|
|
84
|
+
export const useTheme = (): ThemeContextValue => {
|
|
85
|
+
const context = useContext(ThemeContext);
|
|
86
|
+
if (!context) {
|
|
87
|
+
throw new Error('useTheme must be used within a ThemeProvider');
|
|
88
|
+
}
|
|
89
|
+
return context;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the default theme based on user preference or fallback
|
|
94
|
+
* @param defaultTheme - The desired default theme
|
|
95
|
+
* @param warn - Whether to issue a warning for invalid themes
|
|
96
|
+
* @returns The resolved theme mode
|
|
97
|
+
*/
|
|
98
|
+
const resolveDefaultTheme = (defaultTheme: ThemeMode | undefined, warn: boolean = true): ThemeMode => {
|
|
99
|
+
if (defaultTheme && ['light', 'dark', 'system'].includes(defaultTheme)) {
|
|
100
|
+
return defaultTheme;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fallback to 'system' if defaultTheme is invalid
|
|
104
|
+
if (warn) {
|
|
105
|
+
console.warn(`[ThemeProvider] Invalid defaultTheme '${defaultTheme}'. Using 'system'.`);
|
|
106
|
+
}
|
|
107
|
+
return 'system';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
111
|
+
children,
|
|
112
|
+
appId,
|
|
113
|
+
includePalette = true,
|
|
114
|
+
defaultTheme = 'light',
|
|
115
|
+
defaultPalette
|
|
116
|
+
}) => {
|
|
117
|
+
// Determine storage key based on appId
|
|
118
|
+
const themeStorageKey = React.useMemo(() => {
|
|
119
|
+
if (appId === false) {
|
|
120
|
+
return null; // No persistence
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (typeof appId === 'string') {
|
|
124
|
+
return `${appId}.theme`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// appId is true or undefined - use default key with warning
|
|
128
|
+
const defaultKey = 'qwickapps-react-framework-theme';
|
|
129
|
+
console.warn(
|
|
130
|
+
`[ThemeProvider] Using default storage key '${defaultKey}'. ` +
|
|
131
|
+
`Consider providing an appId for production apps to avoid conflicts. ` +
|
|
132
|
+
`Example: <ThemeProvider appId="com.mycompany.myapp">`
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return defaultKey;
|
|
136
|
+
}, [appId]);
|
|
137
|
+
|
|
138
|
+
// Resolve the actual storage key based on strategy
|
|
139
|
+
const actualStorageKey = themeStorageKey;
|
|
140
|
+
|
|
141
|
+
// User's preferred theme (what they explicitly chose)
|
|
142
|
+
const [preferredTheme, setPreferredThemeState] = useState<ThemeMode | null>(() => {
|
|
143
|
+
return loadUserThemePreference(actualStorageKey);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Current theme being applied (resolved from preferred/system/default)
|
|
147
|
+
const [currentTheme, setCurrentThemeState] = useState<ThemeMode>(() => {
|
|
148
|
+
const userPreference = loadUserThemePreference(actualStorageKey);
|
|
149
|
+
return userPreference || resolveDefaultTheme(defaultTheme);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Force re-render when palette changes
|
|
153
|
+
const [paletteChangeCounter, setPaletteChangeCounter] = useState<number>(0);
|
|
154
|
+
|
|
155
|
+
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
|
156
|
+
|
|
157
|
+
// Calculate actual theme mode (resolve 'system' to 'light' or 'dark')
|
|
158
|
+
const actualThemeMode: ActualThemeMode = currentTheme === 'system'
|
|
159
|
+
? (prefersDarkMode ? 'dark' : 'light')
|
|
160
|
+
: currentTheme;
|
|
161
|
+
|
|
162
|
+
// Listen for both theme and palette changes
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (typeof window !== 'undefined') {
|
|
165
|
+
const handlePaletteChange = (): void => {
|
|
166
|
+
setPaletteChangeCounter(prev => prev + 1);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleThemeChange = (): void => {
|
|
170
|
+
setPaletteChangeCounter(prev => prev + 1);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Listen for custom palette change events
|
|
174
|
+
window.addEventListener('palette-changed', handlePaletteChange);
|
|
175
|
+
window.addEventListener('theme-changed', handleThemeChange);
|
|
176
|
+
|
|
177
|
+
return () => {
|
|
178
|
+
window.removeEventListener('palette-changed', handlePaletteChange);
|
|
179
|
+
window.removeEventListener('theme-changed', handleThemeChange);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}, []);
|
|
183
|
+
|
|
184
|
+
// Dispatch theme change event when actualThemeMode changes
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (typeof window !== 'undefined') {
|
|
187
|
+
const event = new CustomEvent('theme-changed', {
|
|
188
|
+
detail: { theme: actualThemeMode }
|
|
189
|
+
});
|
|
190
|
+
window.dispatchEvent(event);
|
|
191
|
+
}
|
|
192
|
+
}, [actualThemeMode]);
|
|
193
|
+
|
|
194
|
+
// Update HTML data-theme attribute when actual theme mode changes
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (typeof window !== 'undefined') {
|
|
197
|
+
setThemeAttribute(actualThemeMode);
|
|
198
|
+
}
|
|
199
|
+
}, [actualThemeMode]);
|
|
200
|
+
|
|
201
|
+
// Helper function to get CSS custom property value
|
|
202
|
+
const getCSSCustomProperty = (property: string): string => {
|
|
203
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
204
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(property).trim();
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
return '';
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Create MUI theme
|
|
211
|
+
const theme = React.useMemo((): Theme => {
|
|
212
|
+
const isDark = actualThemeMode === 'dark';
|
|
213
|
+
|
|
214
|
+
// Get colors from CSS theme variables (not palette variables)
|
|
215
|
+
const primaryLight = getCSSCustomProperty('--theme-primary-light') || (isDark ? '#87ceeb' : '#4a90e2');
|
|
216
|
+
const primaryMain = getCSSCustomProperty('--theme-primary') || (isDark ? '#90caf9' : '#1976d2');
|
|
217
|
+
const primaryDark = getCSSCustomProperty('--theme-primary-dark') || (isDark ? '#1a365d' : '#0056b3');
|
|
218
|
+
const primaryContrastText = getCSSCustomProperty('--theme-on-primary') || (isDark ? '#000000' : '#ffffff');
|
|
219
|
+
|
|
220
|
+
const secondaryLight = getCSSCustomProperty('--theme-secondary-light') || (isDark ? '#adb5bd' : '#868e96');
|
|
221
|
+
const secondaryMain = getCSSCustomProperty('--theme-secondary') || (isDark ? '#f48fb1' : '#dc004e');
|
|
222
|
+
const secondaryDark = getCSSCustomProperty('--theme-secondary-dark') || (isDark ? '#6c757d' : '#495057');
|
|
223
|
+
const secondaryContrastText = getCSSCustomProperty('--theme-on-secondary') || (isDark ? '#b0b0b0' : '#666666');
|
|
224
|
+
|
|
225
|
+
const errorLight = getCSSCustomProperty('--theme-error-light') || (isDark ? '#f9c2c2' : '#f5c6cb');
|
|
226
|
+
const errorMain = getCSSCustomProperty('--theme-error') || (isDark ? '#f87171' : '#dc3545');
|
|
227
|
+
const errorDark = getCSSCustomProperty('--theme-error-dark') || (isDark ? '#b91c1c' : '#a71d2a');
|
|
228
|
+
|
|
229
|
+
const warningLight = getCSSCustomProperty('--theme-warning-light') || (isDark ? '#fbbf24' : '#ffc107');
|
|
230
|
+
const warningMain = getCSSCustomProperty('--theme-warning') || (isDark ? '#fbbf24' : '#ffc107');
|
|
231
|
+
const warningDark = getCSSCustomProperty('--theme-warning-dark') || (isDark ? '#b57c00' : '#856404');
|
|
232
|
+
|
|
233
|
+
const infoLight = getCSSCustomProperty('--theme-info-light') || (isDark ? '#60a5fa' : '#007acc');
|
|
234
|
+
const infoMain = getCSSCustomProperty('--theme-info') || (isDark ? '#60a5fa' : '#007acc');
|
|
235
|
+
const infoDark = getCSSCustomProperty('--theme-info-dark') || (isDark ? '#1e3a8a' : '#0d6efd');
|
|
236
|
+
|
|
237
|
+
const successLight = getCSSCustomProperty('--theme-success-light') || (isDark ? '#4ade80' : '#28a745');
|
|
238
|
+
const successMain = getCSSCustomProperty('--theme-success') || (isDark ? '#4ade80' : '#28a745');
|
|
239
|
+
const successDark = getCSSCustomProperty('--theme-success-dark') || (isDark ? '#1f9e3d' : '#1c7430');
|
|
240
|
+
|
|
241
|
+
const backgroundMain = getCSSCustomProperty('--theme-background') || (isDark ? '#121212' : '#ffffff');
|
|
242
|
+
const surfaceMain = getCSSCustomProperty('--theme-surface') || (isDark ? '#1e1e1e' : '#ffffff');
|
|
243
|
+
|
|
244
|
+
const textPrimary = getCSSCustomProperty('--theme-text-primary') || (isDark ? '#ffffff' : '#000000');
|
|
245
|
+
const textSecondary = getCSSCustomProperty('--theme-text-secondary') || (isDark ? '#000000' : '#ffffff');
|
|
246
|
+
const textDisabled = getCSSCustomProperty('--theme-on-disabled') || (isDark ? 'rgba(255, 255, 255, 0.38)' : 'rgba(0, 0, 0, 0.38)');
|
|
247
|
+
|
|
248
|
+
return createTheme({
|
|
249
|
+
palette: {
|
|
250
|
+
mode: actualThemeMode,
|
|
251
|
+
primary: {
|
|
252
|
+
main: primaryMain,
|
|
253
|
+
light: primaryLight,
|
|
254
|
+
dark: primaryDark,
|
|
255
|
+
contrastText: primaryContrastText,
|
|
256
|
+
},
|
|
257
|
+
secondary: {
|
|
258
|
+
main: secondaryMain,
|
|
259
|
+
light: secondaryLight,
|
|
260
|
+
dark: secondaryDark,
|
|
261
|
+
contrastText: secondaryContrastText,
|
|
262
|
+
},
|
|
263
|
+
error: {
|
|
264
|
+
light: errorLight,
|
|
265
|
+
main: errorMain,
|
|
266
|
+
dark: errorDark,
|
|
267
|
+
},
|
|
268
|
+
warning: {
|
|
269
|
+
light: warningLight,
|
|
270
|
+
main: warningMain,
|
|
271
|
+
dark: warningDark,
|
|
272
|
+
},
|
|
273
|
+
info: {
|
|
274
|
+
light: infoLight,
|
|
275
|
+
main: infoMain,
|
|
276
|
+
dark: infoDark,
|
|
277
|
+
},
|
|
278
|
+
background: {
|
|
279
|
+
default: backgroundMain,
|
|
280
|
+
paper: surfaceMain,
|
|
281
|
+
},
|
|
282
|
+
text: {
|
|
283
|
+
primary: textPrimary,
|
|
284
|
+
secondary: textSecondary,
|
|
285
|
+
disabled: textDisabled,
|
|
286
|
+
},
|
|
287
|
+
success: {
|
|
288
|
+
light: successLight,
|
|
289
|
+
main: successMain,
|
|
290
|
+
dark: successDark,
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
typography: {
|
|
294
|
+
fontFamily: '"Segoe UI", "Roboto", "Arial", sans-serif',
|
|
295
|
+
},
|
|
296
|
+
components: {
|
|
297
|
+
MuiButton: {
|
|
298
|
+
styleOverrides: {
|
|
299
|
+
root: {
|
|
300
|
+
textTransform: 'none',
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
307
|
+
}, [actualThemeMode, paletteChangeCounter]); // paletteChangeCounter is needed to force theme rebuild // Re-create theme when palette changes
|
|
308
|
+
|
|
309
|
+
// Theme preference management methods
|
|
310
|
+
const setPreferredTheme = (theme: ThemeMode): void => {
|
|
311
|
+
setPreferredThemeState(theme);
|
|
312
|
+
setCurrentThemeState(theme);
|
|
313
|
+
saveUserThemePreference(actualStorageKey, theme);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const clearPreferredTheme = (): void => {
|
|
317
|
+
setPreferredThemeState(null);
|
|
318
|
+
clearUserThemePreference(actualStorageKey);
|
|
319
|
+
|
|
320
|
+
// Reset to default theme
|
|
321
|
+
const fallbackTheme = resolveDefaultTheme(defaultTheme, false);
|
|
322
|
+
setCurrentThemeState(fallbackTheme);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const getPreferredTheme = (): ThemeMode | null => {
|
|
326
|
+
return preferredTheme;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Current theme management methods (for admin/preview overrides)
|
|
330
|
+
const getCurrentTheme = (): ThemeMode => {
|
|
331
|
+
return currentTheme;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const setCurrentTheme = (theme: ThemeMode): void => {
|
|
335
|
+
setCurrentThemeState(theme);
|
|
336
|
+
// Note: This does NOT update preferredTheme or localStorage
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const value: ThemeContextValue = {
|
|
340
|
+
currentTheme,
|
|
341
|
+
preferredTheme,
|
|
342
|
+
actualThemeMode,
|
|
343
|
+
theme,
|
|
344
|
+
isDark: actualThemeMode === 'dark',
|
|
345
|
+
isPersistent: actualStorageKey !== null,
|
|
346
|
+
|
|
347
|
+
// Theme preference management
|
|
348
|
+
setPreferredTheme,
|
|
349
|
+
clearPreferredTheme,
|
|
350
|
+
getPreferredTheme,
|
|
351
|
+
|
|
352
|
+
// Current theme management
|
|
353
|
+
getCurrentTheme,
|
|
354
|
+
setCurrentTheme,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const content = (
|
|
358
|
+
<ThemeContext.Provider value={value}>
|
|
359
|
+
<MuiThemeProvider theme={theme}>
|
|
360
|
+
{children}
|
|
361
|
+
</MuiThemeProvider>
|
|
362
|
+
</ThemeContext.Provider>
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// Conditionally wrap with PaletteProvider if enabled
|
|
366
|
+
if (includePalette) {
|
|
367
|
+
return (
|
|
368
|
+
<PaletteProvider appId={appId} defaultPalette={defaultPalette}>
|
|
369
|
+
{content}
|
|
370
|
+
</PaletteProvider>
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// No palette provider
|
|
375
|
+
return content;
|
|
376
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Context exports for easier importing
|
|
2
|
+
export { DataProvider, t, T, useData, useDataContext, useDataProvider, useResolveTemplate, useTemplate } from './DataContext';
|
|
3
|
+
export type { DataProviderProps } from './DataContext';
|
|
4
|
+
export { DimensionsProvider, useDimensions } from './DimensionsContext';
|
|
5
|
+
export type { DimensionsContextValue, DimensionTokens } from './DimensionsContext';
|
|
6
|
+
export { PaletteProvider, usePalette } from './PaletteContext';
|
|
7
|
+
export type { PaletteConfig, PaletteContextValue } from './PaletteContext';
|
|
8
|
+
export { ThemeProvider, useTheme } from './ThemeContext';
|
|
9
|
+
export type { ActualThemeMode, ThemeContextValue, ThemeMode } from './ThemeContext';
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for useDataBinding hook
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { render, waitFor } from '@testing-library/react';
|
|
9
|
+
import { useDataBinding } from '../useDataBinding';
|
|
10
|
+
import { DataProvider } from '../../contexts/DataContext';
|
|
11
|
+
import { JsonDataProvider } from '../../providers/JsonDataProvider';
|
|
12
|
+
import { ComponentSchema } from '../../schemas/types';
|
|
13
|
+
|
|
14
|
+
// Test component that uses useDataBinding
|
|
15
|
+
interface TestComponentProps {
|
|
16
|
+
html?: string;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const TestComponent: React.FC<{
|
|
21
|
+
dataSource?: string;
|
|
22
|
+
fallbackProps?: TestComponentProps;
|
|
23
|
+
schema?: ComponentSchema;
|
|
24
|
+
strict?: boolean;
|
|
25
|
+
}> = ({ dataSource, fallbackProps, schema, strict = false }) => {
|
|
26
|
+
const resolvedProps = useDataBinding<TestComponentProps>(
|
|
27
|
+
dataSource,
|
|
28
|
+
fallbackProps,
|
|
29
|
+
schema,
|
|
30
|
+
{ strict }
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div data-testid="test-component">
|
|
35
|
+
<div data-testid="html">{resolvedProps.html || ''}</div>
|
|
36
|
+
<div data-testid="placeholder">{resolvedProps.placeholder || ''}</div>
|
|
37
|
+
<div data-testid="loading">{resolvedProps.loading ? 'loading' : 'ready'}</div>
|
|
38
|
+
<div data-testid="error">{resolvedProps.error?.message || 'none'}</div>
|
|
39
|
+
<div data-testid="data-source">{resolvedProps.dataSource || 'none'}</div>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe('useDataBinding', () => {
|
|
45
|
+
const testData = {
|
|
46
|
+
'company.description': [
|
|
47
|
+
{
|
|
48
|
+
html: '<p>QwickApps helps you <strong>build apps faster</strong> with our comprehensive framework.</p>',
|
|
49
|
+
placeholder: 'Loading company description...'
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
'pages.home.tagline': [
|
|
53
|
+
{
|
|
54
|
+
html: '<h1>Welcome to QwickApps</h1>',
|
|
55
|
+
placeholder: 'Loading tagline...'
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const dataProvider = new JsonDataProvider(testData);
|
|
61
|
+
|
|
62
|
+
it('should return fallback props when no dataSource is provided', () => {
|
|
63
|
+
const fallbackProps = { html: '<p>Fallback content</p>', placeholder: 'Fallback placeholder' };
|
|
64
|
+
|
|
65
|
+
const { getByTestId } = render(
|
|
66
|
+
<TestComponent fallbackProps={fallbackProps} />
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(getByTestId('html')).toHaveTextContent('Fallback content');
|
|
70
|
+
expect(getByTestId('placeholder')).toHaveTextContent('Fallback placeholder');
|
|
71
|
+
expect(getByTestId('loading')).toHaveTextContent('ready');
|
|
72
|
+
expect(getByTestId('data-source')).toHaveTextContent('none');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should resolve data from dataSource when provided', async () => {
|
|
76
|
+
const { getByTestId } = render(
|
|
77
|
+
<DataProvider dataSource={dataProvider}>
|
|
78
|
+
<TestComponent dataSource="company.description" />
|
|
79
|
+
</DataProvider>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
await waitFor(() => {
|
|
83
|
+
expect(getByTestId('html')).toHaveTextContent('QwickApps helps you build apps faster with our comprehensive framework.');
|
|
84
|
+
expect(getByTestId('placeholder')).toHaveTextContent('Loading company description...');
|
|
85
|
+
expect(getByTestId('data-source')).toHaveTextContent('company.description');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should merge dataSource data with fallback props', async () => {
|
|
90
|
+
const fallbackProps = { placeholder: 'Default placeholder' };
|
|
91
|
+
|
|
92
|
+
const { getByTestId } = render(
|
|
93
|
+
<DataProvider dataSource={dataProvider}>
|
|
94
|
+
<TestComponent
|
|
95
|
+
dataSource="company.description"
|
|
96
|
+
fallbackProps={fallbackProps}
|
|
97
|
+
/>
|
|
98
|
+
</DataProvider>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
// Data source should override fallback
|
|
103
|
+
expect(getByTestId('html')).toHaveTextContent('QwickApps helps you build apps faster with our comprehensive framework.');
|
|
104
|
+
expect(getByTestId('placeholder')).toHaveTextContent('Loading company description...');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should use fallback props when dataSource returns no data', async () => {
|
|
109
|
+
const fallbackProps = { html: '<p>Fallback content</p>', placeholder: 'Fallback placeholder' };
|
|
110
|
+
|
|
111
|
+
const { getByTestId } = render(
|
|
112
|
+
<DataProvider dataSource={dataProvider}>
|
|
113
|
+
<TestComponent
|
|
114
|
+
dataSource="nonexistent.data"
|
|
115
|
+
fallbackProps={fallbackProps}
|
|
116
|
+
/>
|
|
117
|
+
</DataProvider>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
await waitFor(() => {
|
|
121
|
+
expect(getByTestId('html')).toHaveTextContent('Fallback content');
|
|
122
|
+
expect(getByTestId('placeholder')).toHaveTextContent('Fallback placeholder');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should validate data against schema in strict mode', async () => {
|
|
127
|
+
const incompleteData = {
|
|
128
|
+
'test.incomplete': [
|
|
129
|
+
{
|
|
130
|
+
html: '<p>Missing required field</p>'
|
|
131
|
+
// Missing placeholder which is not required, but let's test custom validation
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const strictProvider = new JsonDataProvider(incompleteData);
|
|
137
|
+
const fallbackProps = { html: '<p>Fallback</p>', placeholder: 'Fallback placeholder' };
|
|
138
|
+
|
|
139
|
+
// Create schema with custom validation
|
|
140
|
+
const strictSchema: ComponentSchema = {
|
|
141
|
+
...undefined,
|
|
142
|
+
fields: {
|
|
143
|
+
...undefined.fields,
|
|
144
|
+
html: {
|
|
145
|
+
...undefined.fields.html,
|
|
146
|
+
required: true,
|
|
147
|
+
validation: {
|
|
148
|
+
custom: (value: string) => value.includes('<p>') || 'HTML must contain paragraph tags'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const { getByTestId } = render(
|
|
155
|
+
<DataProvider dataSource={strictProvider}>
|
|
156
|
+
<TestComponent
|
|
157
|
+
dataSource="test.incomplete"
|
|
158
|
+
fallbackProps={fallbackProps}
|
|
159
|
+
schema={strictSchema}
|
|
160
|
+
strict={true}
|
|
161
|
+
/>
|
|
162
|
+
</DataProvider>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
await waitFor(() => {
|
|
166
|
+
// Should pass validation since HTML contains <p>
|
|
167
|
+
expect(getByTestId('html')).toHaveTextContent('Missing required field');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should handle validation failures in strict mode', async () => {
|
|
172
|
+
const invalidData = {
|
|
173
|
+
'test.invalid': [
|
|
174
|
+
{
|
|
175
|
+
html: '<div>Invalid HTML structure</div>', // Will fail custom validation
|
|
176
|
+
placeholder: 'Test placeholder'
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const strictProvider = new JsonDataProvider(invalidData);
|
|
182
|
+
const fallbackProps = { html: '<p>Fallback</p>', placeholder: 'Fallback placeholder' };
|
|
183
|
+
|
|
184
|
+
// Create schema with failing validation
|
|
185
|
+
const strictSchema: ComponentSchema = {
|
|
186
|
+
...undefined,
|
|
187
|
+
fields: {
|
|
188
|
+
...undefined.fields,
|
|
189
|
+
html: {
|
|
190
|
+
...undefined.fields.html,
|
|
191
|
+
validation: {
|
|
192
|
+
custom: (value: string) => value.includes('<p>') || 'HTML must contain paragraph tags'
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const { getByTestId } = render(
|
|
199
|
+
<DataProvider dataSource={strictProvider}>
|
|
200
|
+
<TestComponent
|
|
201
|
+
dataSource="test.invalid"
|
|
202
|
+
fallbackProps={fallbackProps}
|
|
203
|
+
schema={strictSchema}
|
|
204
|
+
strict={true}
|
|
205
|
+
/>
|
|
206
|
+
</DataProvider>
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
await waitFor(() => {
|
|
210
|
+
// Should fall back due to validation failure
|
|
211
|
+
expect(getByTestId('html')).toHaveTextContent('Fallback');
|
|
212
|
+
expect(getByTestId('placeholder')).toHaveTextContent('Fallback placeholder');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should include metadata in returned props', async () => {
|
|
217
|
+
const { getByTestId } = render(
|
|
218
|
+
<DataProvider dataSource={dataProvider}>
|
|
219
|
+
<TestComponent dataSource="company.description" schema={undefined} />
|
|
220
|
+
</DataProvider>
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
await waitFor(() => {
|
|
224
|
+
expect(getByTestId('loading')).toHaveTextContent('ready');
|
|
225
|
+
expect(getByTestId('error')).toHaveTextContent('none');
|
|
226
|
+
expect(getByTestId('data-source')).toHaveTextContent('company.description');
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks available in QwickApps React Framework
|
|
3
|
+
*
|
|
4
|
+
* Common hooks used across QwickApps components
|
|
5
|
+
*/
|
|
6
|
+
export { useBaseProps } from './useBaseProps';
|
|
7
|
+
export type { BaseComponentProps, WithBaseProps } from './useBaseProps';
|
|
8
|
+
|
|
9
|
+
// Data Binding hooks for AI-driven component system
|
|
10
|
+
export { useDataBinding } from './useDataBinding';
|
|
11
|
+
export { QWICKAPP_COMPONENT } from './useBaseProps';
|