@telefonica/mistica 17.3.0 → 17.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/css/blau.css +24 -16
  2. package/css/esimflag.css +35 -18
  3. package/css/mistica.css +1 -1
  4. package/css/movistar.css +29 -18
  5. package/css/o2.css +26 -18
  6. package/css/telefonica.css +38 -18
  7. package/css/vivo-evolution.css +66 -20
  8. package/css/vivo.css +26 -18
  9. package/dist/accordion.css-mistica.js +10 -10
  10. package/dist/align.css-mistica.js +1 -1
  11. package/dist/autocomplete.css-mistica.js +4 -4
  12. package/dist/avatar.css-mistica.js +1 -1
  13. package/dist/badge.css-mistica.js +1 -1
  14. package/dist/box.css-mistica.js +13 -13
  15. package/dist/boxed.css-mistica.js +24 -24
  16. package/dist/boxed.d.ts +9 -0
  17. package/dist/boxed.js +18 -15
  18. package/dist/button-group.css-mistica.js +1 -1
  19. package/dist/button-layout.css-mistica.js +19 -19
  20. package/dist/button.css-mistica.js +50 -50
  21. package/dist/callout.css-mistica.js +13 -13
  22. package/dist/card-internal.css-mistica.js +22 -22
  23. package/dist/carousel.css-mistica.js +30 -30
  24. package/dist/checkbox.css-mistica.js +15 -15
  25. package/dist/chip.css-mistica.js +18 -18
  26. package/dist/circle.css-mistica.js +2 -2
  27. package/dist/community/advanced-data-card.css-mistica.js +19 -19
  28. package/dist/community/ai-card.css-mistica.js +12 -12
  29. package/dist/community/blocks.css-mistica.js +1 -1
  30. package/dist/community/cyber/example-component.css-mistica.js +1 -1
  31. package/dist/community/example-component.css-mistica.js +2 -2
  32. package/dist/community/skins/cyber-skin.js +19 -17
  33. package/dist/counter.css-mistica.js +6 -6
  34. package/dist/cover-hero.css-mistica.js +7 -7
  35. package/dist/credit-card-number-field.css-mistica.js +3 -3
  36. package/dist/date-field.css-mistica.js +1 -1
  37. package/dist/date-time-picker.css-mistica.js +1 -1
  38. package/dist/dialog.css-mistica.js +10 -10
  39. package/dist/divider.css-mistica.js +5 -5
  40. package/dist/double-field.css-mistica.js +2 -2
  41. package/dist/drawer.css-mistica.js +4 -4
  42. package/dist/empty-state-card.css-mistica.js +1 -1
  43. package/dist/empty-state.css-mistica.js +8 -8
  44. package/dist/fade-in.css-mistica.js +1 -1
  45. package/dist/feedback.css-mistica.js +5 -5
  46. package/dist/file-upload.css-mistica.js +10 -10
  47. package/dist/fixed-footer-layout.css-mistica.js +6 -6
  48. package/dist/form.css-mistica.js +1 -1
  49. package/dist/generated/mistica-icons/icon-panel-collapse-regular.js +165 -0
  50. package/dist/generated/mistica-icons/icon-panel-expand-regular.js +159 -0
  51. package/dist/grid-layout.css-mistica.js +3 -3
  52. package/dist/grid.css-mistica.js +120 -120
  53. package/dist/header.css-mistica.js +1 -1
  54. package/dist/hero.css-mistica.js +4 -4
  55. package/dist/horizontal-scroll.css-mistica.js +1 -1
  56. package/dist/icon-button.css-mistica.js +53 -53
  57. package/dist/icons/icon-chevron.css-mistica.js +2 -2
  58. package/dist/icons/icon-error.css-mistica.js +1 -1
  59. package/dist/image.css-mistica.js +8 -8
  60. package/dist/index.d.ts +5 -0
  61. package/dist/index.js +10 -1
  62. package/dist/inline.css-mistica.js +11 -11
  63. package/dist/list.css-mistica.js +40 -28
  64. package/dist/list.css.d.ts +2 -0
  65. package/dist/list.d.ts +3 -1
  66. package/dist/list.js +176 -165
  67. package/dist/loading-bar.css-mistica.js +1 -1
  68. package/dist/loading-screen.css-mistica.js +12 -12
  69. package/dist/logo.css-mistica.js +5 -5
  70. package/dist/menu.css-mistica.js +13 -13
  71. package/dist/mobile-navigation-menu.d.ts +26 -0
  72. package/dist/mobile-navigation-menu.js +145 -0
  73. package/dist/mosaic.css-mistica.js +1 -1
  74. package/dist/navigation-bar.css-mistica.js +31 -97
  75. package/dist/navigation-bar.css.d.ts +0 -24
  76. package/dist/navigation-bar.d.ts +3 -38
  77. package/dist/navigation-bar.js +399 -586
  78. package/dist/navigation-breadcrumbs.css-mistica.js +1 -1
  79. package/dist/navigation-shared.css-mistica.js +79 -0
  80. package/dist/navigation-shared.css.d.ts +26 -0
  81. package/dist/navigation-shared.d.ts +66 -0
  82. package/dist/navigation-shared.js +264 -0
  83. package/dist/package-version.js +1 -1
  84. package/dist/pagination.css-mistica.js +22 -22
  85. package/dist/pin-field.css-mistica.js +4 -4
  86. package/dist/popover.css-mistica.js +1 -1
  87. package/dist/portal.d.ts +1 -1
  88. package/dist/progress-bar.css-mistica.js +7 -7
  89. package/dist/radio-button.css-mistica.js +27 -27
  90. package/dist/rating.css-mistica.js +4 -4
  91. package/dist/responsive-layout.css-mistica.js +6 -6
  92. package/dist/screen-reader-only.css-mistica.js +1 -1
  93. package/dist/select.css-mistica.js +21 -21
  94. package/dist/sheet-action-row.css-mistica.js +1 -1
  95. package/dist/sheet-common.css-mistica.js +7 -7
  96. package/dist/sheet-info.css-mistica.js +1 -1
  97. package/dist/sidenav-bar-context.d.ts +25 -0
  98. package/dist/sidenav-bar-context.js +72 -0
  99. package/dist/sidenav-bar-data.d.ts +15 -0
  100. package/dist/sidenav-bar-data.js +38 -0
  101. package/dist/sidenav-bar-first-level-item.d.ts +42 -0
  102. package/dist/sidenav-bar-first-level-item.js +174 -0
  103. package/dist/sidenav-bar-keyboard.d.ts +13 -0
  104. package/dist/sidenav-bar-keyboard.js +314 -0
  105. package/dist/sidenav-bar-layout.css-mistica.js +28 -0
  106. package/dist/sidenav-bar-layout.css.d.ts +5 -0
  107. package/dist/sidenav-bar-layout.d.ts +22 -0
  108. package/dist/sidenav-bar-layout.js +103 -0
  109. package/dist/sidenav-bar-mobile.d.ts +24 -0
  110. package/dist/sidenav-bar-mobile.js +332 -0
  111. package/dist/sidenav-bar-motion.d.ts +32 -0
  112. package/dist/sidenav-bar-motion.js +84 -0
  113. package/dist/sidenav-bar-panel.d.ts +31 -0
  114. package/dist/sidenav-bar-panel.js +208 -0
  115. package/dist/sidenav-bar-row.d.ts +82 -0
  116. package/dist/sidenav-bar-row.js +230 -0
  117. package/dist/sidenav-bar-second-level-item.d.ts +43 -0
  118. package/dist/sidenav-bar-second-level-item.js +89 -0
  119. package/dist/sidenav-bar-section.d.ts +23 -0
  120. package/dist/sidenav-bar-section.js +167 -0
  121. package/dist/sidenav-bar-types.d.ts +192 -0
  122. package/dist/sidenav-bar-types.js +11 -0
  123. package/dist/sidenav-bar.css-mistica.js +325 -0
  124. package/dist/sidenav-bar.css.d.ts +93 -0
  125. package/dist/sidenav-bar.d.ts +102 -0
  126. package/dist/sidenav-bar.js +492 -0
  127. package/dist/skeletons.css-mistica.js +8 -8
  128. package/dist/skins/blau.js +12 -8
  129. package/dist/skins/esimflag.js +13 -9
  130. package/dist/skins/movistar.js +13 -9
  131. package/dist/skins/o2.js +13 -9
  132. package/dist/skins/skin-contract.css-mistica.js +735 -731
  133. package/dist/skins/skin-contract.css.d.ts +4 -0
  134. package/dist/skins/telefonica.js +13 -9
  135. package/dist/skins/types/colors.d.ts +2 -0
  136. package/dist/skins/vivo-evolution.js +14 -10
  137. package/dist/skins/vivo.js +13 -9
  138. package/dist/skip-link.css-mistica.js +2 -2
  139. package/dist/slider.css-mistica.js +24 -24
  140. package/dist/snackbar.css-mistica.js +14 -14
  141. package/dist/spinner.css-mistica.js +1 -1
  142. package/dist/square.css-mistica.js +1 -1
  143. package/dist/stack.css-mistica.js +5 -5
  144. package/dist/stacking-group.css-mistica.js +1 -1
  145. package/dist/stepper.css-mistica.js +9 -9
  146. package/dist/switch-component.css-mistica.js +48 -42
  147. package/dist/switch-component.css.d.ts +2 -0
  148. package/dist/switch-component.js +18 -18
  149. package/dist/table.css-mistica.js +9 -9
  150. package/dist/tabs.css-mistica.js +22 -22
  151. package/dist/tag.css-mistica.js +3 -3
  152. package/dist/text-field-base.css-mistica.js +19 -19
  153. package/dist/text-field-components.css-mistica.js +10 -10
  154. package/dist/text-link.css-mistica.js +6 -6
  155. package/dist/text-tokens.d.ts +6 -0
  156. package/dist/text-tokens.js +157 -133
  157. package/dist/text.css-mistica.js +6 -6
  158. package/dist/timeline.css-mistica.js +11 -11
  159. package/dist/timer.css-mistica.js +6 -6
  160. package/dist/tooltip.css-mistica.js +2 -2
  161. package/dist/touchable.css-mistica.js +4 -4
  162. package/dist/utils/aspect-ratio-support.css-mistica.js +2 -2
  163. package/dist/video.css-mistica.js +1 -1
  164. package/dist/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +1 -1
  165. package/dist-es/accordion.css-mistica.js +7 -7
  166. package/dist-es/align.css-mistica.js +1 -1
  167. package/dist-es/autocomplete.css-mistica.js +2 -2
  168. package/dist-es/avatar.css-mistica.js +1 -1
  169. package/dist-es/badge.css-mistica.js +1 -1
  170. package/dist-es/box.css-mistica.js +13 -13
  171. package/dist-es/boxed.css-mistica.js +23 -23
  172. package/dist-es/boxed.js +25 -25
  173. package/dist-es/button-group.css-mistica.js +1 -1
  174. package/dist-es/button-layout.css-mistica.js +16 -16
  175. package/dist-es/button.css-mistica.js +38 -38
  176. package/dist-es/callout.css-mistica.js +12 -12
  177. package/dist-es/card-internal.css-mistica.js +17 -17
  178. package/dist-es/carousel.css-mistica.js +10 -10
  179. package/dist-es/checkbox.css-mistica.js +13 -13
  180. package/dist-es/chip.css-mistica.js +17 -17
  181. package/dist-es/circle.css-mistica.js +2 -2
  182. package/dist-es/community/advanced-data-card.css-mistica.js +7 -7
  183. package/dist-es/community/ai-card.css-mistica.js +4 -4
  184. package/dist-es/community/blocks.css-mistica.js +1 -1
  185. package/dist-es/community/cyber/example-component.css-mistica.js +1 -1
  186. package/dist-es/community/example-component.css-mistica.js +2 -2
  187. package/dist-es/community/skins/cyber-skin.js +19 -17
  188. package/dist-es/counter.css-mistica.js +2 -2
  189. package/dist-es/cover-hero.css-mistica.js +4 -4
  190. package/dist-es/credit-card-number-field.css-mistica.js +3 -3
  191. package/dist-es/date-field.css-mistica.js +1 -1
  192. package/dist-es/date-time-picker.css-mistica.js +1 -1
  193. package/dist-es/dialog.css-mistica.js +5 -5
  194. package/dist-es/divider.css-mistica.js +5 -5
  195. package/dist-es/double-field.css-mistica.js +2 -2
  196. package/dist-es/drawer.css-mistica.js +2 -2
  197. package/dist-es/empty-state-card.css-mistica.js +1 -1
  198. package/dist-es/empty-state.css-mistica.js +7 -7
  199. package/dist-es/fade-in.css-mistica.js +1 -1
  200. package/dist-es/feedback.css-mistica.js +2 -2
  201. package/dist-es/file-upload.css-mistica.js +8 -8
  202. package/dist-es/fixed-footer-layout.css-mistica.js +3 -3
  203. package/dist-es/form.css-mistica.js +1 -1
  204. package/dist-es/generated/mistica-icons/icon-panel-collapse-regular.js +115 -0
  205. package/dist-es/generated/mistica-icons/icon-panel-expand-regular.js +109 -0
  206. package/dist-es/grid-layout.css-mistica.js +3 -3
  207. package/dist-es/grid.css-mistica.js +120 -120
  208. package/dist-es/header.css-mistica.js +1 -1
  209. package/dist-es/hero.css-mistica.js +3 -3
  210. package/dist-es/horizontal-scroll.css-mistica.js +1 -1
  211. package/dist-es/icon-button.css-mistica.js +53 -53
  212. package/dist-es/icons/icon-chevron.css-mistica.js +2 -2
  213. package/dist-es/icons/icon-error.css-mistica.js +1 -1
  214. package/dist-es/image.css-mistica.js +4 -4
  215. package/dist-es/index.js +2279 -2276
  216. package/dist-es/inline.css-mistica.js +11 -11
  217. package/dist-es/list.css-mistica.js +8 -2
  218. package/dist-es/list.js +275 -264
  219. package/dist-es/loading-bar.css-mistica.js +1 -1
  220. package/dist-es/loading-screen.css-mistica.js +5 -5
  221. package/dist-es/logo.css-mistica.js +5 -5
  222. package/dist-es/menu.css-mistica.js +13 -13
  223. package/dist-es/mobile-navigation-menu.js +90 -0
  224. package/dist-es/mosaic.css-mistica.js +1 -1
  225. package/dist-es/navigation-bar.css-mistica.js +11 -20
  226. package/dist-es/navigation-bar.js +469 -656
  227. package/dist-es/navigation-breadcrumbs.css-mistica.js +1 -1
  228. package/dist-es/navigation-shared.css-mistica.js +11 -0
  229. package/dist-es/navigation-shared.js +192 -0
  230. package/dist-es/package-version.js +1 -1
  231. package/dist-es/pagination.css-mistica.js +19 -19
  232. package/dist-es/pin-field.css-mistica.js +2 -2
  233. package/dist-es/popover.css-mistica.js +1 -1
  234. package/dist-es/progress-bar.css-mistica.js +7 -7
  235. package/dist-es/radio-button.css-mistica.js +22 -22
  236. package/dist-es/rating.css-mistica.js +3 -3
  237. package/dist-es/responsive-layout.css-mistica.js +6 -6
  238. package/dist-es/screen-reader-only.css-mistica.js +1 -1
  239. package/dist-es/select.css-mistica.js +18 -18
  240. package/dist-es/sheet-action-row.css-mistica.js +1 -1
  241. package/dist-es/sheet-common.css-mistica.js +2 -2
  242. package/dist-es/sheet-info.css-mistica.js +1 -1
  243. package/dist-es/sidenav-bar-context.js +14 -0
  244. package/dist-es/sidenav-bar-data.js +12 -0
  245. package/dist-es/sidenav-bar-first-level-item.js +124 -0
  246. package/dist-es/sidenav-bar-keyboard.js +253 -0
  247. package/dist-es/sidenav-bar-layout.css-mistica.js +2 -0
  248. package/dist-es/sidenav-bar-layout.js +86 -0
  249. package/dist-es/sidenav-bar-mobile.js +277 -0
  250. package/dist-es/sidenav-bar-motion.js +26 -0
  251. package/dist-es/sidenav-bar-panel.js +146 -0
  252. package/dist-es/sidenav-bar-row.js +208 -0
  253. package/dist-es/sidenav-bar-second-level-item.js +72 -0
  254. package/dist-es/sidenav-bar-section.js +112 -0
  255. package/dist-es/sidenav-bar-types.js +2 -0
  256. package/dist-es/sidenav-bar.css-mistica.js +74 -0
  257. package/dist-es/sidenav-bar.js +429 -0
  258. package/dist-es/skeletons.css-mistica.js +7 -7
  259. package/dist-es/skins/blau.js +12 -8
  260. package/dist-es/skins/esimflag.js +13 -9
  261. package/dist-es/skins/movistar.js +13 -9
  262. package/dist-es/skins/o2.js +13 -9
  263. package/dist-es/skins/skin-contract.css-mistica.js +735 -731
  264. package/dist-es/skins/telefonica.js +13 -9
  265. package/dist-es/skins/vivo-evolution.js +14 -10
  266. package/dist-es/skins/vivo.js +13 -9
  267. package/dist-es/skip-link.css-mistica.js +2 -2
  268. package/dist-es/slider.css-mistica.js +20 -20
  269. package/dist-es/snackbar.css-mistica.js +5 -5
  270. package/dist-es/spinner.css-mistica.js +1 -1
  271. package/dist-es/square.css-mistica.js +1 -1
  272. package/dist-es/stack.css-mistica.js +5 -5
  273. package/dist-es/stacking-group.css-mistica.js +1 -1
  274. package/dist-es/stepper.css-mistica.js +3 -3
  275. package/dist-es/style.css +1 -1
  276. package/dist-es/switch-component.css-mistica.js +37 -37
  277. package/dist-es/switch-component.js +26 -26
  278. package/dist-es/table.css-mistica.js +9 -9
  279. package/dist-es/tabs.css-mistica.js +19 -19
  280. package/dist-es/tag.css-mistica.js +2 -2
  281. package/dist-es/text-field-base.css-mistica.js +17 -17
  282. package/dist-es/text-field-components.css-mistica.js +4 -4
  283. package/dist-es/text-link.css-mistica.js +6 -6
  284. package/dist-es/text-tokens.js +82 -67
  285. package/dist-es/text.css-mistica.js +6 -6
  286. package/dist-es/timeline.css-mistica.js +10 -10
  287. package/dist-es/timer.css-mistica.js +6 -6
  288. package/dist-es/tooltip.css-mistica.js +2 -2
  289. package/dist-es/touchable.css-mistica.js +2 -2
  290. package/dist-es/utils/aspect-ratio-support.css-mistica.js +2 -2
  291. package/dist-es/video.css-mistica.js +1 -1
  292. package/dist-es/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +1 -1
  293. package/package.json +2 -1
  294. package/src/boxed.css.ts +1 -0
  295. package/src/boxed.tsx +18 -5
  296. package/src/callout.css.ts +4 -2
  297. package/src/community/skins/cyber-skin.tsx +19 -16
  298. package/src/index.tsx +14 -0
  299. package/src/list.css.ts +36 -1
  300. package/src/list.tsx +370 -344
  301. package/src/mobile-navigation-menu.tsx +168 -0
  302. package/src/navigation-bar.css.ts +2 -267
  303. package/src/navigation-bar.tsx +77 -370
  304. package/src/navigation-shared.css.ts +289 -0
  305. package/src/navigation-shared.tsx +262 -0
  306. package/src/package-version.tsx +1 -1
  307. package/src/portal.tsx +1 -1
  308. package/src/sidenav-bar-context.tsx +41 -0
  309. package/src/sidenav-bar-data.ts +49 -0
  310. package/src/sidenav-bar-first-level-item.tsx +203 -0
  311. package/src/sidenav-bar-keyboard.tsx +393 -0
  312. package/src/sidenav-bar-layout.css.ts +89 -0
  313. package/src/sidenav-bar-layout.tsx +61 -0
  314. package/src/sidenav-bar-mobile.tsx +302 -0
  315. package/src/sidenav-bar-motion.tsx +70 -0
  316. package/src/sidenav-bar-panel.tsx +269 -0
  317. package/src/sidenav-bar-row.tsx +272 -0
  318. package/src/sidenav-bar-second-level-item.tsx +99 -0
  319. package/src/sidenav-bar-section.tsx +98 -0
  320. package/src/sidenav-bar-types.ts +224 -0
  321. package/src/sidenav-bar.css.ts +1018 -0
  322. package/src/sidenav-bar.tsx +821 -0
  323. package/src/skins/blau.tsx +12 -8
  324. package/src/skins/esimflag.tsx +13 -9
  325. package/src/skins/movistar.tsx +13 -9
  326. package/src/skins/o2.tsx +13 -9
  327. package/src/skins/skin-contract.css.ts +2 -0
  328. package/src/skins/telefonica.tsx +13 -9
  329. package/src/skins/types/colors.tsx +2 -0
  330. package/src/skins/vivo-evolution.tsx +14 -10
  331. package/src/skins/vivo.tsx +13 -9
  332. package/src/switch-component.css.ts +20 -0
  333. package/src/switch-component.tsx +9 -1
  334. package/src/text-tokens.tsx +24 -0
package/src/list.tsx CHANGED
@@ -446,361 +446,373 @@ const getNodeText = (node: HTMLElement | null): string => {
446
446
  return raw;
447
447
  };
448
448
 
449
- const RowContent = React.forwardRef<TouchableElement, RowContentProps & {hasDivider: boolean}>(
450
- (props, ref) => {
451
- const titleId = React.useId();
452
- const outsideVariant = useThemeVariant();
453
- const isOverBrand =
454
- outsideVariant === 'brand' || outsideVariant === 'media' || outsideVariant === 'negative';
455
- const {
456
- asset,
457
- headline,
458
- title,
459
- titleAs,
460
- titleLinesMax,
461
- subtitle,
462
- subtitleLinesMax,
463
- description,
464
- descriptionLinesMax,
465
- detail,
466
- danger,
467
- badge,
468
- role,
469
- touchableRole,
470
- slot,
471
- withChevron,
472
- dataAttributes,
473
- right,
474
- 'aria-label': ariaLabelProp,
475
- tabIndex,
476
- atomicReading,
477
- hasDivider,
478
- } = props;
479
-
480
- const [headlineText, setHeadlineText] = React.useState<string>('');
481
- const [slotText, setSlotText] = React.useState<string>('');
482
- const [rightText, setRightText] = React.useState<string>('');
483
- const assetText = getAssetText(asset);
484
- const [dividerOffset, setDividerOffset] = React.useState(0);
485
- const measureDividerOffset = React.useCallback(
486
- (dualActionContainerElement: HTMLDivElement | null) => {
487
- if (!dualActionContainerElement || !hasDivider) {
488
- return;
489
- }
449
+ const RowContent = React.forwardRef<
450
+ TouchableElement,
451
+ RowContentProps & {hasDivider: boolean; onSelectedChange?: (selected: boolean) => void}
452
+ >((props, ref) => {
453
+ const titleId = React.useId();
454
+ const outsideVariant = useThemeVariant();
455
+ const isOverBrand =
456
+ outsideVariant === 'brand' || outsideVariant === 'media' || outsideVariant === 'negative';
457
+ const {
458
+ asset,
459
+ headline,
460
+ title,
461
+ titleAs,
462
+ titleLinesMax,
463
+ subtitle,
464
+ subtitleLinesMax,
465
+ description,
466
+ descriptionLinesMax,
467
+ detail,
468
+ danger,
469
+ badge,
470
+ role,
471
+ touchableRole,
472
+ slot,
473
+ withChevron,
474
+ dataAttributes,
475
+ right,
476
+ 'aria-label': ariaLabelProp,
477
+ tabIndex,
478
+ atomicReading,
479
+ hasDivider,
480
+ onSelectedChange,
481
+ } = props;
482
+
483
+ const [headlineText, setHeadlineText] = React.useState<string>('');
484
+ const [slotText, setSlotText] = React.useState<string>('');
485
+ const [rightText, setRightText] = React.useState<string>('');
486
+ const assetText = getAssetText(asset);
487
+ const [dividerOffset, setDividerOffset] = React.useState(0);
488
+ const measureDividerOffset = React.useCallback(
489
+ (dualActionContainerElement: HTMLDivElement | null) => {
490
+ if (!dualActionContainerElement || !hasDivider) {
491
+ return;
492
+ }
490
493
 
491
- const innerContentElement = dualActionContainerElement.querySelector<HTMLElement>(
492
- `.${styles.innerContent}`
493
- );
494
+ const innerContentElement = dualActionContainerElement.querySelector<HTMLElement>(
495
+ `.${styles.innerContent}`
496
+ );
494
497
 
495
- if (!innerContentElement) {
496
- return;
497
- }
498
+ if (!innerContentElement) {
499
+ return;
500
+ }
498
501
 
499
- const containerLeft = dualActionContainerElement.getBoundingClientRect().left;
500
- const contentLeft = innerContentElement.getBoundingClientRect().left;
502
+ const containerLeft = dualActionContainerElement.getBoundingClientRect().left;
503
+ const contentLeft = innerContentElement.getBoundingClientRect().left;
501
504
 
502
- setDividerOffset(contentLeft - containerLeft);
503
- },
504
- [hasDivider]
505
- );
505
+ setDividerOffset(contentLeft - containerLeft);
506
+ },
507
+ [hasDivider]
508
+ );
506
509
 
507
- // iOS voiceover reads links with multiple lines as separate links. By setting aria-label and marking content as aria-hidden, we can make it read the whole row as one link.
508
- const computedAriaLabel = [
509
- title,
510
- assetText,
511
- headlineText,
512
- subtitle,
513
- description,
514
- slotText,
515
- detail,
516
- rightText,
517
- ]
518
- .filter(Boolean)
519
- .join(' ');
520
-
521
- const isInteractive = !!props.onPress || !!props.href || !!props.to;
522
- const ariaLabel = ariaLabelProp ?? (isInteractive ? computedAriaLabel : undefined);
523
-
524
- const radioContext = useRadioContext();
525
- const disabled = props.disabled || (props.radioValue !== undefined && radioContext.disabled);
526
- const hasHoverDefault = !disabled && !isOverBrand;
527
- const hasHoverInverse = !disabled && isOverBrand;
528
- const hasControl = hasControlProps(props);
529
- const hasChevron = hasControl ? false : withChevron ?? isInteractive;
530
-
531
- const interactiveProps = {
532
- href: props.href,
533
- newTab: props.newTab,
534
- loadOnTop: props.loadOnTop,
535
-
536
- to: props.to,
537
- fullPageOnWebView: props.fullPageOnWebView,
538
- replace: props.replace,
539
-
540
- onNavigate: props.onNavigate,
541
- onPress: props.onPress,
542
- trackingEvent: props.trackingEvent,
543
-
544
- 'aria-label': ariaLabel,
545
- 'aria-labelledby': props['aria-labelledby'],
546
- 'aria-description': props['aria-description'],
547
- 'aria-describedby': props['aria-describedby'],
548
- 'aria-current': props['aria-current'],
549
- } as TouchableProps;
550
-
551
- const [isChecked, toggle] = useControlState(props.switch || props.checkbox || {});
552
-
553
- const renderContent = (contentProps?: {
554
- control?: React.ReactNode;
555
- labelId?: string;
556
- role?: string;
557
- withDivider?: boolean;
558
- }) => (
559
- <Content
560
- asset={asset}
561
- headline={headline}
562
- headlineRef={(node) => {
563
- if (node) {
564
- setHeadlineText(getNodeText(node));
565
- }
566
- }}
567
- title={title}
568
- titleAs={titleAs}
569
- subtitle={subtitle}
570
- description={description}
571
- badge={badge}
572
- titleLinesMax={titleLinesMax}
573
- subtitleLinesMax={subtitleLinesMax}
574
- descriptionLinesMax={descriptionLinesMax}
575
- detail={detail}
576
- danger={danger}
577
- right={right}
578
- rightRef={(node) => {
579
- if (node) {
580
- setRightText(getNodeText(node));
581
- }
582
- }}
583
- control={contentProps?.control}
584
- role={contentProps?.role}
585
- slot={slot}
586
- slotRef={(node) => {
587
- if (node) {
588
- setSlotText(getNodeText(node));
589
- }
590
- }}
591
- labelId={contentProps?.labelId}
510
+ // iOS voiceover reads links with multiple lines as separate links. By setting aria-label and marking content as aria-hidden, we can make it read the whole row as one link.
511
+ const computedAriaLabel = [
512
+ title,
513
+ assetText,
514
+ headlineText,
515
+ subtitle,
516
+ description,
517
+ slotText,
518
+ detail,
519
+ rightText,
520
+ ]
521
+ .filter(Boolean)
522
+ .join(' ');
523
+
524
+ const isInteractive = !!props.onPress || !!props.href || !!props.to;
525
+ const ariaLabel = ariaLabelProp ?? (isInteractive ? computedAriaLabel : undefined);
526
+
527
+ const radioContext = useRadioContext();
528
+ const disabled = props.disabled || (props.radioValue !== undefined && radioContext.disabled);
529
+ const hasHoverDefault = !disabled && !isOverBrand;
530
+ const hasHoverInverse = !disabled && isOverBrand;
531
+ const hasControl = hasControlProps(props);
532
+ const hasChevron = hasControl ? false : withChevron ?? isInteractive;
533
+
534
+ const interactiveProps = {
535
+ href: props.href,
536
+ newTab: props.newTab,
537
+ loadOnTop: props.loadOnTop,
538
+
539
+ to: props.to,
540
+ fullPageOnWebView: props.fullPageOnWebView,
541
+ replace: props.replace,
542
+
543
+ onNavigate: props.onNavigate,
544
+ onPress: props.onPress,
545
+ trackingEvent: props.trackingEvent,
546
+
547
+ 'aria-label': ariaLabel,
548
+ 'aria-labelledby': props['aria-labelledby'],
549
+ 'aria-description': props['aria-description'],
550
+ 'aria-describedby': props['aria-describedby'],
551
+ 'aria-current': props['aria-current'],
552
+ } as TouchableProps;
553
+
554
+ const [isChecked, toggle] = useControlState(props.switch || props.checkbox || {});
555
+ const isControlSelected =
556
+ props.switch || props.checkbox
557
+ ? isChecked
558
+ : props.radioValue !== undefined
559
+ ? radioContext.selectedValue === props.radioValue
560
+ : undefined;
561
+
562
+ React.useEffect(() => {
563
+ if (isControlSelected !== undefined) {
564
+ onSelectedChange?.(isControlSelected);
565
+ }
566
+ }, [onSelectedChange, isControlSelected]);
567
+
568
+ const renderContent = (contentProps?: {
569
+ control?: React.ReactNode;
570
+ labelId?: string;
571
+ role?: string;
572
+ withDivider?: boolean;
573
+ }) => (
574
+ <Content
575
+ asset={asset}
576
+ headline={headline}
577
+ headlineRef={(node) => {
578
+ if (node) {
579
+ setHeadlineText(getNodeText(node));
580
+ }
581
+ }}
582
+ title={title}
583
+ titleAs={titleAs}
584
+ subtitle={subtitle}
585
+ description={description}
586
+ badge={badge}
587
+ titleLinesMax={titleLinesMax}
588
+ subtitleLinesMax={subtitleLinesMax}
589
+ descriptionLinesMax={descriptionLinesMax}
590
+ detail={detail}
591
+ danger={danger}
592
+ right={right}
593
+ rightRef={(node) => {
594
+ if (node) {
595
+ setRightText(getNodeText(node));
596
+ }
597
+ }}
598
+ control={contentProps?.control}
599
+ role={contentProps?.role}
600
+ slot={slot}
601
+ slotRef={(node) => {
602
+ if (node) {
603
+ setSlotText(getNodeText(node));
604
+ }
605
+ }}
606
+ labelId={contentProps?.labelId}
607
+ disabled={disabled}
608
+ withChevron={hasChevron}
609
+ withDivider={contentProps?.withDivider ?? hasDivider}
610
+ />
611
+ );
612
+
613
+ if (isInteractive && !hasControl) {
614
+ return (
615
+ <BaseTouchable
616
+ ref={ref}
617
+ className={classNames(styles.rowContent, {
618
+ [styles.touchableBackground]: hasHoverDefault,
619
+ [styles.touchableBackgroundBrand]: hasHoverInverse,
620
+ [styles.pointer]: !disabled,
621
+ })}
622
+ {...interactiveProps}
623
+ role={touchableRole}
624
+ dataAttributes={dataAttributes}
592
625
  disabled={disabled}
593
- withChevron={hasChevron}
594
- withDivider={contentProps?.withDivider ?? hasDivider}
595
- />
626
+ tabIndex={tabIndex}
627
+ >
628
+ <div
629
+ className={styles.rowContentPadding}
630
+ aria-hidden={!!props.to || !!props.href || props.touchableRole === 'link' || undefined}
631
+ data-testid="content-container"
632
+ >
633
+ {renderContent({role})}
634
+ </div>
635
+ </BaseTouchable>
596
636
  );
637
+ }
597
638
 
598
- if (isInteractive && !hasControl) {
599
- return (
639
+ const renderRowWithDoubleInteraction = (control: React.ReactNode) => (
640
+ <div
641
+ className={styles.dualActionContainer}
642
+ ref={combineRefs(ref, measureDividerOffset)}
643
+ {...getPrefixedDataAttributes(dataAttributes)}
644
+ >
645
+ <div className={styles.dualActionBody}>
600
646
  <BaseTouchable
601
- ref={ref}
602
- className={classNames(styles.rowContent, {
647
+ disabled={disabled}
648
+ {...interactiveProps}
649
+ role={touchableRole}
650
+ className={classNames(styles.dualActionLeft, {
603
651
  [styles.touchableBackground]: hasHoverDefault,
604
652
  [styles.touchableBackgroundBrand]: hasHoverInverse,
605
- [styles.pointer]: !disabled,
606
653
  })}
607
- {...interactiveProps}
608
- role={touchableRole}
609
- dataAttributes={dataAttributes}
610
- disabled={disabled}
611
654
  tabIndex={tabIndex}
612
655
  >
613
- <div
614
- className={styles.rowContentPadding}
615
- aria-hidden={
616
- !!props.to || !!props.href || props.touchableRole === 'link' || undefined
617
- }
618
- data-testid="content-container"
619
- >
620
- {renderContent({role})}
621
- </div>
656
+ {renderContent({labelId: titleId, role, withDivider: false})}
622
657
  </BaseTouchable>
623
- );
624
- }
625
-
626
- const renderRowWithDoubleInteraction = (control: React.ReactNode) => (
627
- <div
628
- className={styles.dualActionContainer}
629
- ref={combineRefs(ref, measureDividerOffset)}
630
- {...getPrefixedDataAttributes(dataAttributes)}
631
- >
632
- <div className={styles.dualActionBody}>
633
- <BaseTouchable
634
- disabled={disabled}
635
- {...interactiveProps}
636
- role={touchableRole}
637
- className={classNames(styles.dualActionLeft, {
638
- [styles.touchableBackground]: hasHoverDefault,
639
- [styles.touchableBackgroundBrand]: hasHoverInverse,
640
- })}
641
- tabIndex={tabIndex}
642
- >
643
- {renderContent({labelId: titleId, role, withDivider: false})}
644
- </BaseTouchable>
645
- <div className={styles.dualActionDivider} />
646
- {control}
647
- </div>
648
- {hasDivider && (
649
- <div
650
- className={styles.rowDividerDualAction}
651
- data-testid="row-divider-dual-action"
652
- style={{paddingLeft: dividerOffset}}
653
- >
654
- <Divider />
655
- </div>
656
- )}
658
+ <div className={styles.dualActionDivider} />
659
+ {control}
657
660
  </div>
658
- );
661
+ {hasDivider && (
662
+ <div
663
+ className={styles.rowDividerDualAction}
664
+ data-testid="row-divider-dual-action"
665
+ style={{paddingLeft: dividerOffset}}
666
+ >
667
+ <Divider />
668
+ </div>
669
+ )}
670
+ </div>
671
+ );
659
672
 
660
- const renderRowWithSingleControl = (content: React.ReactNode, isContentInsideControl?: boolean) => (
661
- <div
662
- className={classNames(styles.rowContent, {
663
- [styles.touchableBackground]: hasHoverDefault && isContentInsideControl,
664
- [styles.touchableBackgroundBrand]: hasHoverInverse && isContentInsideControl,
665
- [styles.pointer]: !disabled && isContentInsideControl,
666
- })}
667
- ref={ref as React.Ref<HTMLDivElement>}
668
- {...getPrefixedDataAttributes(dataAttributes)}
669
- >
670
- {content}
671
- </div>
672
- );
673
+ const renderRowWithSingleControl = (content: React.ReactNode, isContentInsideControl?: boolean) => (
674
+ <div
675
+ className={classNames(styles.rowContent, {
676
+ [styles.touchableBackground]: hasHoverDefault && isContentInsideControl,
677
+ [styles.touchableBackgroundBrand]: hasHoverInverse && isContentInsideControl,
678
+ [styles.pointer]: !disabled && isContentInsideControl,
679
+ })}
680
+ ref={ref as React.Ref<HTMLDivElement>}
681
+ {...getPrefixedDataAttributes(dataAttributes)}
682
+ >
683
+ {content}
684
+ </div>
685
+ );
673
686
 
674
- const renderDualActionRight = ({controlElement}: {controlElement: React.ReactNode}) => (
675
- <div className={styles.dualActionRight}>{controlElement}</div>
676
- );
687
+ const renderDualActionRight = ({controlElement}: {controlElement: React.ReactNode}) => (
688
+ <div className={styles.dualActionRight}>{controlElement}</div>
689
+ );
677
690
 
678
- if (props.switch || props.checkbox) {
679
- const Control = props.switch ? Switch : Checkbox;
680
- const name = props.switch?.name ?? props.checkbox?.name ?? titleId;
681
-
682
- return isInteractive
683
- ? renderRowWithDoubleInteraction(
684
- <Control
685
- disabled={disabled}
686
- name={name}
687
- checked={isChecked}
688
- aria-label={ariaLabel}
689
- aria-labelledby={titleId}
690
- onChange={toggle}
691
- render={renderDualActionRight}
692
- />
693
- )
694
- : renderRowWithSingleControl(
695
- <Control
696
- disabled={disabled}
697
- name={name}
698
- checked={isChecked}
699
- aria-label={ariaLabel}
700
- aria-labelledby={titleId}
701
- onChange={toggle}
702
- render={({controlElement, labelId}) => (
703
- <div className={styles.rowContentPadding} role={role}>
704
- {renderContent({
705
- labelId,
706
- control: <Stack space="around">{controlElement}</Stack>,
707
- })}
708
- </div>
709
- )}
710
- />,
711
- true
712
- );
713
- }
691
+ if (props.switch || props.checkbox) {
692
+ const Control = props.switch ? Switch : Checkbox;
693
+ const name = props.switch?.name ?? props.checkbox?.name ?? titleId;
694
+
695
+ return isInteractive
696
+ ? renderRowWithDoubleInteraction(
697
+ <Control
698
+ disabled={disabled}
699
+ name={name}
700
+ checked={isChecked}
701
+ aria-label={ariaLabel}
702
+ aria-labelledby={titleId}
703
+ onChange={toggle}
704
+ render={renderDualActionRight}
705
+ />
706
+ )
707
+ : renderRowWithSingleControl(
708
+ <Control
709
+ disabled={disabled}
710
+ name={name}
711
+ checked={isChecked}
712
+ aria-label={ariaLabel}
713
+ aria-labelledby={titleId}
714
+ onChange={toggle}
715
+ render={({controlElement, labelId}) => (
716
+ <div className={styles.rowContentPadding} role={role}>
717
+ {renderContent({
718
+ labelId,
719
+ control: <Stack space="around">{controlElement}</Stack>,
720
+ })}
721
+ </div>
722
+ )}
723
+ />,
724
+ true
725
+ );
726
+ }
714
727
 
715
- if (props.radioValue) {
716
- return isInteractive
717
- ? renderRowWithDoubleInteraction(
718
- <RadioButton
719
- value={props.radioValue}
720
- aria-label={ariaLabel}
721
- aria-labelledby={titleId}
722
- render={renderDualActionRight}
723
- />
724
- )
725
- : renderRowWithSingleControl(
726
- <RadioButton
727
- value={props.radioValue}
728
- aria-label={ariaLabel}
729
- aria-labelledby={titleId}
730
- render={({controlElement}) => (
731
- <div className={styles.rowContentPadding} role={role}>
732
- {renderContent({
733
- labelId: titleId,
734
- control: <Stack space="around">{controlElement}</Stack>,
735
- })}
736
- </div>
737
- )}
738
- />,
739
- true
740
- );
741
- }
728
+ if (props.radioValue) {
729
+ return isInteractive
730
+ ? renderRowWithDoubleInteraction(
731
+ <RadioButton
732
+ value={props.radioValue}
733
+ aria-label={ariaLabel}
734
+ aria-labelledby={titleId}
735
+ render={renderDualActionRight}
736
+ />
737
+ )
738
+ : renderRowWithSingleControl(
739
+ <RadioButton
740
+ value={props.radioValue}
741
+ aria-label={ariaLabel}
742
+ aria-labelledby={titleId}
743
+ render={({controlElement}) => (
744
+ <div className={styles.rowContentPadding} role={role}>
745
+ {renderContent({
746
+ labelId: titleId,
747
+ control: <Stack space="around">{controlElement}</Stack>,
748
+ })}
749
+ </div>
750
+ )}
751
+ />,
752
+ true
753
+ );
754
+ }
742
755
 
743
- if (props.iconButton) {
744
- return isInteractive
745
- ? renderRowWithDoubleInteraction(
746
- <div className={styles.dualActionRightIconButton}>
747
- {props.iconButton.Icon ? (
748
- <IconButton {...props.iconButton} disabled={props.disabled} />
749
- ) : (
750
- <ToggleIconButton {...props.iconButton} disabled={props.disabled} />
751
- )}
752
- </div>
753
- )
754
- : renderRowWithSingleControl(
755
- <div className={styles.rowContentPadding}>
756
- {renderContent({
757
- labelId: titleId,
758
- control: (
759
- <Stack space="around">
760
- {props.iconButton.Icon ? (
761
- <IconButton
762
- {...props.iconButton}
763
- disabled={props.disabled}
764
- role={role}
765
- />
766
- ) : (
767
- <ToggleIconButton
768
- {...props.iconButton}
769
- disabled={props.disabled}
770
- role={role}
771
- />
772
- )}
773
- </Stack>
774
- ),
775
- })}
776
- </div>
777
- );
778
- }
756
+ if (props.iconButton) {
757
+ return isInteractive
758
+ ? renderRowWithDoubleInteraction(
759
+ <div className={styles.dualActionRightIconButton}>
760
+ {props.iconButton.Icon ? (
761
+ <IconButton {...props.iconButton} disabled={props.disabled} />
762
+ ) : (
763
+ <ToggleIconButton {...props.iconButton} disabled={props.disabled} />
764
+ )}
765
+ </div>
766
+ )
767
+ : renderRowWithSingleControl(
768
+ <div className={styles.rowContentPadding}>
769
+ {renderContent({
770
+ labelId: titleId,
771
+ control: (
772
+ <Stack space="around">
773
+ {props.iconButton.Icon ? (
774
+ <IconButton
775
+ {...props.iconButton}
776
+ disabled={props.disabled}
777
+ role={role}
778
+ />
779
+ ) : (
780
+ <ToggleIconButton
781
+ {...props.iconButton}
782
+ disabled={props.disabled}
783
+ role={role}
784
+ />
785
+ )}
786
+ </Stack>
787
+ ),
788
+ })}
789
+ </div>
790
+ );
791
+ }
779
792
 
780
- const shouldRenderScreenReaderOnly = !!ariaLabelProp || atomicReading;
793
+ const shouldRenderScreenReaderOnly = !!ariaLabelProp || atomicReading;
781
794
 
782
- return (
783
- <div role={role}>
784
- <div
785
- className={classNames(styles.rowContent, styles.rowContentPadding)}
786
- // role="text" makes VoiceOver read the whole div as a single text block. This is needed for VoiceOver rectangle to
787
- // cover the whole row, otherwise it only covers the text inside ScreenReaderOnly
788
- role={shouldRenderScreenReaderOnly ? 'text' : undefined}
789
- {...getPrefixedDataAttributes(dataAttributes)}
790
- ref={ref as React.Ref<HTMLDivElement>}
791
- tabIndex={tabIndex}
792
- >
793
- <div aria-hidden={shouldRenderScreenReaderOnly}>{renderContent({role})}</div>
794
- {shouldRenderScreenReaderOnly && (
795
- <ScreenReaderOnly>
796
- <span>{ariaLabelProp ?? computedAriaLabel}</span>
797
- </ScreenReaderOnly>
798
- )}
799
- </div>
795
+ return (
796
+ <div role={role}>
797
+ <div
798
+ className={classNames(styles.rowContent, styles.rowContentPadding)}
799
+ // role="text" makes VoiceOver read the whole div as a single text block. This is needed for VoiceOver rectangle to
800
+ // cover the whole row, otherwise it only covers the text inside ScreenReaderOnly
801
+ role={shouldRenderScreenReaderOnly ? 'text' : undefined}
802
+ {...getPrefixedDataAttributes(dataAttributes)}
803
+ ref={ref as React.Ref<HTMLDivElement>}
804
+ tabIndex={tabIndex}
805
+ >
806
+ <div aria-hidden={shouldRenderScreenReaderOnly}>{renderContent({role})}</div>
807
+ {shouldRenderScreenReaderOnly && (
808
+ <ScreenReaderOnly>
809
+ <span>{ariaLabelProp ?? computedAriaLabel}</span>
810
+ </ScreenReaderOnly>
811
+ )}
800
812
  </div>
801
- );
802
- }
803
- );
813
+ </div>
814
+ );
815
+ });
804
816
 
805
817
  export const Row = React.forwardRef<TouchableElement, RowContentProps>(
806
818
  ({dataAttributes, role = 'listitem', ...props}, ref) => (
@@ -851,7 +863,9 @@ export const RowList = ({
851
863
  };
852
864
 
853
865
  // danger + variant="brand" is not allowed
854
- type CommonBoxedRowProps =
866
+ type CommonBoxedRowProps = {
867
+ selected?: boolean;
868
+ } & (
855
869
  | {
856
870
  variant?: 'default';
857
871
  danger: true;
@@ -863,7 +877,8 @@ type CommonBoxedRowProps =
863
877
  | {
864
878
  variant?: 'default';
865
879
  danger: boolean;
866
- };
880
+ }
881
+ );
867
882
 
868
883
  type BoxedRowProps = ExclusifyUnion<
869
884
  | BasicRowContentProps
@@ -877,17 +892,28 @@ type BoxedRowProps = ExclusifyUnion<
877
892
  > &
878
893
  CommonBoxedRowProps;
879
894
 
880
- export const BoxedRow = React.forwardRef<HTMLDivElement, BoxedRowProps>(({dataAttributes, ...props}, ref) => (
881
- <InternalBoxed
882
- overflow="visible"
883
- className={styles.boxed}
884
- variant={props.variant ?? 'default'}
885
- ref={ref}
886
- dataAttributes={{testid: 'BoxedRow', ...dataAttributes}}
887
- >
888
- <RowContent {...props} hasDivider={false} />
889
- </InternalBoxed>
890
- ));
895
+ export const BoxedRow = React.forwardRef<HTMLDivElement, BoxedRowProps>(({dataAttributes, ...props}, ref) => {
896
+ const {selected, ...rowProps} = props;
897
+ const outsideVariant = useThemeVariant();
898
+ const [controlSelected, setControlSelected] = React.useState(false);
899
+ const isSelected = selected ?? controlSelected;
900
+
901
+ return (
902
+ <InternalBoxed
903
+ overflow="visible"
904
+ className={classNames(
905
+ styles.boxed,
906
+ styles.selectionOutline,
907
+ isSelected && styles.selectionOutlineColor[outsideVariant]
908
+ )}
909
+ variant={rowProps.variant ?? 'default'}
910
+ ref={ref}
911
+ dataAttributes={{testid: 'BoxedRow', ...dataAttributes}}
912
+ >
913
+ <RowContent {...rowProps} hasDivider={false} onSelectedChange={setControlSelected} />
914
+ </InternalBoxed>
915
+ );
916
+ });
891
917
 
892
918
  type BoxedRowListProps = {
893
919
  children: React.ReactNode;