@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,886 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for SelectInputField component
|
|
3
|
+
*
|
|
4
|
+
* Tests both traditional props usage and data binding functionality
|
|
5
|
+
* with the new schema system.
|
|
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 SelectInputField from '../input/SelectInputField';
|
|
12
|
+
import { DataProvider } from '../../contexts/DataContext';
|
|
13
|
+
import { JsonDataProvider } from '@qwickapps/schema';
|
|
14
|
+
import { ThemeProvider, PaletteProvider } from '../../contexts';
|
|
15
|
+
|
|
16
|
+
// Test data for data binding
|
|
17
|
+
const sampleCmsData = {
|
|
18
|
+
'selectFields': {
|
|
19
|
+
'country-select': {
|
|
20
|
+
label: 'Country',
|
|
21
|
+
value: '',
|
|
22
|
+
options: [
|
|
23
|
+
{ value: 'us', label: 'United States', disabled: false },
|
|
24
|
+
{ value: 'ca', label: 'Canada', disabled: false },
|
|
25
|
+
{ value: 'uk', label: 'United Kingdom', disabled: false },
|
|
26
|
+
{ value: 'de', label: 'Germany', disabled: false }
|
|
27
|
+
],
|
|
28
|
+
required: false,
|
|
29
|
+
disabled: false,
|
|
30
|
+
placeholder: 'Select a country'
|
|
31
|
+
},
|
|
32
|
+
'color-select': {
|
|
33
|
+
label: 'Favorite Color',
|
|
34
|
+
value: 'blue',
|
|
35
|
+
options: [
|
|
36
|
+
{ value: 'red', label: 'Red', disabled: false },
|
|
37
|
+
{ value: 'blue', label: 'Blue', disabled: false },
|
|
38
|
+
{ value: 'green', label: 'Green', disabled: false },
|
|
39
|
+
{ value: 'yellow', label: 'Yellow', disabled: true }
|
|
40
|
+
],
|
|
41
|
+
required: true,
|
|
42
|
+
disabled: false,
|
|
43
|
+
helperText: 'Choose your favorite color'
|
|
44
|
+
},
|
|
45
|
+
'disabled-select': {
|
|
46
|
+
label: 'Disabled Field',
|
|
47
|
+
value: 'option1',
|
|
48
|
+
options: [
|
|
49
|
+
{ value: 'option1', label: 'Option 1', disabled: false },
|
|
50
|
+
{ value: 'option2', label: 'Option 2', disabled: false }
|
|
51
|
+
],
|
|
52
|
+
required: false,
|
|
53
|
+
disabled: true
|
|
54
|
+
},
|
|
55
|
+
'error-select': {
|
|
56
|
+
label: 'Field with Error',
|
|
57
|
+
value: '',
|
|
58
|
+
options: [
|
|
59
|
+
{ value: 'valid', label: 'Valid Option', disabled: false },
|
|
60
|
+
{ value: 'invalid', label: 'Invalid Option', disabled: false }
|
|
61
|
+
],
|
|
62
|
+
required: true,
|
|
63
|
+
disabled: false,
|
|
64
|
+
error: 'This field is required'
|
|
65
|
+
},
|
|
66
|
+
'numeric-select': {
|
|
67
|
+
label: 'Priority Level',
|
|
68
|
+
value: 2,
|
|
69
|
+
options: [
|
|
70
|
+
{ value: 1, label: 'Low Priority', disabled: false },
|
|
71
|
+
{ value: 2, label: 'Medium Priority', disabled: false },
|
|
72
|
+
{ value: 3, label: 'High Priority', disabled: false }
|
|
73
|
+
],
|
|
74
|
+
required: false,
|
|
75
|
+
disabled: false
|
|
76
|
+
},
|
|
77
|
+
'empty-options': {
|
|
78
|
+
label: 'No Options Field',
|
|
79
|
+
value: '',
|
|
80
|
+
options: [],
|
|
81
|
+
required: false,
|
|
82
|
+
disabled: false
|
|
83
|
+
},
|
|
84
|
+
'single-option': {
|
|
85
|
+
label: 'Single Option',
|
|
86
|
+
value: '',
|
|
87
|
+
options: [
|
|
88
|
+
{ value: 'only', label: 'Only Option', disabled: false }
|
|
89
|
+
],
|
|
90
|
+
required: false,
|
|
91
|
+
disabled: false
|
|
92
|
+
},
|
|
93
|
+
'empty': {
|
|
94
|
+
label: '',
|
|
95
|
+
options: []
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Wrapper component for tests that need providers
|
|
101
|
+
const TestWrapper: React.FC<{ children: React.ReactNode; dataProvider?: any }> = ({
|
|
102
|
+
children,
|
|
103
|
+
dataProvider
|
|
104
|
+
}) => (
|
|
105
|
+
<ThemeProvider>
|
|
106
|
+
<PaletteProvider>
|
|
107
|
+
{dataProvider ? (
|
|
108
|
+
<DataProvider dataSource={{ dataProvider }}>
|
|
109
|
+
{children}
|
|
110
|
+
</DataProvider>
|
|
111
|
+
) : (
|
|
112
|
+
children
|
|
113
|
+
)}
|
|
114
|
+
</PaletteProvider>
|
|
115
|
+
</ThemeProvider>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
describe.skip('SelectInputField', () => {
|
|
119
|
+
describe('Traditional Props Usage', () => {
|
|
120
|
+
const mockOptions = [
|
|
121
|
+
{ value: 'option1', label: 'Option 1' },
|
|
122
|
+
{ value: 'option2', label: 'Option 2' },
|
|
123
|
+
{ value: 'option3', label: 'Option 3', disabled: true }
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
it('renders basic select field', () => {
|
|
127
|
+
render(
|
|
128
|
+
<TestWrapper>
|
|
129
|
+
<SelectInputField label="Test Select" options={mockOptions} />
|
|
130
|
+
</TestWrapper>
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(screen.getByLabelText('Test Select')).toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('displays options when clicked', async () => {
|
|
137
|
+
render(
|
|
138
|
+
<TestWrapper>
|
|
139
|
+
<SelectInputField label="Test Select" options={mockOptions} />
|
|
140
|
+
</TestWrapper>
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const selectButton = screen.getByRole('combobox');
|
|
144
|
+
fireEvent.mouseDown(selectButton);
|
|
145
|
+
|
|
146
|
+
await waitFor(() => {
|
|
147
|
+
expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument();
|
|
148
|
+
expect(screen.getByRole('option', { name: 'Option 2' })).toBeInTheDocument();
|
|
149
|
+
expect(screen.getByRole('option', { name: 'Option 3' })).toBeInTheDocument();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('handles selection changes', async () => {
|
|
154
|
+
const handleChange = jest.fn();
|
|
155
|
+
|
|
156
|
+
render(
|
|
157
|
+
<TestWrapper>
|
|
158
|
+
<SelectInputField label="Test Select" options={mockOptions} onChange={handleChange} />
|
|
159
|
+
</TestWrapper>
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const selectButton = screen.getByRole('combobox');
|
|
163
|
+
fireEvent.mouseDown(selectButton);
|
|
164
|
+
|
|
165
|
+
await waitFor(() => {
|
|
166
|
+
expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
fireEvent.click(screen.getByRole('option', { name: 'Option 1' }));
|
|
170
|
+
|
|
171
|
+
expect(handleChange).toHaveBeenCalledWith('option1');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('displays selected value', () => {
|
|
175
|
+
render(
|
|
176
|
+
<TestWrapper>
|
|
177
|
+
<SelectInputField label="Test Select" options={mockOptions} value="option2" />
|
|
178
|
+
</TestWrapper>
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
expect(screen.getByDisplayValue('Option 2')).toBeInTheDocument();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('handles numeric values', () => {
|
|
185
|
+
const numericOptions = [
|
|
186
|
+
{ value: 1, label: 'One' },
|
|
187
|
+
{ value: 2, label: 'Two' },
|
|
188
|
+
{ value: 3, label: 'Three' }
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
render(
|
|
192
|
+
<TestWrapper>
|
|
193
|
+
<SelectInputField label="Numeric Select" options={numericOptions} value={2} />
|
|
194
|
+
</TestWrapper>
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(screen.getByDisplayValue('Two')).toBeInTheDocument();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('handles disabled options', async () => {
|
|
201
|
+
render(
|
|
202
|
+
<TestWrapper>
|
|
203
|
+
<SelectInputField label="Test Select" options={mockOptions} />
|
|
204
|
+
</TestWrapper>
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const selectButton = screen.getByRole('combobox');
|
|
208
|
+
fireEvent.mouseDown(selectButton);
|
|
209
|
+
|
|
210
|
+
await waitFor(() => {
|
|
211
|
+
const disabledOption = screen.getByRole('option', { name: 'Option 3' });
|
|
212
|
+
expect(disabledOption).toHaveAttribute('aria-disabled', 'true');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('shows placeholder when no value selected', () => {
|
|
217
|
+
render(
|
|
218
|
+
<TestWrapper>
|
|
219
|
+
<SelectInputField
|
|
220
|
+
label="Test Select"
|
|
221
|
+
options={mockOptions}
|
|
222
|
+
placeholder="Select an option"
|
|
223
|
+
/>
|
|
224
|
+
</TestWrapper>
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
expect(screen.getByText('Select an option')).toBeInTheDocument();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('shows placeholder option in dropdown', async () => {
|
|
231
|
+
render(
|
|
232
|
+
<TestWrapper>
|
|
233
|
+
<SelectInputField
|
|
234
|
+
label="Test Select"
|
|
235
|
+
options={mockOptions}
|
|
236
|
+
placeholder="Select an option"
|
|
237
|
+
/>
|
|
238
|
+
</TestWrapper>
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const selectButton = screen.getByRole('combobox');
|
|
242
|
+
fireEvent.mouseDown(selectButton);
|
|
243
|
+
|
|
244
|
+
await waitFor(() => {
|
|
245
|
+
expect(screen.getByRole('option', { name: 'Select an option' })).toBeInTheDocument();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('handles focus events', () => {
|
|
250
|
+
const handleFocus = jest.fn();
|
|
251
|
+
|
|
252
|
+
render(
|
|
253
|
+
<TestWrapper>
|
|
254
|
+
<SelectInputField label="Test Select" options={mockOptions} onFocus={handleFocus} />
|
|
255
|
+
</TestWrapper>
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const selectButton = screen.getByRole('combobox');
|
|
259
|
+
fireEvent.focus(selectButton);
|
|
260
|
+
|
|
261
|
+
expect(handleFocus).toHaveBeenCalled();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('shows required indicator', () => {
|
|
265
|
+
render(
|
|
266
|
+
<TestWrapper>
|
|
267
|
+
<SelectInputField label="Test Select" options={mockOptions} required />
|
|
268
|
+
</TestWrapper>
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const selectElement = screen.getByRole('combobox');
|
|
272
|
+
expect(selectElement).toHaveAttribute('required');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('shows disabled state', () => {
|
|
276
|
+
render(
|
|
277
|
+
<TestWrapper>
|
|
278
|
+
<SelectInputField label="Test Select" options={mockOptions} disabled />
|
|
279
|
+
</TestWrapper>
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const selectElement = screen.getByRole('combobox');
|
|
283
|
+
expect(selectElement).toHaveAttribute('aria-disabled', 'true');
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('displays error message', () => {
|
|
287
|
+
render(
|
|
288
|
+
<TestWrapper>
|
|
289
|
+
<SelectInputField
|
|
290
|
+
label="Test Select"
|
|
291
|
+
options={mockOptions}
|
|
292
|
+
error="This field is required"
|
|
293
|
+
/>
|
|
294
|
+
</TestWrapper>
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
expect(screen.getByText('This field is required')).toBeInTheDocument();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('displays helper text', () => {
|
|
301
|
+
render(
|
|
302
|
+
<TestWrapper>
|
|
303
|
+
<SelectInputField
|
|
304
|
+
label="Test Select"
|
|
305
|
+
options={mockOptions}
|
|
306
|
+
helperText="Choose the best option"
|
|
307
|
+
/>
|
|
308
|
+
</TestWrapper>
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
expect(screen.getByText('Choose the best option')).toBeInTheDocument();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('prioritizes error over helper text', () => {
|
|
315
|
+
render(
|
|
316
|
+
<TestWrapper>
|
|
317
|
+
<SelectInputField
|
|
318
|
+
label="Test Select"
|
|
319
|
+
options={mockOptions}
|
|
320
|
+
error="This field is required"
|
|
321
|
+
helperText="Choose the best option"
|
|
322
|
+
/>
|
|
323
|
+
</TestWrapper>
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
expect(screen.getByText('This field is required')).toBeInTheDocument();
|
|
327
|
+
expect(screen.queryByText('Choose the best option')).not.toBeInTheDocument();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('handles empty options array', () => {
|
|
331
|
+
render(
|
|
332
|
+
<TestWrapper>
|
|
333
|
+
<SelectInputField label="Test Select" options={[]} />
|
|
334
|
+
</TestWrapper>
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
expect(screen.getByText('No options provided for select field')).toBeInTheDocument();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('handles controlled selection', () => {
|
|
341
|
+
const TestControlledSelect = () => {
|
|
342
|
+
const [value, setValue] = React.useState('');
|
|
343
|
+
return (
|
|
344
|
+
<TestWrapper>
|
|
345
|
+
<SelectInputField
|
|
346
|
+
label="Controlled Select"
|
|
347
|
+
options={mockOptions}
|
|
348
|
+
value={value}
|
|
349
|
+
onChange={setValue}
|
|
350
|
+
/>
|
|
351
|
+
</TestWrapper>
|
|
352
|
+
);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
render(<TestControlledSelect />);
|
|
356
|
+
|
|
357
|
+
const selectButton = screen.getByRole('combobox');
|
|
358
|
+
fireEvent.mouseDown(selectButton);
|
|
359
|
+
|
|
360
|
+
const option1 = screen.getByRole('option', { name: 'Option 1' });
|
|
361
|
+
fireEvent.click(option1);
|
|
362
|
+
|
|
363
|
+
expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('handles empty label gracefully', () => {
|
|
367
|
+
render(
|
|
368
|
+
<TestWrapper>
|
|
369
|
+
<SelectInputField label="" options={mockOptions} />
|
|
370
|
+
</TestWrapper>
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('Data Binding Usage', () => {
|
|
378
|
+
let dataProvider: JsonDataProvider;
|
|
379
|
+
|
|
380
|
+
beforeEach(() => {
|
|
381
|
+
dataProvider = new JsonDataProvider({ data: sampleCmsData });
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('renders with dataSource prop (country select)', async () => {
|
|
385
|
+
render(
|
|
386
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
387
|
+
<SelectInputField dataSource="selectFields.country-select" />
|
|
388
|
+
</TestWrapper>
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
await screen.findByLabelText('Country');
|
|
392
|
+
expect(screen.getByText('Select a country')).toBeInTheDocument();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('renders with pre-selected value from data source', async () => {
|
|
396
|
+
render(
|
|
397
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
398
|
+
<SelectInputField dataSource="selectFields.color-select" />
|
|
399
|
+
</TestWrapper>
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
await screen.findByDisplayValue('Blue');
|
|
403
|
+
expect(screen.getByText('Choose your favorite color')).toBeInTheDocument();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('handles selection from data binding', async () => {
|
|
407
|
+
const handleChange = jest.fn();
|
|
408
|
+
|
|
409
|
+
render(
|
|
410
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
411
|
+
<SelectInputField dataSource="selectFields.country-select" onChange={handleChange} />
|
|
412
|
+
</TestWrapper>
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
await screen.findByLabelText('Country');
|
|
416
|
+
|
|
417
|
+
const selectButton = screen.getByRole('combobox');
|
|
418
|
+
fireEvent.mouseDown(selectButton);
|
|
419
|
+
|
|
420
|
+
await waitFor(() => {
|
|
421
|
+
expect(screen.getByRole('option', { name: 'United States' })).toBeInTheDocument();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
fireEvent.click(screen.getByRole('option', { name: 'United States' }));
|
|
425
|
+
|
|
426
|
+
expect(handleChange).toHaveBeenCalledWith('us');
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('shows options from data source', async () => {
|
|
430
|
+
render(
|
|
431
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
432
|
+
<SelectInputField dataSource="selectFields.country-select" />
|
|
433
|
+
</TestWrapper>
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
await screen.findByLabelText('Country');
|
|
437
|
+
|
|
438
|
+
const selectButton = screen.getByRole('combobox');
|
|
439
|
+
fireEvent.mouseDown(selectButton);
|
|
440
|
+
|
|
441
|
+
await waitFor(() => {
|
|
442
|
+
expect(screen.getByRole('option', { name: 'United States' })).toBeInTheDocument();
|
|
443
|
+
expect(screen.getByRole('option', { name: 'Canada' })).toBeInTheDocument();
|
|
444
|
+
expect(screen.getByRole('option', { name: 'United Kingdom' })).toBeInTheDocument();
|
|
445
|
+
expect(screen.getByRole('option', { name: 'Germany' })).toBeInTheDocument();
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('handles disabled options from data source', async () => {
|
|
450
|
+
render(
|
|
451
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
452
|
+
<SelectInputField dataSource="selectFields.color-select" />
|
|
453
|
+
</TestWrapper>
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
await screen.findByLabelText('Favorite Color');
|
|
457
|
+
|
|
458
|
+
const selectButton = screen.getByRole('combobox');
|
|
459
|
+
fireEvent.mouseDown(selectButton);
|
|
460
|
+
|
|
461
|
+
await waitFor(() => {
|
|
462
|
+
const yellowOption = screen.getByRole('option', { name: 'Yellow' });
|
|
463
|
+
expect(yellowOption).toHaveAttribute('aria-disabled', 'true');
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('renders disabled field from data source', async () => {
|
|
468
|
+
render(
|
|
469
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
470
|
+
<SelectInputField dataSource="selectFields.disabled-select" />
|
|
471
|
+
</TestWrapper>
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
await screen.findByDisplayValue('Option 1');
|
|
475
|
+
const selectElement = screen.getByRole('combobox');
|
|
476
|
+
expect(selectElement).toHaveAttribute('aria-disabled', 'true');
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('renders field with error from data source', async () => {
|
|
480
|
+
render(
|
|
481
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
482
|
+
<SelectInputField dataSource="selectFields.error-select" />
|
|
483
|
+
</TestWrapper>
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
await screen.findByLabelText('Field with Error');
|
|
487
|
+
expect(screen.getByText('This field is required')).toBeInTheDocument();
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('handles numeric values from data source', async () => {
|
|
491
|
+
render(
|
|
492
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
493
|
+
<SelectInputField dataSource="selectFields.numeric-select" />
|
|
494
|
+
</TestWrapper>
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
await screen.findByDisplayValue('Medium Priority');
|
|
498
|
+
|
|
499
|
+
const selectButton = screen.getByRole('combobox');
|
|
500
|
+
fireEvent.mouseDown(selectButton);
|
|
501
|
+
|
|
502
|
+
await waitFor(() => {
|
|
503
|
+
expect(screen.getByRole('option', { name: 'Low Priority' })).toBeInTheDocument();
|
|
504
|
+
expect(screen.getByRole('option', { name: 'Medium Priority' })).toBeInTheDocument();
|
|
505
|
+
expect(screen.getByRole('option', { name: 'High Priority' })).toBeInTheDocument();
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('shows loading state while data is loading', () => {
|
|
510
|
+
render(
|
|
511
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
512
|
+
<SelectInputField dataSource="selectFields.nonexistent" />
|
|
513
|
+
</TestWrapper>
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
expect(screen.getByText('Loading SelectInputField...')).toBeInTheDocument();
|
|
517
|
+
expect(screen.getByText(/Loading select field configuration from data source/)).toBeInTheDocument();
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('handles empty options from data source', async () => {
|
|
521
|
+
render(
|
|
522
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
523
|
+
<SelectInputField dataSource="selectFields.empty-options" />
|
|
524
|
+
</TestWrapper>
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
await waitFor(() => {
|
|
528
|
+
expect(screen.getByText('No options provided for select field')).toBeInTheDocument();
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('handles single option from data source', async () => {
|
|
533
|
+
render(
|
|
534
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
535
|
+
<SelectInputField dataSource="selectFields.single-option" />
|
|
536
|
+
</TestWrapper>
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
await screen.findByLabelText('Single Option');
|
|
540
|
+
|
|
541
|
+
const selectButton = screen.getByRole('combobox');
|
|
542
|
+
fireEvent.mouseDown(selectButton);
|
|
543
|
+
|
|
544
|
+
await waitFor(() => {
|
|
545
|
+
expect(screen.getByRole('option', { name: 'Only Option' })).toBeInTheDocument();
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('works with custom binding options', async () => {
|
|
550
|
+
render(
|
|
551
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
552
|
+
<SelectInputField
|
|
553
|
+
dataSource="selectFields.country-select"
|
|
554
|
+
bindingOptions={{ cache: false, strict: true }}
|
|
555
|
+
/>
|
|
556
|
+
</TestWrapper>
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
await screen.findByLabelText('Country');
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('uses fallback props when dataSource has no content', async () => {
|
|
563
|
+
render(
|
|
564
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
565
|
+
<SelectInputField
|
|
566
|
+
dataSource="selectFields.nonexistent"
|
|
567
|
+
label="Fallback Select"
|
|
568
|
+
options={[{ value: 'fallback', label: 'Fallback Option' }]}
|
|
569
|
+
/>
|
|
570
|
+
</TestWrapper>
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
// Should stay in loading state for nonexistent data source
|
|
574
|
+
expect(screen.getByText('Loading SelectInputField...')).toBeInTheDocument();
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('handles empty data from CMS', async () => {
|
|
578
|
+
render(
|
|
579
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
580
|
+
<SelectInputField dataSource="selectFields.empty" />
|
|
581
|
+
</TestWrapper>
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
await waitFor(() => {
|
|
585
|
+
expect(screen.getByText('No options provided for select field')).toBeInTheDocument();
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('shows error state in development mode', async () => {
|
|
590
|
+
// Temporarily set NODE_ENV to development for this test
|
|
591
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
592
|
+
process.env.NODE_ENV = 'development';
|
|
593
|
+
|
|
594
|
+
// Mock console.error to avoid noise in test output
|
|
595
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
596
|
+
|
|
597
|
+
// Create a data provider that will throw an error
|
|
598
|
+
const errorDataProvider = new JsonDataProvider({
|
|
599
|
+
data: {} // Empty data will cause a binding error
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
render(
|
|
603
|
+
<TestWrapper dataProvider={errorDataProvider}>
|
|
604
|
+
<SelectInputField dataSource="selectFields.nonexistent-key" />
|
|
605
|
+
</TestWrapper>
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
await waitFor(() => {
|
|
609
|
+
const errorElement = screen.queryByText(/Error loading select field:/);
|
|
610
|
+
if (errorElement) {
|
|
611
|
+
expect(errorElement).toBeInTheDocument();
|
|
612
|
+
} else {
|
|
613
|
+
// If no error is displayed, that's also acceptable behavior
|
|
614
|
+
// depending on the exact error handling implementation
|
|
615
|
+
expect(screen.getByText('Loading SelectInputField...')).toBeInTheDocument();
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Restore NODE_ENV
|
|
620
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
621
|
+
consoleSpy.mockRestore();
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('returns null on error in production mode', async () => {
|
|
625
|
+
// Temporarily set NODE_ENV to production for this test
|
|
626
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
627
|
+
process.env.NODE_ENV = 'production';
|
|
628
|
+
|
|
629
|
+
// Mock console.error to avoid noise in test output
|
|
630
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
631
|
+
|
|
632
|
+
// Create a data provider that will throw an error
|
|
633
|
+
const errorDataProvider = new JsonDataProvider({
|
|
634
|
+
data: {} // Empty data will cause a binding error
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
const { container } = render(
|
|
638
|
+
<TestWrapper dataProvider={errorDataProvider}>
|
|
639
|
+
<SelectInputField dataSource="selectFields.nonexistent-key" />
|
|
640
|
+
</TestWrapper>
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
await waitFor(() => {
|
|
644
|
+
// In production, error should either return null (empty container)
|
|
645
|
+
// or show loading state - both are acceptable
|
|
646
|
+
const hasContent = container.firstChild;
|
|
647
|
+
// The component should handle the error gracefully
|
|
648
|
+
expect(hasContent).toBeDefined(); // Component should render something or nothing
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// Restore NODE_ENV
|
|
652
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
653
|
+
consoleSpy.mockRestore();
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('supports mixed data sources in same component tree', async () => {
|
|
657
|
+
render(
|
|
658
|
+
<TestWrapper dataProvider={dataProvider}>
|
|
659
|
+
<div>
|
|
660
|
+
<SelectInputField dataSource="selectFields.country-select" />
|
|
661
|
+
<SelectInputField dataSource="selectFields.color-select" />
|
|
662
|
+
<SelectInputField dataSource="selectFields.numeric-select" />
|
|
663
|
+
</div>
|
|
664
|
+
</TestWrapper>
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
// All three selects should render with their respective content
|
|
668
|
+
await screen.findByLabelText('Country');
|
|
669
|
+
await screen.findByLabelText('Favorite Color');
|
|
670
|
+
await screen.findByLabelText('Priority Level');
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it.skip('preserves component marking for QwickApp framework', () => {
|
|
674
|
+
// The component should be marked as a QwickApp component
|
|
675
|
+
// This is important for framework identification - test skipped due to test environment limitations
|
|
676
|
+
const selectInputFieldComponent = SelectInputField as any;
|
|
677
|
+
expect(selectInputFieldComponent.QWICKAPP_COMPONENT).toBeTruthy();
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
describe('Edge Cases', () => {
|
|
682
|
+
const mockOptions = [
|
|
683
|
+
{ value: 'option1', label: 'Option 1' },
|
|
684
|
+
{ value: 'option2', label: 'Option 2' }
|
|
685
|
+
];
|
|
686
|
+
|
|
687
|
+
it('handles very long option labels', async () => {
|
|
688
|
+
const longOptions = [
|
|
689
|
+
{ value: 'long1', label: 'This is a very long option label that might cause layout issues in some scenarios but should be handled gracefully' },
|
|
690
|
+
{ value: 'long2', label: 'Another extremely long option label with lots of text that tests how the component handles overflow' }
|
|
691
|
+
];
|
|
692
|
+
|
|
693
|
+
render(
|
|
694
|
+
<TestWrapper>
|
|
695
|
+
<SelectInputField label="Long Options" options={longOptions} />
|
|
696
|
+
</TestWrapper>
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
const selectButton = screen.getByRole('combobox');
|
|
700
|
+
fireEvent.mouseDown(selectButton);
|
|
701
|
+
|
|
702
|
+
await waitFor(() => {
|
|
703
|
+
expect(screen.getByRole('option', { name: /This is a very long option label/ })).toBeInTheDocument();
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
it('handles special characters in options', async () => {
|
|
708
|
+
const specialOptions = [
|
|
709
|
+
{ value: 'special1', label: 'Option with émojis 🎉 & spëcial chars!' },
|
|
710
|
+
{ value: 'special2', label: 'HTML <tags> & "quotes" & ampersands' }
|
|
711
|
+
];
|
|
712
|
+
|
|
713
|
+
render(
|
|
714
|
+
<TestWrapper>
|
|
715
|
+
<SelectInputField label="Special Options" options={specialOptions} />
|
|
716
|
+
</TestWrapper>
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
const selectButton = screen.getByRole('combobox');
|
|
720
|
+
fireEvent.mouseDown(selectButton);
|
|
721
|
+
|
|
722
|
+
await waitFor(() => {
|
|
723
|
+
expect(screen.getByRole('option', { name: /Option with émojis 🎉/ })).toBeInTheDocument();
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('handles duplicate option values gracefully', async () => {
|
|
728
|
+
const duplicateOptions = [
|
|
729
|
+
{ value: 'duplicate', label: 'First Option' },
|
|
730
|
+
{ value: 'duplicate', label: 'Second Option' },
|
|
731
|
+
{ value: 'unique', label: 'Unique Option' }
|
|
732
|
+
];
|
|
733
|
+
|
|
734
|
+
render(
|
|
735
|
+
<TestWrapper>
|
|
736
|
+
<SelectInputField label="Duplicate Options" options={duplicateOptions} />
|
|
737
|
+
</TestWrapper>
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
const selectButton = screen.getByRole('combobox');
|
|
741
|
+
fireEvent.mouseDown(selectButton);
|
|
742
|
+
|
|
743
|
+
await waitFor(() => {
|
|
744
|
+
// Both options should be present, though React may warn about duplicate keys
|
|
745
|
+
expect(screen.getByRole('option', { name: 'First Option' })).toBeInTheDocument();
|
|
746
|
+
expect(screen.getByRole('option', { name: 'Second Option' })).toBeInTheDocument();
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('handles rapid selection changes', async () => {
|
|
751
|
+
const handleChange = jest.fn();
|
|
752
|
+
|
|
753
|
+
render(
|
|
754
|
+
<TestWrapper>
|
|
755
|
+
<SelectInputField label="Rapid Changes" options={mockOptions} onChange={handleChange} />
|
|
756
|
+
</TestWrapper>
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
const selectButton = screen.getByRole('combobox');
|
|
760
|
+
|
|
761
|
+
// First selection
|
|
762
|
+
fireEvent.mouseDown(selectButton);
|
|
763
|
+
await waitFor(() => expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument());
|
|
764
|
+
fireEvent.click(screen.getByRole('option', { name: 'Option 1' }));
|
|
765
|
+
|
|
766
|
+
// Second selection
|
|
767
|
+
fireEvent.mouseDown(selectButton);
|
|
768
|
+
await waitFor(() => expect(screen.getByRole('option', { name: 'Option 2' })).toBeInTheDocument());
|
|
769
|
+
fireEvent.click(screen.getByRole('option', { name: 'Option 2' }));
|
|
770
|
+
|
|
771
|
+
expect(handleChange).toHaveBeenCalledTimes(2);
|
|
772
|
+
expect(handleChange).toHaveBeenCalledWith('option1');
|
|
773
|
+
expect(handleChange).toHaveBeenCalledWith('option2');
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it('handles keyboard navigation', async () => {
|
|
777
|
+
render(
|
|
778
|
+
<TestWrapper>
|
|
779
|
+
<SelectInputField label="Keyboard Navigation" options={mockOptions} />
|
|
780
|
+
</TestWrapper>
|
|
781
|
+
);
|
|
782
|
+
|
|
783
|
+
const selectButton = screen.getByRole('combobox');
|
|
784
|
+
selectButton.focus();
|
|
785
|
+
|
|
786
|
+
// Space or Enter should open the dropdown
|
|
787
|
+
fireEvent.keyDown(selectButton, { key: 'ArrowDown', code: 'ArrowDown' });
|
|
788
|
+
|
|
789
|
+
await waitFor(() => {
|
|
790
|
+
expect(screen.getByRole('listbox')).toBeInTheDocument();
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
it('handles mixed data types in options', () => {
|
|
795
|
+
const mixedOptions = [
|
|
796
|
+
{ value: 'string', label: 'String Option' },
|
|
797
|
+
{ value: 123, label: 'Number Option' },
|
|
798
|
+
{ value: '', label: 'Empty String Option' }
|
|
799
|
+
];
|
|
800
|
+
|
|
801
|
+
render(
|
|
802
|
+
<TestWrapper>
|
|
803
|
+
<SelectInputField label="Mixed Types" options={mixedOptions} />
|
|
804
|
+
</TestWrapper>
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
it('handles concurrent value updates', () => {
|
|
811
|
+
const TestConcurrentUpdates = () => {
|
|
812
|
+
const [value, setValue] = React.useState('');
|
|
813
|
+
|
|
814
|
+
const handleUpdate = () => {
|
|
815
|
+
setValue('option1');
|
|
816
|
+
setTimeout(() => setValue('option2'), 0);
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
return (
|
|
820
|
+
<TestWrapper>
|
|
821
|
+
<SelectInputField
|
|
822
|
+
label="Concurrent Updates"
|
|
823
|
+
options={mockOptions}
|
|
824
|
+
value={value}
|
|
825
|
+
onChange={setValue}
|
|
826
|
+
/>
|
|
827
|
+
<button onClick={handleUpdate}>Update</button>
|
|
828
|
+
</TestWrapper>
|
|
829
|
+
);
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
render(<TestConcurrentUpdates />);
|
|
833
|
+
|
|
834
|
+
fireEvent.click(screen.getByText('Update'));
|
|
835
|
+
|
|
836
|
+
expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it('handles empty string values correctly', async () => {
|
|
840
|
+
const optionsWithEmpty = [
|
|
841
|
+
{ value: '', label: 'Empty Option' },
|
|
842
|
+
{ value: 'filled', label: 'Filled Option' }
|
|
843
|
+
];
|
|
844
|
+
|
|
845
|
+
render(
|
|
846
|
+
<TestWrapper>
|
|
847
|
+
<SelectInputField
|
|
848
|
+
label="Empty Values"
|
|
849
|
+
options={optionsWithEmpty}
|
|
850
|
+
placeholder="Choose option"
|
|
851
|
+
/>
|
|
852
|
+
</TestWrapper>
|
|
853
|
+
);
|
|
854
|
+
|
|
855
|
+
const selectButton = screen.getByRole('combobox');
|
|
856
|
+
fireEvent.mouseDown(selectButton);
|
|
857
|
+
|
|
858
|
+
await waitFor(() => {
|
|
859
|
+
expect(screen.getByRole('option', { name: 'Empty Option' })).toBeInTheDocument();
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('handles large option lists efficiently', async () => {
|
|
864
|
+
const largeOptionsList = Array.from({ length: 1000 }, (_, i) => ({
|
|
865
|
+
value: `option${i}`,
|
|
866
|
+
label: `Option ${i + 1}`
|
|
867
|
+
}));
|
|
868
|
+
|
|
869
|
+
render(
|
|
870
|
+
<TestWrapper>
|
|
871
|
+
<SelectInputField label="Large List" options={largeOptionsList} />
|
|
872
|
+
</TestWrapper>
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
const selectButton = screen.getByRole('combobox');
|
|
876
|
+
fireEvent.mouseDown(selectButton);
|
|
877
|
+
|
|
878
|
+
await waitFor(() => {
|
|
879
|
+
expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument();
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
// Should handle large lists without performance issues
|
|
883
|
+
expect(screen.getAllByRole('option')).toHaveLength(1000);
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
});
|