@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,612 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Content, Section } from '../components/blocks';
|
|
4
|
+
import { GridCell, GridLayout } from '../components/layout';
|
|
5
|
+
import SafeSpan from '../components/SafeSpan';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof SafeSpan> = {
|
|
8
|
+
title: 'Components/SafeSpan',
|
|
9
|
+
component: SafeSpan,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: `SafeSpan safely renders HTML content with automatic sanitization, protecting against XSS attacks while preserving formatting.
|
|
14
|
+
|
|
15
|
+
**Key Features:**
|
|
16
|
+
- **XSS Protection**: Automatically sanitizes dangerous HTML elements and attributes
|
|
17
|
+
- **Configurable Sanitization**: Customizable allow/disallow lists for fine-tuned security
|
|
18
|
+
- **Rich Text Support**: Preserves safe formatting like bold, italic, links, and lists
|
|
19
|
+
- **Performance Optimized**: Efficient sanitization without unnecessary re-processing
|
|
20
|
+
- **TypeScript Support**: Full type safety for sanitization options and HTML content
|
|
21
|
+
- **Framework Agnostic**: Uses industry-standard sanitize-html library
|
|
22
|
+
- **Accessibility Maintained**: Preserves ARIA attributes and semantic HTML structure
|
|
23
|
+
|
|
24
|
+
**Perfect for:**
|
|
25
|
+
- User-generated content display (comments, posts, reviews)
|
|
26
|
+
- Rich text editor output rendering
|
|
27
|
+
- Email content display in web applications
|
|
28
|
+
- Markdown-to-HTML conversion output
|
|
29
|
+
- Third-party content integration
|
|
30
|
+
- CMS and blog content display
|
|
31
|
+
- Any scenario requiring safe HTML rendering`,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|
|
38
|
+
type Story = StoryObj<typeof meta>;
|
|
39
|
+
|
|
40
|
+
// Sample HTML content for demonstrations
|
|
41
|
+
const safeHtml = `
|
|
42
|
+
<p>This is <strong>safe HTML</strong> content with <em>emphasis</em> and a <a href="#safe-link">safe link</a>.</p>
|
|
43
|
+
<ul>
|
|
44
|
+
<li>List item with <code>code formatting</code></li>
|
|
45
|
+
<li>Another item with <mark>highlighted text</mark></li>
|
|
46
|
+
</ul>
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const unsafeHtml = `
|
|
50
|
+
<p>This content contains <strong>potentially dangerous</strong> elements:</p>
|
|
51
|
+
<script>alert('XSS Attack!');</script>
|
|
52
|
+
<img src="x" onerror="alert('Image XSS')">
|
|
53
|
+
<p onclick="alert('Click XSS')">This paragraph has an onclick handler</p>
|
|
54
|
+
<iframe src="javascript:alert('Iframe XSS')"></iframe>
|
|
55
|
+
<style>body { display: none; }</style>
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const richContentHtml = `
|
|
59
|
+
<div>
|
|
60
|
+
<h3>Rich Content Example</h3>
|
|
61
|
+
<p>This is a paragraph with <strong>bold text</strong>, <em>italic text</em>, and <u>underlined text</u>.</p>
|
|
62
|
+
<blockquote>This is a blockquote with important information.</blockquote>
|
|
63
|
+
<ul>
|
|
64
|
+
<li>First item</li>
|
|
65
|
+
<li>Second item with <code>inline code</code></li>
|
|
66
|
+
<li>Third item</li>
|
|
67
|
+
</ul>
|
|
68
|
+
<p>Links: <a href="https://example.com" target="_blank">External Link</a> | <a href="#internal">Internal Link</a></p>
|
|
69
|
+
</div>
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
export const BasicUsage: Story = {
|
|
73
|
+
render: () => (
|
|
74
|
+
<Section padding="large">
|
|
75
|
+
<Content title="Basic SafeSpan Usage" centered maxWidth="large">
|
|
76
|
+
<GridLayout columns={2} spacing="large">
|
|
77
|
+
<GridCell>
|
|
78
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
79
|
+
<h3>Safe HTML Content</h3>
|
|
80
|
+
<div style={{
|
|
81
|
+
background: 'var(--theme-surface-variant)',
|
|
82
|
+
padding: '1rem',
|
|
83
|
+
borderRadius: '8px',
|
|
84
|
+
marginBottom: '1rem'
|
|
85
|
+
}}>
|
|
86
|
+
<SafeSpan html={safeHtml} />
|
|
87
|
+
</div>
|
|
88
|
+
<p><strong>Note:</strong> All HTML is safely rendered with proper sanitization.</p>
|
|
89
|
+
</Content>
|
|
90
|
+
</GridCell>
|
|
91
|
+
|
|
92
|
+
<GridCell>
|
|
93
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
94
|
+
<h3>With Placeholder</h3>
|
|
95
|
+
<div style={{
|
|
96
|
+
background: 'var(--theme-surface-variant)',
|
|
97
|
+
padding: '1rem',
|
|
98
|
+
borderRadius: '8px',
|
|
99
|
+
marginBottom: '1rem'
|
|
100
|
+
}}>
|
|
101
|
+
<SafeSpan html="" placeholder="No content available" />
|
|
102
|
+
</div>
|
|
103
|
+
<p><strong>Note:</strong> When no HTML is provided, the placeholder text is shown.</p>
|
|
104
|
+
</Content>
|
|
105
|
+
</GridCell>
|
|
106
|
+
</GridLayout>
|
|
107
|
+
</Content>
|
|
108
|
+
</Section>
|
|
109
|
+
),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const SecurityDemonstration: Story = {
|
|
113
|
+
render: () => (
|
|
114
|
+
<Section padding="large">
|
|
115
|
+
<Content title="Security Features" centered maxWidth="large">
|
|
116
|
+
<div style={{
|
|
117
|
+
background: 'var(--theme-error, #dc2626)',
|
|
118
|
+
color: 'white',
|
|
119
|
+
padding: '1rem',
|
|
120
|
+
borderRadius: '8px',
|
|
121
|
+
marginBottom: '2rem',
|
|
122
|
+
textAlign: 'center'
|
|
123
|
+
}}>
|
|
124
|
+
<strong>⚠️ Security Demo:</strong> The content below contains malicious scripts that are automatically sanitized.
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<GridLayout columns={1} spacing="large">
|
|
128
|
+
<GridCell>
|
|
129
|
+
<Content variant="elevated" spacing='comfortable'>
|
|
130
|
+
<h3>Dangerous HTML Input (Sanitized Output)</h3>
|
|
131
|
+
<div style={{
|
|
132
|
+
background: 'var(--theme-surface-variant)',
|
|
133
|
+
padding: '1rem',
|
|
134
|
+
borderRadius: '8px',
|
|
135
|
+
marginBottom: '1rem',
|
|
136
|
+
border: '2px solid var(--theme-primary)'
|
|
137
|
+
}}>
|
|
138
|
+
<SafeSpan html={unsafeHtml} />
|
|
139
|
+
</div>
|
|
140
|
+
<p><strong>✅ SafeSpan automatically removed:</strong></p>
|
|
141
|
+
<ul>
|
|
142
|
+
<li><code><script></code> tags</li>
|
|
143
|
+
<li><code>onerror</code> and <code>onclick</code> event handlers</li>
|
|
144
|
+
<li><code><iframe></code> with javascript: protocol</li>
|
|
145
|
+
<li><code><style></code> tags that could hide content</li>
|
|
146
|
+
</ul>
|
|
147
|
+
<p>Only safe HTML elements and attributes are preserved.</p>
|
|
148
|
+
</Content>
|
|
149
|
+
</GridCell>
|
|
150
|
+
</GridLayout>
|
|
151
|
+
</Content>
|
|
152
|
+
</Section>
|
|
153
|
+
),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const RichContentExample: Story = {
|
|
157
|
+
render: () => (
|
|
158
|
+
<Section padding="large">
|
|
159
|
+
<Content title="Rich Content Support" centered maxWidth="large">
|
|
160
|
+
<GridLayout columns={1} spacing="large">
|
|
161
|
+
<GridCell>
|
|
162
|
+
<Content variant="filled" spacing='comfortable'>
|
|
163
|
+
<div style={{
|
|
164
|
+
background: 'var(--theme-surface)',
|
|
165
|
+
padding: '2rem',
|
|
166
|
+
borderRadius: '12px',
|
|
167
|
+
boxShadow: 'var(--theme-elevation-1, 0 2px 4px rgba(0, 0, 0, 0.1))'
|
|
168
|
+
}}>
|
|
169
|
+
<SafeSpan html={richContentHtml} />
|
|
170
|
+
</div>
|
|
171
|
+
</Content>
|
|
172
|
+
</GridCell>
|
|
173
|
+
</GridLayout>
|
|
174
|
+
|
|
175
|
+
<Content spacing='comfortable' centered>
|
|
176
|
+
<p><strong>Supported HTML Elements:</strong></p>
|
|
177
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem', marginTop: '1rem' }}>
|
|
178
|
+
<div>
|
|
179
|
+
<strong>Text Formatting:</strong>
|
|
180
|
+
<ul>
|
|
181
|
+
<li>p, div, span</li>
|
|
182
|
+
<li>strong, b, em, i</li>
|
|
183
|
+
<li>u, mark, code</li>
|
|
184
|
+
</ul>
|
|
185
|
+
</div>
|
|
186
|
+
<div>
|
|
187
|
+
<strong>Structure:</strong>
|
|
188
|
+
<ul>
|
|
189
|
+
<li>h1, h2, h3, h4, h5, h6</li>
|
|
190
|
+
<li>ul, ol, li</li>
|
|
191
|
+
<li>blockquote</li>
|
|
192
|
+
</ul>
|
|
193
|
+
</div>
|
|
194
|
+
<div>
|
|
195
|
+
<strong>Links & Media:</strong>
|
|
196
|
+
<ul>
|
|
197
|
+
<li>a (with safe href)</li>
|
|
198
|
+
<li>img (with safe src)</li>
|
|
199
|
+
<li>br, hr</li>
|
|
200
|
+
</ul>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</Content>
|
|
204
|
+
</Content>
|
|
205
|
+
</Section>
|
|
206
|
+
),
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export const CustomStyling: Story = {
|
|
210
|
+
render: () => (
|
|
211
|
+
<Section padding="large">
|
|
212
|
+
<Content title="Custom Styling" centered maxWidth="large">
|
|
213
|
+
<GridLayout columns={2} spacing="large">
|
|
214
|
+
<GridCell>
|
|
215
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
216
|
+
<h3>Custom CSS Class</h3>
|
|
217
|
+
<div style={{ marginBottom: '1rem' }}>
|
|
218
|
+
<SafeSpan
|
|
219
|
+
html="<p>This content has <strong>custom styling</strong> applied.</p>"
|
|
220
|
+
className="custom-safe-span"
|
|
221
|
+
style={{
|
|
222
|
+
background: 'linear-gradient(45deg, #667eea, #764ba2)',
|
|
223
|
+
padding: '1rem',
|
|
224
|
+
borderRadius: '8px',
|
|
225
|
+
color: 'white'
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
229
|
+
<p>You can apply custom CSS classes and inline styles to SafeSpan.</p>
|
|
230
|
+
</Content>
|
|
231
|
+
</GridCell>
|
|
232
|
+
|
|
233
|
+
<GridCell>
|
|
234
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
235
|
+
<h3>Themed Content</h3>
|
|
236
|
+
<div style={{
|
|
237
|
+
background: 'var(--theme-primary)',
|
|
238
|
+
color: 'var(--theme-on-primary)',
|
|
239
|
+
padding: '1rem',
|
|
240
|
+
borderRadius: '8px',
|
|
241
|
+
marginBottom: '1rem'
|
|
242
|
+
}}>
|
|
243
|
+
<SafeSpan
|
|
244
|
+
html="<p>This content uses <em>theme variables</em> for consistent styling.</p>"
|
|
245
|
+
style={{ color: 'inherit' }}
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
248
|
+
<p>SafeSpan works seamlessly with the QwickApps theme system.</p>
|
|
249
|
+
</Content>
|
|
250
|
+
</GridCell>
|
|
251
|
+
</GridLayout>
|
|
252
|
+
</Content>
|
|
253
|
+
</Section>
|
|
254
|
+
),
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const EmptyStates: Story = {
|
|
258
|
+
render: () => (
|
|
259
|
+
<Section padding="large">
|
|
260
|
+
<Content title="Empty States and Fallbacks" centered maxWidth="large">
|
|
261
|
+
<GridLayout columns={3} spacing="large">
|
|
262
|
+
<GridCell>
|
|
263
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
264
|
+
<h4>No Content, No Placeholder</h4>
|
|
265
|
+
<div style={{
|
|
266
|
+
minHeight: '60px',
|
|
267
|
+
background: 'var(--theme-surface-variant)',
|
|
268
|
+
padding: '1rem',
|
|
269
|
+
borderRadius: '8px',
|
|
270
|
+
marginBottom: '1rem',
|
|
271
|
+
border: '1px dashed var(--theme-outline)'
|
|
272
|
+
}}>
|
|
273
|
+
<SafeSpan html="" />
|
|
274
|
+
<em style={{ opacity: 0.6 }}>(Component returns null)</em>
|
|
275
|
+
</div>
|
|
276
|
+
</Content>
|
|
277
|
+
</GridCell>
|
|
278
|
+
|
|
279
|
+
<GridCell>
|
|
280
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
281
|
+
<h4>Empty with Placeholder</h4>
|
|
282
|
+
<div style={{
|
|
283
|
+
background: 'var(--theme-surface-variant)',
|
|
284
|
+
padding: '1rem',
|
|
285
|
+
borderRadius: '8px',
|
|
286
|
+
marginBottom: '1rem'
|
|
287
|
+
}}>
|
|
288
|
+
<SafeSpan html="" placeholder="No content to display" />
|
|
289
|
+
</div>
|
|
290
|
+
</Content>
|
|
291
|
+
</GridCell>
|
|
292
|
+
|
|
293
|
+
<GridCell>
|
|
294
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
295
|
+
<h4>Undefined with Placeholder</h4>
|
|
296
|
+
<div style={{
|
|
297
|
+
background: 'var(--theme-surface-variant)',
|
|
298
|
+
padding: '1rem',
|
|
299
|
+
borderRadius: '8px',
|
|
300
|
+
marginBottom: '1rem'
|
|
301
|
+
}}>
|
|
302
|
+
<SafeSpan placeholder="Loading content..." />
|
|
303
|
+
</div>
|
|
304
|
+
</Content>
|
|
305
|
+
</GridCell>
|
|
306
|
+
</GridLayout>
|
|
307
|
+
</Content>
|
|
308
|
+
</Section>
|
|
309
|
+
),
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
export const DataBindingExample: Story = {
|
|
313
|
+
render: () => (
|
|
314
|
+
<Section padding="large">
|
|
315
|
+
<Content title="Data Binding with SafeSpan" centered maxWidth="large">
|
|
316
|
+
<GridLayout columns={2} spacing="large">
|
|
317
|
+
<GridCell>
|
|
318
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
319
|
+
<h3>Traditional Props Usage</h3>
|
|
320
|
+
<div style={{
|
|
321
|
+
background: 'var(--theme-surface-variant)',
|
|
322
|
+
padding: '1rem',
|
|
323
|
+
borderRadius: '8px',
|
|
324
|
+
marginBottom: '1rem'
|
|
325
|
+
}}>
|
|
326
|
+
<SafeSpan
|
|
327
|
+
html="<p>This content is passed as <strong>traditional props</strong>.</p>"
|
|
328
|
+
placeholder="Loading traditional content..."
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
<p><strong>Usage:</strong> Direct props without dataSource</p>
|
|
332
|
+
</Content>
|
|
333
|
+
</GridCell>
|
|
334
|
+
|
|
335
|
+
<GridCell>
|
|
336
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
337
|
+
<h3>Data Binding Usage</h3>
|
|
338
|
+
<div style={{
|
|
339
|
+
background: 'var(--theme-surface-variant)',
|
|
340
|
+
padding: '1rem',
|
|
341
|
+
borderRadius: '8px',
|
|
342
|
+
marginBottom: '1rem'
|
|
343
|
+
}}>
|
|
344
|
+
<SafeSpan
|
|
345
|
+
dataSource="company.description"
|
|
346
|
+
bindingOptions={{
|
|
347
|
+
fallback: {
|
|
348
|
+
html: "<p>Fallback content when data binding fails</p>",
|
|
349
|
+
placeholder: "Loading from data source..."
|
|
350
|
+
}
|
|
351
|
+
}}
|
|
352
|
+
/>
|
|
353
|
+
</div>
|
|
354
|
+
<p><strong>Usage:</strong> Data-driven with dataSource prop</p>
|
|
355
|
+
</Content>
|
|
356
|
+
</GridCell>
|
|
357
|
+
</GridLayout>
|
|
358
|
+
|
|
359
|
+
<Content spacing='comfortable'>
|
|
360
|
+
<div style={{
|
|
361
|
+
background: 'var(--theme-info, #0ea5e9)',
|
|
362
|
+
color: 'white',
|
|
363
|
+
padding: '1rem',
|
|
364
|
+
borderRadius: '8px',
|
|
365
|
+
textAlign: 'center'
|
|
366
|
+
}}>
|
|
367
|
+
<strong>💡 Data Structure Expected:</strong>
|
|
368
|
+
<pre style={{
|
|
369
|
+
background: 'rgba(0,0,0,0.2)',
|
|
370
|
+
padding: '1rem',
|
|
371
|
+
borderRadius: '6px',
|
|
372
|
+
marginTop: '1rem',
|
|
373
|
+
textAlign: 'left',
|
|
374
|
+
overflow: 'auto'
|
|
375
|
+
}}>
|
|
376
|
+
{`{
|
|
377
|
+
"safeSpans": {
|
|
378
|
+
"richText": {
|
|
379
|
+
"html": "<p>Rich HTML <strong>content</strong> here</p>",
|
|
380
|
+
"placeholder": "Loading rich content..."
|
|
381
|
+
},
|
|
382
|
+
"basicText": {
|
|
383
|
+
"html": "<span>Basic HTML content</span>",
|
|
384
|
+
"placeholder": "Loading..."
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}`}
|
|
388
|
+
</pre>
|
|
389
|
+
</div>
|
|
390
|
+
</Content>
|
|
391
|
+
</Content>
|
|
392
|
+
</Section>
|
|
393
|
+
),
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export const SecurityTestingExample: Story = {
|
|
397
|
+
render: () => (
|
|
398
|
+
<Section padding="large">
|
|
399
|
+
<Content title="Advanced Security Testing" centered maxWidth="large">
|
|
400
|
+
<GridLayout columns={1} spacing="large">
|
|
401
|
+
<GridCell>
|
|
402
|
+
<Content variant="elevated" spacing='comfortable'>
|
|
403
|
+
<h3>XSS Attack Vector Testing</h3>
|
|
404
|
+
|
|
405
|
+
{/* Test various XSS attack patterns */}
|
|
406
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: '1rem' }}>
|
|
407
|
+
|
|
408
|
+
<div style={{
|
|
409
|
+
background: 'var(--theme-surface-variant)',
|
|
410
|
+
padding: '1rem',
|
|
411
|
+
borderRadius: '8px',
|
|
412
|
+
border: '2px solid var(--theme-warning, #f59e0b)'
|
|
413
|
+
}}>
|
|
414
|
+
<h4>Script Injection Test</h4>
|
|
415
|
+
<SafeSpan
|
|
416
|
+
html={`<p>Normal content followed by: <script>alert('XSS')</script></p>`}
|
|
417
|
+
placeholder="Testing script injection..."
|
|
418
|
+
/>
|
|
419
|
+
<p><small>✅ Script tags should be completely removed</small></p>
|
|
420
|
+
</div>
|
|
421
|
+
|
|
422
|
+
<div style={{
|
|
423
|
+
background: 'var(--theme-surface-variant)',
|
|
424
|
+
padding: '1rem',
|
|
425
|
+
borderRadius: '8px',
|
|
426
|
+
border: '2px solid var(--theme-warning, #f59e0b)'
|
|
427
|
+
}}>
|
|
428
|
+
<h4>Event Handler Test</h4>
|
|
429
|
+
<SafeSpan
|
|
430
|
+
html={`<p onclick="alert('Click XSS')" onmouseover="alert('Hover XSS')">Hover or click this text</p>`}
|
|
431
|
+
placeholder="Testing event handlers..."
|
|
432
|
+
/>
|
|
433
|
+
<p><small>✅ Event handlers should be stripped</small></p>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
<div style={{
|
|
437
|
+
background: 'var(--theme-surface-variant)',
|
|
438
|
+
padding: '1rem',
|
|
439
|
+
borderRadius: '8px',
|
|
440
|
+
border: '2px solid var(--theme-warning, #f59e0b)'
|
|
441
|
+
}}>
|
|
442
|
+
<h4>JavaScript URL Test</h4>
|
|
443
|
+
<SafeSpan
|
|
444
|
+
html={`<a href="javascript:alert('Link XSS')">Malicious Link</a>`}
|
|
445
|
+
placeholder="Testing javascript URLs..."
|
|
446
|
+
/>
|
|
447
|
+
<p><small>✅ javascript: URLs should be blocked</small></p>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
<div style={{
|
|
451
|
+
background: 'var(--theme-surface-variant)',
|
|
452
|
+
padding: '1rem',
|
|
453
|
+
borderRadius: '8px',
|
|
454
|
+
border: '2px solid var(--theme-warning, #f59e0b)'
|
|
455
|
+
}}>
|
|
456
|
+
<h4>Data URL Test</h4>
|
|
457
|
+
<SafeSpan
|
|
458
|
+
html={`<img src="data:text/html,<script>alert('Data URL XSS')</script>" alt="test">`}
|
|
459
|
+
placeholder="Testing data URLs..."
|
|
460
|
+
/>
|
|
461
|
+
<p><small>✅ Malicious data URLs should be blocked</small></p>
|
|
462
|
+
</div>
|
|
463
|
+
|
|
464
|
+
<div style={{
|
|
465
|
+
background: 'var(--theme-success, #10b981)',
|
|
466
|
+
color: 'white',
|
|
467
|
+
padding: '1rem',
|
|
468
|
+
borderRadius: '8px',
|
|
469
|
+
}}>
|
|
470
|
+
<h4>✅ Safe Content (Should Render)</h4>
|
|
471
|
+
<SafeSpan
|
|
472
|
+
html={`<p>This is <strong>safe content</strong> with <a href="https://example.com" target="_blank" rel="noopener noreferrer">external link</a></p>`}
|
|
473
|
+
placeholder="Loading safe content..."
|
|
474
|
+
/>
|
|
475
|
+
<p><small>✅ Safe HTML should render with security attributes added</small></p>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
</div>
|
|
479
|
+
</Content>
|
|
480
|
+
</GridCell>
|
|
481
|
+
</GridLayout>
|
|
482
|
+
</Content>
|
|
483
|
+
</Section>
|
|
484
|
+
),
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
export const InteractiveExample: Story = {
|
|
488
|
+
render: () => {
|
|
489
|
+
const [htmlContent, setHtmlContent] = useState(safeHtml);
|
|
490
|
+
const [placeholder, setPlaceholder] = useState('Enter some HTML content...');
|
|
491
|
+
|
|
492
|
+
return (
|
|
493
|
+
<Section padding="large">
|
|
494
|
+
<Content title="Interactive SafeSpan Demo" centered maxWidth="large">
|
|
495
|
+
<GridLayout columns={2} spacing="large">
|
|
496
|
+
<GridCell>
|
|
497
|
+
<Content variant="outlined" spacing='comfortable'>
|
|
498
|
+
<h3>HTML Input</h3>
|
|
499
|
+
<textarea
|
|
500
|
+
value={htmlContent}
|
|
501
|
+
onChange={(e) => setHtmlContent(e.target.value)}
|
|
502
|
+
placeholder="Enter HTML content to test..."
|
|
503
|
+
style={{
|
|
504
|
+
width: '100%',
|
|
505
|
+
minHeight: '150px',
|
|
506
|
+
padding: '1rem',
|
|
507
|
+
border: '1px solid var(--theme-outline)',
|
|
508
|
+
borderRadius: '8px',
|
|
509
|
+
background: 'var(--theme-surface)',
|
|
510
|
+
color: 'var(--theme-on-surface)',
|
|
511
|
+
fontFamily: 'monospace',
|
|
512
|
+
fontSize: '0.9rem',
|
|
513
|
+
marginBottom: '1rem'
|
|
514
|
+
}}
|
|
515
|
+
/>
|
|
516
|
+
<input
|
|
517
|
+
type="text"
|
|
518
|
+
value={placeholder}
|
|
519
|
+
onChange={(e) => setPlaceholder(e.target.value)}
|
|
520
|
+
placeholder="Placeholder text..."
|
|
521
|
+
style={{
|
|
522
|
+
width: '100%',
|
|
523
|
+
padding: '0.75rem',
|
|
524
|
+
border: '1px solid var(--theme-outline)',
|
|
525
|
+
borderRadius: '8px',
|
|
526
|
+
background: 'var(--theme-surface)',
|
|
527
|
+
color: 'var(--theme-on-surface)'
|
|
528
|
+
}}
|
|
529
|
+
/>
|
|
530
|
+
</Content>
|
|
531
|
+
</GridCell>
|
|
532
|
+
|
|
533
|
+
<GridCell>
|
|
534
|
+
<Content variant="elevated" spacing='comfortable'>
|
|
535
|
+
<h3>SafeSpan Output</h3>
|
|
536
|
+
<div style={{
|
|
537
|
+
background: 'var(--theme-surface-variant)',
|
|
538
|
+
padding: '1rem',
|
|
539
|
+
borderRadius: '8px',
|
|
540
|
+
minHeight: '200px',
|
|
541
|
+
border: '1px solid var(--theme-outline)'
|
|
542
|
+
}}>
|
|
543
|
+
<SafeSpan html={htmlContent} placeholder={placeholder} />
|
|
544
|
+
</div>
|
|
545
|
+
<p style={{ marginTop: '1rem', fontSize: '0.9rem', opacity: 0.8 }}>
|
|
546
|
+
<strong>Try entering:</strong> HTML with scripts, event handlers, or other potentially dangerous content to see how it's automatically sanitized.
|
|
547
|
+
</p>
|
|
548
|
+
</Content>
|
|
549
|
+
</GridCell>
|
|
550
|
+
</GridLayout>
|
|
551
|
+
|
|
552
|
+
<Content spacing='comfortable'>
|
|
553
|
+
<div style={{ display: 'flex', gap: '1rem', justifyContent: 'center', flexWrap: 'wrap' }}>
|
|
554
|
+
<button
|
|
555
|
+
onClick={() => setHtmlContent(safeHtml)}
|
|
556
|
+
style={{
|
|
557
|
+
padding: '0.75rem 1.5rem',
|
|
558
|
+
background: 'var(--theme-primary)',
|
|
559
|
+
color: 'var(--theme-on-primary)',
|
|
560
|
+
border: 'none',
|
|
561
|
+
borderRadius: '6px',
|
|
562
|
+
cursor: 'pointer'
|
|
563
|
+
}}
|
|
564
|
+
>
|
|
565
|
+
Load Safe HTML
|
|
566
|
+
</button>
|
|
567
|
+
<button
|
|
568
|
+
onClick={() => setHtmlContent(unsafeHtml)}
|
|
569
|
+
style={{
|
|
570
|
+
padding: '0.75rem 1.5rem',
|
|
571
|
+
background: 'var(--theme-error, #dc2626)',
|
|
572
|
+
color: 'white',
|
|
573
|
+
border: 'none',
|
|
574
|
+
borderRadius: '6px',
|
|
575
|
+
cursor: 'pointer'
|
|
576
|
+
}}
|
|
577
|
+
>
|
|
578
|
+
Load Unsafe HTML
|
|
579
|
+
</button>
|
|
580
|
+
<button
|
|
581
|
+
onClick={() => setHtmlContent(richContentHtml)}
|
|
582
|
+
style={{
|
|
583
|
+
padding: '0.75rem 1.5rem',
|
|
584
|
+
background: 'transparent',
|
|
585
|
+
color: 'var(--theme-primary)',
|
|
586
|
+
border: '1px solid var(--theme-primary)',
|
|
587
|
+
borderRadius: '6px',
|
|
588
|
+
cursor: 'pointer'
|
|
589
|
+
}}
|
|
590
|
+
>
|
|
591
|
+
Load Rich Content
|
|
592
|
+
</button>
|
|
593
|
+
<button
|
|
594
|
+
onClick={() => setHtmlContent('')}
|
|
595
|
+
style={{
|
|
596
|
+
padding: '0.75rem 1.5rem',
|
|
597
|
+
background: 'transparent',
|
|
598
|
+
color: 'var(--theme-on-surface)',
|
|
599
|
+
border: '1px solid var(--theme-outline)',
|
|
600
|
+
borderRadius: '6px',
|
|
601
|
+
cursor: 'pointer'
|
|
602
|
+
}}
|
|
603
|
+
>
|
|
604
|
+
Clear Content
|
|
605
|
+
</button>
|
|
606
|
+
</div>
|
|
607
|
+
</Content>
|
|
608
|
+
</Content>
|
|
609
|
+
</Section>
|
|
610
|
+
);
|
|
611
|
+
},
|
|
612
|
+
};
|