@ledgerhq/lumen-ui-rnative 0.1.10 → 0.1.12

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 (233) hide show
  1. package/dist/module/i18n/locales/de.json +3 -0
  2. package/dist/module/i18n/locales/en.json +3 -0
  3. package/dist/module/i18n/locales/es.json +3 -0
  4. package/dist/module/i18n/locales/fr.json +3 -0
  5. package/dist/module/i18n/locales/ja.json +3 -0
  6. package/dist/module/i18n/locales/ko.json +3 -0
  7. package/dist/module/i18n/locales/pt.json +3 -0
  8. package/dist/module/i18n/locales/ru.json +3 -0
  9. package/dist/module/i18n/locales/th.json +3 -0
  10. package/dist/module/i18n/locales/tr.json +3 -0
  11. package/dist/module/i18n/locales/zh.json +3 -0
  12. package/dist/module/lib/Animations/Pulse/Pulse.js +1 -1
  13. package/dist/module/lib/Animations/Spin/Spin.js +1 -1
  14. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.js +21 -21
  15. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.js.map +1 -1
  16. package/dist/module/lib/Components/AmountInput/AmountInput.js +19 -13
  17. package/dist/module/lib/Components/AmountInput/AmountInput.js.map +1 -1
  18. package/dist/module/lib/Components/BaseInput/BaseInput.js +16 -9
  19. package/dist/module/lib/Components/BaseInput/BaseInput.js.map +1 -1
  20. package/dist/module/lib/Components/BottomSheet/BottomSheet.stories.js.map +1 -1
  21. package/dist/module/lib/Components/Button/BaseButton.js +8 -1
  22. package/dist/module/lib/Components/Button/BaseButton.js.map +1 -1
  23. package/dist/module/lib/Components/Card/Card.js +20 -14
  24. package/dist/module/lib/Components/Card/Card.js.map +1 -1
  25. package/dist/module/lib/Components/CardButton/CardButton.js +8 -1
  26. package/dist/module/lib/Components/CardButton/CardButton.js.map +1 -1
  27. package/dist/module/lib/Components/Checkbox/Checkbox.js +8 -1
  28. package/dist/module/lib/Components/Checkbox/Checkbox.js.map +1 -1
  29. package/dist/module/lib/Components/InteractiveIcon/InteractiveIcon.js +32 -5
  30. package/dist/module/lib/Components/InteractiveIcon/InteractiveIcon.js.map +1 -1
  31. package/dist/module/lib/Components/Label/Label.js +9 -2
  32. package/dist/module/lib/Components/Label/Label.js.map +1 -1
  33. package/dist/module/lib/Components/Link/Link.mdx +1 -0
  34. package/dist/module/lib/Components/ListItem/ListItem.js +14 -19
  35. package/dist/module/lib/Components/ListItem/ListItem.js.map +1 -1
  36. package/dist/module/lib/Components/MediaCard/MediaCard.js +183 -0
  37. package/dist/module/lib/Components/MediaCard/MediaCard.js.map +1 -0
  38. package/dist/module/lib/Components/MediaCard/MediaCard.mdx +111 -0
  39. package/dist/module/lib/Components/MediaCard/MediaCard.stories.js +199 -0
  40. package/dist/module/lib/Components/MediaCard/MediaCard.stories.js.map +1 -0
  41. package/dist/module/lib/Components/MediaCard/MediaCard.test.js +140 -0
  42. package/dist/module/lib/Components/MediaCard/MediaCard.test.js.map +1 -0
  43. package/dist/module/lib/Components/MediaCard/index.js +5 -0
  44. package/dist/module/lib/Components/MediaCard/index.js.map +1 -0
  45. package/dist/module/lib/Components/MediaCard/types.js +4 -0
  46. package/dist/module/lib/Components/MediaCard/types.js.map +1 -0
  47. package/dist/module/lib/Components/PageIndicator/PageIndicator.js +2 -2
  48. package/dist/module/lib/Components/SegmentedControl/SegmentedControl.js +8 -1
  49. package/dist/module/lib/Components/SegmentedControl/SegmentedControl.js.map +1 -1
  50. package/dist/module/lib/Components/SegmentedControl/usePillLayout.js +1 -1
  51. package/dist/module/lib/Components/Select/Select.js +8 -1
  52. package/dist/module/lib/Components/Select/Select.js.map +1 -1
  53. package/dist/module/lib/Components/Spot/Spot.js +4 -4
  54. package/dist/module/lib/Components/Spot/Spot.js.map +1 -1
  55. package/dist/module/lib/Components/Stepper/Stepper.js +10 -4
  56. package/dist/module/lib/Components/Stepper/Stepper.js.map +1 -1
  57. package/dist/module/lib/Components/Subheader/Subheader.js +1 -34
  58. package/dist/module/lib/Components/Subheader/Subheader.js.map +1 -1
  59. package/dist/module/lib/Components/Subheader/Subheader.mdx +26 -61
  60. package/dist/module/lib/Components/Subheader/Subheader.stories.js +23 -12
  61. package/dist/module/lib/Components/Subheader/Subheader.stories.js.map +1 -1
  62. package/dist/module/lib/Components/Subheader/Subheader.test.js +2 -26
  63. package/dist/module/lib/Components/Subheader/Subheader.test.js.map +1 -1
  64. package/dist/module/lib/Components/Subheader/index.js +1 -1
  65. package/dist/module/lib/Components/Subheader/index.js.map +1 -1
  66. package/dist/module/lib/Components/Switch/BaseSwitch.js +1 -1
  67. package/dist/module/lib/Components/Switch/Switch.js +8 -1
  68. package/dist/module/lib/Components/Switch/Switch.js.map +1 -1
  69. package/dist/module/lib/Components/TabBar/TabBar.js +8 -7
  70. package/dist/module/lib/Components/TabBar/TabBar.js.map +1 -1
  71. package/dist/module/lib/Components/Tag/Tag.js +9 -2
  72. package/dist/module/lib/Components/Tag/Tag.js.map +1 -1
  73. package/dist/module/lib/Components/ThemeProvider/ThemeProvider.test.js +22 -20
  74. package/dist/module/lib/Components/ThemeProvider/ThemeProvider.test.js.map +1 -1
  75. package/dist/module/lib/Components/Tile/Tile.js +27 -48
  76. package/dist/module/lib/Components/Tile/Tile.js.map +1 -1
  77. package/dist/module/lib/Components/Tile/Tile.mdx +34 -26
  78. package/dist/module/lib/Components/Tile/Tile.stories.js +31 -28
  79. package/dist/module/lib/Components/Tile/Tile.stories.js.map +1 -1
  80. package/dist/module/lib/Components/Tile/Tile.test.js +33 -188
  81. package/dist/module/lib/Components/Tile/Tile.test.js.map +1 -1
  82. package/dist/module/lib/Components/Tile/index.js +1 -1
  83. package/dist/module/lib/Components/Tile/index.js.map +1 -1
  84. package/dist/module/lib/Components/TileButton/TileButton.js +8 -2
  85. package/dist/module/lib/Components/TileButton/TileButton.js.map +1 -1
  86. package/dist/module/lib/Components/TriggerButton/TriggerButton.js +197 -0
  87. package/dist/module/lib/Components/TriggerButton/TriggerButton.js.map +1 -0
  88. package/dist/module/lib/Components/TriggerButton/TriggerButton.mdx +44 -0
  89. package/dist/module/lib/Components/TriggerButton/TriggerButton.stories.js +170 -0
  90. package/dist/module/lib/Components/TriggerButton/TriggerButton.stories.js.map +1 -0
  91. package/dist/module/lib/Components/TriggerButton/TriggerButton.test.js +146 -0
  92. package/dist/module/lib/Components/TriggerButton/TriggerButton.test.js.map +1 -0
  93. package/dist/module/lib/Components/TriggerButton/index.js +5 -0
  94. package/dist/module/lib/Components/TriggerButton/index.js.map +1 -0
  95. package/dist/module/lib/Components/TriggerButton/types.js +4 -0
  96. package/dist/module/lib/Components/TriggerButton/types.js.map +1 -0
  97. package/dist/module/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.js.map +1 -1
  98. package/dist/module/lib/Components/index.js +2 -0
  99. package/dist/module/lib/Components/index.js.map +1 -1
  100. package/dist/module/lib/Symbols/Icons/NanoGen5.js +49 -0
  101. package/dist/module/lib/Symbols/Icons/NanoGen5.js.map +1 -0
  102. package/dist/module/lib/Symbols/index.js +1 -0
  103. package/dist/module/lib/Symbols/index.js.map +1 -1
  104. package/dist/typescript/src/lib/Components/AmountInput/AmountInput.d.ts +1 -1
  105. package/dist/typescript/src/lib/Components/AmountInput/AmountInput.d.ts.map +1 -1
  106. package/dist/typescript/src/lib/Components/BaseInput/BaseInput.d.ts +1 -1
  107. package/dist/typescript/src/lib/Components/BaseInput/BaseInput.d.ts.map +1 -1
  108. package/dist/typescript/src/lib/Components/Button/BaseButton.d.ts +1 -1
  109. package/dist/typescript/src/lib/Components/Button/BaseButton.d.ts.map +1 -1
  110. package/dist/typescript/src/lib/Components/Card/Card.d.ts.map +1 -1
  111. package/dist/typescript/src/lib/Components/Card/types.d.ts +1 -3
  112. package/dist/typescript/src/lib/Components/Card/types.d.ts.map +1 -1
  113. package/dist/typescript/src/lib/Components/CardButton/CardButton.d.ts +1 -1
  114. package/dist/typescript/src/lib/Components/CardButton/CardButton.d.ts.map +1 -1
  115. package/dist/typescript/src/lib/Components/Checkbox/Checkbox.d.ts +1 -1
  116. package/dist/typescript/src/lib/Components/Checkbox/Checkbox.d.ts.map +1 -1
  117. package/dist/typescript/src/lib/Components/InteractiveIcon/InteractiveIcon.d.ts +1 -1
  118. package/dist/typescript/src/lib/Components/InteractiveIcon/InteractiveIcon.d.ts.map +1 -1
  119. package/dist/typescript/src/lib/Components/InteractiveIcon/types.d.ts +14 -1
  120. package/dist/typescript/src/lib/Components/InteractiveIcon/types.d.ts.map +1 -1
  121. package/dist/typescript/src/lib/Components/Label/Label.d.ts +1 -1
  122. package/dist/typescript/src/lib/Components/Label/Label.d.ts.map +1 -1
  123. package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts +1 -1
  124. package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts.map +1 -1
  125. package/dist/typescript/src/lib/Components/MediaCard/MediaCard.d.ts +32 -0
  126. package/dist/typescript/src/lib/Components/MediaCard/MediaCard.d.ts.map +1 -0
  127. package/dist/typescript/src/lib/Components/MediaCard/index.d.ts +3 -0
  128. package/dist/typescript/src/lib/Components/MediaCard/index.d.ts.map +1 -0
  129. package/dist/typescript/src/lib/Components/MediaCard/types.d.ts +38 -0
  130. package/dist/typescript/src/lib/Components/MediaCard/types.d.ts.map +1 -0
  131. package/dist/typescript/src/lib/Components/SegmentedControl/SegmentedControl.d.ts +1 -1
  132. package/dist/typescript/src/lib/Components/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  133. package/dist/typescript/src/lib/Components/SegmentedControl/SegmentedControlContext.d.ts +1 -1
  134. package/dist/typescript/src/lib/Components/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  135. package/dist/typescript/src/lib/Components/Select/Select.d.ts +1 -1
  136. package/dist/typescript/src/lib/Components/Select/Select.d.ts.map +1 -1
  137. package/dist/typescript/src/lib/Components/Stepper/Stepper.d.ts +1 -1
  138. package/dist/typescript/src/lib/Components/Stepper/Stepper.d.ts.map +1 -1
  139. package/dist/typescript/src/lib/Components/Subheader/Subheader.d.ts +2 -7
  140. package/dist/typescript/src/lib/Components/Subheader/Subheader.d.ts.map +1 -1
  141. package/dist/typescript/src/lib/Components/Subheader/index.d.ts +1 -1
  142. package/dist/typescript/src/lib/Components/Subheader/index.d.ts.map +1 -1
  143. package/dist/typescript/src/lib/Components/Subheader/types.d.ts +1 -11
  144. package/dist/typescript/src/lib/Components/Subheader/types.d.ts.map +1 -1
  145. package/dist/typescript/src/lib/Components/Switch/Switch.d.ts +1 -1
  146. package/dist/typescript/src/lib/Components/Switch/Switch.d.ts.map +1 -1
  147. package/dist/typescript/src/lib/Components/TabBar/TabBar.d.ts.map +1 -1
  148. package/dist/typescript/src/lib/Components/Tag/Tag.d.ts +1 -1
  149. package/dist/typescript/src/lib/Components/Tag/Tag.d.ts.map +1 -1
  150. package/dist/typescript/src/lib/Components/Tile/Tile.d.ts +17 -23
  151. package/dist/typescript/src/lib/Components/Tile/Tile.d.ts.map +1 -1
  152. package/dist/typescript/src/lib/Components/Tile/index.d.ts +1 -1
  153. package/dist/typescript/src/lib/Components/Tile/index.d.ts.map +1 -1
  154. package/dist/typescript/src/lib/Components/Tile/types.d.ts +0 -9
  155. package/dist/typescript/src/lib/Components/Tile/types.d.ts.map +1 -1
  156. package/dist/typescript/src/lib/Components/TileButton/TileButton.d.ts +1 -1
  157. package/dist/typescript/src/lib/Components/TileButton/TileButton.d.ts.map +1 -1
  158. package/dist/typescript/src/lib/Components/TriggerButton/TriggerButton.d.ts +26 -0
  159. package/dist/typescript/src/lib/Components/TriggerButton/TriggerButton.d.ts.map +1 -0
  160. package/dist/typescript/src/lib/Components/TriggerButton/index.d.ts +3 -0
  161. package/dist/typescript/src/lib/Components/TriggerButton/index.d.ts.map +1 -0
  162. package/dist/typescript/src/lib/Components/TriggerButton/types.d.ts +38 -0
  163. package/dist/typescript/src/lib/Components/TriggerButton/types.d.ts.map +1 -0
  164. package/dist/typescript/src/lib/Components/index.d.ts +2 -0
  165. package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
  166. package/dist/typescript/src/lib/Symbols/Icons/NanoGen5.d.ts +35 -0
  167. package/dist/typescript/src/lib/Symbols/Icons/NanoGen5.d.ts.map +1 -0
  168. package/dist/typescript/src/lib/Symbols/index.d.ts +1 -0
  169. package/dist/typescript/src/lib/Symbols/index.d.ts.map +1 -1
  170. package/package.json +4 -4
  171. package/src/i18n/locales/de.json +3 -0
  172. package/src/i18n/locales/en.json +3 -0
  173. package/src/i18n/locales/es.json +3 -0
  174. package/src/i18n/locales/fr.json +3 -0
  175. package/src/i18n/locales/ja.json +3 -0
  176. package/src/i18n/locales/ko.json +3 -0
  177. package/src/i18n/locales/pt.json +3 -0
  178. package/src/i18n/locales/ru.json +3 -0
  179. package/src/i18n/locales/th.json +3 -0
  180. package/src/i18n/locales/tr.json +3 -0
  181. package/src/i18n/locales/zh.json +3 -0
  182. package/src/lib/Components/AmountDisplay/AmountDisplay.tsx +20 -20
  183. package/src/lib/Components/AmountInput/AmountInput.tsx +15 -7
  184. package/src/lib/Components/BaseInput/BaseInput.tsx +12 -7
  185. package/src/lib/Components/BottomSheet/BottomSheet.stories.tsx +9 -9
  186. package/src/lib/Components/Button/BaseButton.tsx +6 -1
  187. package/src/lib/Components/Card/Card.tsx +16 -16
  188. package/src/lib/Components/Card/types.ts +1 -4
  189. package/src/lib/Components/CardButton/CardButton.tsx +7 -1
  190. package/src/lib/Components/Checkbox/Checkbox.tsx +7 -1
  191. package/src/lib/Components/InteractiveIcon/InteractiveIcon.tsx +34 -7
  192. package/src/lib/Components/InteractiveIcon/types.ts +14 -1
  193. package/src/lib/Components/Label/Label.tsx +7 -2
  194. package/src/lib/Components/Link/Link.mdx +1 -0
  195. package/src/lib/Components/ListItem/ListItem.tsx +15 -12
  196. package/src/lib/Components/MediaCard/MediaCard.mdx +111 -0
  197. package/src/lib/Components/MediaCard/MediaCard.stories.tsx +190 -0
  198. package/src/lib/Components/MediaCard/MediaCard.test.tsx +125 -0
  199. package/src/lib/Components/MediaCard/MediaCard.tsx +203 -0
  200. package/src/lib/Components/MediaCard/index.ts +2 -0
  201. package/src/lib/Components/MediaCard/types.ts +39 -0
  202. package/src/lib/Components/SegmentedControl/SegmentedControl.tsx +6 -1
  203. package/src/lib/Components/SegmentedControl/SegmentedControlContext.tsx +1 -1
  204. package/src/lib/Components/Select/Select.tsx +6 -1
  205. package/src/lib/Components/Spot/Spot.tsx +4 -4
  206. package/src/lib/Components/Stepper/Stepper.tsx +9 -2
  207. package/src/lib/Components/Subheader/Subheader.mdx +26 -61
  208. package/src/lib/Components/Subheader/Subheader.stories.tsx +16 -10
  209. package/src/lib/Components/Subheader/Subheader.test.tsx +0 -22
  210. package/src/lib/Components/Subheader/Subheader.tsx +1 -42
  211. package/src/lib/Components/Subheader/index.ts +0 -1
  212. package/src/lib/Components/Subheader/types.ts +1 -16
  213. package/src/lib/Components/Switch/Switch.tsx +6 -1
  214. package/src/lib/Components/TabBar/TabBar.tsx +5 -2
  215. package/src/lib/Components/Tag/Tag.tsx +7 -2
  216. package/src/lib/Components/ThemeProvider/ThemeProvider.test.tsx +16 -18
  217. package/src/lib/Components/Tile/Tile.mdx +34 -26
  218. package/src/lib/Components/Tile/Tile.stories.tsx +26 -26
  219. package/src/lib/Components/Tile/Tile.test.tsx +17 -137
  220. package/src/lib/Components/Tile/Tile.tsx +28 -40
  221. package/src/lib/Components/Tile/index.ts +0 -1
  222. package/src/lib/Components/Tile/types.ts +0 -11
  223. package/src/lib/Components/TileButton/TileButton.tsx +9 -2
  224. package/src/lib/Components/TriggerButton/TriggerButton.mdx +44 -0
  225. package/src/lib/Components/TriggerButton/TriggerButton.stories.tsx +132 -0
  226. package/src/lib/Components/TriggerButton/TriggerButton.test.tsx +157 -0
  227. package/src/lib/Components/TriggerButton/TriggerButton.tsx +228 -0
  228. package/src/lib/Components/TriggerButton/index.ts +2 -0
  229. package/src/lib/Components/TriggerButton/types.ts +38 -0
  230. package/src/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.tsx +1 -1
  231. package/src/lib/Components/index.ts +2 -0
  232. package/src/lib/Symbols/Icons/NanoGen5.tsx +44 -0
  233. package/src/lib/Symbols/index.ts +1 -0
@@ -0,0 +1,111 @@
1
+ import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as MediaCardStories from './MediaCard.stories';
3
+ import { CustomTabs, Tab } from '../../../../.storybook/components';
4
+ import CommonRulesDoAndDont from '../../../../.storybook/components/DoVsDont/CommonRulesDoAndDont.mdx';
5
+
6
+ <Meta title='Communication/MediaCard' of={MediaCardStories} />
7
+
8
+ # MediaCard
9
+
10
+ <CustomTabs>
11
+ <Tab label="Overview">
12
+
13
+ ## Introduction
14
+
15
+ MediaCard is a promotional card component that displays a full-bleed background image with gradient overlays for text readability. It uses a simplified compound pattern with `MediaCardTitle` for the text and free-form children for leading content (e.g. tags, icons).
16
+
17
+ > View in [Figma](https://www.figma.com/design/JxaLVMTWirCpU0rsbZ30k7?node-id=15160-2853).
18
+
19
+ ## Anatomy
20
+
21
+ <Canvas of={MediaCardStories.Base} />
22
+
23
+ - **MediaCard**: Root pressable container with background image and gradient overlays
24
+ - **MediaCardTitle**: Styled title text (clamps at 3 lines)
25
+ - **Leading content**: Any element rendered before `MediaCardTitle` (tags, badges, icons) — no wrapper needed
26
+ - **Close button**: Rendered via the `onClose` prop (positioned absolute top-right) — only visible when `onClose` is provided
27
+
28
+ ## Properties
29
+
30
+ <Canvas of={MediaCardStories.Base} />
31
+ <Controls of={MediaCardStories.Base} />
32
+
33
+ ### Layout
34
+
35
+ The card fills its parent width by default.
36
+
37
+ <Canvas of={MediaCardStories.LayoutShowcase} />
38
+
39
+ ### Compositions
40
+
41
+ Leading content is optional — just place any element before `MediaCardTitle` inside `MediaCard`.
42
+
43
+ <Canvas of={MediaCardStories.CompositionShowcase} />
44
+
45
+ ## Accessibility
46
+
47
+ - The root element uses `accessibilityRole='button'` when `onPress` is provided
48
+ - The close button includes an `accessibilityLabel` (auto-translated via i18n) — only rendered when `onClose` is provided
49
+ - The background image is not accessible (`accessible={false}`) since it is decorative
50
+ - Components forward refs and spread props for accessibility support
51
+
52
+ </Tab>
53
+ <Tab label="Implementation">
54
+
55
+ ## Setup
56
+
57
+ Install and set up the library with our [Setup Guide →](?path=/docs/getting-started-setup--docs).
58
+
59
+ ## Basic Usage
60
+
61
+ Both `onPress` and `onClose` are optional. When `onClose` is omitted, the close button is hidden. When `onPress` is omitted, the card is non-interactive.
62
+
63
+ ```tsx
64
+ import { MediaCard, MediaCardTitle, Tag } from '@ledgerhq/lumen-ui-rnative';
65
+
66
+ function MyComponent() {
67
+ return (
68
+ <MediaCard
69
+ imageUrl='https://example.com/promo.jpg'
70
+ onPress={() => console.log('pressed')}
71
+ onClose={() => console.log('closed')}
72
+ >
73
+ <Tag label='New' size='md' />
74
+ <MediaCardTitle>Card title</MediaCardTitle>
75
+ </MediaCard>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ### Without Close Button
81
+
82
+ Omit `onClose` to hide the close button — useful for non-dismissible promotions:
83
+
84
+ ```tsx
85
+ <MediaCard
86
+ imageUrl='https://example.com/promo.jpg'
87
+ onPress={() => console.log('pressed')}
88
+ >
89
+ <Tag label='New' size='md' />
90
+ <MediaCardTitle>Card title</MediaCardTitle>
91
+ </MediaCard>
92
+ ```
93
+
94
+ ### Layout Adjustments with lx
95
+
96
+ Use the `lx` prop for layout adjustments like margins or positioning:
97
+
98
+ ```tsx
99
+ <MediaCard
100
+ imageUrl='https://example.com/promo.jpg'
101
+ onPress={() => console.log('pressed')}
102
+ lx={{ marginTop: 's16', marginBottom: 's8' }}
103
+ >
104
+ <MediaCardTitle>With margin</MediaCardTitle>
105
+ </MediaCard>
106
+ ```
107
+
108
+ <CommonRulesDoAndDont />
109
+
110
+ </Tab>
111
+ </CustomTabs>
@@ -0,0 +1,190 @@
1
+ import { CryptoIcon } from '@ledgerhq/crypto-icons';
2
+ import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
3
+ import { useState } from 'react';
4
+ import { Button } from '../Button';
5
+ import { Tag } from '../Tag';
6
+ import { Box, Text } from '../Utility';
7
+ import { MediaCard, MediaCardTitle } from './MediaCard';
8
+
9
+ const meta = {
10
+ component: MediaCard,
11
+ subcomponents: { MediaCardTitle },
12
+ title: 'Communication/MediaCard',
13
+ parameters: {
14
+ layout: 'centered',
15
+ backgrounds: { default: 'light' },
16
+ docs: {
17
+ source: {
18
+ language: 'tsx',
19
+ format: true,
20
+ type: 'code',
21
+ },
22
+ },
23
+ },
24
+ } satisfies Meta<typeof MediaCard>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof MediaCard>;
28
+
29
+ const EXAMPLE_SRC =
30
+ 'https://ledger-wp-website-s3-prd.ledger.com/uploads/2026/03/hero_visual-1.webp';
31
+
32
+ const baseArgs = {
33
+ imageUrl: EXAMPLE_SRC,
34
+ onPress: () => ({}),
35
+ onClose: () => ({}),
36
+ };
37
+
38
+ export const Base: Story = {
39
+ args: baseArgs,
40
+ render: (args) => (
41
+ <Box lx={{ width: 's320' }}>
42
+ <MediaCard {...args}>
43
+ <Tag label='Label' size='md' />
44
+ <MediaCardTitle>
45
+ Black Friday sale.
46
+ <Text typography='heading3SemiBold' lx={{ color: 'active' }}>
47
+ {' '}
48
+ 3 days with no fees{' '}
49
+ </Text>
50
+ on your transactions.
51
+ </MediaCardTitle>
52
+ </MediaCard>
53
+ </Box>
54
+ ),
55
+ parameters: {
56
+ docs: {
57
+ source: {
58
+ code: `
59
+ <MediaCard imageUrl="/promo.jpg" onPress={() => {}} onClose={() => {}}>
60
+ <Tag label='Label' size='md' />
61
+ <MediaCardTitle>
62
+ Black Friday sale.
63
+ <Text typography='heading3SemiBold' lx={{ color: 'active' }}>
64
+ {' '}
65
+ 3 days with no fees{' '}
66
+ </Text>
67
+ on your transactions.
68
+ </MediaCardTitle>
69
+ </MediaCard>`,
70
+ },
71
+ },
72
+ },
73
+ };
74
+
75
+ export const LayoutShowcase: Story = {
76
+ render: () => (
77
+ <Box
78
+ lx={{
79
+ flexDirection: 'column',
80
+ width: 's320',
81
+ gap: 's16',
82
+ padding: 's8',
83
+ }}
84
+ >
85
+ <MediaCard {...baseArgs}>
86
+ <Tag label='Label' size='md' />
87
+ <MediaCardTitle>Full width card</MediaCardTitle>
88
+ </MediaCard>
89
+ </Box>
90
+ ),
91
+ parameters: {
92
+ docs: {
93
+ source: {
94
+ code: `
95
+ <MediaCard imageUrl="/promo.jpg" onPress={() => {}} onClose={() => {}}>
96
+ <Tag label="Label" size="md" />
97
+ <MediaCardTitle>Full width card</MediaCardTitle>
98
+ </MediaCard>`,
99
+ },
100
+ },
101
+ },
102
+ };
103
+
104
+ export const CompositionShowcase: Story = {
105
+ render: () => (
106
+ <Box
107
+ lx={{
108
+ flexDirection: 'column',
109
+ width: 's320',
110
+ gap: 's16',
111
+ padding: 's8',
112
+ }}
113
+ >
114
+ <MediaCard {...baseArgs}>
115
+ <MediaCardTitle>Title only</MediaCardTitle>
116
+ </MediaCard>
117
+
118
+ <MediaCard {...baseArgs}>
119
+ <Tag label='Promo' size='md' />
120
+ <MediaCardTitle>With tag and title</MediaCardTitle>
121
+ </MediaCard>
122
+
123
+ <MediaCard {...baseArgs}>
124
+ <CryptoIcon ledgerId='bitcoin' ticker='BTC' size='32px' />
125
+ <MediaCardTitle>With crypto icon</MediaCardTitle>
126
+ </MediaCard>
127
+ </Box>
128
+ ),
129
+ parameters: {
130
+ docs: {
131
+ source: {
132
+ code: `
133
+ {/* Title only */}
134
+ <MediaCard imageUrl="/promo.jpg" onPress={() => {}} onClose={() => {}}>
135
+ <MediaCardTitle>Title only</MediaCardTitle>
136
+ </MediaCard>
137
+
138
+ {/* With tag */}
139
+ <MediaCard imageUrl="/promo.jpg" onPress={() => {}} onClose={() => {}}>
140
+ <Tag label="Promo" size="md" />
141
+ <MediaCardTitle>With tag and title</MediaCardTitle>
142
+ </MediaCard>
143
+
144
+ {/* With crypto icon */}
145
+ <MediaCard imageUrl="/promo.jpg" onPress={() => {}} onClose={() => {}}>
146
+ <CryptoIcon ledgerId="bitcoin" ticker="BTC" size="32px" />
147
+ <MediaCardTitle>With crypto icon</MediaCardTitle>
148
+ </MediaCard>`,
149
+ },
150
+ },
151
+ },
152
+ };
153
+
154
+ export const WithClose: Story = {
155
+ render: () => {
156
+ const [visible, setVisible] = useState(true);
157
+
158
+ if (!visible) {
159
+ return (
160
+ <Button
161
+ appearance='transparent'
162
+ size='sm'
163
+ onPress={() => setVisible(true)}
164
+ >
165
+ Show card again
166
+ </Button>
167
+ );
168
+ }
169
+
170
+ return (
171
+ <Box lx={{ width: 's320' }}>
172
+ <MediaCard
173
+ imageUrl={EXAMPLE_SRC}
174
+ onPress={() => ({})}
175
+ onClose={() => setVisible(false)}
176
+ >
177
+ <Tag label='Label' size='md' />
178
+ <MediaCardTitle>
179
+ Black Friday sale.
180
+ <Text typography='heading3SemiBold' lx={{ color: 'active' }}>
181
+ {' '}
182
+ 3 days with no fees{' '}
183
+ </Text>
184
+ on your transactions.
185
+ </MediaCardTitle>
186
+ </MediaCard>
187
+ </Box>
188
+ );
189
+ },
190
+ };
@@ -0,0 +1,125 @@
1
+ import { describe, it, expect, jest } from '@jest/globals';
2
+ import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
3
+ import { fireEvent, render } from '@testing-library/react-native';
4
+ import type { ReactNode } from 'react';
5
+ import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
6
+ import { MediaCard, MediaCardTitle } from './MediaCard';
7
+
8
+ const TestWrapper = ({ children }: { children: ReactNode }) => (
9
+ <ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
10
+ {children}
11
+ </ThemeProvider>
12
+ );
13
+
14
+ const makeProps = () => ({
15
+ imageUrl: 'https://example.com/image.jpg',
16
+ onPress: jest.fn(),
17
+ onClose: jest.fn(),
18
+ });
19
+
20
+ describe('MediaCard', () => {
21
+ it('should render title', () => {
22
+ const { getByText } = render(
23
+ <TestWrapper>
24
+ <MediaCard {...makeProps()}>
25
+ <MediaCardTitle>Title</MediaCardTitle>
26
+ </MediaCard>
27
+ </TestWrapper>,
28
+ );
29
+
30
+ getByText('Title');
31
+ });
32
+
33
+ it('should render leading content and title', () => {
34
+ const { getByText } = render(
35
+ <TestWrapper>
36
+ <MediaCard {...makeProps()}>
37
+ <MediaCardTitle>Tag</MediaCardTitle>
38
+ <MediaCardTitle>Title</MediaCardTitle>
39
+ </MediaCard>
40
+ </TestWrapper>,
41
+ );
42
+
43
+ getByText('Tag');
44
+ getByText('Title');
45
+ });
46
+
47
+ it('should fire onPress on press', () => {
48
+ const props = makeProps();
49
+ const { getByTestId } = render(
50
+ <TestWrapper>
51
+ <MediaCard {...props} testID='media-card'>
52
+ <MediaCardTitle>Title</MediaCardTitle>
53
+ </MediaCard>
54
+ </TestWrapper>,
55
+ );
56
+
57
+ fireEvent.press(getByTestId('media-card'));
58
+ expect(props.onPress).toHaveBeenCalledTimes(1);
59
+ });
60
+
61
+ it('should render image when imageUrl is provided', () => {
62
+ const { getByTestId } = render(
63
+ <TestWrapper>
64
+ <MediaCard {...makeProps()}>
65
+ <MediaCardTitle>Title</MediaCardTitle>
66
+ </MediaCard>
67
+ </TestWrapper>,
68
+ );
69
+
70
+ expect(getByTestId('media-card-image')).toBeTruthy();
71
+ });
72
+
73
+ it('should hide image on error', () => {
74
+ const { getByTestId, queryByTestId } = render(
75
+ <TestWrapper>
76
+ <MediaCard {...makeProps()}>
77
+ <MediaCardTitle>Title</MediaCardTitle>
78
+ </MediaCard>
79
+ </TestWrapper>,
80
+ );
81
+
82
+ const img = getByTestId('media-card-image');
83
+ fireEvent(img, 'onError');
84
+
85
+ expect(queryByTestId('media-card-image')).toBeNull();
86
+ });
87
+
88
+ it('should fire onClose on close button press', () => {
89
+ const props = makeProps();
90
+ const { getByTestId } = render(
91
+ <TestWrapper>
92
+ <MediaCard {...props}>
93
+ <MediaCardTitle>Title</MediaCardTitle>
94
+ </MediaCard>
95
+ </TestWrapper>,
96
+ );
97
+
98
+ fireEvent.press(getByTestId('media-card-close-button'));
99
+ expect(props.onClose).toHaveBeenCalledTimes(1);
100
+ });
101
+
102
+ it('should not render close button when onClose is not provided', () => {
103
+ const { queryByTestId } = render(
104
+ <TestWrapper>
105
+ <MediaCard imageUrl='https://example.com/image.jpg'>
106
+ <MediaCardTitle>Title</MediaCardTitle>
107
+ </MediaCard>
108
+ </TestWrapper>,
109
+ );
110
+
111
+ expect(queryByTestId('media-card-close-button')).toBeNull();
112
+ });
113
+
114
+ it('should not have button role when onPress is not provided', () => {
115
+ const { getByTestId } = render(
116
+ <TestWrapper>
117
+ <MediaCard imageUrl='https://example.com/image.jpg' testID='media-card'>
118
+ <MediaCardTitle>Title</MediaCardTitle>
119
+ </MediaCard>
120
+ </TestWrapper>,
121
+ );
122
+
123
+ expect(getByTestId('media-card').props.accessibilityRole).toBeUndefined();
124
+ });
125
+ });
@@ -0,0 +1,203 @@
1
+ import { useState } from 'react';
2
+ import { Image, StyleSheet, View } from 'react-native';
3
+ import { useCommonTranslation } from '../../../i18n';
4
+ import { useStyleSheet } from '../../../styles';
5
+ import { Close } from '../../Symbols';
6
+ import { InteractiveIcon } from '../InteractiveIcon';
7
+ import { LinearGradient, Pressable, Text } from '../Utility';
8
+ import { MediaCardProps, MediaCardTitleProps } from './types';
9
+
10
+ const CARD_HEIGHT = 164;
11
+
12
+ const useStyles = () =>
13
+ useStyleSheet(
14
+ (t) => ({
15
+ root: {
16
+ position: 'relative',
17
+ width: t.sizes.full,
18
+ height: CARD_HEIGHT,
19
+ borderRadius: t.borderRadius.md,
20
+ overflow: 'hidden',
21
+ flexDirection: 'column',
22
+ alignItems: 'flex-start',
23
+ justifyContent: 'flex-end',
24
+ backgroundColor: t.colors.bg.muted,
25
+ },
26
+ content: {
27
+ flexDirection: 'column',
28
+ alignItems: 'flex-start',
29
+ gap: t.spacings.s8,
30
+ width: t.sizes.full,
31
+ minWidth: 0,
32
+ padding: t.spacings.s12,
33
+ },
34
+ title: {
35
+ ...t.typographies.heading3SemiBold,
36
+ color: t.colors.text.white,
37
+ },
38
+ closeButton: {
39
+ position: 'absolute',
40
+ top: t.spacings.s12,
41
+ right: t.spacings.s12,
42
+ },
43
+ gradientOverlays: {
44
+ color: t.colors.text.black,
45
+ },
46
+ pressedOverlay: {
47
+ backgroundColor: t.colors.bg.mutedTransparentPressed,
48
+ },
49
+ }),
50
+ [],
51
+ );
52
+
53
+ /**
54
+ * Title text for the card, styled with heading typography and white color.
55
+ */
56
+ export const MediaCardTitle = ({
57
+ children,
58
+ lx = {},
59
+ style,
60
+ ref,
61
+ ...props
62
+ }: MediaCardTitleProps) => {
63
+ const styles = useStyles();
64
+
65
+ return (
66
+ <Text
67
+ ref={ref}
68
+ lx={lx}
69
+ style={StyleSheet.flatten([styles.title, style])}
70
+ numberOfLines={3}
71
+ ellipsizeMode='tail'
72
+ {...props}
73
+ >
74
+ {children}
75
+ </Text>
76
+ );
77
+ };
78
+
79
+ MediaCardTitle.displayName = 'MediaCardTitle';
80
+
81
+ const GradientOverlays = () => {
82
+ const styles = useStyles();
83
+
84
+ return (
85
+ <>
86
+ <LinearGradient
87
+ direction='to-top'
88
+ stops={[
89
+ { color: styles.gradientOverlays.color, opacity: 0.8, offset: 0 },
90
+ { color: styles.gradientOverlays.color, opacity: 0, offset: 0.75 },
91
+ ]}
92
+ style={StyleSheet.absoluteFill}
93
+ pointerEvents='none'
94
+ accessible={false}
95
+ />
96
+
97
+ <LinearGradient
98
+ direction={45}
99
+ stops={[
100
+ { color: styles.gradientOverlays.color, opacity: 0, offset: 0.6 },
101
+ { color: styles.gradientOverlays.color, opacity: 0.8 },
102
+ ]}
103
+ style={StyleSheet.absoluteFill}
104
+ pointerEvents='none'
105
+ accessible={false}
106
+ />
107
+ </>
108
+ );
109
+ };
110
+
111
+ /**
112
+ * A media card component for displaying a full-bleed background image with
113
+ * composable content and a close button, using gradient overlays to ensure
114
+ * readability.
115
+ *
116
+ * @example
117
+ * import { MediaCard, MediaCardTitle } from '@ledgerhq/lumen-ui-rnative';
118
+ * import { Tag } from '@ledgerhq/lumen-ui-rnative';
119
+ *
120
+ * <MediaCard imageUrl="/image.jpg" onPress={() => {}} onClose={() => {}}>
121
+ * <Tag label="New" size="md" />
122
+ * <MediaCardTitle>Card title</MediaCardTitle>
123
+ * </MediaCard>
124
+ *
125
+ * // Without close button
126
+ * <MediaCard imageUrl="/image.jpg" onPress={() => {}}>
127
+ * <MediaCardTitle>Card title</MediaCardTitle>
128
+ * </MediaCard>
129
+ */
130
+ export const MediaCard = ({
131
+ ref,
132
+ children,
133
+ imageUrl,
134
+ onPress,
135
+ onClose,
136
+ closeAccessibilityLabel,
137
+ lx = {},
138
+ style,
139
+ ...pressableProps
140
+ }: MediaCardProps) => {
141
+ const { t } = useCommonTranslation();
142
+ const [imageLoadError, setImageLoadError] = useState(false);
143
+ const showImage = imageUrl && !imageLoadError;
144
+
145
+ const styles = useStyles();
146
+
147
+ return (
148
+ <Pressable
149
+ ref={ref}
150
+ lx={lx}
151
+ style={StyleSheet.flatten([styles.root, style])}
152
+ accessibilityRole={onPress ? 'button' : undefined}
153
+ onPress={onPress}
154
+ {...pressableProps}
155
+ >
156
+ {({ pressed }) => (
157
+ <>
158
+ {showImage && (
159
+ <Image
160
+ source={{ uri: imageUrl }}
161
+ style={[
162
+ StyleSheet.absoluteFill,
163
+ imageLoadError && { opacity: 0 },
164
+ ]}
165
+ accessible={false}
166
+ onError={() => setImageLoadError(true)}
167
+ testID='media-card-image'
168
+ />
169
+ )}
170
+
171
+ <GradientOverlays />
172
+
173
+ <View style={styles.content}>{children}</View>
174
+
175
+ {onClose && (
176
+ <InteractiveIcon
177
+ iconType='stroked'
178
+ appearance='white'
179
+ style={styles.closeButton}
180
+ onPress={onClose}
181
+ accessibilityLabel={
182
+ closeAccessibilityLabel || t('common.closeAriaLabel')
183
+ }
184
+ testID='media-card-close-button'
185
+ >
186
+ <Close size={20} />
187
+ </InteractiveIcon>
188
+ )}
189
+
190
+ {pressed && (
191
+ <View
192
+ style={[StyleSheet.absoluteFill, styles.pressedOverlay]}
193
+ pointerEvents='none'
194
+ accessible={false}
195
+ />
196
+ )}
197
+ </>
198
+ )}
199
+ </Pressable>
200
+ );
201
+ };
202
+
203
+ MediaCard.displayName = 'MediaCard';
@@ -0,0 +1,2 @@
1
+ export { MediaCard, MediaCardTitle } from './MediaCard';
2
+ export * from './types';
@@ -0,0 +1,39 @@
1
+ import type { ReactNode } from 'react';
2
+ import { StyledPressableProps, StyledTextProps } from '../../../styles';
3
+
4
+ /**
5
+ * Props for the MediaCard root component
6
+ */
7
+ export type MediaCardProps = {
8
+ /**
9
+ * The source URL for the background image.
10
+ */
11
+ imageUrl: string;
12
+ /**
13
+ * Callback fired when the card is pressed.
14
+ */
15
+ onPress?: () => void;
16
+ /**
17
+ * Callback fired when the close button is pressed.
18
+ */
19
+ onClose?: () => void;
20
+ /**
21
+ * Optional accessibility label for the close button.
22
+ */
23
+ closeAccessibilityLabel?: string;
24
+ /**
25
+ * The card content — typically a `MediaCardTitle` and optional
26
+ * leading content such as tags or icons.
27
+ */
28
+ children: ReactNode;
29
+ } & Omit<StyledPressableProps, 'children' | 'onPress'>;
30
+
31
+ /**
32
+ * Props for the MediaCardTitle component
33
+ */
34
+ export type MediaCardTitleProps = {
35
+ /**
36
+ * The title text or custom content.
37
+ */
38
+ children: ReactNode;
39
+ } & Omit<StyledTextProps, 'children'>;
@@ -1,3 +1,4 @@
1
+ import { useDisabledContext } from '@ledgerhq/lumen-utils-shared';
1
2
  import Animated from 'react-native-reanimated';
2
3
  import { useStyleSheet } from '../../../styles';
3
4
  import type { LumenTextStyle, LumenTypographyTokenName } from '../../../styles';
@@ -111,10 +112,14 @@ export function SegmentedControl({
111
112
  onSelectedChange,
112
113
  accessibilityLabel,
113
114
  children,
114
- disabled,
115
+ disabled: disabledProp,
115
116
  appearance = 'background',
116
117
  ...props
117
118
  }: SegmentedControlProps) {
119
+ const disabled = useDisabledContext({
120
+ consumerName: 'SegmentedControl',
121
+ mergeWith: { disabled: disabledProp },
122
+ });
118
123
  const styles = useRootStyles({
119
124
  disabled: Boolean(disabled),
120
125
  appearance,
@@ -3,7 +3,7 @@ import { createSafeContext } from '@ledgerhq/lumen-utils-shared';
3
3
  export type SegmentedControlContextValue = {
4
4
  selectedValue: string;
5
5
  onSelectedChange: (value: string) => void;
6
- disabled?: boolean;
6
+ disabled: boolean;
7
7
  };
8
8
 
9
9
  const [SegmentedControlContextProvider, _useSegmentedControlSafeContext] =