@epa-wg/custom-element-dist 0.0.33 → 0.0.34

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 (175) hide show
  1. package/.claude/settings.local.json +18 -0
  2. package/.github/workflows/deploy.yml +59 -0
  3. package/.idea/copilot.data.migration.agent.xml +6 -0
  4. package/.idea/copilot.data.migration.ask.xml +6 -0
  5. package/.idea/copilot.data.migration.edit.xml +6 -0
  6. package/.idea/custom-element-dist.iml +2 -0
  7. package/.storybook/main.ts +20 -17
  8. package/.storybook/preview.ts +23 -23
  9. package/README.md +6 -4
  10. package/coverage/block-navigation.js +1 -1
  11. package/coverage/coverage-final.json +4 -3
  12. package/coverage/index.html +34 -19
  13. package/coverage/sorter.js +21 -7
  14. package/coverage/src/custom-element/coverage.svg +1 -1
  15. package/coverage/src/custom-element/custom-element.js/coverage.svg +1 -1
  16. package/coverage/src/custom-element/custom-element.js.html +435 -378
  17. package/coverage/src/custom-element/http-request.js/coverage.svg +1 -1
  18. package/coverage/src/custom-element/http-request.js.html +38 -17
  19. package/coverage/src/custom-element/index.html +26 -26
  20. package/coverage/src/custom-element/local-storage.js.html +1 -1
  21. package/coverage/src/custom-element/location-element.js.html +1 -1
  22. package/coverage/src/custom-element/module-url.js.html +1 -1
  23. package/coverage/src/index.html +1 -1
  24. package/coverage/src/material/theme/colors.js/coverage.svg +10 -0
  25. package/coverage/src/material/theme/colors.js.html +217 -0
  26. package/coverage/src/material/theme/coverage.svg +10 -0
  27. package/coverage/src/material/theme/index.html +116 -0
  28. package/coverage/src/mocks/handlers.ts.html +1 -1
  29. package/coverage/src/mocks/index.html +1 -1
  30. package/coverage/src/stories/frame.canvas.ts.html +1 -1
  31. package/coverage/src/stories/http-request.stories.ts.html +1 -1
  32. package/coverage/src/stories/index.html +1 -1
  33. package/coverage/src/stories/testStoryBook.ts.html +12 -12
  34. package/coverage/src/sum.ts.html +1 -1
  35. package/dist/{custom-element-WnOqmEOe.js → custom-element-BoYMoUtP.js} +193 -183
  36. package/dist/custom-element-BqtjrCRF.cjs +97 -0
  37. package/dist/custom-element-bundle.cjs +1 -1
  38. package/dist/custom-element-bundle.js +3 -3
  39. package/dist/demo/a.html +10 -3
  40. package/dist/demo/a.svg +26 -26
  41. package/dist/demo/html-template.html +4 -3
  42. package/dist/demo/s.xml +3856 -67
  43. package/dist/demo/s.xslt +13 -72
  44. package/dist/demo/s1.xml +3706 -0
  45. package/dist/http-request-DSaowcG1.cjs +1 -0
  46. package/dist/{http-request-BOvP4KTl.js → http-request-DTCzZ1gc.js} +15 -9
  47. package/package.json +2 -2
  48. package/public/demo/a.html +10 -3
  49. package/public/demo/a.svg +26 -26
  50. package/public/demo/html-template.html +4 -3
  51. package/public/demo/s.xml +3856 -67
  52. package/public/demo/s.xslt +13 -72
  53. package/public/demo/s1.xml +3706 -0
  54. package/src/custom-element/custom-element.js +28 -9
  55. package/src/custom-element/demo/a.html +10 -3
  56. package/src/custom-element/demo/a.svg +26 -26
  57. package/src/custom-element/demo/html-template.html +4 -3
  58. package/src/custom-element/demo/s.xml +3856 -67
  59. package/src/custom-element/demo/s.xslt +13 -72
  60. package/src/custom-element/demo/s1.xml +3706 -0
  61. package/src/custom-element/http-request.js +7 -0
  62. package/src/custom-element/ide/web-types-dce.json +1 -1
  63. package/src/custom-element/ide/web-types-xsl.json +1 -1
  64. package/src/material/angular.css +987 -987
  65. package/src/material/components/action.html +262 -0
  66. package/src/material/components/autocomplete.html +167 -239
  67. package/src/material/components/badge.html +238 -239
  68. package/src/material/components/dropdown.html +0 -1
  69. package/src/material/components/icon-link.html +160 -161
  70. package/src/material/components/icon.html +251 -252
  71. package/src/material/components/input.html +569 -570
  72. package/src/material/components/menu.html +235 -236
  73. package/src/material/components.html +157 -158
  74. package/src/material/demo.css +36 -36
  75. package/src/material/index.html +109 -110
  76. package/src/material/material.css +356 -356
  77. package/src/material/theme/Base-Principles.md +339 -0
  78. package/src/material/theme/README.md +298 -18
  79. package/src/material/theme/UI Domain Model in web applications.svg +1 -0
  80. package/src/material/theme/User Semantic Theme tokens.svg +1 -0
  81. package/src/material/theme/action-pending-poc.html +62 -0
  82. package/src/material/theme/actions-color.html +141 -0
  83. package/src/material/theme/colors-light.html +631 -0
  84. package/src/material/theme/colors-native.html +51 -0
  85. package/src/material/theme/colors-poc.html +66 -0
  86. package/src/material/theme/colors.html +297 -0
  87. package/src/material/theme/colors.js +44 -0
  88. package/src/material/theme/consumer-theme.css +745 -0
  89. package/src/material/theme/semantic.css +132 -132
  90. package/src/material/theme/style-bug.html +123 -0
  91. package/src/material/theme/theme-data.css +43 -0
  92. package/src/material/theme/theme-data.xhtml +2926 -0
  93. package/src/material/theme/todo.md +274 -0
  94. package/src/material/theme/tokens/action-colors.png +0 -0
  95. package/src/material/theme/tokens/cem-article-illustration-4x1-letterbox-2000x500.png +0 -0
  96. package/src/material/theme/tokens/cem-breakpoints.md +519 -0
  97. package/src/material/theme/tokens/cem-colors.md +715 -0
  98. package/src/material/theme/tokens/cem-consumerflow-typography-matrix.svg +198 -0
  99. package/src/material/theme/tokens/cem-coupling.md +372 -0
  100. package/src/material/theme/tokens/cem-data-vs-reading-numerals.svg +164 -0
  101. package/src/material/theme/tokens/cem-dimension.md +625 -0
  102. package/src/material/theme/tokens/cem-layering.md +562 -0
  103. package/src/material/theme/tokens/cem-m3-parity.md +343 -0
  104. package/src/material/theme/tokens/cem-responsive.md +238 -0
  105. package/src/material/theme/tokens/cem-shape.md +691 -0
  106. package/src/material/theme/tokens/cem-stroke-density-illustration-4to1-v3.svg +102 -0
  107. package/src/material/theme/tokens/cem-stroke.md +480 -0
  108. package/src/material/theme/tokens/cem-timing.md +198 -0
  109. package/src/material/theme/tokens/cem-typography-model-stack.svg +64 -0
  110. package/src/material/theme/tokens/cem-voice-fonts-typography.md +718 -0
  111. package/src/material/theme/tokens/cem-voice-ladder.svg +91 -0
  112. package/src/material/theme/tokens/chips.png +0 -0
  113. package/src/material/theme/tokens/columns-page.png +0 -0
  114. package/src/material/theme/tokens/initials.png +0 -0
  115. package/src/material/theme/tokens/nav-buttons.png +0 -0
  116. package/src/material/theme/tokens/script.png +0 -0
  117. package/src/material/theme/tokens/sufler.png +0 -0
  118. package/src/material/theme/tokens/typography-icons.png +0 -0
  119. package/src/mocks/versions.mock.ts +1 -1
  120. package/src/stories/__screenshots__/dom-merge.test.stories.ts/dom-merge-dom-merge-OrderPreservingOn2ndTransform-1.png +0 -0
  121. package/src/stories/__screenshots__/xslt-conditionals.test.stories.ts/xslt-conditionals-xslt-conditionals-MultipleIfOrderingWorkaround-1.png +0 -0
  122. package/src/stories/dom-merge.test.stories.ts +25 -1
  123. package/src/stories/xslt-conditionals.test.stories.ts +492 -0
  124. package/src/stories/xslt-if.test.stories.ts +89 -0
  125. package/storybook-static/assets/{Color-F6OSRLHC-Cbp293x2.js → Color-F6OSRLHC-CzTOSlqB.js} +1 -1
  126. package/storybook-static/assets/{Configure-BrFr4SLE.js → Configure-7GqRsAoJ.js} +1 -1
  127. package/storybook-static/assets/{DocsRenderer-CFRXHY34-DhHzJiIO.js → DocsRenderer-CFRXHY34-Duc5rSIm.js} +2 -2
  128. package/storybook-static/assets/{attributes.test.stories-Gg9LQTEK.js → attributes.test.stories-DYuxF8h1.js} +1 -1
  129. package/storybook-static/assets/{css.test.stories-B_3ltOrx.js → css.test.stories-LOmvINyb.js} +1 -1
  130. package/storybook-static/assets/{custom-element-CPnvJnn8.js → custom-element-Bwx7otrT.js} +6 -6
  131. package/storybook-static/assets/{dom-merge.test.stories-nQxcgLoM.js → dom-merge.test.stories-CEKhWjaS.js} +47 -6
  132. package/storybook-static/assets/entry-preview-BNCt9WBs.js +26 -0
  133. package/storybook-static/assets/{entry-preview-docs-Dwczwtsc.js → entry-preview-docs-CbF8-81D.js} +2 -2
  134. package/storybook-static/assets/{external-template.test.stories-DZ-rjnfd.js → external-template.test.stories-jHu0wsJ-.js} +1 -1
  135. package/storybook-static/assets/{form.test.stories-DQhPYtMj.js → form.test.stories-CUyUnmwP.js} +1 -1
  136. package/storybook-static/assets/{frame.canvas-ClTqYyMN.js → frame.canvas-E5n9h6j1.js} +1 -1
  137. package/storybook-static/assets/{handlers-CLkps6Nz.js → handlers-F7GUfMqr.js} +1 -1
  138. package/storybook-static/assets/http-request-BWeEEBkP.js +1 -0
  139. package/storybook-static/assets/{http-request.stories-jo0f73nw.js → http-request.stories-wyX5-QOv.js} +1 -1
  140. package/storybook-static/assets/{iframe-CZwRpnn9.js → iframe-BS_DPWl0.js} +11 -11
  141. package/storybook-static/assets/{index-Dr4PwNfd.js → index-CGuyH0k-.js} +87 -87
  142. package/storybook-static/assets/{index-CJQtnF9V.js → index-DB7LLObI.js} +1 -1
  143. package/storybook-static/assets/index-DO1nmyvI.js +11 -0
  144. package/storybook-static/assets/{index-B68YUdzy.js → index-V1EGs-wm.js} +3 -3
  145. package/storybook-static/assets/{local-storage.test.stories-uA5EKRPf.js → local-storage.test.stories-BxOhsf1k.js} +1 -1
  146. package/storybook-static/assets/{location-element.test.stories-Cu-6Elcg.js → location-element.test.stories-DqhvvUoa.js} +1 -1
  147. package/storybook-static/assets/{module-url.test.stories-CD_wusXQ.js → module-url.test.stories-C1gG9G7Y.js} +1 -1
  148. package/storybook-static/assets/{preview-BFlNN3Wj.js → preview-Bn8igYMp.js} +1 -1
  149. package/storybook-static/assets/{preview-CuCH40jj.js → preview-Cwy1XFu2.js} +2 -2
  150. package/storybook-static/assets/{preview-Cm4PPhHS.js → preview-D6sehqkw.js} +1 -1
  151. package/storybook-static/assets/{set-url.test.stories-CY7B9BVZ.js → set-url.test.stories-BKQNdknJ.js} +1 -1
  152. package/storybook-static/assets/{slice-events.test.stories-BVnPXm6e.js → slice-events.test.stories-ChqULCeA.js} +1 -1
  153. package/storybook-static/assets/{slots.test.stories-Dxsa9KdA.js → slots.test.stories-BlyLoCRe.js} +1 -1
  154. package/storybook-static/assets/{version-select.test.stories-Buga1PAa.js → version-select.test.stories-CPGSh1tR.js} +1 -1
  155. package/storybook-static/assets/xslt-conditionals.test.stories-YC6QPqWZ.js +633 -0
  156. package/storybook-static/assets/xslt-if.test.stories-BRSWy2-x.js +71 -0
  157. package/storybook-static/demo/a.html +10 -3
  158. package/storybook-static/demo/a.svg +26 -26
  159. package/storybook-static/demo/html-template.html +4 -3
  160. package/storybook-static/demo/s.xml +3856 -67
  161. package/storybook-static/demo/s.xslt +13 -72
  162. package/storybook-static/demo/s1.xml +3706 -0
  163. package/storybook-static/iframe.html +2 -2
  164. package/storybook-static/index.json +1 -1
  165. package/storybook-static/project.json +1 -1
  166. package/storybook-static/sb-addons/essentials-controls-1/manager-bundle.js +69 -66
  167. package/storybook-static/sb-addons/essentials-docs-3/manager-bundle.js +62 -59
  168. package/dist/custom-element-6slVaFEs.cjs +0 -97
  169. package/dist/http-request-DPrY7mGh.cjs +0 -1
  170. package/storybook-static/assets/attributes.test.stories-CzWkKw0e.js +0 -1
  171. package/storybook-static/assets/entry-preview-DHVXbf3x.js +0 -26
  172. package/storybook-static/assets/external-template.test.stories-BivZqBTp.js +0 -1
  173. package/storybook-static/assets/http-request-DNq59pnj.js +0 -1
  174. package/storybook-static/assets/index-BwkS7JH_.js +0 -8
  175. package/storybook-static/assets/module-url.test.stories-CTjUAk3J.js +0 -1
@@ -0,0 +1,91 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="860" viewBox="0 0 1200 860" role="img" aria-label="Voice ladder: seven levels with ink thickness and speech prosody mappings">
3
+ <defs>
4
+ <style>
5
+ :root {
6
+ --cem-ink: #111;
7
+ --cem-surface: #fff;
8
+ --cem-outline: #c7c7c7;
9
+ --cem-muted: #666;
10
+ --cem-accent: #005a9c;
11
+ --cem-soft: #f6f7f9;
12
+ }
13
+ .frame { fill: var(--cem-surface); stroke: var(--cem-outline); stroke-width: 2; rx: 18; }
14
+ .step { fill: var(--cem-surface); stroke: var(--cem-outline); stroke-width: 1.8; rx: 18; }
15
+ .step-accent { fill: var(--cem-surface); stroke: var(--cem-accent); stroke-width: 2.4; rx: 18; }
16
+ .title { font: 700 32px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-ink); }
17
+ .muted { font: 400 16px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-muted); }
18
+ .h { font: 650 20px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-ink); }
19
+ .t { font: 400 16px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-ink); }
20
+ .mono { font: 550 15px ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; fill: var(--cem-muted); }
21
+ .pill { fill: var(--cem-soft); stroke: var(--cem-outline); stroke-width: 1.2; rx: 999; }
22
+ .pilltext { font: 650 13px ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; fill: var(--cem-accent); }
23
+ .colh { font: 650 16px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-muted); }
24
+ </style>
25
+ </defs>
26
+
27
+ <rect x="30" y="30" width="1140" height="800" class="frame"/>
28
+
29
+ <text x="70" y="92" class="title">Voice ladder (7 levels) with ink and speech projections</text>
30
+ <text x="70" y="125" class="muted">Use the same voice semantics across modalities. Map voice → ink thickness and voice → read‑aloud prosody.</text>
31
+
32
+ <text x="90" y="170" class="colh">Voice level</text>
33
+ <text x="340" y="170" class="colh">Ink projection</text>
34
+ <text x="690" y="170" class="colh">Speech projection (TTS)</text>
35
+
36
+ <rect x="70" y="195" width="1060" height="78" class="step"/>
37
+ <text x="90" y="225" class="h">whisper</text>
38
+ <rect x="90" y="238" width="150" height="24" class="pill"/><text x="102" y="255" class="pilltext">de-emphasize</text>
39
+ <text x="340" y="225" class="t">ink thickness:</text>
40
+ <text x="455" y="225" class="mono">var(--cem-thickness-x-light)</text>
41
+ <text x="690" y="225" class="t">rate / pitch / volume:</text>
42
+ <text x="865" y="225" class="mono">1.00 / 0.95 / 0.65</text>
43
+
44
+ <rect x="70" y="285" width="1060" height="78" class="step"/>
45
+ <text x="90" y="315" class="h">soft</text>
46
+ <rect x="90" y="328" width="120" height="24" class="pill"/><text x="102" y="345" class="pilltext">secondary</text>
47
+ <text x="340" y="315" class="t">ink thickness:</text>
48
+ <text x="455" y="315" class="mono">var(--cem-thickness-light)</text>
49
+ <text x="690" y="315" class="t">rate / pitch / volume:</text>
50
+ <text x="865" y="315" class="mono">1.00 / 0.98 / 0.75</text>
51
+
52
+ <rect x="70" y="375" width="1060" height="78" class="step"/>
53
+ <text x="90" y="405" class="h">gentle</text>
54
+ <rect x="90" y="418" width="130" height="24" class="pill"/><text x="102" y="435" class="pilltext">navigation</text>
55
+ <text x="340" y="405" class="t">ink thickness:</text>
56
+ <text x="455" y="405" class="mono">var(--cem-thickness-normal)</text>
57
+ <text x="690" y="405" class="t">rate / pitch / volume:</text>
58
+ <text x="865" y="405" class="mono">1.00 / 1.00 / 0.85</text>
59
+
60
+ <rect x="70" y="465" width="1060" height="78" class="step-accent"/>
61
+ <text x="90" y="495" class="h">regular</text>
62
+ <rect x="90" y="508" width="140" height="24" class="pill"/><text x="102" y="525" class="pilltext">default</text>
63
+ <text x="340" y="495" class="t">ink thickness:</text>
64
+ <text x="455" y="495" class="mono">var(--cem-thickness-normal)</text>
65
+ <text x="690" y="495" class="t">rate / pitch / volume:</text>
66
+ <text x="865" y="495" class="mono">1.00 / 1.00 / 1.00</text>
67
+
68
+ <rect x="70" y="555" width="1060" height="78" class="step"/>
69
+ <text x="90" y="585" class="h">firm</text>
70
+ <rect x="90" y="598" width="140" height="24" class="pill"/><text x="102" y="615" class="pilltext">actions</text>
71
+ <text x="340" y="585" class="t">ink thickness:</text>
72
+ <text x="455" y="585" class="mono">var(--cem-thickness-bold)</text>
73
+ <text x="690" y="585" class="t">rate / pitch / volume:</text>
74
+ <text x="865" y="585" class="mono">0.98 / 1.03 / 1.00</text>
75
+
76
+ <rect x="70" y="645" width="1060" height="78" class="step"/>
77
+ <text x="90" y="675" class="h">strong</text>
78
+ <rect x="90" y="688" width="160" height="24" class="pill"/><text x="102" y="705" class="pilltext">section focus</text>
79
+ <text x="340" y="675" class="t">ink thickness:</text>
80
+ <text x="455" y="675" class="mono">var(--cem-thickness-x-bold)</text>
81
+ <text x="690" y="675" class="t">rate / pitch / volume:</text>
82
+ <text x="865" y="675" class="mono">0.96 / 1.06 / 1.00</text>
83
+
84
+ <rect x="70" y="735" width="1060" height="78" class="step"/>
85
+ <text x="90" y="765" class="h">loud</text>
86
+ <rect x="90" y="778" width="180" height="24" class="pill"/><text x="102" y="795" class="pilltext">use sparingly</text>
87
+ <text x="340" y="765" class="t">ink thickness:</text>
88
+ <text x="455" y="765" class="mono">var(--cem-thickness-xx-bold)</text>
89
+ <text x="690" y="765" class="t">rate / pitch / volume:</text>
90
+ <text x="865" y="765" class="mono">0.94 / 1.10 / 1.00</text>
91
+ </svg>
@@ -3,7 +3,7 @@ export default {
3
3
  "_rev": "5-df363ab4a2b9c478c01e021bde4fbafe",
4
4
  "name": "@epa-wg/custom-element-dist",
5
5
  "dist-tags": {
6
- "latest": "0.0.33"
6
+ "latest": "0.0.34"
7
7
  },
8
8
  "versions": {
9
9
  "0.0.1": {
@@ -154,7 +154,7 @@ export const OrderPreservingOn2ndTransform:Story =
154
154
 
155
155
  await fireEvent.click(canvas.getByTestId('cb2'));
156
156
  await expect(await canvas.findByText('#2')).toBeInTheDocument();
157
- await expect(canvas.getByTestId("beforeC1").nextElementSibling).toEqual(canvas.getByTestId("isC1"))
157
+ await expect(canvas.getByTestId("beforeC1").nextElementSibling.textContent).toEqual('#1')
158
158
  },
159
159
  };
160
160
  export const ReadSystemValidityMessage:Story =
@@ -194,6 +194,30 @@ export const ReadSystemValidityMessage:Story =
194
194
  },
195
195
  };
196
196
 
197
+ export const EmbedDCE:Story =
198
+ { args : {title: 'Render inner components', body:`
199
+ <template id="test-icon">
200
+ <attribute name="img"></attribute>
201
+ <i>{img}</i>
202
+ </template>
203
+ <template id="test-button">
204
+ <attribute name="text"></attribute>
205
+ <button>
206
+ <slot>{text}</slot>
207
+ </button>
208
+ </template>
209
+
210
+ <custom-element src="#test-icon" tag="test-icon"></custom-element>
211
+ <custom-element src="#test-button" tag="test-button"></custom-element>
212
+ <test-button>icon:<test-icon img="👍"></test-icon></test-button>
213
+ `}
214
+ , play: async ({canvasElement}) =>
215
+ {
216
+ const canvas = within(canvasElement);
217
+ await expect(await canvas.findByText('👍')).toBeInTheDocument();
218
+ },
219
+ };
220
+
197
221
  //#region unit tests
198
222
  /* istanbul ignore else -- @preserve */
199
223
  if( 'test' === import.meta.env.MODE &&
@@ -0,0 +1,492 @@
1
+ // noinspection DuplicatedCode
2
+
3
+ import type { StoryObj } from '@storybook/web-components';
4
+ import { expect, userEvent, within } from '@storybook/test';
5
+
6
+ import '../custom-element/custom-element.js';
7
+
8
+ type TProps = { title: string; tag: string; template: string; payload: string };
9
+
10
+ type Story = StoryObj<TProps>;
11
+
12
+ function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); }
13
+
14
+ function render(args: TProps) {
15
+ const { title, tag, template, payload } = args;
16
+ return `
17
+ <fieldset>
18
+ <legend>${title}</legend>
19
+ <custom-element tag="${tag}" hidden>
20
+ <template>
21
+ ${template}
22
+ </template>
23
+ </custom-element>
24
+ ${payload}
25
+ </fieldset>
26
+ `;
27
+ }
28
+
29
+ const meta = {
30
+ title: 'xslt-conditionals',
31
+ render
32
+ };
33
+
34
+ export default meta;
35
+
36
+ export const IfTrue: Story = {
37
+ args: {
38
+ title: 'xsl:if - condition is true',
39
+ tag: 'if-true-test',
40
+ template: `
41
+ <attribute name="show-message"></attribute>
42
+ <if test="//@show-message">
43
+ <span data-testid="result">Message is visible</span>
44
+ </if>
45
+ `,
46
+ payload: `<if-true-test show-message="yes"></if-true-test>`
47
+ },
48
+ play: async ({ canvasElement }) => {
49
+ const canvas = within(canvasElement);
50
+ await sleep(50);
51
+ const result = await canvas.findByTestId('result');
52
+ expect(result).toBeInTheDocument();
53
+ expect(result).toHaveTextContent('Message is visible');
54
+ }
55
+ };
56
+
57
+ export const IfFalse: Story = {
58
+ args: {
59
+ title: 'xsl:if - condition is false',
60
+ tag: 'if-false-test',
61
+ template: `
62
+ <attribute name="show-message"></attribute>
63
+ <if test="//@show-message = 'yes'">
64
+ <span data-testid="hidden-result">Should not appear</span>
65
+ </if>
66
+ <span data-testid="fallback">Fallback content</span>
67
+ `,
68
+ payload: `<if-false-test show-message="no"></if-false-test>`
69
+ },
70
+ play: async ({ canvasElement }) => {
71
+ const canvas = within(canvasElement);
72
+ await sleep(50);
73
+ const fallback = await canvas.findByTestId('fallback');
74
+ expect(fallback).toBeInTheDocument();
75
+ expect(canvasElement.querySelector('[data-testid="hidden-result"]')).toBeNull();
76
+ }
77
+ };
78
+
79
+ export const IfNotExists: Story = {
80
+ args: {
81
+ title: 'xsl:if - attribute not exists',
82
+ tag: 'if-not-exists-test',
83
+ template: `
84
+ <attribute name="optional"></attribute>
85
+ <if test="not(//attributes/@optional)">
86
+ <span data-testid="result">No attribute provided</span>
87
+ </if>
88
+ <if test="//attributes/@optional">
89
+ <span data-testid="has-attr">Attribute exists</span>
90
+ </if>
91
+ `,
92
+ payload: `<if-not-exists-test></if-not-exists-test>`
93
+ },
94
+ play: async ({ canvasElement }) => {
95
+ const canvas = within(canvasElement);
96
+ await sleep(50);
97
+ const result = await canvas.findByTestId('result');
98
+ expect(result).toBeInTheDocument();
99
+ expect(result).toHaveTextContent('No attribute provided');
100
+ expect(canvasElement.querySelector('[data-testid="has-attr"]')).toBeNull();
101
+ }
102
+ };
103
+
104
+ export const ChooseWhenOtherwise: Story = {
105
+ args: {
106
+ title: 'xsl:choose/when/otherwise - basic switch',
107
+ tag: 'choose-basic-test',
108
+ template: `
109
+ <attribute name="status"></attribute>
110
+ <choose>
111
+ <when test="//attributes/@status = 'success'">
112
+ <span data-testid="result" class="success">Operation succeeded</span>
113
+ </when>
114
+ <when test="//attributes/@status = 'error'">
115
+ <span data-testid="result" class="error">Operation failed</span>
116
+ </when>
117
+ <otherwise>
118
+ <span data-testid="result" class="unknown">Unknown status</span>
119
+ </otherwise>
120
+ </choose>
121
+ `,
122
+ payload: `<choose-basic-test status="success"></choose-basic-test>`
123
+ },
124
+ play: async ({ canvasElement }) => {
125
+ const canvas = within(canvasElement);
126
+ await sleep(50);
127
+ const result = await canvas.findByTestId('result');
128
+ expect(result).toBeInTheDocument();
129
+ expect(result).toHaveTextContent('Operation succeeded');
130
+ expect(result).toHaveClass('success');
131
+ }
132
+ };
133
+
134
+ export const ChooseSecondWhen: Story = {
135
+ args: {
136
+ title: 'xsl:choose - second when branch',
137
+ tag: 'choose-second-test',
138
+ template: `
139
+ <attribute name="status"></attribute>
140
+ <choose>
141
+ <when test="//attributes/@status = 'success'">
142
+ <span data-testid="result" class="success">Operation succeeded</span>
143
+ </when>
144
+ <when test="//attributes/@status = 'error'">
145
+ <span data-testid="result" class="error">Operation failed</span>
146
+ </when>
147
+ <otherwise>
148
+ <span data-testid="result" class="unknown">Unknown status</span>
149
+ </otherwise>
150
+ </choose>
151
+ `,
152
+ payload: `<choose-second-test status="error"></choose-second-test>`
153
+ },
154
+ play: async ({ canvasElement }) => {
155
+ const canvas = within(canvasElement);
156
+ await sleep(50);
157
+ const result = await canvas.findByTestId('result');
158
+ expect(result).toBeInTheDocument();
159
+ expect(result).toHaveTextContent('Operation failed');
160
+ expect(result).toHaveClass('error');
161
+ }
162
+ };
163
+
164
+ export const ChooseOtherwise: Story = {
165
+ args: {
166
+ title: 'xsl:choose - otherwise fallback',
167
+ tag: 'choose-otherwise-test',
168
+ template: `
169
+ <attribute name="status"></attribute>
170
+ <choose>
171
+ <when test="//attributes/@status = 'success'">
172
+ <span data-testid="result" class="success">Operation succeeded</span>
173
+ </when>
174
+ <when test="//attributes/@status = 'error'">
175
+ <span data-testid="result" class="error">Operation failed</span>
176
+ </when>
177
+ <otherwise>
178
+ <span data-testid="result" class="unknown">Unknown status</span>
179
+ </otherwise>
180
+ </choose>
181
+ `,
182
+ payload: `<choose-otherwise-test status="pending"></choose-otherwise-test>`
183
+ },
184
+ play: async ({ canvasElement }) => {
185
+ const canvas = within(canvasElement);
186
+ await sleep(50);
187
+ const result = await canvas.findByTestId('result');
188
+ expect(result).toBeInTheDocument();
189
+ expect(result).toHaveTextContent('Unknown status');
190
+ expect(result).toHaveClass('unknown');
191
+ }
192
+ };
193
+
194
+ export const ChooseNoAttribute: Story = {
195
+ args: {
196
+ title: 'xsl:choose - no attribute provided',
197
+ tag: 'choose-no-attr-test',
198
+ template: `
199
+ <attribute name="level"></attribute>
200
+ <choose>
201
+ <when test="//attributes/@level = 'high'">
202
+ <span data-testid="result">High priority</span>
203
+ </when>
204
+ <when test="//attributes/@level = 'low'">
205
+ <span data-testid="result">Low priority</span>
206
+ </when>
207
+ <otherwise>
208
+ <span data-testid="result">Default priority</span>
209
+ </otherwise>
210
+ </choose>
211
+ `,
212
+ payload: `<choose-no-attr-test></choose-no-attr-test>`
213
+ },
214
+ play: async ({ canvasElement }) => {
215
+ const canvas = within(canvasElement);
216
+ await sleep(50);
217
+ const result = await canvas.findByTestId('result');
218
+ expect(result).toBeInTheDocument();
219
+ expect(result).toHaveTextContent('Default priority');
220
+ }
221
+ };
222
+
223
+ export const NestedConditions: Story = {
224
+ args: {
225
+ title: 'Nested conditionals',
226
+ tag: 'nested-cond-test',
227
+ template: `
228
+ <attribute name="type"></attribute>
229
+ <attribute name="active"></attribute>
230
+ <choose>
231
+ <when test="//attributes/@type = 'user'">
232
+ <if test="//attributes/@active = 'true'">
233
+ <span data-testid="result">Active user</span>
234
+ </if>
235
+ <if test="//attributes/@active != 'true'">
236
+ <span data-testid="result">Inactive user</span>
237
+ </if>
238
+ </when>
239
+ <otherwise>
240
+ <span data-testid="result">Not a user</span>
241
+ </otherwise>
242
+ </choose>
243
+ `,
244
+ payload: `<nested-cond-test type="user" active="true"></nested-cond-test>`
245
+ },
246
+ play: async ({ canvasElement }) => {
247
+ const canvas = within(canvasElement);
248
+ await sleep(50);
249
+ const result = await canvas.findByTestId('result');
250
+ expect(result).toBeInTheDocument();
251
+ expect(result).toHaveTextContent('Active user');
252
+ }
253
+ };
254
+
255
+ export const NestedConditionsInactive: Story = {
256
+ args: {
257
+ title: 'Nested conditionals - inactive user',
258
+ tag: 'nested-cond-inactive-test',
259
+ template: `
260
+ <attribute name="type"></attribute>
261
+ <attribute name="active"></attribute>
262
+ <choose>
263
+ <when test="//attributes/@type = 'user'">
264
+ <if test="//attributes/@active = 'true'">
265
+ <span data-testid="result">Active user</span>
266
+ </if>
267
+ <if test="//attributes/@active != 'true'">
268
+ <span data-testid="result">Inactive user</span>
269
+ </if>
270
+ </when>
271
+ <otherwise>
272
+ <span data-testid="result">Not a user</span>
273
+ </otherwise>
274
+ </choose>
275
+ `,
276
+ payload: `<nested-cond-inactive-test type="user" active="false"></nested-cond-inactive-test>`
277
+ },
278
+ play: async ({ canvasElement }) => {
279
+ const canvas = within(canvasElement);
280
+ await sleep(50);
281
+ const result = await canvas.findByTestId('result');
282
+ expect(result).toBeInTheDocument();
283
+ expect(result).toHaveTextContent('Inactive user');
284
+ }
285
+ };
286
+
287
+ export const NumericComparison: Story = {
288
+ args: {
289
+ title: 'Numeric comparison in conditions',
290
+ tag: 'numeric-cond-test',
291
+ template: `
292
+ <attribute name="count"></attribute>
293
+ <choose>
294
+ <when test="//attributes/@count > 10">
295
+ <span data-testid="result">Many items</span>
296
+ </when>
297
+ <when test="//attributes/@count > 0">
298
+ <span data-testid="result">Some items</span>
299
+ </when>
300
+ <otherwise>
301
+ <span data-testid="result">No items</span>
302
+ </otherwise>
303
+ </choose>
304
+ `,
305
+ payload: `<numeric-cond-test count="15"></numeric-cond-test>`
306
+ },
307
+ play: async ({ canvasElement }) => {
308
+ const canvas = within(canvasElement);
309
+ await sleep(50);
310
+ const result = await canvas.findByTestId('result');
311
+ expect(result).toBeInTheDocument();
312
+ expect(result).toHaveTextContent('Many items');
313
+ }
314
+ };
315
+
316
+ export const NumericComparisonLow: Story = {
317
+ args: {
318
+ title: 'Numeric comparison - low value',
319
+ tag: 'numeric-cond-low-test',
320
+ template: `
321
+ <attribute name="count"></attribute>
322
+ <choose>
323
+ <when test="//attributes/@count > 10">
324
+ <span data-testid="result">Many items</span>
325
+ </when>
326
+ <when test="//attributes/@count > 0">
327
+ <span data-testid="result">Some items</span>
328
+ </when>
329
+ <otherwise>
330
+ <span data-testid="result">No items</span>
331
+ </otherwise>
332
+ </choose>
333
+ `,
334
+ payload: `<numeric-cond-low-test count="5"></numeric-cond-low-test>`
335
+ },
336
+ play: async ({ canvasElement }) => {
337
+ const canvas = within(canvasElement);
338
+ await sleep(50);
339
+ const result = await canvas.findByTestId('result');
340
+ expect(result).toBeInTheDocument();
341
+ expect(result).toHaveTextContent('Some items');
342
+ }
343
+ };
344
+
345
+ export const BooleanAndCondition: Story = {
346
+ args: {
347
+ title: 'Boolean AND condition',
348
+ tag: 'and-cond-test',
349
+ template: `
350
+ <attribute name="logged-in"></attribute>
351
+ <attribute name="admin"></attribute>
352
+ <if test="//attributes/@logged-in = 'true' and //attributes/@admin = 'true'">
353
+ <span data-testid="result">Admin panel access granted</span>
354
+ </if>
355
+ <if test="not(//attributes/@logged-in = 'true' and //attributes/@admin = 'true')">
356
+ <span data-testid="result">Access denied</span>
357
+ </if>
358
+ `,
359
+ payload: `<and-cond-test logged-in="true" admin="true"></and-cond-test>`
360
+ },
361
+ play: async ({ canvasElement }) => {
362
+ const canvas = within(canvasElement);
363
+ await sleep(50);
364
+ const result = await canvas.findByTestId('result');
365
+ expect(result).toBeInTheDocument();
366
+ expect(result).toHaveTextContent('Admin panel access granted');
367
+ }
368
+ };
369
+
370
+ export const BooleanOrCondition: Story = {
371
+ args: {
372
+ title: 'Boolean OR condition',
373
+ tag: 'or-cond-test',
374
+ template: `
375
+ <attribute name="role"></attribute>
376
+ <if test="//attributes/@role = 'admin' or //attributes/@role = 'moderator'">
377
+ <span data-testid="result">Elevated privileges</span>
378
+ </if>
379
+ <if test="not(//attributes/@role = 'admin' or //attributes/@role = 'moderator')">
380
+ <span data-testid="result">Standard user</span>
381
+ </if>
382
+ `,
383
+ payload: `<or-cond-test role="moderator"></or-cond-test>`
384
+ },
385
+ play: async ({ canvasElement }) => {
386
+ const canvas = within(canvasElement);
387
+ await sleep(50);
388
+ const result = await canvas.findByTestId('result');
389
+ expect(result).toBeInTheDocument();
390
+ expect(result).toHaveTextContent('Elevated privileges');
391
+ }
392
+ };
393
+
394
+ export const MultipleInstances: Story = {
395
+ args: {
396
+ title: 'Multiple instances with different conditions',
397
+ tag: 'multi-instance-test',
398
+ template: `
399
+ <attribute name="data-testid"></attribute>
400
+ <attribute name="variant"></attribute>
401
+ <choose>
402
+ <when test="//attributes/@variant = 'primary'">
403
+ <button data-testid="{$data-testid}-primary" class="primary">Primary Button</button>
404
+ </when>
405
+ <when test="//attributes/@variant = 'secondary'">
406
+ <button data-testid="{$data-testid}-secondary" class="secondary">Secondary Button</button>
407
+ </when>
408
+ <otherwise>
409
+ <button data-testid="{$data-testid}-otherwise" class="default">Default Button</button>
410
+ </otherwise>
411
+ </choose>
412
+ `,
413
+ payload: `
414
+ <multi-instance-test data-testid="btn1" variant="primary"></multi-instance-test>
415
+ <multi-instance-test data-testid="btn2" variant="secondary"></multi-instance-test>
416
+ <multi-instance-test data-testid="btn3" variant="unknown"></multi-instance-test>
417
+ `
418
+ },
419
+ play: async ({ canvasElement }) => {
420
+ const canvas = within(canvasElement);
421
+ await sleep(50);
422
+
423
+ const btn1 = await canvas.findByTestId('btn1-primary');
424
+ expect(btn1).toHaveTextContent('Primary Button');
425
+ expect(btn1).toHaveClass('primary');
426
+
427
+ const btn2 = await canvas.findByTestId('btn2-secondary');
428
+ expect(btn2).toHaveTextContent('Secondary Button');
429
+ expect(btn2).toHaveClass('secondary');
430
+
431
+ const btn3 = await canvas.findByTestId('btn3-otherwise');
432
+ expect(btn3).toHaveTextContent('Default Button');
433
+ expect(btn3).toHaveClass('default');
434
+ }
435
+ };
436
+
437
+
438
+ export const MultipleIfOrderingIssue: Story = {
439
+ args: {
440
+ title: 'KNOWN ISSUE: Multiple IF blocks - out of order',
441
+ tag: 'multi-if-order-issue',
442
+ template: `
443
+ <div data-testid="whole-text">
444
+ <label><input type="checkbox" data-testid="toggle-a" slice="show-a" value="AA" /> A</label>
445
+
446
+ <hr/>
447
+
448
+ <if test="//show-a = 'AA'">
449
+ !A
450
+ </if>
451
+
452
+ </div>
453
+ <if test="//show-a = 'AA'">
454
+ <div data-testid="t-1">T1</div>
455
+ </if>
456
+ `,
457
+ payload: `
458
+ <multi-if-order-issue></multi-if-order-issue>
459
+ `
460
+ },
461
+ play: async ({ canvasElement }) => {
462
+ const canvas = within(canvasElement);
463
+
464
+ // Initially ▶\s+◀ should be visible, !A should not
465
+ const container = await canvas.findByTestId('whole-text');
466
+ expect(container.textContent).to.toMatch(/▶\s+◀/);
467
+
468
+ // check A - show !A
469
+ const toggleA = await canvas.findByTestId('toggle-a');
470
+ debugger;
471
+ await userEvent.click(toggleA);
472
+
473
+ expect( await await canvas.findByTestId('t-1')).toBeInTheDocument();
474
+ // !A is rendered
475
+ expect(container.textContent).to.toMatch(/\!A/);
476
+ // !A afer ▶
477
+ expect(container.textContent).to.toMatch(/▶\s+\!A/);
478
+ // !A before ◀
479
+ expect(container.textContent).to.toMatch(/\!A\s+◀/);
480
+ }
481
+ };
482
+
483
+ //#region unit tests
484
+ /* istanbul ignore else -- @preserve */
485
+ if ('test' === import.meta.env.MODE &&
486
+ !import.meta.url.includes('skiptest')) {
487
+ const mod = await import('./xslt-conditionals.test.stories.ts?skiptest');
488
+ const { testStoryBook } = await import('./testStoryBook');
489
+ const { describe } = await import('vitest');
490
+ describe(meta.title, () => testStoryBook(mod, meta));
491
+ }
492
+ //#endregion