@sapudex/web-components 2.48.0 → 2.50.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 (341) hide show
  1. package/bundle.esm.js +2 -0
  2. package/cypress/specs/AccordionSpotlight.cy.tsx +402 -0
  3. package/cypress/specs/BusyIndicator.cy.tsx +15 -0
  4. package/cypress/specs/Carousel.cy.tsx +142 -0
  5. package/cypress/specs/FilterMenu.cy.tsx +968 -10
  6. package/cypress/specs/List.cy.tsx +106 -0
  7. package/cypress/specs/ListItem.cy.tsx +291 -1
  8. package/cypress/specs/MediaPlayer.cy.tsx +32 -0
  9. package/cypress/specs/SelectBox.cy.tsx +134 -3
  10. package/cypress/specs/TabNavigation.cy.tsx +56 -0
  11. package/cypress/support/component-imports.instrumented.ts +6 -0
  12. package/cypress/support/component-imports.src.ts +6 -0
  13. package/dist/AccordionSpotlight.d.ts +149 -0
  14. package/dist/AccordionSpotlight.js +284 -0
  15. package/dist/AccordionSpotlight.js.map +1 -0
  16. package/dist/AccordionSpotlightItem.d.ts +76 -0
  17. package/dist/AccordionSpotlightItem.js +115 -0
  18. package/dist/AccordionSpotlightItem.js.map +1 -0
  19. package/dist/AccordionSpotlightItemTemplate.d.ts +2 -0
  20. package/dist/AccordionSpotlightItemTemplate.js +5 -0
  21. package/dist/AccordionSpotlightItemTemplate.js.map +1 -0
  22. package/dist/AccordionSpotlightTemplate.d.ts +2 -0
  23. package/dist/AccordionSpotlightTemplate.js +10 -0
  24. package/dist/AccordionSpotlightTemplate.js.map +1 -0
  25. package/dist/BusyIndicator.d.ts +9 -0
  26. package/dist/BusyIndicator.js +7 -1
  27. package/dist/BusyIndicator.js.map +1 -1
  28. package/dist/Carousel.d.ts +26 -15
  29. package/dist/Carousel.js +71 -55
  30. package/dist/Carousel.js.map +1 -1
  31. package/dist/FilterMenu.d.ts +12 -2
  32. package/dist/FilterMenu.js +24 -4
  33. package/dist/FilterMenu.js.map +1 -1
  34. package/dist/FilterMenuTemplate.js +1 -1
  35. package/dist/FilterMenuTemplate.js.map +1 -1
  36. package/dist/HeroBanner.d.ts +18 -9
  37. package/dist/HeroBanner.js +38 -35
  38. package/dist/HeroBanner.js.map +1 -1
  39. package/dist/HeroBannerTemplate.js +2 -2
  40. package/dist/HeroBannerTemplate.js.map +1 -1
  41. package/dist/List.d.ts +1 -0
  42. package/dist/List.js +24 -11
  43. package/dist/List.js.map +1 -1
  44. package/dist/ListGroupHeader.js +5 -1
  45. package/dist/ListGroupHeader.js.map +1 -1
  46. package/dist/ListGroupHeaderBase.d.ts +22 -0
  47. package/dist/ListGroupHeaderBase.js +44 -0
  48. package/dist/ListGroupHeaderBase.js.map +1 -1
  49. package/dist/ListItem.d.ts +4 -0
  50. package/dist/ListItem.js +32 -0
  51. package/dist/ListItem.js.map +1 -1
  52. package/dist/MediaPlayer.d.ts +2 -0
  53. package/dist/MediaPlayer.js +12 -0
  54. package/dist/MediaPlayer.js.map +1 -1
  55. package/dist/PromoTile.js +1 -1
  56. package/dist/PromoTile.js.map +1 -1
  57. package/dist/SearchableListBase.d.ts +10 -1
  58. package/dist/SearchableListBase.js +23 -7
  59. package/dist/SearchableListBase.js.map +1 -1
  60. package/dist/SearchableListBaseTemplate.js +3 -2
  61. package/dist/SearchableListBaseTemplate.js.map +1 -1
  62. package/dist/SelectBase.d.ts +14 -0
  63. package/dist/SelectBase.js +56 -3
  64. package/dist/SelectBase.js.map +1 -1
  65. package/dist/SelectBoxTemplate.js +1 -1
  66. package/dist/SelectBoxTemplate.js.map +1 -1
  67. package/dist/TabNavigation.d.ts +7 -1
  68. package/dist/TabNavigation.js +8 -2
  69. package/dist/TabNavigation.js.map +1 -1
  70. package/dist/Wizard.d.ts +5 -0
  71. package/dist/Wizard.js +11 -0
  72. package/dist/Wizard.js.map +1 -1
  73. package/dist/assets/{InputSuggestions-BcK6O4Bk.js → InputSuggestions-DZR626c7.js} +1 -1
  74. package/dist/assets/{ListItemStandardExpandableTextTemplate-u600dmHC.js → ListItemStandardExpandableTextTemplate-D_5VXMzU.js} +1 -1
  75. package/dist/assets/{bundle-JYo3bV8v.css → bundle-CkxyEA8c.css} +1 -1
  76. package/dist/assets/{bundle.esm-C6Ie2WeI.js → bundle.esm-DuQLY8Jz.js} +241 -238
  77. package/dist/assets/messagebundle_ar-BUZ_gTvQ.js +1 -0
  78. package/dist/assets/messagebundle_no-BZQfeWxT.js +1 -0
  79. package/dist/css/themes/Accordion.css +1 -1
  80. package/dist/css/themes/AccordionItem.css +1 -1
  81. package/dist/css/themes/AccordionSpotlight.css +1 -0
  82. package/dist/css/themes/AccordionSpotlightItem.css +1 -0
  83. package/dist/css/themes/Badge.css +1 -1
  84. package/dist/css/themes/BusyIndicator.css +1 -0
  85. package/dist/css/themes/Button.css +1 -1
  86. package/dist/css/themes/Divider.css +1 -1
  87. package/dist/css/themes/Dropdown.css +1 -1
  88. package/dist/css/themes/FilterMenu.css +1 -1
  89. package/dist/css/themes/Footer.css +1 -1
  90. package/dist/css/themes/HeroBanner.css +1 -1
  91. package/dist/css/themes/ImageGridTableItem.css +1 -1
  92. package/dist/css/themes/InputGroup.css +1 -1
  93. package/dist/css/themes/MastheadShell.css +1 -1
  94. package/dist/css/themes/MediaPlayer.css +1 -1
  95. package/dist/css/themes/PhoneNumberField.css +1 -1
  96. package/dist/css/themes/ProgressBar.css +1 -1
  97. package/dist/css/themes/PromoTile.css +1 -1
  98. package/dist/css/themes/RadioButton.css +1 -1
  99. package/dist/css/themes/Review.css +1 -1
  100. package/dist/css/themes/ReviewItem.css +1 -1
  101. package/dist/css/themes/Search.css +1 -1
  102. package/dist/css/themes/SelectBase.css +1 -1
  103. package/dist/css/themes/SelectBox.css +1 -1
  104. package/dist/css/themes/SideNavigation.css +1 -1
  105. package/dist/css/themes/SideNavigationGroup.css +1 -1
  106. package/dist/css/themes/SocialMediaButton.css +1 -1
  107. package/dist/css/themes/StepInput.css +1 -1
  108. package/dist/css/themes/Switch.css +1 -1
  109. package/dist/css/themes/TabNavigation.css +1 -1
  110. package/dist/css/themes/Tag.css +1 -1
  111. package/dist/css/themes/TagGroup.css +1 -1
  112. package/dist/css/themes/TextArea.css +1 -1
  113. package/dist/css/themes/TextField.css +1 -1
  114. package/dist/css/themes/Toast.css +1 -1
  115. package/dist/custom-elements-internal.json +508 -42
  116. package/dist/custom-elements.json +407 -52
  117. package/dist/generated/assets/i18n/messagebundle_ar.json +1 -1
  118. package/dist/generated/assets/i18n/messagebundle_no.json +1 -1
  119. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js +24 -21
  120. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js.map +1 -1
  121. package/dist/generated/templates/ListItemTemplate.lit.js +2 -2
  122. package/dist/generated/templates/ListItemTemplate.lit.js.map +1 -1
  123. package/dist/generated/templates/TabNavigationTemplate.lit.js +1 -1
  124. package/dist/generated/templates/TabNavigationTemplate.lit.js.map +1 -1
  125. package/dist/generated/themes/Accordion.css.d.ts +1 -1
  126. package/dist/generated/themes/Accordion.css.js +1 -1
  127. package/dist/generated/themes/Accordion.css.js.map +1 -1
  128. package/dist/generated/themes/AccordionItem.css.d.ts +1 -1
  129. package/dist/generated/themes/AccordionItem.css.js +1 -1
  130. package/dist/generated/themes/AccordionItem.css.js.map +1 -1
  131. package/dist/generated/themes/AccordionSpotlight.css.d.ts +2 -0
  132. package/dist/generated/themes/AccordionSpotlight.css.js +8 -0
  133. package/dist/generated/themes/AccordionSpotlight.css.js.map +1 -0
  134. package/dist/generated/themes/AccordionSpotlightItem.css.d.ts +2 -0
  135. package/dist/generated/themes/AccordionSpotlightItem.css.js +8 -0
  136. package/dist/generated/themes/AccordionSpotlightItem.css.js.map +1 -0
  137. package/dist/generated/themes/Badge.css.d.ts +1 -1
  138. package/dist/generated/themes/Badge.css.js +1 -1
  139. package/dist/generated/themes/Badge.css.js.map +1 -1
  140. package/dist/generated/themes/BusyIndicator.css.d.ts +2 -0
  141. package/dist/generated/themes/BusyIndicator.css.js +8 -0
  142. package/dist/generated/themes/BusyIndicator.css.js.map +1 -0
  143. package/dist/generated/themes/Button.css.d.ts +1 -1
  144. package/dist/generated/themes/Button.css.js +1 -1
  145. package/dist/generated/themes/Button.css.js.map +1 -1
  146. package/dist/generated/themes/Divider.css.d.ts +1 -1
  147. package/dist/generated/themes/Divider.css.js +1 -1
  148. package/dist/generated/themes/Divider.css.js.map +1 -1
  149. package/dist/generated/themes/Dropdown.css.d.ts +1 -1
  150. package/dist/generated/themes/Dropdown.css.js +1 -1
  151. package/dist/generated/themes/Dropdown.css.js.map +1 -1
  152. package/dist/generated/themes/FilterMenu.css.d.ts +1 -1
  153. package/dist/generated/themes/FilterMenu.css.js +1 -1
  154. package/dist/generated/themes/FilterMenu.css.js.map +1 -1
  155. package/dist/generated/themes/Footer.css.d.ts +1 -1
  156. package/dist/generated/themes/Footer.css.js +1 -1
  157. package/dist/generated/themes/Footer.css.js.map +1 -1
  158. package/dist/generated/themes/HeroBanner.css.d.ts +1 -1
  159. package/dist/generated/themes/HeroBanner.css.js +1 -1
  160. package/dist/generated/themes/HeroBanner.css.js.map +1 -1
  161. package/dist/generated/themes/ImageGridTableItem.css.d.ts +1 -1
  162. package/dist/generated/themes/ImageGridTableItem.css.js +1 -1
  163. package/dist/generated/themes/ImageGridTableItem.css.js.map +1 -1
  164. package/dist/generated/themes/InputGroup.css.d.ts +1 -1
  165. package/dist/generated/themes/InputGroup.css.js +1 -1
  166. package/dist/generated/themes/InputGroup.css.js.map +1 -1
  167. package/dist/generated/themes/MastheadShell.css.d.ts +1 -1
  168. package/dist/generated/themes/MastheadShell.css.js +1 -1
  169. package/dist/generated/themes/MastheadShell.css.js.map +1 -1
  170. package/dist/generated/themes/MediaPlayer.css.d.ts +1 -1
  171. package/dist/generated/themes/MediaPlayer.css.js +1 -1
  172. package/dist/generated/themes/MediaPlayer.css.js.map +1 -1
  173. package/dist/generated/themes/PhoneNumberField.css.d.ts +1 -1
  174. package/dist/generated/themes/PhoneNumberField.css.js +1 -1
  175. package/dist/generated/themes/PhoneNumberField.css.js.map +1 -1
  176. package/dist/generated/themes/ProgressBar.css.d.ts +1 -1
  177. package/dist/generated/themes/ProgressBar.css.js +1 -1
  178. package/dist/generated/themes/ProgressBar.css.js.map +1 -1
  179. package/dist/generated/themes/PromoTile.css.d.ts +1 -1
  180. package/dist/generated/themes/PromoTile.css.js +1 -1
  181. package/dist/generated/themes/PromoTile.css.js.map +1 -1
  182. package/dist/generated/themes/RadioButton.css.d.ts +1 -1
  183. package/dist/generated/themes/RadioButton.css.js +1 -1
  184. package/dist/generated/themes/RadioButton.css.js.map +1 -1
  185. package/dist/generated/themes/Review.css.d.ts +1 -1
  186. package/dist/generated/themes/Review.css.js +1 -1
  187. package/dist/generated/themes/Review.css.js.map +1 -1
  188. package/dist/generated/themes/ReviewItem.css.d.ts +1 -1
  189. package/dist/generated/themes/ReviewItem.css.js +1 -1
  190. package/dist/generated/themes/ReviewItem.css.js.map +1 -1
  191. package/dist/generated/themes/Search.css.d.ts +1 -1
  192. package/dist/generated/themes/Search.css.js +1 -1
  193. package/dist/generated/themes/Search.css.js.map +1 -1
  194. package/dist/generated/themes/SelectBase.css.d.ts +1 -1
  195. package/dist/generated/themes/SelectBase.css.js +1 -1
  196. package/dist/generated/themes/SelectBase.css.js.map +1 -1
  197. package/dist/generated/themes/SelectBox.css.d.ts +1 -1
  198. package/dist/generated/themes/SelectBox.css.js +1 -1
  199. package/dist/generated/themes/SelectBox.css.js.map +1 -1
  200. package/dist/generated/themes/SideNavigation.css.d.ts +1 -1
  201. package/dist/generated/themes/SideNavigation.css.js +1 -1
  202. package/dist/generated/themes/SideNavigation.css.js.map +1 -1
  203. package/dist/generated/themes/SideNavigationGroup.css.d.ts +1 -1
  204. package/dist/generated/themes/SideNavigationGroup.css.js +1 -1
  205. package/dist/generated/themes/SideNavigationGroup.css.js.map +1 -1
  206. package/dist/generated/themes/SocialMediaButton.css.d.ts +1 -1
  207. package/dist/generated/themes/SocialMediaButton.css.js +1 -1
  208. package/dist/generated/themes/SocialMediaButton.css.js.map +1 -1
  209. package/dist/generated/themes/StepInput.css.d.ts +1 -1
  210. package/dist/generated/themes/StepInput.css.js +1 -1
  211. package/dist/generated/themes/StepInput.css.js.map +1 -1
  212. package/dist/generated/themes/Switch.css.d.ts +1 -1
  213. package/dist/generated/themes/Switch.css.js +1 -1
  214. package/dist/generated/themes/Switch.css.js.map +1 -1
  215. package/dist/generated/themes/TabNavigation.css.d.ts +1 -1
  216. package/dist/generated/themes/TabNavigation.css.js +1 -1
  217. package/dist/generated/themes/TabNavigation.css.js.map +1 -1
  218. package/dist/generated/themes/Tag.css.d.ts +1 -1
  219. package/dist/generated/themes/Tag.css.js +1 -1
  220. package/dist/generated/themes/Tag.css.js.map +1 -1
  221. package/dist/generated/themes/TagGroup.css.d.ts +1 -1
  222. package/dist/generated/themes/TagGroup.css.js +1 -1
  223. package/dist/generated/themes/TagGroup.css.js.map +1 -1
  224. package/dist/generated/themes/TextArea.css.d.ts +1 -1
  225. package/dist/generated/themes/TextArea.css.js +1 -1
  226. package/dist/generated/themes/TextArea.css.js.map +1 -1
  227. package/dist/generated/themes/TextField.css.d.ts +1 -1
  228. package/dist/generated/themes/TextField.css.js +1 -1
  229. package/dist/generated/themes/TextField.css.js.map +1 -1
  230. package/dist/generated/themes/Toast.css.d.ts +1 -1
  231. package/dist/generated/themes/Toast.css.js +1 -1
  232. package/dist/generated/themes/Toast.css.js.map +1 -1
  233. package/dist/messagebundle_ar.properties +99 -0
  234. package/dist/messagebundle_no.properties +126 -1
  235. package/dist/vscode.html-custom-data.json +79 -14
  236. package/dist/web-types.json +211 -32
  237. package/package.json +1 -1
  238. package/src/AccordionSpotlight.ts +321 -0
  239. package/src/AccordionSpotlightItem.ts +117 -0
  240. package/src/AccordionSpotlightItemTemplate.tsx +7 -0
  241. package/src/AccordionSpotlightTemplate.tsx +107 -0
  242. package/src/BusyIndicator.ts +17 -2
  243. package/src/Carousel.ts +118 -80
  244. package/src/FilterMenu.ts +25 -4
  245. package/src/FilterMenuTemplate.tsx +1 -1
  246. package/src/HeroBanner.ts +60 -46
  247. package/src/HeroBannerTemplate.tsx +35 -22
  248. package/src/List.ts +23 -11
  249. package/src/ListGroupHeader.hbs +11 -10
  250. package/src/ListGroupHeader.ts +5 -1
  251. package/src/ListGroupHeaderBase.ts +48 -0
  252. package/src/ListItem.hbs +5 -1
  253. package/src/ListItem.ts +36 -0
  254. package/src/MediaPlayer.ts +11 -0
  255. package/src/PromoTile.ts +1 -1
  256. package/src/SearchableListBase.ts +24 -7
  257. package/src/SearchableListBaseTemplate.tsx +5 -2
  258. package/src/SelectBase.ts +58 -3
  259. package/src/SelectBoxTemplate.tsx +1 -0
  260. package/src/TabNavigation.hbs +1 -1
  261. package/src/TabNavigation.ts +8 -1
  262. package/src/Wizard.ts +15 -1
  263. package/src/generated/templates/ListGroupHeaderTemplate.lit.ts +24 -21
  264. package/src/generated/templates/ListItemTemplate.lit.ts +2 -2
  265. package/src/generated/templates/TabNavigationTemplate.lit.ts +1 -1
  266. package/src/generated/themes/Accordion.css.ts +1 -1
  267. package/src/generated/themes/AccordionItem.css.ts +1 -1
  268. package/src/generated/themes/AccordionSpotlight.css.ts +9 -0
  269. package/src/generated/themes/AccordionSpotlightItem.css.ts +9 -0
  270. package/src/generated/themes/Badge.css.ts +1 -1
  271. package/src/generated/themes/BusyIndicator.css.ts +9 -0
  272. package/src/generated/themes/Button.css.ts +1 -1
  273. package/src/generated/themes/Divider.css.ts +1 -1
  274. package/src/generated/themes/Dropdown.css.ts +1 -1
  275. package/src/generated/themes/FilterMenu.css.ts +1 -1
  276. package/src/generated/themes/Footer.css.ts +1 -1
  277. package/src/generated/themes/HeroBanner.css.ts +1 -1
  278. package/src/generated/themes/ImageGridTableItem.css.ts +1 -1
  279. package/src/generated/themes/InputGroup.css.ts +1 -1
  280. package/src/generated/themes/MastheadShell.css.ts +1 -1
  281. package/src/generated/themes/MediaPlayer.css.ts +1 -1
  282. package/src/generated/themes/PhoneNumberField.css.ts +1 -1
  283. package/src/generated/themes/ProgressBar.css.ts +1 -1
  284. package/src/generated/themes/PromoTile.css.ts +1 -1
  285. package/src/generated/themes/RadioButton.css.ts +1 -1
  286. package/src/generated/themes/Review.css.ts +1 -1
  287. package/src/generated/themes/ReviewItem.css.ts +1 -1
  288. package/src/generated/themes/Search.css.ts +1 -1
  289. package/src/generated/themes/SelectBase.css.ts +1 -1
  290. package/src/generated/themes/SelectBox.css.ts +1 -1
  291. package/src/generated/themes/SideNavigation.css.ts +1 -1
  292. package/src/generated/themes/SideNavigationGroup.css.ts +1 -1
  293. package/src/generated/themes/SocialMediaButton.css.ts +1 -1
  294. package/src/generated/themes/StepInput.css.ts +1 -1
  295. package/src/generated/themes/Switch.css.ts +1 -1
  296. package/src/generated/themes/TabNavigation.css.ts +1 -1
  297. package/src/generated/themes/Tag.css.ts +1 -1
  298. package/src/generated/themes/TagGroup.css.ts +1 -1
  299. package/src/generated/themes/TextArea.css.ts +1 -1
  300. package/src/generated/themes/TextField.css.ts +1 -1
  301. package/src/generated/themes/Toast.css.ts +1 -1
  302. package/src/i18n/messagebundle_ar.properties +99 -0
  303. package/src/i18n/messagebundle_no.properties +126 -1
  304. package/src/themes/Accordion.css +1 -1
  305. package/src/themes/AccordionItem.css +4 -4
  306. package/src/themes/AccordionSpotlight.css +264 -0
  307. package/src/themes/AccordionSpotlightItem.css +3 -0
  308. package/src/themes/Badge.css +7 -7
  309. package/src/themes/BusyIndicator.css +24 -0
  310. package/src/themes/Button.css +57 -57
  311. package/src/themes/Divider.css +1 -1
  312. package/src/themes/Dropdown.css +1 -1
  313. package/src/themes/FilterMenu.css +2 -2
  314. package/src/themes/Footer.css +1 -1
  315. package/src/themes/HeroBanner.css +2 -0
  316. package/src/themes/ImageGridTableItem.css +1 -1
  317. package/src/themes/InputGroup.css +1 -1
  318. package/src/themes/MastheadShell.css +2 -2
  319. package/src/themes/MediaPlayer.css +5 -34
  320. package/src/themes/PhoneNumberField.css +6 -6
  321. package/src/themes/ProgressBar.css +4 -0
  322. package/src/themes/PromoTile.css +160 -4
  323. package/src/themes/RadioButton.css +2 -2
  324. package/src/themes/Review.css +2 -2
  325. package/src/themes/ReviewItem.css +2 -2
  326. package/src/themes/Search.css +5 -5
  327. package/src/themes/SelectBase.css +14 -14
  328. package/src/themes/SelectBox.css +1 -1
  329. package/src/themes/SideNavigation.css +1 -1
  330. package/src/themes/SideNavigationGroup.css +1 -1
  331. package/src/themes/SocialMediaButton.css +2 -2
  332. package/src/themes/StepInput.css +8 -8
  333. package/src/themes/Switch.css +9 -9
  334. package/src/themes/TabNavigation.css +1 -1
  335. package/src/themes/Tag.css +4 -4
  336. package/src/themes/TagGroup.css +1 -0
  337. package/src/themes/TextArea.css +4 -4
  338. package/src/themes/TextField.css +13 -13
  339. package/src/themes/Toast.css +1 -1
  340. package/dist/assets/messagebundle_ar-BNhVFt1-.js +0 -1
  341. package/dist/assets/messagebundle_no-BIUfmTWJ.js +0 -1
@@ -170,6 +170,112 @@ describe("List component - collapsible group header", () => {
170
170
  });
171
171
  });
172
172
 
173
+ // Added: covers the fix for a collapsible SingleSelect group header being incorrectly
174
+ // selectable via click/Enter. Because UDExList does not opt into the tree structure
175
+ // (supportsTreeStructure stays false), a collapsible SingleSelect header here renders
176
+ // the plain `single-group-header-collapsible` <div> with role="button" (non-tree branch
177
+ // of singleCollapsibleHeaderRole). The <div> has no @click handler and only a
178
+ // Left/Right arrow @keydown handler — so it can never be selected, only collapsed via
179
+ // the arrows or the arrow <udex-button>.
180
+ describe("List - SingleSelect collapsible group header is never selectable", () => {
181
+ const headerDiv = () =>
182
+ cy.get("#group-header-collapsible").shadow().find(".udex-list__group-content");
183
+
184
+ const childItems = () =>
185
+ cy.get("#group-header-collapsible").find("udex-list-item");
186
+
187
+ beforeEach(() => {
188
+ cy.mount(collapsibleListSingleSelect);
189
+ });
190
+
191
+ it("renders the header as a plain div with role=\"button\" (non-tree mode)", () => {
192
+ headerDiv()
193
+ .should("exist")
194
+ .and("have.attr", "role", "button")
195
+ .and("have.attr", "tabindex", "0");
196
+ cy.get("#group-header-collapsible").shadow()
197
+ .find("udex-list-item").should("not.exist");
198
+ });
199
+
200
+ // Updated: split the chained `.and("not.have.attr", ...)` assertions into separate
201
+ // freshly-queried `.should()` calls. The doubled `.and("not.have.attr", ...)` after a
202
+ // shadow `.find()` re-yielded the subject as undefined (chai-jQuery "attr on undefined").
203
+ // The standalone-List SingleSelect collapsible header renders the plain
204
+ // `single-group-header-collapsible` <div> (.udex-list__group-content) with role="button"
205
+ // (non-tree branch), aria-expanded, and no selection-state attribute.
206
+ it("exposes aria-expanded but no selection-state attribute", () => {
207
+ headerDiv().should("have.attr", "aria-expanded", "true");
208
+ headerDiv().should("not.have.attr", "aria-selected");
209
+ headerDiv().should("not.have.attr", "aria-checked");
210
+ });
211
+
212
+ it("clicking the header body does NOT select it and does NOT collapse it", () => {
213
+ headerDiv().realClick();
214
+
215
+ // Updated: separate assertions — chaining `.and("not.have.attr", ...)` after a
216
+ // value-form `.should("have.attr", name, value)` re-yields the attribute string, not
217
+ // the element, causing "attr on undefined".
218
+ headerDiv().should("have.attr", "aria-expanded", "true");
219
+ headerDiv().should("not.have.attr", "aria-selected");
220
+ cy.get("#group-header-collapsible").should("not.have.attr", "collapsed");
221
+ cy.get("#group-header-collapsible").then($gh => {
222
+ expect(($gh[0] as HTMLElement & { selected: boolean }).selected).to.not.equal(true);
223
+ });
224
+ });
225
+
226
+ it("clicking the header body does NOT select any child item", () => {
227
+ headerDiv().realClick();
228
+
229
+ childItems().each($item => {
230
+ cy.wrap($item).should("not.have.attr", "selected");
231
+ });
232
+ });
233
+
234
+ it("pressing Enter on the focused header does NOT select it and does NOT collapse it", () => {
235
+ headerDiv().focus();
236
+ cy.realPress("Enter");
237
+
238
+ // Updated: separate assertions (see note above on chained `.and("not.have.attr", ...)`).
239
+ headerDiv().should("have.attr", "aria-expanded", "true");
240
+ headerDiv().should("not.have.attr", "aria-selected");
241
+ cy.get("#group-header-collapsible").then($gh => {
242
+ expect(($gh[0] as HTMLElement & { selected: boolean }).selected).to.not.equal(true);
243
+ });
244
+ });
245
+
246
+ it("ArrowLeft collapses the expanded group (aria-expanded=false, host collapsed)", () => {
247
+ headerDiv().should("have.attr", "aria-expanded", "true");
248
+
249
+ headerDiv().focus();
250
+ cy.realPress("ArrowLeft");
251
+
252
+ headerDiv().should("have.attr", "aria-expanded", "false");
253
+ cy.get("#group-header-collapsible").should("have.attr", "collapsed", "");
254
+ });
255
+
256
+ it("ArrowRight expands the collapsed group again", () => {
257
+ headerDiv().focus();
258
+ cy.realPress("ArrowLeft");
259
+ headerDiv().should("have.attr", "aria-expanded", "false");
260
+
261
+ headerDiv().focus();
262
+ cy.realPress("ArrowRight");
263
+ headerDiv().should("have.attr", "aria-expanded", "true");
264
+ });
265
+
266
+ it("clicking the arrow button collapses/expands without selecting", () => {
267
+ cy.get("#group-header-collapsible").shadow()
268
+ .find(".udex-list__group-header-wrap").as("grouping-header");
269
+
270
+ cy.get("@grouping-header").find(".udex-list__group-heading__icon").realClick();
271
+ cy.get("#group-header-collapsible").should("have.attr", "collapsed", "");
272
+ headerDiv().should("not.have.attr", "aria-selected");
273
+
274
+ cy.get("@grouping-header").find(".udex-list__group-heading__icon").realClick();
275
+ cy.get("#group-header-collapsible").should("not.have.attr", "collapsed", "");
276
+ });
277
+ });
278
+
173
279
  describe("List component - SingleContent accessible name", () => {
174
280
  it("uses accessible-name as aria-label when only accessible-name is set", () => {
175
281
  cy.mount(
@@ -2,7 +2,7 @@ import { UDExListItem } from "../support/component-imports.js";
2
2
  import UDExButton from "../../src/Button.js"
3
3
  import { UDExAvatarShape } from "../../dist/Avatar.js";
4
4
  import { UDExAvatarSize } from "../../src/Avatar.js";
5
- import { UDExAdditionalTextState, UDExListStyleType } from "../../src/types/List.js";
5
+ import { UDExAdditionalTextState, UDExListMode, UDExListStyleType } from "../../src/types/List.js";
6
6
  import UDExStatusBadgeValueState from "../../dist/types/StatusBadgeValueState.js";
7
7
  import { UDExDisplayType } from "../../src/RatingIndicator.js";
8
8
 
@@ -221,4 +221,294 @@ describe("ListItem", () => {
221
221
  cy.get("@rating").should("have.attr", "aria-label", "Rating 4.0, Total 20, Rating Indicator 4 of 5");
222
222
  });
223
223
  });
224
+
225
+ // Added: unit-level coverage for the WCAG 4.1.2 Treeview Checkbox pattern getters
226
+ // (ariaCheckedValue / checkboxAriaHidden). These only activate when the item's
227
+ // accessible-role === "treeitem" (set by SearchableListBase in the tree pattern).
228
+ describe("Tree accessibility pattern (accessible-role=\"treeitem\")", () => {
229
+ it("[part=item] has role=\"treeitem\" and aria-checked=\"false\" when unselected", () => {
230
+ cy.mount(<UDExListItem
231
+ id="tree-item"
232
+ accessible-role="treeitem"
233
+ mode={UDExListMode.MultiSelect}
234
+ label="JavaScript">
235
+ </UDExListItem>);
236
+
237
+ cy.get("#tree-item").shadow()
238
+ .find("[part=item]")
239
+ .should("have.attr", "role", "treeitem")
240
+ .and("have.attr", "aria-checked", "false");
241
+ });
242
+
243
+ it("aria-checked=\"true\" when selected", () => {
244
+ cy.mount(<UDExListItem
245
+ id="tree-item-selected"
246
+ accessible-role="treeitem"
247
+ mode={UDExListMode.MultiSelect}
248
+ selected
249
+ label="JavaScript">
250
+ </UDExListItem>);
251
+
252
+ cy.get("#tree-item-selected").shadow()
253
+ .find("[part=item]").should("have.attr", "aria-checked", "true");
254
+ });
255
+
256
+ it("aria-checked=\"mixed\" when indeterminate", () => {
257
+ cy.mount(<UDExListItem
258
+ id="tree-item-mixed"
259
+ accessible-role="treeitem"
260
+ mode={UDExListMode.MultiSelect}
261
+ indeterminate
262
+ label="JavaScript">
263
+ </UDExListItem>);
264
+
265
+ cy.get("#tree-item-mixed").shadow()
266
+ .find("[part=item]").should("have.attr", "aria-checked", "mixed");
267
+ });
268
+
269
+ it("inner udex-checkbox is aria-hidden (JAWS double-announce guard)", () => {
270
+ cy.mount(<UDExListItem
271
+ id="tree-item-checkbox"
272
+ accessible-role="treeitem"
273
+ mode={UDExListMode.MultiSelect}
274
+ label="JavaScript">
275
+ </UDExListItem>);
276
+
277
+ cy.get("#tree-item-checkbox").shadow()
278
+ .find("udex-checkbox").should("have.attr", "aria-hidden");
279
+ });
280
+
281
+ it("aria-checked reflects reactive update from unselected -> selected", () => {
282
+ cy.mount(<UDExListItem
283
+ id="tree-item-toggle"
284
+ accessible-role="treeitem"
285
+ mode={UDExListMode.MultiSelect}
286
+ label="JavaScript">
287
+ </UDExListItem>);
288
+
289
+ cy.get("#tree-item-toggle").shadow()
290
+ .find("[part=item]").should("have.attr", "aria-checked", "false");
291
+
292
+ cy.get("#tree-item-toggle").then($el => {
293
+ ($el[0] as UDExListItem).selected = true;
294
+ });
295
+
296
+ cy.get("#tree-item-toggle").shadow()
297
+ .find("[part=item]").should("have.attr", "aria-checked", "true");
298
+ });
299
+
300
+ it("indeterminate takes precedence over selected", () => {
301
+ cy.mount(<UDExListItem
302
+ id="tree-item-precedence"
303
+ accessible-role="treeitem"
304
+ mode={UDExListMode.MultiSelect}
305
+ selected
306
+ indeterminate
307
+ label="JavaScript">
308
+ </UDExListItem>);
309
+
310
+ cy.get("#tree-item-precedence").shadow()
311
+ .find("[part=item]").should("have.attr", "aria-checked", "mixed");
312
+ });
313
+
314
+ // Added: aria-expanded is emitted on the shadow [part="item"] div (the same
315
+ // element that carries role/aria-checked). Previously it was only on the host,
316
+ // which has role="none" and is therefore invisible to AT.
317
+ it("[part=item] has aria-expanded=\"true\" when collapsible and expanded (not collapsed)", () => {
318
+ cy.mount(<UDExListItem
319
+ id="tree-item-expanded"
320
+ accessible-role="treeitem"
321
+ mode={UDExListMode.MultiSelect}
322
+ collapsible
323
+ label="Programming Languages">
324
+ </UDExListItem>);
325
+
326
+ cy.get("#tree-item-expanded").shadow()
327
+ .find("[part=item]").should("have.attr", "aria-expanded", "true");
328
+ });
329
+
330
+ it("[part=item] has aria-expanded=\"false\" when collapsible and collapsed", () => {
331
+ cy.mount(<UDExListItem
332
+ id="tree-item-collapsed"
333
+ accessible-role="treeitem"
334
+ mode={UDExListMode.MultiSelect}
335
+ collapsible
336
+ collapsed
337
+ label="Programming Languages">
338
+ </UDExListItem>);
339
+
340
+ cy.get("#tree-item-collapsed").shadow()
341
+ .find("[part=item]").should("have.attr", "aria-expanded", "false");
342
+ });
343
+
344
+ it("[part=item] has NO aria-expanded when the item is not collapsible", () => {
345
+ cy.mount(<UDExListItem
346
+ id="tree-item-leaf"
347
+ accessible-role="treeitem"
348
+ mode={UDExListMode.MultiSelect}
349
+ label="JavaScript">
350
+ </UDExListItem>);
351
+
352
+ cy.get("#tree-item-leaf").shadow()
353
+ .find("[part=item]").should("not.have.attr", "aria-expanded");
354
+ });
355
+
356
+ // Regression: ariaExpandedValue getter guards on `collapsible`. A non-collapsible
357
+ // treeitem must not surface aria-expanded even though `expanded` (a derived getter,
358
+ // `!collapsed`) is truthy by default.
359
+ it("non-collapsible treeitem: aria-expanded absent (getter guards on collapsible)", () => {
360
+ cy.mount(<UDExListItem
361
+ id="tree-item-leaf-guard"
362
+ accessible-role="treeitem"
363
+ mode={UDExListMode.MultiSelect}
364
+ label="JavaScript">
365
+ </UDExListItem>);
366
+
367
+ // sanity: the derived `expanded` getter is truthy on a fresh item (collapsed defaults false)
368
+ cy.get("#tree-item-leaf-guard").should($el => {
369
+ expect(($el[0] as UDExListItem).expanded).to.equal(true);
370
+ });
371
+
372
+ // ...but the shadow attr is still absent because collapsible is false
373
+ cy.get("#tree-item-leaf-guard").shadow()
374
+ .find("[part=item]").should("not.have.attr", "aria-expanded");
375
+ });
376
+
377
+ // Added: SingleSelect treeitem exposes aria-selected (not aria-checked).
378
+ // The tree-pattern getters split by mode:
379
+ // - MultiSelect treeitem -> aria-checked (true|false|mixed)
380
+ // - SingleSelect treeitem -> aria-selected (true|false)
381
+ // Both getters return undefined for the wrong mode, so the attribute is absent.
382
+ describe("SingleSelect treeitem exposes aria-selected (not aria-checked)", () => {
383
+ it("aria-selected=\"false\" when unselected", () => {
384
+ cy.mount(<UDExListItem
385
+ id="single-tree-item"
386
+ accessible-role="treeitem"
387
+ mode={UDExListMode.SingleSelect}
388
+ label="JavaScript">
389
+ </UDExListItem>);
390
+
391
+ cy.get("#single-tree-item").shadow()
392
+ .find("[part=item]")
393
+ .should("have.attr", "role", "treeitem")
394
+ .and("have.attr", "aria-selected", "false");
395
+ });
396
+
397
+ it("aria-selected=\"true\" when selected", () => {
398
+ cy.mount(<UDExListItem
399
+ id="single-tree-item-selected"
400
+ accessible-role="treeitem"
401
+ mode={UDExListMode.SingleSelect}
402
+ selected
403
+ label="JavaScript">
404
+ </UDExListItem>);
405
+
406
+ cy.get("#single-tree-item-selected").shadow()
407
+ .find("[part=item]").should("have.attr", "aria-selected", "true");
408
+ });
409
+
410
+ it("no aria-checked in SingleSelect tree mode (ariaCheckedValue undefined)", () => {
411
+ cy.mount(<UDExListItem
412
+ id="single-tree-item-no-checked"
413
+ accessible-role="treeitem"
414
+ mode={UDExListMode.SingleSelect}
415
+ selected
416
+ label="JavaScript">
417
+ </UDExListItem>);
418
+
419
+ cy.get("#single-tree-item-no-checked").shadow()
420
+ .find("[part=item]").should("not.have.attr", "aria-checked");
421
+ });
422
+
423
+ it("aria-selected reflects reactive update from unselected -> selected", () => {
424
+ cy.mount(<UDExListItem
425
+ id="single-tree-item-toggle"
426
+ accessible-role="treeitem"
427
+ mode={UDExListMode.SingleSelect}
428
+ label="JavaScript">
429
+ </UDExListItem>);
430
+
431
+ cy.get("#single-tree-item-toggle").shadow()
432
+ .find("[part=item]").should("have.attr", "aria-selected", "false");
433
+
434
+ cy.get("#single-tree-item-toggle").then($el => {
435
+ ($el[0] as UDExListItem).selected = true;
436
+ });
437
+
438
+ cy.get("#single-tree-item-toggle").shadow()
439
+ .find("[part=item]").should("have.attr", "aria-selected", "true");
440
+ });
441
+
442
+ // Regression: aria-selected must be absent when the item is NOT a treeitem
443
+ // (default listbox/option role). ariaSelectedValue guards on roleItem === "treeitem".
444
+ it("no aria-selected when accessible-role is not \"treeitem\" (SingleSelect default option)", () => {
445
+ cy.mount(<UDExListItem
446
+ id="single-opt-item"
447
+ mode={UDExListMode.SingleSelect}
448
+ selected
449
+ label="JavaScript">
450
+ </UDExListItem>);
451
+
452
+ cy.get("#single-opt-item").shadow()
453
+ .find("[part=item]").should("not.have.attr", "aria-selected");
454
+ });
455
+ });
456
+
457
+ // Added: cross-mode isolation — the tree-pattern getters are strictly split by
458
+ // mode, so a MultiSelect treeitem must NOT surface aria-selected, mirroring the
459
+ // SingleSelect-must-not-surface-aria-checked invariant above.
460
+ it("MultiSelect treeitem: aria-selected absent (ariaSelectedValue guards on !modeMultiSelect)", () => {
461
+ cy.mount(<UDExListItem
462
+ id="multi-tree-item-no-selected"
463
+ accessible-role="treeitem"
464
+ mode={UDExListMode.MultiSelect}
465
+ selected
466
+ label="JavaScript">
467
+ </UDExListItem>);
468
+
469
+ cy.get("#multi-tree-item-no-selected").shadow()
470
+ .find("[part=item]").should("not.have.attr", "aria-selected");
471
+ });
472
+ });
473
+
474
+ // Added: regression - the tree-pattern getters must be silent when the item is
475
+ // rendered with the default role (i.e. everywhere the tree pattern is NOT active).
476
+ describe("Non-tree role (default listbox/option) regression", () => {
477
+ it("no aria-checked attribute when accessible-role is not \"treeitem\"", () => {
478
+ cy.mount(<UDExListItem
479
+ id="opt-item"
480
+ mode={UDExListMode.MultiSelect}
481
+ label="JavaScript">
482
+ </UDExListItem>);
483
+
484
+ cy.get("#opt-item").shadow()
485
+ .find("[part=item]").should("not.have.attr", "aria-checked");
486
+ });
487
+
488
+ it("inner udex-checkbox is NOT aria-hidden when not in tree mode", () => {
489
+ cy.mount(<UDExListItem
490
+ id="opt-item-checkbox"
491
+ mode={UDExListMode.MultiSelect}
492
+ label="JavaScript">
493
+ </UDExListItem>);
494
+
495
+ cy.get("#opt-item-checkbox").shadow()
496
+ .find("udex-checkbox").should("not.have.attr", "aria-hidden");
497
+ });
498
+
499
+ it("explicit non-tree accessible-role (e.g. \"option\") still suppresses aria-checked", () => {
500
+ cy.mount(<UDExListItem
501
+ id="opt-item-explicit"
502
+ accessible-role="option"
503
+ mode={UDExListMode.MultiSelect}
504
+ selected
505
+ label="JavaScript">
506
+ </UDExListItem>);
507
+
508
+ cy.get("#opt-item-explicit").shadow()
509
+ .find("[part=item]")
510
+ .should("have.attr", "role", "option")
511
+ .and("not.have.attr", "aria-checked");
512
+ });
513
+ });
224
514
  });
@@ -170,6 +170,38 @@ describe("MediaPlayer — custom plugin support", () => {
170
170
  });
171
171
  });
172
172
 
173
+ describe("busy indicator injection", () => {
174
+ it("injects an active udex-busy-indicator inside the loading spinner after init", () => {
175
+ cy.mount(basicPlayer);
176
+
177
+ // The busy indicator is injected in _injectBusyIndicator(), called after
178
+ // the videojs player is created. Waiting for the player to exist ensures
179
+ // onAfterRendering has run and the spinner is present in the shadow DOM.
180
+ cy.get("#test-media-player").should(() => {
181
+ expect(
182
+ (Cypress.$("#test-media-player")[0] as any).player,
183
+ "player must be created before busy indicator is injected",
184
+ ).to.exist;
185
+ });
186
+
187
+ cy.get("#test-media-player")
188
+ .shadow()
189
+ .find(".vjs-loading-spinner")
190
+ .find("[udex-busy-indicator]")
191
+ .should("exist")
192
+ .and("have.attr", "active");
193
+
194
+ // Updated: _injectBusyIndicator() now also sets the surface-container
195
+ // attribute. A separate query is used because chaining a second
196
+ // have.attr changes the subject to the first attribute's value.
197
+ cy.get("#test-media-player")
198
+ .shadow()
199
+ .find(".vjs-loading-spinner")
200
+ .find("[udex-busy-indicator]")
201
+ .should("have.attr", "surface-container");
202
+ });
203
+ });
204
+
173
205
  describe("backward compatibility", () => {
174
206
  it("renders the video-js element without any custom plugins", () => {
175
207
  cy.mount(basicPlayer);