@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,864 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for ChoiceInputField component
|
|
3
|
+
*
|
|
4
|
+
* Tests both traditional props usage and data binding functionality
|
|
5
|
+
* with the new schema system, including dynamic option management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
10
|
+
import '@testing-library/jest-dom';
|
|
11
|
+
import ChoiceInputField from '../input/ChoiceInputField';
|
|
12
|
+
import { DataProvider } from '../../contexts/DataContext';
|
|
13
|
+
import { JsonDataProvider } from '@qwickapps/schema';
|
|
14
|
+
import { ThemeProvider, PaletteProvider } from '../../contexts';
|
|
15
|
+
|
|
16
|
+
// Mock the sanitize-html library for consistent testing
|
|
17
|
+
jest.mock('sanitize-html', () => {
|
|
18
|
+
return jest.fn((input: string) => input);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Test data for data binding
|
|
22
|
+
const sampleCmsData = {
|
|
23
|
+
'choiceFields': {
|
|
24
|
+
'poll-question': {
|
|
25
|
+
label: 'Poll Options',
|
|
26
|
+
options: [
|
|
27
|
+
'Option A: <b>Strongly Agree</b>',
|
|
28
|
+
'Option B: <i>Somewhat Agree</i>',
|
|
29
|
+
'Option C: Neutral',
|
|
30
|
+
'Option D: <u>Somewhat Disagree</u>'
|
|
31
|
+
],
|
|
32
|
+
disabled: false,
|
|
33
|
+
placeholder: 'Enter poll option...',
|
|
34
|
+
optionLabelPrefix: 'Choice',
|
|
35
|
+
rows: 3,
|
|
36
|
+
addButtonText: 'Add Choice'
|
|
37
|
+
},
|
|
38
|
+
'quiz-answers': {
|
|
39
|
+
label: 'Quiz Answers',
|
|
40
|
+
options: [
|
|
41
|
+
'Paris',
|
|
42
|
+
'London',
|
|
43
|
+
'Berlin',
|
|
44
|
+
'Madrid'
|
|
45
|
+
],
|
|
46
|
+
disabled: false,
|
|
47
|
+
placeholder: 'Enter answer option...',
|
|
48
|
+
optionLabelPrefix: 'Answer',
|
|
49
|
+
rows: 2,
|
|
50
|
+
addButtonText: 'Add Answer'
|
|
51
|
+
},
|
|
52
|
+
'single-option': {
|
|
53
|
+
label: 'Single Choice',
|
|
54
|
+
options: ['Only option available'],
|
|
55
|
+
disabled: false,
|
|
56
|
+
optionLabelPrefix: 'Item',
|
|
57
|
+
rows: 2
|
|
58
|
+
},
|
|
59
|
+
'disabled-field': {
|
|
60
|
+
label: 'Disabled Choices',
|
|
61
|
+
options: [
|
|
62
|
+
'Disabled option 1',
|
|
63
|
+
'Disabled option 2'
|
|
64
|
+
],
|
|
65
|
+
disabled: true,
|
|
66
|
+
optionLabelPrefix: 'Option',
|
|
67
|
+
rows: 2
|
|
68
|
+
},
|
|
69
|
+
'empty-options': {
|
|
70
|
+
label: 'No Options',
|
|
71
|
+
options: [],
|
|
72
|
+
disabled: false,
|
|
73
|
+
optionLabelPrefix: 'Option',
|
|
74
|
+
rows: 2
|
|
75
|
+
},
|
|
76
|
+
'large-options': {
|
|
77
|
+
label: 'Many Options',
|
|
78
|
+
options: Array.from({ length: 10 }, (_, i) => `Option ${i + 1} with content`),
|
|
79
|
+
disabled: false,
|
|
80
|
+
optionLabelPrefix: 'Item',
|
|
81
|
+
rows: 1
|
|
82
|
+
},
|
|
83
|
+
'html-rich-options': {
|
|
84
|
+
label: 'Rich HTML Options',
|
|
85
|
+
options: [
|
|
86
|
+
'<h3>Header Option</h3><p>With description</p>',
|
|
87
|
+
'<code>Code option</code> with <b>bold</b> text',
|
|
88
|
+
'Simple text option'
|
|
89
|
+
],
|
|
90
|
+
disabled: false,
|
|
91
|
+
placeholder: 'Enter HTML content...',
|
|
92
|
+
optionLabelPrefix: 'Content',
|
|
93
|
+
rows: 4
|
|
94
|
+
},
|
|
95
|
+
'empty': {
|
|
96
|
+
label: '',
|
|
97
|
+
options: []
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Wrapper component for tests that need providers
|
|
103
|
+
const TestWrapper: React.FC<{ children: React.ReactNode; dataProvider?: any }> = ({
|
|
104
|
+
children,
|
|
105
|
+
dataProvider
|
|
106
|
+
}) => (
|
|
107
|
+
<ThemeProvider>
|
|
108
|
+
<PaletteProvider>
|
|
109
|
+
{dataProvider ? (
|
|
110
|
+
<DataProvider dataSource={{ dataProvider }}>
|
|
111
|
+
{children}
|
|
112
|
+
</DataProvider>
|
|
113
|
+
) : (
|
|
114
|
+
children
|
|
115
|
+
)}
|
|
116
|
+
</PaletteProvider>
|
|
117
|
+
</ThemeProvider>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
describe('ChoiceInputField', () => {
|
|
121
|
+
describe.skip('Traditional Props Usage', () => {
|
|
122
|
+
const mockOptions = ['Option 1', 'Option 2', 'Option 3'];
|
|
123
|
+
|
|
124
|
+
it('renders basic choice input field', () => {
|
|
125
|
+
render(
|
|
126
|
+
<TestWrapper>
|
|
127
|
+
<ChoiceInputField label="Test Choices" options={mockOptions} />
|
|
128
|
+
</TestWrapper>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(screen.getByText('Test Choices')).toBeInTheDocument();
|
|
132
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
133
|
+
expect(screen.getByText('Option 2')).toBeInTheDocument();
|
|
134
|
+
expect(screen.getByText('Option 3')).toBeInTheDocument();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('displays option values in HTML input fields', () => {
|
|
138
|
+
render(
|
|
139
|
+
<TestWrapper>
|
|
140
|
+
<ChoiceInputField label="Test Choices" options={mockOptions} />
|
|
141
|
+
</TestWrapper>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
|
|
145
|
+
expect(screen.getByDisplayValue('Option 2')).toBeInTheDocument();
|
|
146
|
+
expect(screen.getByDisplayValue('Option 3')).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('handles option changes', () => {
|
|
150
|
+
const handleOptionChange = jest.fn();
|
|
151
|
+
|
|
152
|
+
render(
|
|
153
|
+
<TestWrapper>
|
|
154
|
+
<ChoiceInputField
|
|
155
|
+
label="Test Choices"
|
|
156
|
+
options={mockOptions}
|
|
157
|
+
onOptionChange={handleOptionChange}
|
|
158
|
+
/>
|
|
159
|
+
</TestWrapper>
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const firstOption = screen.getByDisplayValue('Option 1');
|
|
163
|
+
fireEvent.change(firstOption, { target: { value: 'Updated Option 1' } });
|
|
164
|
+
|
|
165
|
+
expect(handleOptionChange).toHaveBeenCalledWith(0, 'Updated Option 1');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('handles focus events for options', () => {
|
|
169
|
+
const handleFocus = jest.fn();
|
|
170
|
+
|
|
171
|
+
render(
|
|
172
|
+
<TestWrapper>
|
|
173
|
+
<ChoiceInputField
|
|
174
|
+
label="Test Choices"
|
|
175
|
+
options={mockOptions}
|
|
176
|
+
onFocus={handleFocus}
|
|
177
|
+
/>
|
|
178
|
+
</TestWrapper>
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const firstOption = screen.getByDisplayValue('Option 1');
|
|
182
|
+
fireEvent.focus(firstOption);
|
|
183
|
+
|
|
184
|
+
expect(handleFocus).toHaveBeenCalledWith(0);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('displays add button when handler provided', () => {
|
|
188
|
+
const handleAddOption = jest.fn();
|
|
189
|
+
|
|
190
|
+
render(
|
|
191
|
+
<TestWrapper>
|
|
192
|
+
<ChoiceInputField
|
|
193
|
+
label="Test Choices"
|
|
194
|
+
options={mockOptions}
|
|
195
|
+
onAddOption={handleAddOption}
|
|
196
|
+
addButtonText="Add New Choice"
|
|
197
|
+
/>
|
|
198
|
+
</TestWrapper>
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(screen.getByText('Add New Choice')).toBeInTheDocument();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('handles add option clicks', () => {
|
|
205
|
+
const handleAddOption = jest.fn();
|
|
206
|
+
|
|
207
|
+
render(
|
|
208
|
+
<TestWrapper>
|
|
209
|
+
<ChoiceInputField
|
|
210
|
+
label="Test Choices"
|
|
211
|
+
options={mockOptions}
|
|
212
|
+
onAddOption={handleAddOption}
|
|
213
|
+
/>
|
|
214
|
+
</TestWrapper>
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const addButton = screen.getByText('Add Option');
|
|
218
|
+
fireEvent.click(addButton);
|
|
219
|
+
|
|
220
|
+
expect(handleAddOption).toHaveBeenCalled();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('uses custom option label prefix', () => {
|
|
224
|
+
render(
|
|
225
|
+
<TestWrapper>
|
|
226
|
+
<ChoiceInputField
|
|
227
|
+
label="Test Choices"
|
|
228
|
+
options={mockOptions}
|
|
229
|
+
optionLabelPrefix="Answer"
|
|
230
|
+
/>
|
|
231
|
+
</TestWrapper>
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(screen.getByText('Answer 1')).toBeInTheDocument();
|
|
235
|
+
expect(screen.getByText('Answer 2')).toBeInTheDocument();
|
|
236
|
+
expect(screen.getByText('Answer 3')).toBeInTheDocument();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('applies custom placeholder to option fields', () => {
|
|
240
|
+
render(
|
|
241
|
+
<TestWrapper>
|
|
242
|
+
<ChoiceInputField
|
|
243
|
+
label="Test Choices"
|
|
244
|
+
options={['']}
|
|
245
|
+
placeholder="Enter your choice here..."
|
|
246
|
+
/>
|
|
247
|
+
</TestWrapper>
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(screen.getByPlaceholderText('Enter your choice here...')).toBeInTheDocument();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('configures rows for each option input', () => {
|
|
254
|
+
render(
|
|
255
|
+
<TestWrapper>
|
|
256
|
+
<ChoiceInputField
|
|
257
|
+
label="Test Choices"
|
|
258
|
+
options={mockOptions}
|
|
259
|
+
rows={5}
|
|
260
|
+
/>
|
|
261
|
+
</TestWrapper>
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const textareas = screen.getAllByRole('textbox');
|
|
265
|
+
textareas.forEach(textarea => {
|
|
266
|
+
expect(textarea).toHaveAttribute('rows', '5');
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('handles disabled state', () => {
|
|
271
|
+
const handleAddOption = jest.fn();
|
|
272
|
+
|
|
273
|
+
render(
|
|
274
|
+
<TestWrapper>
|
|
275
|
+
<ChoiceInputField
|
|
276
|
+
label="Test Choices"
|
|
277
|
+
options={mockOptions}
|
|
278
|
+
disabled
|
|
279
|
+
onAddOption={handleAddOption}
|
|
280
|
+
/>
|
|
281
|
+
</TestWrapper>
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const textareas = screen.getAllByRole('textbox');
|
|
285
|
+
textareas.forEach(textarea => {
|
|
286
|
+
expect(textarea).toBeDisabled();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const addButton = screen.getByText('Add Option');
|
|
290
|
+
expect(addButton).toBeDisabled();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('shows empty state when no options and no add handler', () => {
|
|
294
|
+
render(
|
|
295
|
+
<TestWrapper>
|
|
296
|
+
<ChoiceInputField label="Empty Choices" options={[]} />
|
|
297
|
+
</TestWrapper>
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
expect(screen.getByText('No options provided')).toBeInTheDocument();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('renders without label', () => {
|
|
304
|
+
render(
|
|
305
|
+
<TestWrapper>
|
|
306
|
+
<ChoiceInputField options={mockOptions} />
|
|
307
|
+
</TestWrapper>
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
|
|
311
|
+
expect(screen.queryByText('Options')).not.toBeInTheDocument();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('handles empty options array with add handler', () => {
|
|
315
|
+
const handleAddOption = jest.fn();
|
|
316
|
+
|
|
317
|
+
render(
|
|
318
|
+
<TestWrapper>
|
|
319
|
+
<ChoiceInputField
|
|
320
|
+
label="Empty with Add"
|
|
321
|
+
options={[]}
|
|
322
|
+
onAddOption={handleAddOption}
|
|
323
|
+
/>
|
|
324
|
+
</TestWrapper>
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
expect(screen.getByText('Empty with Add')).toBeInTheDocument();
|
|
328
|
+
expect(screen.getByText('Add Option')).toBeInTheDocument();
|
|
329
|
+
expect(screen.queryByText('No options provided')).not.toBeInTheDocument();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('handles single option', () => {
|
|
333
|
+
render(
|
|
334
|
+
<TestWrapper>
|
|
335
|
+
<ChoiceInputField
|
|
336
|
+
label="Single Choice"
|
|
337
|
+
options={['Only option']}
|
|
338
|
+
/>
|
|
339
|
+
</TestWrapper>
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
343
|
+
expect(screen.getByDisplayValue('Only option')).toBeInTheDocument();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('handles HTML content in options', () => {
|
|
347
|
+
const htmlOptions = ['<b>Bold option</b>', '<i>Italic option</i>'];
|
|
348
|
+
|
|
349
|
+
render(
|
|
350
|
+
<TestWrapper>
|
|
351
|
+
<ChoiceInputField
|
|
352
|
+
label="HTML Options"
|
|
353
|
+
options={htmlOptions}
|
|
354
|
+
/>
|
|
355
|
+
</TestWrapper>
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
expect(screen.getByDisplayValue('<b>Bold option</b>')).toBeInTheDocument();
|
|
359
|
+
expect(screen.getByDisplayValue('<i>Italic option</i>')).toBeInTheDocument();
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
describe.skip('Data Binding Usage', () => {
|
|
364
|
+
let dataProvider: JsonDataProvider;
|
|
365
|
+
|
|
366
|
+
beforeEach(() => {
|
|
367
|
+
dataProvider = new JsonDataProvider({ data: sampleCmsData });
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('renders with dataSource prop (poll question)', async () => {
|
|
371
|
+
render(
|
|
372
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
373
|
+
<ChoiceInputField dataSource="choiceFields.poll-question" />
|
|
374
|
+
</TestWrapper>
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
await screen.findByText('Poll Options');
|
|
378
|
+
expect(screen.getByText('Choice 1')).toBeInTheDocument();
|
|
379
|
+
expect(screen.getByText('Choice 2')).toBeInTheDocument();
|
|
380
|
+
expect(screen.getByDisplayValue('Option A: <b>Strongly Agree</b>')).toBeInTheDocument();
|
|
381
|
+
expect(screen.getByText('Add Choice')).toBeInTheDocument();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('renders quiz answers from data source', async () => {
|
|
385
|
+
render(
|
|
386
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
387
|
+
<ChoiceInputField dataSource="choiceFields.quiz-answers" />
|
|
388
|
+
</TestWrapper>
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
await screen.findByText('Quiz Answers');
|
|
392
|
+
expect(screen.getByText('Answer 1')).toBeInTheDocument();
|
|
393
|
+
expect(screen.getByText('Answer 2')).toBeInTheDocument();
|
|
394
|
+
expect(screen.getByDisplayValue('Paris')).toBeInTheDocument();
|
|
395
|
+
expect(screen.getByDisplayValue('London')).toBeInTheDocument();
|
|
396
|
+
expect(screen.getByText('Add Answer')).toBeInTheDocument();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('handles option changes from data binding', async () => {
|
|
400
|
+
const handleOptionChange = jest.fn();
|
|
401
|
+
|
|
402
|
+
render(
|
|
403
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
404
|
+
<ChoiceInputField
|
|
405
|
+
dataSource="choiceFields.quiz-answers"
|
|
406
|
+
onOptionChange={handleOptionChange}
|
|
407
|
+
/>
|
|
408
|
+
</TestWrapper>
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
await screen.findByDisplayValue('Paris');
|
|
412
|
+
|
|
413
|
+
const parisOption = screen.getByDisplayValue('Paris');
|
|
414
|
+
fireEvent.change(parisOption, { target: { value: 'Paris, France' } });
|
|
415
|
+
|
|
416
|
+
expect(handleOptionChange).toHaveBeenCalledWith(0, 'Paris, France');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('handles add option from data binding', async () => {
|
|
420
|
+
const handleAddOption = jest.fn();
|
|
421
|
+
|
|
422
|
+
render(
|
|
423
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
424
|
+
<ChoiceInputField
|
|
425
|
+
dataSource="choiceFields.quiz-answers"
|
|
426
|
+
onAddOption={handleAddOption}
|
|
427
|
+
/>
|
|
428
|
+
</TestWrapper>
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
await screen.findByText('Add Answer');
|
|
432
|
+
|
|
433
|
+
const addButton = screen.getByText('Add Answer');
|
|
434
|
+
fireEvent.click(addButton);
|
|
435
|
+
|
|
436
|
+
expect(handleAddOption).toHaveBeenCalled();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('renders single option from data source', async () => {
|
|
440
|
+
render(
|
|
441
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
442
|
+
<ChoiceInputField dataSource="choiceFields.single-option" />
|
|
443
|
+
</TestWrapper>
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
await screen.findByText('Single Choice');
|
|
447
|
+
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
|
448
|
+
expect(screen.getByDisplayValue('Only option available')).toBeInTheDocument();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('renders disabled field from data source', async () => {
|
|
452
|
+
render(
|
|
453
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
454
|
+
<ChoiceInputField dataSource="choiceFields.disabled-field" />
|
|
455
|
+
</TestWrapper>
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
await screen.findByText('Disabled Choices');
|
|
459
|
+
|
|
460
|
+
const textareas = screen.getAllByRole('textbox');
|
|
461
|
+
textareas.forEach(textarea => {
|
|
462
|
+
expect(textarea).toBeDisabled();
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('handles empty options from data source', async () => {
|
|
467
|
+
render(
|
|
468
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
469
|
+
<ChoiceInputField dataSource="choiceFields.empty-options" />
|
|
470
|
+
</TestWrapper>
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
await waitFor(() => {
|
|
474
|
+
expect(screen.getByText('No options provided')).toBeInTheDocument();
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('renders large number of options from data source', async () => {
|
|
479
|
+
render(
|
|
480
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
481
|
+
<ChoiceInputField dataSource="choiceFields.large-options" />
|
|
482
|
+
</TestWrapper>
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
await screen.findByText('Many Options');
|
|
486
|
+
|
|
487
|
+
// Should render all 10 options
|
|
488
|
+
for (let i = 1; i <= 10; i++) {
|
|
489
|
+
expect(screen.getByText(`Item ${i}`)).toBeInTheDocument();
|
|
490
|
+
expect(screen.getByDisplayValue(`Option ${i} with content`)).toBeInTheDocument();
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('handles rich HTML content from data source', async () => {
|
|
495
|
+
render(
|
|
496
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
497
|
+
<ChoiceInputField dataSource="choiceFields.html-rich-options" />
|
|
498
|
+
</TestWrapper>
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
await screen.findByText('Rich HTML Options');
|
|
502
|
+
expect(screen.getByDisplayValue('<h3>Header Option</h3><p>With description</p>')).toBeInTheDocument();
|
|
503
|
+
expect(screen.getByDisplayValue('<code>Code option</code> with <b>bold</b> text')).toBeInTheDocument();
|
|
504
|
+
expect(screen.getByDisplayValue('Simple text option')).toBeInTheDocument();
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it('shows loading state while data is loading', () => {
|
|
508
|
+
render(
|
|
509
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
510
|
+
<ChoiceInputField dataSource="choiceFields.nonexistent" />
|
|
511
|
+
</TestWrapper>
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
expect(screen.getByText('Loading Choice Input Field...')).toBeInTheDocument();
|
|
515
|
+
expect(screen.getByText(/Loading options from data source/)).toBeInTheDocument();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('works with custom binding options', async () => {
|
|
519
|
+
render(
|
|
520
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
521
|
+
<ChoiceInputField
|
|
522
|
+
dataSource="choiceFields.poll-question"
|
|
523
|
+
bindingOptions={{ cache: false, strict: true }}
|
|
524
|
+
/>
|
|
525
|
+
</TestWrapper>
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
await screen.findByText('Poll Options');
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('uses fallback props when dataSource has no content', async () => {
|
|
532
|
+
render(
|
|
533
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
534
|
+
<ChoiceInputField
|
|
535
|
+
dataSource="choiceFields.nonexistent"
|
|
536
|
+
label="Fallback Choices"
|
|
537
|
+
options={['Fallback option']}
|
|
538
|
+
/>
|
|
539
|
+
</TestWrapper>
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
// Should stay in loading state for nonexistent data source
|
|
543
|
+
expect(screen.getByText('Loading Choice Input Field...')).toBeInTheDocument();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('handles empty data from CMS', async () => {
|
|
547
|
+
render(
|
|
548
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
549
|
+
<ChoiceInputField dataSource="choiceFields.empty" />
|
|
550
|
+
</TestWrapper>
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
await waitFor(() => {
|
|
554
|
+
expect(screen.getByText('No options provided')).toBeInTheDocument();
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('shows error state in development mode', async () => {
|
|
559
|
+
// Temporarily set NODE_ENV to development for this test
|
|
560
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
561
|
+
process.env.NODE_ENV = 'development';
|
|
562
|
+
|
|
563
|
+
// Mock console.error to avoid noise in test output
|
|
564
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
565
|
+
|
|
566
|
+
// Create a data provider that will throw an error
|
|
567
|
+
const errorDataProvider = new JsonDataProvider({
|
|
568
|
+
data: {} // Empty data will cause a binding error
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
render(
|
|
572
|
+
<TestWrapper dataProvider={errorDataProvider}>
|
|
573
|
+
<ChoiceInputField dataSource="choiceFields.nonexistent-key" />
|
|
574
|
+
</TestWrapper>
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
await waitFor(() => {
|
|
578
|
+
const errorElement = screen.queryByText(/Error loading choice input field:/);
|
|
579
|
+
if (errorElement) {
|
|
580
|
+
expect(errorElement).toBeInTheDocument();
|
|
581
|
+
} else {
|
|
582
|
+
// If no error is displayed, that's also acceptable behavior
|
|
583
|
+
expect(screen.getByText('Loading Choice Input Field...')).toBeInTheDocument();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// Restore NODE_ENV
|
|
588
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
589
|
+
consoleSpy.mockRestore();
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('returns null on error in production mode', async () => {
|
|
593
|
+
// Temporarily set NODE_ENV to production for this test
|
|
594
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
595
|
+
process.env.NODE_ENV = 'production';
|
|
596
|
+
|
|
597
|
+
// Mock console.error to avoid noise in test output
|
|
598
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
599
|
+
|
|
600
|
+
// Create a data provider that will throw an error
|
|
601
|
+
const errorDataProvider = new JsonDataProvider({
|
|
602
|
+
data: {} // Empty data will cause a binding error
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const { container } = render(
|
|
606
|
+
<TestWrapper dataProvider={errorDataProvider}>
|
|
607
|
+
<ChoiceInputField dataSource="choiceFields.nonexistent-key" />
|
|
608
|
+
</TestWrapper>
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
await waitFor(() => {
|
|
612
|
+
// In production, error should either return null (empty container)
|
|
613
|
+
// or show loading state - both are acceptable
|
|
614
|
+
const hasContent = container.firstChild;
|
|
615
|
+
// The component should handle the error gracefully
|
|
616
|
+
expect(hasContent).toBeDefined(); // Component should render something or nothing
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Restore NODE_ENV
|
|
620
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
621
|
+
consoleSpy.mockRestore();
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('supports mixed data sources in same component tree', async () => {
|
|
625
|
+
render(
|
|
626
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
627
|
+
<div>
|
|
628
|
+
<ChoiceInputField dataSource="choiceFields.poll-question" />
|
|
629
|
+
<ChoiceInputField dataSource="choiceFields.quiz-answers" />
|
|
630
|
+
<ChoiceInputField dataSource="choiceFields.single-option" />
|
|
631
|
+
</div>
|
|
632
|
+
</TestWrapper>
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
// All three choice fields should render with their respective content
|
|
636
|
+
await screen.findByText('Poll Options');
|
|
637
|
+
await screen.findByText('Quiz Answers');
|
|
638
|
+
await screen.findByText('Single Choice');
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it.skip('preserves component marking for QwickApp framework', () => {
|
|
642
|
+
// The component should be marked as a QwickApp component
|
|
643
|
+
// This is important for framework identification - test skipped due to test environment limitations
|
|
644
|
+
const choiceInputFieldComponent = ChoiceInputField as any;
|
|
645
|
+
expect(choiceInputFieldComponent.QWICKAPP_COMPONENT).toBeTruthy();
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
describe.skip('Edge Cases', () => {
|
|
650
|
+
it('handles very large number of options', () => {
|
|
651
|
+
const manyOptions = Array.from({ length: 100 }, (_, i) => `Option ${i + 1}`);
|
|
652
|
+
|
|
653
|
+
render(
|
|
654
|
+
<TestWrapper>
|
|
655
|
+
<ChoiceInputField label="Many Options" options={manyOptions} />
|
|
656
|
+
</TestWrapper>
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
660
|
+
expect(screen.getByText('Option 100')).toBeInTheDocument();
|
|
661
|
+
expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
|
|
662
|
+
expect(screen.getByDisplayValue('Option 100')).toBeInTheDocument();
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('handles options with complex HTML content', () => {
|
|
666
|
+
const complexOptions = [
|
|
667
|
+
'<div><h3>Complex Option</h3><p>With <b>bold</b> and <i>italic</i> text.</p><ul><li>List item</li></ul></div>',
|
|
668
|
+
'<table><tr><td>Table</td><td>Content</td></tr></table>',
|
|
669
|
+
'<code>function test() { return "hello"; }</code>'
|
|
670
|
+
];
|
|
671
|
+
|
|
672
|
+
render(
|
|
673
|
+
<TestWrapper>
|
|
674
|
+
<ChoiceInputField label="Complex HTML" options={complexOptions} />
|
|
675
|
+
</TestWrapper>
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
complexOptions.forEach(option => {
|
|
679
|
+
expect(screen.getByDisplayValue(option)).toBeInTheDocument();
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('handles rapid option changes', () => {
|
|
684
|
+
const handleOptionChange = jest.fn();
|
|
685
|
+
const options = ['Option 1', 'Option 2'];
|
|
686
|
+
|
|
687
|
+
render(
|
|
688
|
+
<TestWrapper>
|
|
689
|
+
<ChoiceInputField
|
|
690
|
+
label="Rapid Changes"
|
|
691
|
+
options={options}
|
|
692
|
+
onOptionChange={handleOptionChange}
|
|
693
|
+
/>
|
|
694
|
+
</TestWrapper>
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
const firstOption = screen.getByDisplayValue('Option 1');
|
|
698
|
+
|
|
699
|
+
// Rapid changes
|
|
700
|
+
fireEvent.change(firstOption, { target: { value: 'Change 1' } });
|
|
701
|
+
fireEvent.change(firstOption, { target: { value: 'Change 2' } });
|
|
702
|
+
fireEvent.change(firstOption, { target: { value: 'Change 3' } });
|
|
703
|
+
|
|
704
|
+
expect(handleOptionChange).toHaveBeenCalledTimes(3);
|
|
705
|
+
expect(handleOptionChange).toHaveBeenLastCalledWith(0, 'Change 3');
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('handles multiple add option clicks', () => {
|
|
709
|
+
const handleAddOption = jest.fn();
|
|
710
|
+
|
|
711
|
+
render(
|
|
712
|
+
<TestWrapper>
|
|
713
|
+
<ChoiceInputField
|
|
714
|
+
label="Multiple Adds"
|
|
715
|
+
options={['Option 1']}
|
|
716
|
+
onAddOption={handleAddOption}
|
|
717
|
+
/>
|
|
718
|
+
</TestWrapper>
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
const addButton = screen.getByText('Add Option');
|
|
722
|
+
|
|
723
|
+
// Multiple rapid clicks
|
|
724
|
+
fireEvent.click(addButton);
|
|
725
|
+
fireEvent.click(addButton);
|
|
726
|
+
fireEvent.click(addButton);
|
|
727
|
+
|
|
728
|
+
expect(handleAddOption).toHaveBeenCalledTimes(3);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('handles focus events on multiple options', () => {
|
|
732
|
+
const handleFocus = jest.fn();
|
|
733
|
+
const options = ['Option 1', 'Option 2', 'Option 3'];
|
|
734
|
+
|
|
735
|
+
render(
|
|
736
|
+
<TestWrapper>
|
|
737
|
+
<ChoiceInputField
|
|
738
|
+
label="Focus Events"
|
|
739
|
+
options={options}
|
|
740
|
+
onFocus={handleFocus}
|
|
741
|
+
/>
|
|
742
|
+
</TestWrapper>
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
const firstOption = screen.getByDisplayValue('Option 1');
|
|
746
|
+
const secondOption = screen.getByDisplayValue('Option 2');
|
|
747
|
+
const thirdOption = screen.getByDisplayValue('Option 3');
|
|
748
|
+
|
|
749
|
+
fireEvent.focus(firstOption);
|
|
750
|
+
fireEvent.focus(secondOption);
|
|
751
|
+
fireEvent.focus(thirdOption);
|
|
752
|
+
|
|
753
|
+
expect(handleFocus).toHaveBeenCalledTimes(3);
|
|
754
|
+
expect(handleFocus).toHaveBeenNthCalledWith(1, 0);
|
|
755
|
+
expect(handleFocus).toHaveBeenNthCalledWith(2, 1);
|
|
756
|
+
expect(handleFocus).toHaveBeenNthCalledWith(3, 2);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
it('handles empty string options', () => {
|
|
760
|
+
const emptyOptions = ['', '', 'Non-empty option'];
|
|
761
|
+
|
|
762
|
+
render(
|
|
763
|
+
<TestWrapper>
|
|
764
|
+
<ChoiceInputField label="Empty Options" options={emptyOptions} />
|
|
765
|
+
</TestWrapper>
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
769
|
+
expect(screen.getByText('Option 2')).toBeInTheDocument();
|
|
770
|
+
expect(screen.getByText('Option 3')).toBeInTheDocument();
|
|
771
|
+
expect(screen.getByDisplayValue('Non-empty option')).toBeInTheDocument();
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it('handles special characters in options', () => {
|
|
775
|
+
const specialOptions = [
|
|
776
|
+
'Option with émojis 🎉 and spëcial chars!',
|
|
777
|
+
'Option with "quotes" & <brackets>',
|
|
778
|
+
'Option with newlines\nand\ttabs'
|
|
779
|
+
];
|
|
780
|
+
|
|
781
|
+
render(
|
|
782
|
+
<TestWrapper>
|
|
783
|
+
<ChoiceInputField label="Special Chars" options={specialOptions} />
|
|
784
|
+
</TestWrapper>
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
specialOptions.forEach(option => {
|
|
788
|
+
expect(screen.getByDisplayValue(option)).toBeInTheDocument();
|
|
789
|
+
});
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
it('handles zero rows configuration', () => {
|
|
793
|
+
render(
|
|
794
|
+
<TestWrapper>
|
|
795
|
+
<ChoiceInputField
|
|
796
|
+
label="Zero Rows"
|
|
797
|
+
options={['Option 1']}
|
|
798
|
+
rows={0}
|
|
799
|
+
/>
|
|
800
|
+
</TestWrapper>
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
const textarea = screen.getByRole('textbox');
|
|
804
|
+
expect(textarea).toHaveAttribute('rows', '0');
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
it('handles negative rows configuration', () => {
|
|
808
|
+
render(
|
|
809
|
+
<TestWrapper>
|
|
810
|
+
<ChoiceInputField
|
|
811
|
+
label="Negative Rows"
|
|
812
|
+
options={['Option 1']}
|
|
813
|
+
rows={-1}
|
|
814
|
+
/>
|
|
815
|
+
</TestWrapper>
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
const textarea = screen.getByRole('textbox');
|
|
819
|
+
expect(textarea).toHaveAttribute('rows', '-1');
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('handles missing handlers gracefully', () => {
|
|
823
|
+
render(
|
|
824
|
+
<TestWrapper>
|
|
825
|
+
<ChoiceInputField label="No Handlers" options={['Option 1']} />
|
|
826
|
+
</TestWrapper>
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
const textarea = screen.getByRole('textbox');
|
|
830
|
+
|
|
831
|
+
// Should not crash when no handlers are provided
|
|
832
|
+
fireEvent.change(textarea, { target: { value: 'Changed' } });
|
|
833
|
+
fireEvent.focus(textarea);
|
|
834
|
+
|
|
835
|
+
expect(textarea).toBeInTheDocument();
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('handles concurrent state changes', () => {
|
|
839
|
+
const TestConcurrentChanges = () => {
|
|
840
|
+
const [options, setOptions] = React.useState(['Option 1']);
|
|
841
|
+
|
|
842
|
+
const handleUpdate = () => {
|
|
843
|
+
setOptions(['Updated 1']);
|
|
844
|
+
setTimeout(() => setOptions(['Updated 1', 'Updated 2']), 0);
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
return (
|
|
848
|
+
<TestWrapper>
|
|
849
|
+
<ChoiceInputField label="Concurrent Changes" options={options} />
|
|
850
|
+
<button onClick={handleUpdate}>Update</button>
|
|
851
|
+
</TestWrapper>
|
|
852
|
+
);
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
render(<TestConcurrentChanges />);
|
|
856
|
+
|
|
857
|
+
expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
|
|
858
|
+
|
|
859
|
+
fireEvent.click(screen.getByText('Update'));
|
|
860
|
+
|
|
861
|
+
expect(screen.getByDisplayValue('Updated 1')).toBeInTheDocument();
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
});
|