@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
package/src/Carousel.ts CHANGED
@@ -7,6 +7,8 @@ import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
7
7
  import jsxRenderer from "@ui5/webcomponents-base/renderer/JsxRenderer.js";
8
8
  import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
9
9
  import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
10
+ import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
11
+ import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
10
12
  import {
11
13
  isUp, isDown, isRight, isLeft,
12
14
  } from "@ui5/webcomponents-base/dist/Keys.js";
@@ -194,6 +196,15 @@ class UDExCarousel extends UI5Element {
194
196
  @property({ type: String })
195
197
  accessibleName: string = "";
196
198
 
199
+ /**
200
+ * Tracks whether the inner carousel has multiple pages (updated after render).
201
+ *
202
+ * @default false
203
+ * @private
204
+ */
205
+ @property({ type: Boolean, noAttribute: true })
206
+ _hasManyPages = false;
207
+
197
208
  @i18n("@sapudex/web-components")
198
209
  static i18nBundle: I18nBundle;
199
210
 
@@ -201,88 +212,66 @@ class UDExCarousel extends UI5Element {
201
212
  _autoplayLastToggle = Date.now();
202
213
  _autoplayMinTimeBetweenToggle = 250;
203
214
  _autoplayManualStop = false;
204
- _autoplayLoop = () => {
205
- const items = this._carousel?.items;
206
- if (!items) { return; }
207
-
208
- const currentSlide = this._carousel._currentPageIndex;
209
- const itemsPerPage = this._carousel.effectiveItemsPerPage;
210
- const isLastSlide = currentSlide === (items.length - itemsPerPage);
211
-
212
- if (isLastSlide && !this.cyclic) {
213
- this._stopAutoplay();
214
- this._autoplayManualStop = true;
215
-
216
- return;
217
- }
218
-
219
- const nextSlide = isLastSlide ? 0 : (currentSlide + itemsPerPage);
220
-
221
- this._carousel?.navigateTo(nextSlide);
222
- this._calculateDisabledButtonsState();
223
- };
215
+ _resizeHandler: ResizeObserverCallback;
224
216
 
225
- get nextPageText(): string {
226
- return translate(UDExCarousel, CAROUSEL_NEXT_ARROW_TEXT);
217
+ constructor() {
218
+ super();
219
+ this._resizeHandler = this._onResize.bind(this);
227
220
  }
228
221
 
229
- get previousPageText(): string {
230
- return translate(UDExCarousel, CAROUSEL_PREVIOUS_ARROW_TEXT);
231
- }
222
+ onAfterRendering() {
223
+ redirectAttributes(this, "carousel");
232
224
 
233
- get carouselClass(): string {
234
- const classes = "udex-carousel";
225
+ this._calculateDisabledButtonsState();
235
226
 
236
- return `${classes} udex-carousel__navigation--${this.navigationPlacement}`;
227
+ if (this._autoplayInterval === null) {
228
+ this._startAutoplay();
229
+ }
237
230
  }
238
231
 
239
- get navigationWrapperClass(): string {
240
- let _class = "udex-carousel__navigation-wrapper";
232
+ onExitDOM(): void {
233
+ ResizeHandler.deregister(this, this._resizeHandler);
241
234
 
242
- if (this.autoplay) {
243
- _class += " udex-carousel__navigation-wrapper--autoplay";
235
+ if (this._autoplayInterval) {
236
+ clearInterval(this._autoplayInterval);
244
237
  }
238
+ }
245
239
 
246
- return _class;
240
+ onEnterDOM(): void {
241
+ ResizeHandler.register(this, this._resizeHandler);
247
242
  }
248
243
 
249
- get navigationButtonWrapperClass(): string {
250
- let _class = "udex-carousel__navigation-button-wrapper";
244
+ _onResize(): void {
245
+ this._hasManyPages = this._carousel?.hasManyPages ?? false;
246
+ }
251
247
 
252
- if (this._carousel?.isPageTypeDots) {
253
- _class += " udex-carousel__navigation-button-wrapper--dot-indicators";
248
+ _autoplayLoop = () => {
249
+ const items = this._carousel?.items;
250
+ if (!items) {
251
+ return;
254
252
  }
255
253
 
256
- return _class;
257
- }
258
-
259
- get buttonDesign(): UDExCarouselButtonDesign {
260
- return this.navigationPlacement === UDExCarouselNavigationPosition.top
261
- ? UDExCarouselButtonDesign.Secondary
262
- : UDExCarouselButtonDesign.Tertiary;
263
- }
254
+ const currentSlide = this._carousel._currentPageIndex;
255
+ const itemsPerPage = this._carousel.effectiveItemsPerPage;
256
+ const isLastSlide = currentSlide === (items.length - itemsPerPage);
264
257
 
265
- get showNavigationButtons(): { top: boolean; bottom: boolean } {
266
- return {
267
- top: this.navigationPlacement === UDExCarouselNavigationPosition.top,
268
- bottom: this.navigationPlacement === UDExCarouselNavigationPosition.bottom,
269
- };
270
- }
258
+ if (isLastSlide && !this.cyclic) {
259
+ this._stopAutoplay();
260
+ this._autoplayManualStop = true;
271
261
 
272
- get _hidePageIndicator(): boolean {
273
- return this.navigationPlacement === UDExCarouselNavigationPosition.top;
274
- }
262
+ return;
263
+ }
275
264
 
276
- get _carousel(): UI5Carousel | null {
277
- return this.shadowRoot!.querySelector<UI5Carousel>("[ui5-carousel]");
278
- }
265
+ const nextSlide = isLastSlide ? 0 : (currentSlide + itemsPerPage);
279
266
 
280
- get _ui5CarouselRoot(): HTMLElement | null | undefined {
281
- return this._carousel?.shadowRoot?.querySelector(".ui5-carousel-root");
282
- }
267
+ this._carousel?.navigateTo(nextSlide);
268
+ this._calculateDisabledButtonsState();
269
+ };
283
270
 
284
271
  _startAutoplay(): void {
285
- if (!this.autoplay) { return; }
272
+ if (!this.autoplay) {
273
+ return;
274
+ }
286
275
 
287
276
  if (!this._carousel?.items) {
288
277
  console.warn("[UDExCarousel] Cannot start autoplay on an empty carousel");
@@ -305,7 +294,9 @@ class UDExCarousel extends UI5Element {
305
294
  }
306
295
 
307
296
  _stopAutoplay(): void {
308
- if (!this._autoplayInterval) { return; }
297
+ if (!this._autoplayInterval) {
298
+ return;
299
+ }
309
300
 
310
301
  clearInterval(this._autoplayInterval);
311
302
  this._autoplayIsPaused = true;
@@ -326,22 +317,14 @@ class UDExCarousel extends UI5Element {
326
317
  });
327
318
  }
328
319
 
329
- get _isBottomNavigation(): boolean {
330
- return this.navigationPlacement === UDExCarouselNavigationPosition.bottom;
331
- }
332
-
333
- get autoplayToggleButtonTooltipText(): string {
334
- return this._autoplayIsPaused
335
- ? translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_START)
336
- : translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_STOP);
337
- }
338
-
339
320
  _onFocusIn(): void {
340
321
  this._stopAutoplay();
341
322
  }
342
323
 
343
324
  _onFocusOut(): void {
344
- if (!this._autoplayManualStop) { this._startAutoplay(); }
325
+ if (!this._autoplayManualStop) {
326
+ this._startAutoplay();
327
+ }
345
328
  }
346
329
 
347
330
  _onMouseEnter(): void {
@@ -349,7 +332,9 @@ class UDExCarousel extends UI5Element {
349
332
  }
350
333
 
351
334
  _onMouseLeave(): void {
352
- if (!this._autoplayManualStop) { this._startAutoplay(); }
335
+ if (!this._autoplayManualStop) {
336
+ this._startAutoplay();
337
+ }
353
338
  }
354
339
 
355
340
  _calculateDisabledButtonsState() {
@@ -387,16 +372,69 @@ class UDExCarousel extends UI5Element {
387
372
  this.fireDecoratorEvent("navigate", e.detail);
388
373
  }
389
374
 
390
- onAfterRendering() {
391
- redirectAttributes(this, "carousel");
375
+ get nextPageText(): string {
376
+ return translate(UDExCarousel, CAROUSEL_NEXT_ARROW_TEXT);
377
+ }
392
378
 
393
- this._calculateDisabledButtonsState();
379
+ get previousPageText(): string {
380
+ return translate(UDExCarousel, CAROUSEL_PREVIOUS_ARROW_TEXT);
381
+ }
382
+
383
+ get carouselClass(): string {
384
+ const classes = "udex-carousel";
394
385
 
395
- if (this._autoplayInterval === null) { this._startAutoplay(); }
386
+ return `${classes} udex-carousel__navigation--${this.navigationPlacement}`;
396
387
  }
397
388
 
398
- onExitDOM(): void {
399
- if (this._autoplayInterval) { clearInterval(this._autoplayInterval); }
389
+ get navigationWrapperClass(): string {
390
+ let _class = "udex-carousel__navigation-wrapper";
391
+
392
+ if (this.autoplay) {
393
+ _class += " udex-carousel__navigation-wrapper--autoplay";
394
+ }
395
+
396
+ return _class;
397
+ }
398
+
399
+ get navigationButtonWrapperClass(): string {
400
+ let _class = "udex-carousel__navigation-button-wrapper";
401
+
402
+ if (this._carousel?.isPageTypeDots) {
403
+ _class += " udex-carousel__navigation-button-wrapper--dot-indicators";
404
+ }
405
+
406
+ return _class;
407
+ }
408
+
409
+ get buttonDesign(): UDExCarouselButtonDesign {
410
+ return this.navigationPlacement === UDExCarouselNavigationPosition.top
411
+ ? UDExCarouselButtonDesign.Secondary
412
+ : UDExCarouselButtonDesign.Tertiary;
413
+ }
414
+
415
+ get showNavigationButtons(): { top: boolean; bottom: boolean } {
416
+ return {
417
+ top: this.navigationPlacement === UDExCarouselNavigationPosition.top && this._hasManyPages,
418
+ bottom: this.navigationPlacement === UDExCarouselNavigationPosition.bottom && this._hasManyPages,
419
+ };
420
+ }
421
+
422
+ get _hidePageIndicator(): boolean {
423
+ return this.navigationPlacement === UDExCarouselNavigationPosition.top;
424
+ }
425
+
426
+ get _carousel(): UI5Carousel | null {
427
+ return this.shadowRoot!.querySelector<UI5Carousel>("[ui5-carousel]");
428
+ }
429
+
430
+ get _isBottomNavigation(): boolean {
431
+ return this.navigationPlacement === UDExCarouselNavigationPosition.bottom;
432
+ }
433
+
434
+ get autoplayToggleButtonTooltipText(): string {
435
+ return this._autoplayIsPaused
436
+ ? translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_START)
437
+ : translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_STOP);
400
438
  }
401
439
  }
402
440
 
package/src/FilterMenu.ts CHANGED
@@ -296,19 +296,19 @@ class UDExFilterMenu extends UDExSearchableListBase {
296
296
  accessibleNameRef!: string;
297
297
  /**
298
298
  * Allows setting a custom role.
299
- * @default "None"
299
+ * @default "Dialog"
300
300
  * @public
301
301
  */
302
302
  @property()
303
- accessibleRole: `${PopupAccessibleRole}` = PopupAccessibleRole.None;
303
+ accessibleRole: `${PopupAccessibleRole}` = PopupAccessibleRole.Dialog;
304
304
 
305
305
  /**
306
306
  * Allows setting a custom role for dropdown trigger.
307
- * @default "combobox"
307
+ * @default "button"
308
308
  * @public
309
309
  */
310
310
  @property({ type: String })
311
- triggerAccessibleRole: string = "combobox";
311
+ triggerAccessibleRole: string = "button";
312
312
 
313
313
  /**
314
314
  * Determines whether the filter should be applied.
@@ -591,6 +591,27 @@ class UDExFilterMenu extends UDExSearchableListBase {
591
591
  return translate(UDExFilterMenu, SELECT_OPTIONS);
592
592
  }
593
593
 
594
+ /**
595
+ * FilterMenu uses the ARIA tree pattern for genuinely grouped items when:
596
+ * - MultiSelect mode (headers/items expose aria-checked), or
597
+ * - SingleSelect mode with at least one collapsible group
598
+ * (headers expose aria-expanded, items expose aria-selected).
599
+ *
600
+ * A "Select All" header is NOT a hierarchical group — it is a bulk-select
601
+ * affordance for a flat list — so it must not trigger the tree pattern.
602
+ */
603
+ override get isTreeStructure(): boolean {
604
+ const groups = this._filteredItems.filter(e => e.isGroup && !(e.item as UDExListGroupHeaderBase).isSelectAllHeader);
605
+ if (!groups.length) {
606
+ return false;
607
+ }
608
+ if (this.modeMultiSelect) {
609
+ return true;
610
+ }
611
+ return this.modeSingleSelect
612
+ && groups.some(e => (e.item as UDExListGroupHeaderBase).collapsible);
613
+ }
614
+
594
615
  get triggerButtonAccessibleName(): string {
595
616
  return this.accessibleName || translate(UDExFilterMenu, SELECT_OPTIONS);
596
617
  }
@@ -19,7 +19,7 @@ export default function FilterMenuTemplate(this: UDExFilterMenu) {
19
19
  aria-label={this.triggerButtonAccessibleName}
20
20
  aria-labelledby={this.accessibleNameRef}
21
21
  aria-expanded={this._open}
22
- aria-haspopup="true"
22
+ aria-haspopup="dialog"
23
23
  role={this.triggerAccessibleRole as AriaRole}
24
24
  aria-controls={`${this._id}_udex-filter-menu-dropdown`}
25
25
  disabled={this.disabled}
package/src/HeroBanner.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
2
2
  import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
3
3
  import property from "@ui5/webcomponents-base/dist/decorators/property.js";
4
- import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
4
+ import type { Slot } from "@ui5/webcomponents-base/dist/UI5Element.js";
5
+ import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
5
6
  import jsxRenderer from "@ui5/webcomponents-base/renderer/JsxRenderer.js";
6
7
  import { isSpace } from "@ui5/webcomponents-base/dist/Keys.js";
7
8
  import Icon from "@ui5/webcomponents/dist/Icon.js";
8
9
  import HeroBannerTemplate from "./HeroBannerTemplate.js";
10
+ import type UDExBreadcrumbs from "./Breadcrumbs.js";
9
11
 
10
12
  // Styles
11
13
  import HeroBannerCss from "./generated/themes/HeroBanner.css.js";
@@ -105,8 +107,8 @@ class UDExHeroBanner extends UI5Element {
105
107
  * @slot
106
108
  * @public
107
109
  */
108
- @slot()
109
- content?: Array<HTMLElement>;
110
+ @slot({ type: HTMLElement })
111
+ content!: Slot<HTMLElement>;
110
112
 
111
113
  /**
112
114
  * Slot for image or video content.
@@ -114,8 +116,8 @@ class UDExHeroBanner extends UI5Element {
114
116
  * @slot
115
117
  * @public
116
118
  */
117
- @slot()
118
- mediaContent?: Array<HTMLElement>;
119
+ @slot({ type: HTMLElement })
120
+ mediaContent!: Slot<HTMLElement>;
119
121
 
120
122
  /**
121
123
  * Slot for any additional content.
@@ -123,8 +125,8 @@ class UDExHeroBanner extends UI5Element {
123
125
  * @slot
124
126
  * @public
125
127
  */
126
- @slot()
127
- additionalContent?: Array<HTMLElement>;
128
+ @slot({ type: HTMLElement })
129
+ additionalContent!: Slot<HTMLElement>;
128
130
 
129
131
  /**
130
132
  * Slot background picture override.
@@ -132,15 +134,63 @@ class UDExHeroBanner extends UI5Element {
132
134
  * @slot
133
135
  * @public
134
136
  */
135
- @slot()
136
- backgroundPicture?: Array<HTMLElement>;
137
+ @slot({ type: HTMLElement })
138
+ backgroundPicture!: Slot<HTMLElement>;
139
+
140
+ /**
141
+ * Slot for breadcrumbs.
142
+ *
143
+ * @slot breadcrumbs
144
+ * @public
145
+ */
146
+ @slot({ type: HTMLElement })
147
+ breadcrumbs!: Slot<UDExBreadcrumbs>;
148
+
149
+ onEnterDOM() {
150
+ if (this.backgroundVideo) {
151
+ if (this.backgroundColor) {
152
+ this.style.setProperty("--udexHeroBannerBackgroundVideoColor", `${this.wrapperBackgroundColor}`);
153
+ }
154
+ this._video?.addEventListener("play", () => {
155
+ this._calculateMediaButtonState();
156
+ });
157
+ }
158
+ }
159
+
160
+ _toggleMediaButton(): void {
161
+ if (this._video?.paused) {
162
+ this._video?.play();
163
+ } else {
164
+ this._video?.pause();
165
+ }
166
+ this._calculateMediaButtonState();
167
+ }
168
+
169
+ _calculateMediaButtonState() {
170
+ this._isVideoRunning = this._video ? !this._video.paused : false;
171
+ }
172
+
173
+ _isHTMLDivElement = (element: HTMLElement): element is HTMLDivElement => {
174
+ return element.tagName === "DIV";
175
+ };
176
+
177
+ _handleKeydown(event: KeyboardEvent): void {
178
+ const target = <HTMLElement>event.target;
179
+ if (this._isHTMLDivElement(target) && isSpace(event)) {
180
+ event.preventDefault();
181
+ event.stopPropagation();
182
+ this._toggleMediaButton();
183
+ }
184
+ }
137
185
 
138
186
  get hasBackgroundImage(): boolean {
139
187
  return !!this.backgroundImage;
140
188
  }
189
+
141
190
  get hasBackgroundVideo(): boolean {
142
191
  return !!this.backgroundVideo;
143
192
  }
193
+
144
194
  get wrapperBackgroundColor(): string {
145
195
  return this.backgroundColor ? this.backgroundColor : "transparent";
146
196
  }
@@ -179,44 +229,8 @@ class UDExHeroBanner extends UI5Element {
179
229
  get _video() {
180
230
  return this.shadowRoot?.querySelector<HTMLVideoElement>("video");
181
231
  }
182
-
183
- _toggleMediaButton(): void {
184
- if (this._video?.paused) {
185
- this._video?.play();
186
- } else {
187
- this._video?.pause();
188
- }
189
- this._calculateMediaButtonState();
190
- }
191
-
192
- _calculateMediaButtonState() {
193
- this._isVideoRunning = this._video ? !this._video.paused : false;
194
- }
195
-
196
- _isHTMLDivElement = (element: HTMLElement): element is HTMLDivElement => {
197
- return element.tagName === "DIV";
198
- };
199
-
200
- _handleKeydown(event: KeyboardEvent): void {
201
- const target = <HTMLElement>event.target;
202
- if (this._isHTMLDivElement(target) && isSpace(event)) {
203
- event.preventDefault();
204
- event.stopPropagation();
205
- this._toggleMediaButton();
206
- }
207
- }
208
-
209
- onEnterDOM() {
210
- if (this.backgroundVideo) {
211
- if (this.backgroundColor) {
212
- this.style.setProperty("--udexHeroBannerBackgroundVideoColor", `${this.wrapperBackgroundColor}`);
213
- }
214
- this._video?.addEventListener("play", () => {
215
- this._calculateMediaButtonState();
216
- });
217
- }
218
- }
219
232
  }
233
+
220
234
  UDExHeroBanner.define();
221
235
 
222
236
  export default UDExHeroBanner;
@@ -43,25 +43,32 @@ export default function HeroBannerTemplate(this: HeroBanner) {
43
43
  alt={this.backgroundImageLabel}/>
44
44
  </picture>
45
45
  )}
46
- <div className={`${rootClass}__content-container`}>
47
- <slot
48
- id={`${rootClass}__content`}
49
- name="content"
50
- part="content">
51
- </slot>
46
+ <>
52
47
  <slot
53
- id={`${rootClass}__additionalContent`}
54
- name="additionalContent"
55
- part="additionalContent">
48
+ id={`${rootClass}__breadcrumbs`}
49
+ name="breadcrumbs"
50
+ part="breadcrumbs">
56
51
  </slot>
57
- </div>
58
- <button
59
- className={`${rootClass}__media-button`}
60
- aria-label="Play / Pause button"
61
- role="button"
62
- onClick={this._toggleMediaButton}>
63
- <UI5Icon name={btnIcon}></UI5Icon>
64
- </button>
52
+ <div className={`${rootClass}__content-container`}>
53
+ <slot
54
+ id={`${rootClass}__content`}
55
+ name="content"
56
+ part="content">
57
+ </slot>
58
+ <slot
59
+ id={`${rootClass}__additionalContent`}
60
+ name="additionalContent"
61
+ part="additionalContent">
62
+ </slot>
63
+ </div>
64
+ <button
65
+ className={`${rootClass}__media-button`}
66
+ aria-label="Play / Pause button"
67
+ role="button"
68
+ onClick={this._toggleMediaButton}>
69
+ <UI5Icon name={btnIcon}></UI5Icon>
70
+ </button>
71
+ </>
65
72
  </div>
66
73
  ) : (
67
74
  <div className={`${rootClass}__container`}>
@@ -79,11 +86,17 @@ export default function HeroBannerTemplate(this: HeroBanner) {
79
86
  </picture>
80
87
  ) : null}
81
88
  <div className={`${rootClass}__content-container`}>
82
- <slot
83
- id={`${rootClass}__content`}
84
- name="content"
85
- part="content">
86
- </slot>
89
+ <div id={`${rootClass}__content`}>
90
+ <slot
91
+ id={`${rootClass}__breadcrumbs`}
92
+ name="breadcrumbs"
93
+ part="breadcrumbs">
94
+ </slot>
95
+ <slot
96
+ name="content"
97
+ part="content">
98
+ </slot>
99
+ </div>
87
100
  {this.hasAdditionalContent && (
88
101
  <slot
89
102
  id={`${rootClass}__additionalContent`}
package/src/List.ts CHANGED
@@ -212,7 +212,7 @@ class UDExList extends UI5Element {
212
212
 
213
213
  handleArrowRight(e: KeyboardEvent): void {
214
214
  const target = e.target as UDExListItemBase;
215
- if ((target.tagName === "UDEX-LIST-GROUP-HEADER" || target.tagName === "UDEX-LIST-ITEM") && target.hasAttribute("collapsible")) {
215
+ if (target.isGroup && target.hasAttribute("collapsible") && target.collapsed) {
216
216
  target.collapsed = false;
217
217
  this.setCollapsedItems(false, target);
218
218
  }
@@ -220,7 +220,7 @@ class UDExList extends UI5Element {
220
220
 
221
221
  handleArrowLeft(e: KeyboardEvent): void {
222
222
  const target = e.target as UDExListItemBase;
223
- if ((target.tagName === "UDEX-LIST-GROUP-HEADER" || target.tagName === "UDEX-LIST-ITEM") && target.hasAttribute("collapsible")) {
223
+ if (target.isGroup && target.hasAttribute("collapsible") && !target.collapsed) {
224
224
  target.collapsed = true;
225
225
  this.setCollapsedItems(true, target);
226
226
  }
@@ -241,7 +241,7 @@ class UDExList extends UI5Element {
241
241
  if (this._scrollIntoView) {
242
242
  this.scrollToItem(items[lastItemIndex]);
243
243
  }
244
- items[lastItemIndex].focus();
244
+ this.focusItem(items[lastItemIndex]);
245
245
  return;
246
246
  }
247
247
 
@@ -251,7 +251,7 @@ class UDExList extends UI5Element {
251
251
  if (this._scrollIntoView) {
252
252
  this.scrollToItem(items[previousSelectIndex - 1]);
253
253
  }
254
- items[previousSelectIndex - 1].focus();
254
+ this.focusItem(items[previousSelectIndex - 1]);
255
255
  return;
256
256
  }
257
257
 
@@ -276,7 +276,7 @@ class UDExList extends UI5Element {
276
276
  if (this._scrollIntoView) {
277
277
  this.scrollToItem(items[previousSelectIndex]);
278
278
  }
279
- items[previousSelectIndex].focus();
279
+ this.focusItem(items[previousSelectIndex]);
280
280
  }, 0);
281
281
  }
282
282
 
@@ -294,7 +294,7 @@ class UDExList extends UI5Element {
294
294
  if (this._scrollIntoView) {
295
295
  this.scrollToItem(items[nextSelectIndex + 1]);
296
296
  }
297
- items[nextSelectIndex + 1].focus();
297
+ this.focusItem(items[nextSelectIndex + 1]);
298
298
  return;
299
299
  }
300
300
 
@@ -319,7 +319,7 @@ class UDExList extends UI5Element {
319
319
  if (this._scrollIntoView) {
320
320
  this.scrollToItem(items[nextSelectIndex]);
321
321
  }
322
- items[nextSelectIndex].focus();
322
+ this.focusItem(items[nextSelectIndex]);
323
323
  }, 0);
324
324
  }
325
325
 
@@ -334,7 +334,7 @@ class UDExList extends UI5Element {
334
334
  if (nonInteractiveItem) {
335
335
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
336
336
  this.setTabIndexByIndex(items, 1);
337
- items[1].focus();
337
+ this.focusItem(items[1]);
338
338
  return;
339
339
  }
340
340
 
@@ -352,7 +352,7 @@ class UDExList extends UI5Element {
352
352
 
353
353
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
354
354
  this.setTabIndexByIndex(items, 0);
355
- items[0].focus();
355
+ this.focusItem(items[0]);
356
356
  }
357
357
 
358
358
  handleEndKey(e: KeyboardEvent): void {
@@ -367,7 +367,7 @@ class UDExList extends UI5Element {
367
367
 
368
368
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
369
369
  this.setTabIndexByIndex(items, items.length - 1);
370
- items[items.length - 1].focus();
370
+ this.focusItem(items[items.length - 1]);
371
371
  }
372
372
 
373
373
  handleLetterKey(e: KeyboardEvent): void {
@@ -417,7 +417,19 @@ class UDExList extends UI5Element {
417
417
 
418
418
  focusHeader(item: UDExListItemBase): void {
419
419
  const header = item.shadowRoot?.querySelector(".udex-list__group-header-wrap") as HTMLElement;
420
- header.focus();
420
+ if (!header) { return; }
421
+ // If the wrap element is a custom element (e.g. udex-list-item used for MultiSelect
422
+ // collapsible headers), its host has no tabindex so .focus() is a no-op. Delegate
423
+ // to the inner [part=item] div which carries the actual tabindex.
424
+ const focusTarget = header.shadowRoot?.querySelector<HTMLElement>("[part=item]") ?? header;
425
+ focusTarget.focus();
426
+ }
427
+
428
+ focusItem(item: UDExListItemBase): void {
429
+ // UI5Element.focus() is async. Synchronously drill to the focusable shadow element
430
+ // so focus is set immediately (required for JAWS and reliable Cypress assertions).
431
+ const focusTarget = item.shadowRoot?.querySelector<HTMLElement>("[part=item]") ?? item;
432
+ focusTarget.focus();
421
433
  }
422
434
 
423
435
  scrollToItem(item: UDExListItemBase): void {