@qwickapps/react-framework 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/README.md +1681 -2
  2. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
  3. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
  4. package/dist/components/ErrorBoundary.d.ts +7 -0
  5. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  6. package/dist/components/Html.d.ts +28 -18
  7. package/dist/components/Html.d.ts.map +1 -1
  8. package/dist/components/Logo.d.ts +12 -35
  9. package/dist/components/Logo.d.ts.map +1 -1
  10. package/dist/components/Markdown.d.ts +18 -13
  11. package/dist/components/Markdown.d.ts.map +1 -1
  12. package/dist/components/QwickApp.d.ts +16 -3
  13. package/dist/components/QwickApp.d.ts.map +1 -1
  14. package/dist/components/QwickIcon.d.ts +23 -0
  15. package/dist/components/QwickIcon.d.ts.map +1 -0
  16. package/dist/components/SafeSpan.d.ts +12 -5
  17. package/dist/components/SafeSpan.d.ts.map +1 -1
  18. package/dist/components/Scaffold.d.ts.map +1 -1
  19. package/dist/components/base/ModelView.d.ts +101 -0
  20. package/dist/components/base/ModelView.d.ts.map +1 -0
  21. package/dist/components/base/index.d.ts +11 -0
  22. package/dist/components/base/index.d.ts.map +1 -0
  23. package/dist/components/blocks/Article.d.ts +12 -2
  24. package/dist/components/blocks/Article.d.ts.map +1 -1
  25. package/dist/components/blocks/Code.d.ts +13 -2
  26. package/dist/components/blocks/Code.d.ts.map +1 -1
  27. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
  28. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  30. package/dist/components/blocks/Footer.d.ts.map +1 -1
  31. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  32. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  33. package/dist/components/blocks/Image.d.ts +41 -0
  34. package/dist/components/blocks/Image.d.ts.map +1 -0
  35. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  36. package/dist/components/blocks/Section.d.ts +16 -2
  37. package/dist/components/blocks/Section.d.ts.map +1 -1
  38. package/dist/components/blocks/Text.d.ts +41 -0
  39. package/dist/components/blocks/Text.d.ts.map +1 -0
  40. package/dist/components/blocks/index.d.ts +4 -0
  41. package/dist/components/blocks/index.d.ts.map +1 -1
  42. package/dist/components/buttons/Button.d.ts +23 -7
  43. package/dist/components/buttons/Button.d.ts.map +1 -1
  44. package/dist/components/forms/FormBlock.d.ts +19 -13
  45. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  46. package/dist/components/index.d.ts +4 -0
  47. package/dist/components/index.d.ts.map +1 -1
  48. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  49. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  50. package/dist/components/input/HtmlInputField.d.ts +17 -11
  51. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  52. package/dist/components/input/SelectInputField.d.ts +16 -10
  53. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  54. package/dist/components/input/SwitchInputField.d.ts +16 -10
  55. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  56. package/dist/components/input/TextField.d.ts.map +1 -1
  57. package/dist/components/input/TextInputField.d.ts +16 -11
  58. package/dist/components/input/TextInputField.d.ts.map +1 -1
  59. package/dist/components/layout/GridCell.d.ts +23 -6
  60. package/dist/components/layout/GridCell.d.ts.map +1 -1
  61. package/dist/components/layout/GridLayout.d.ts +24 -23
  62. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  63. package/dist/components/pages/FormPage.d.ts.map +1 -1
  64. package/dist/components/pages/Page.d.ts +49 -87
  65. package/dist/components/pages/Page.d.ts.map +1 -1
  66. package/dist/components/pages/index.d.ts +2 -2
  67. package/dist/components/pages/index.d.ts.map +1 -1
  68. package/dist/config/AppConfig.d.ts +49 -0
  69. package/dist/config/AppConfig.d.ts.map +1 -0
  70. package/dist/config/AppConfigBuilder.d.ts +75 -0
  71. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  72. package/dist/config/index.d.ts +13 -0
  73. package/dist/config/index.d.ts.map +1 -0
  74. package/dist/config/types.d.ts +130 -0
  75. package/dist/config/types.d.ts.map +1 -0
  76. package/dist/config.d.ts +15 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.esm.js +451 -0
  79. package/dist/config.js +455 -0
  80. package/dist/contexts/PrintModeContext.d.ts +27 -0
  81. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  82. package/dist/contexts/QwickAppContext.d.ts +2 -2
  83. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  84. package/dist/contexts/index.d.ts +2 -0
  85. package/dist/contexts/index.d.ts.map +1 -1
  86. package/dist/hooks/index.d.ts +2 -0
  87. package/dist/hooks/index.d.ts.map +1 -1
  88. package/dist/hooks/usePrintMode.d.ts +39 -0
  89. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  90. package/dist/index.css +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.esm.css +1 -1
  94. package/dist/index.esm.js +10951 -6238
  95. package/dist/index.js +11014 -6287
  96. package/dist/schemas/CodeSchema.d.ts +2 -1
  97. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  98. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  99. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  100. package/dist/schemas/ContentSchema.d.ts +2 -1
  101. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  102. package/dist/schemas/GridCellSchema.d.ts +25 -0
  103. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  104. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  105. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  106. package/dist/schemas/HtmlSchema.d.ts +14 -0
  107. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  108. package/dist/schemas/ImageSchema.d.ts +32 -0
  109. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  110. package/dist/schemas/LogoSchema.d.ts +35 -0
  111. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  112. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  113. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  114. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  115. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  116. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  117. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  118. package/dist/schemas/SectionSchema.d.ts +2 -1
  119. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  120. package/dist/schemas/TextSchema.d.ts +37 -0
  121. package/dist/schemas/TextSchema.d.ts.map +1 -0
  122. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  123. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  124. package/dist/schemas/index.d.ts +15 -1
  125. package/dist/schemas/index.d.ts.map +1 -1
  126. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  127. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  128. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  129. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  130. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  131. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  132. package/dist/schemas/transformers/registry.d.ts +15 -0
  133. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  134. package/dist/schemas/types/Serializable.d.ts +46 -0
  135. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  136. package/dist/utils/htmlTransform.d.ts.map +1 -1
  137. package/dist/utils/reactUtils.d.ts +12 -3
  138. package/dist/utils/reactUtils.d.ts.map +1 -1
  139. package/package.json +17 -3
  140. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  141. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  142. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  143. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  145. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  148. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  150. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  152. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  154. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  155. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  156. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  158. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  159. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  160. package/src/__tests__/components/Logo.test.js +3 -3
  161. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  162. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  163. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  165. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  166. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  167. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  168. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  169. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  172. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  173. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  174. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  175. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  176. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  177. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  178. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  179. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  180. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  181. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  182. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  183. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  184. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  185. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  186. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  187. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  188. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  189. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  190. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  191. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  192. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  193. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  194. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  195. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  196. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  197. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  198. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  199. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  200. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  201. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  202. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  203. package/src/components/ErrorBoundary.tsx +8 -8
  204. package/src/components/Html.tsx +147 -44
  205. package/src/components/Logo.tsx +198 -100
  206. package/src/components/Markdown.tsx +125 -16
  207. package/src/components/QwickApp.tsx +64 -31
  208. package/src/components/QwickIcon.tsx +59 -0
  209. package/src/components/SafeSpan.tsx +65 -10
  210. package/src/components/Scaffold.tsx +2 -8
  211. package/src/components/base/ModelView.tsx +199 -0
  212. package/src/components/base/index.ts +11 -0
  213. package/src/components/blocks/Article.tsx +57 -18
  214. package/src/components/blocks/Code.md +529 -0
  215. package/src/components/blocks/Code.tsx +102 -15
  216. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  217. package/src/components/blocks/FeatureCard.tsx +1 -2
  218. package/src/components/blocks/FeatureGrid.tsx +19 -1
  219. package/src/components/blocks/Footer.tsx +13 -1
  220. package/src/components/blocks/HeroBlock.tsx +87 -20
  221. package/src/components/blocks/Image.tsx +395 -0
  222. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  223. package/src/components/blocks/ProductCard.tsx +1 -1
  224. package/src/components/blocks/Section.tsx +113 -8
  225. package/src/components/blocks/Text.tsx +285 -0
  226. package/src/components/blocks/index.ts +4 -0
  227. package/src/components/buttons/Button.tsx +184 -15
  228. package/src/components/forms/FormBlock.tsx +70 -17
  229. package/src/components/index.ts +5 -0
  230. package/src/components/input/ChoiceInputField.tsx +48 -18
  231. package/src/components/input/HtmlInputField.tsx +48 -18
  232. package/src/components/input/SelectInputField.tsx +48 -16
  233. package/src/components/input/SwitchInputField.tsx +48 -17
  234. package/src/components/input/TextField.tsx +41 -1
  235. package/src/components/input/TextInputField.tsx +52 -18
  236. package/src/components/layout/GridCell.tsx +118 -9
  237. package/src/components/layout/GridLayout.tsx +125 -24
  238. package/src/components/pages/FormPage.tsx +0 -1
  239. package/src/components/pages/Page.css +304 -332
  240. package/src/components/pages/Page.tsx +307 -255
  241. package/src/components/pages/index.ts +2 -2
  242. package/src/config/AppConfig.ts +133 -0
  243. package/src/config/AppConfigBuilder.ts +421 -0
  244. package/src/config/__tests__/AppConfig.test.ts +385 -0
  245. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  246. package/src/config/index.ts +24 -0
  247. package/src/config/types.ts +170 -0
  248. package/src/config.ts +25 -0
  249. package/src/contexts/PrintModeContext.tsx +332 -0
  250. package/src/contexts/QwickAppContext.tsx +2 -2
  251. package/src/contexts/index.ts +2 -0
  252. package/src/hooks/index.ts +5 -1
  253. package/src/hooks/usePrintMode.ts +73 -0
  254. package/src/index.ts +3 -0
  255. package/src/schemas/CodeSchema.ts +3 -3
  256. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  257. package/src/schemas/ContentSchema.ts +2 -1
  258. package/src/schemas/GridCellSchema.ts +164 -0
  259. package/src/schemas/GridLayoutSchema.ts +133 -0
  260. package/src/schemas/HtmlSchema.ts +47 -0
  261. package/src/schemas/ImageSchema.ts +235 -0
  262. package/src/schemas/LogoSchema.ts +241 -0
  263. package/src/schemas/MarkdownSchema.ts +47 -0
  264. package/src/schemas/PageTemplateSchema.ts +186 -0
  265. package/src/schemas/PrintConfigSchema.ts +207 -0
  266. package/src/schemas/README.md +661 -0
  267. package/src/schemas/SectionSchema.ts +2 -1
  268. package/src/schemas/TextSchema.ts +329 -0
  269. package/src/schemas/ViewModelSchema.ts +115 -0
  270. package/src/schemas/index.ts +21 -2
  271. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  272. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  273. package/src/schemas/transformers/registry.ts +72 -0
  274. package/src/schemas/types/Serializable.ts +51 -0
  275. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  276. package/src/stories/Article.stories.tsx +433 -433
  277. package/src/stories/Button.stories.tsx +1 -1
  278. package/src/stories/CardListGrid.stories.tsx +451 -451
  279. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  280. package/src/stories/Code.stories.tsx +1 -1
  281. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  282. package/src/stories/Content.stories.tsx +393 -393
  283. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  284. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  285. package/src/stories/DataProvider.stories.tsx +1192 -1192
  286. package/src/stories/FeatureCard.stories.tsx +557 -557
  287. package/src/stories/FeatureGrid.stories.tsx +594 -594
  288. package/src/stories/Footer.stories.tsx +640 -640
  289. package/src/stories/FormBlock.stories.tsx +760 -760
  290. package/src/stories/FormComponents.stories.tsx +349 -541
  291. package/src/stories/GridCell.stories.tsx +417 -0
  292. package/src/stories/GridLayout.stories.tsx +353 -0
  293. package/src/stories/HeroBlock.stories.tsx +862 -373
  294. package/src/stories/HtmlInputField.stories.tsx +474 -474
  295. package/src/stories/Image.stories.tsx +819 -0
  296. package/src/stories/Introduction.stories.tsx +667 -667
  297. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  298. package/src/stories/Logo.stories.tsx +165 -6
  299. package/src/stories/Markdown.stories.tsx +137 -137
  300. package/src/stories/ModelView.stories.tsx +477 -0
  301. package/src/stories/Page.stories.tsx +688 -688
  302. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  303. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  304. package/src/stories/ProductCard.stories.tsx +424 -424
  305. package/src/stories/QwickApp.stories.tsx +368 -368
  306. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  307. package/src/stories/SafeSpan.stories.tsx +531 -531
  308. package/src/stories/Section.stories.tsx +90 -2
  309. package/src/stories/SelectInputField.stories.tsx +524 -524
  310. package/src/stories/Text.stories.tsx +560 -0
  311. package/src/stories/TextInputField.stories.tsx +443 -443
  312. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  313. package/src/utils/htmlTransform.tsx +74 -53
  314. package/src/utils/reactUtils.tsx +57 -6
  315. package/dist/index.bundled.css +0 -12
  316. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  317. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  318. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  319. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  320. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -5,12 +5,12 @@ import { GridCell, GridLayout } from '../components/layout';
5
5
  import SafeSpan from '../components/SafeSpan';
6
6
 
7
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.
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
14
 
15
15
  **Key Features:**
16
16
  - **XSS Protection**: Automatically sanitizes dangerous HTML elements and attributes
@@ -29,9 +29,9 @@ const meta: Meta<typeof SafeSpan> = {
29
29
  - Third-party content integration
30
30
  - CMS and blog content display
31
31
  - Any scenario requiring safe HTML rendering`,
32
- },
33
- },
34
- },
32
+ },
33
+ },
34
+ },
35
35
  };
36
36
 
37
37
  export default meta;
@@ -39,574 +39,574 @@ type Story = StoryObj<typeof meta>;
39
39
 
40
40
  // Sample HTML content for demonstrations
41
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>
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
47
  `;
48
48
 
49
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>
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
56
  `;
57
57
 
58
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>
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
70
  `;
71
71
 
72
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>
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
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
- ),
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
110
  };
111
111
 
112
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>
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
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>&lt;script&gt;</code> tags</li>
143
- <li><code>onerror</code> and <code>onclick</code> event handlers</li>
144
- <li><code>&lt;iframe&gt;</code> with javascript: protocol</li>
145
- <li><code>&lt;style&gt;</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
- ),
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>&lt;script&gt;</code> tags</li>
143
+ <li><code>onerror</code> and <code>onclick</code> event handlers</li>
144
+ <li><code>&lt;iframe&gt;</code> with javascript: protocol</li>
145
+ <li><code>&lt;style&gt;</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
154
  };
155
155
 
156
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>
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
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
- ),
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
207
  };
208
208
 
209
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>
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
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
- ),
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
255
  };
256
256
 
257
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>
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
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>
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
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
- ),
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
310
  };
311
311
 
312
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>
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
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>
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
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
- }}>
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
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
- }
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
387
  }`}
388
- </pre>
389
- </div>
390
- </Content>
391
- </Content>
392
- </Section>
393
- ),
388
+ </pre>
389
+ </div>
390
+ </Content>
391
+ </Content>
392
+ </Section>
393
+ ),
394
394
  };
395
395
 
396
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>
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
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>
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
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>
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
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>
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
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>
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
477
 
478
- </div>
479
- </Content>
480
- </GridCell>
481
- </GridLayout>
482
- </Content>
483
- </Section>
484
- ),
478
+ </div>
479
+ </Content>
480
+ </GridCell>
481
+ </GridLayout>
482
+ </Content>
483
+ </Section>
484
+ ),
485
485
  };
486
486
 
487
487
  export const InteractiveExample: Story = {
488
- render: () => {
489
- const [htmlContent, setHtmlContent] = useState(safeHtml);
490
- const [placeholder, setPlaceholder] = useState('Enter some HTML content...');
488
+ render: () => {
489
+ const [htmlContent, setHtmlContent] = useState(safeHtml);
490
+ const [placeholder, setPlaceholder] = useState('Enter some HTML content...');
491
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>
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
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>
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
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
- },
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
612
  };