@ledgerhq/lumen-ui-rnative 0.1.35 → 0.1.37

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 (191) hide show
  1. package/dist/module/lib/Animations/Pulse/Pulse.js +2 -2
  2. package/dist/module/lib/Animations/Pulse/Pulse.js.map +1 -1
  3. package/dist/module/lib/Animations/Pulse/Pulse.mdx +1 -1
  4. package/dist/module/lib/Animations/Spin/Spin.mdx +1 -1
  5. package/dist/module/lib/Components/AddressInput/AddressInput.mdx +1 -1
  6. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.js +68 -39
  7. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.js.map +1 -1
  8. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.mdx +7 -1
  9. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.stories.js +24 -0
  10. package/dist/module/lib/Components/AmountDisplay/AmountDisplay.stories.js.map +1 -1
  11. package/dist/module/lib/Components/AmountDisplay/types.js.map +1 -1
  12. package/dist/module/lib/Components/AmountInput/AmountInput.mdx +1 -1
  13. package/dist/module/lib/Components/Avatar/Avatar.mdx +1 -1
  14. package/dist/module/lib/Components/Banner/Banner.mdx +1 -1
  15. package/dist/module/lib/Components/BottomSheet/BottomSheet.mdx +1 -1
  16. package/dist/module/lib/Components/Button/Button.mdx +1 -1
  17. package/dist/module/lib/Components/Card/Card.stories.js +4 -9
  18. package/dist/module/lib/Components/Card/Card.stories.js.map +1 -1
  19. package/dist/module/lib/Components/CardButton/CardButton.mdx +1 -1
  20. package/dist/module/lib/Components/Checkbox/Checkbox.mdx +1 -1
  21. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.js +184 -0
  22. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.js.map +1 -0
  23. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.mdx +139 -0
  24. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.stories.js +258 -0
  25. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.stories.js.map +1 -0
  26. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.test.js +94 -0
  27. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.test.js.map +1 -0
  28. package/dist/module/lib/Components/DescriptionItem/index.js +5 -0
  29. package/dist/module/lib/Components/DescriptionItem/index.js.map +1 -0
  30. package/dist/module/lib/Components/DescriptionItem/types.js +4 -0
  31. package/dist/module/lib/Components/DescriptionItem/types.js.map +1 -0
  32. package/dist/module/lib/Components/Divider/Divider.mdx +1 -1
  33. package/dist/module/lib/Components/DotIcon/DotIcon.js +44 -23
  34. package/dist/module/lib/Components/DotIcon/DotIcon.js.map +1 -1
  35. package/dist/module/lib/Components/DotIcon/DotIcon.mdx +92 -0
  36. package/dist/module/lib/Components/DotIcon/DotIcon.stories.js +47 -0
  37. package/dist/module/lib/Components/DotIcon/DotIcon.stories.js.map +1 -1
  38. package/dist/module/lib/Components/DotSymbol/DotSymbol.js +52 -30
  39. package/dist/module/lib/Components/DotSymbol/DotSymbol.js.map +1 -1
  40. package/dist/module/lib/Components/DotSymbol/DotSymbol.mdx +79 -2
  41. package/dist/module/lib/Components/DotSymbol/DotSymbol.stories.js +41 -0
  42. package/dist/module/lib/Components/DotSymbol/DotSymbol.stories.js.map +1 -1
  43. package/dist/module/lib/Components/IconButton/IconButton.mdx +1 -1
  44. package/dist/module/lib/Components/InteractiveIcon/InteractiveIcon.mdx +1 -1
  45. package/dist/module/lib/Components/Link/Link.mdx +1 -1
  46. package/dist/module/lib/Components/MediaBanner/MediaBanner.mdx +1 -1
  47. package/dist/module/lib/Components/MediaButton/MediaButton.js +17 -17
  48. package/dist/module/lib/Components/MediaButton/MediaButton.js.map +1 -1
  49. package/dist/module/lib/Components/MediaButton/MediaButton.mdx +3 -3
  50. package/dist/module/lib/Components/MediaButton/MediaButton.stories.js +17 -17
  51. package/dist/module/lib/Components/MediaButton/MediaButton.stories.js.map +1 -1
  52. package/dist/module/lib/Components/MediaButton/MediaButton.test.js +4 -4
  53. package/dist/module/lib/Components/MediaButton/MediaButton.test.js.map +1 -1
  54. package/dist/module/lib/Components/MediaImage/MediaImage.js +20 -4
  55. package/dist/module/lib/Components/MediaImage/MediaImage.js.map +1 -1
  56. package/dist/module/lib/Components/MediaImage/MediaImage.stories.js +35 -0
  57. package/dist/module/lib/Components/MediaImage/MediaImage.stories.js.map +1 -1
  58. package/dist/module/lib/Components/NavBar/CoinCapsule.js +3 -2
  59. package/dist/module/lib/Components/NavBar/CoinCapsule.js.map +1 -1
  60. package/dist/module/lib/Components/NavBar/NavBar.js +2 -2
  61. package/dist/module/lib/Components/NavBar/NavBar.js.map +1 -1
  62. package/dist/module/lib/Components/NavBar/NavBar.mdx +2 -2
  63. package/dist/module/lib/Components/NavBar/NavBar.stories.js +1 -1
  64. package/dist/module/lib/Components/NavBar/NavBar.stories.js.map +1 -1
  65. package/dist/module/lib/Components/NavBar/NavBar.test.js +3 -3
  66. package/dist/module/lib/Components/NavBar/NavBar.test.js.map +1 -1
  67. package/dist/module/lib/Components/OptionList/OptionList.stories.js +4 -4
  68. package/dist/module/lib/Components/OptionList/OptionList.stories.js.map +1 -1
  69. package/dist/module/lib/Components/PageIndicator/PageIndicator.js +13 -8
  70. package/dist/module/lib/Components/PageIndicator/PageIndicator.js.map +1 -1
  71. package/dist/module/lib/Components/PageIndicator/PageIndicator.test.js +58 -0
  72. package/dist/module/lib/Components/PageIndicator/PageIndicator.test.js.map +1 -1
  73. package/dist/module/lib/Components/SearchInput/SearchInput.mdx +1 -1
  74. package/dist/module/lib/Components/SegmentedControl/SegmentedControl.mdx +1 -1
  75. package/dist/module/lib/Components/Select/Select.mdx +1 -1
  76. package/dist/module/lib/Components/Spinner/Spinner.mdx +1 -1
  77. package/dist/module/lib/Components/Spot/Spot.mdx +1 -1
  78. package/dist/module/lib/Components/Stepper/Stepper.mdx +1 -1
  79. package/dist/module/lib/Components/Subheader/Subheader.mdx +1 -1
  80. package/dist/module/lib/Components/Switch/Switch.mdx +1 -1
  81. package/dist/module/lib/Components/TabBar/TabBar.mdx +1 -1
  82. package/dist/module/lib/Components/TextInput/TextInput.mdx +1 -1
  83. package/dist/module/lib/Components/Tile/Tile.mdx +1 -1
  84. package/dist/module/lib/Components/Tooltip/Tooltip.mdx +1 -1
  85. package/dist/module/lib/Components/index.js +1 -0
  86. package/dist/module/lib/Components/index.js.map +1 -1
  87. package/dist/typescript/src/lib/Animations/Pulse/Pulse.d.ts.map +1 -1
  88. package/dist/typescript/src/lib/Components/AmountDisplay/AmountDisplay.d.ts +1 -1
  89. package/dist/typescript/src/lib/Components/AmountDisplay/AmountDisplay.d.ts.map +1 -1
  90. package/dist/typescript/src/lib/Components/AmountDisplay/index.d.ts +1 -1
  91. package/dist/typescript/src/lib/Components/AmountDisplay/index.d.ts.map +1 -1
  92. package/dist/typescript/src/lib/Components/AmountDisplay/types.d.ts +10 -3
  93. package/dist/typescript/src/lib/Components/AmountDisplay/types.d.ts.map +1 -1
  94. package/dist/typescript/src/lib/Components/DescriptionItem/DescriptionItem.d.ts +42 -0
  95. package/dist/typescript/src/lib/Components/DescriptionItem/DescriptionItem.d.ts.map +1 -0
  96. package/dist/typescript/src/lib/Components/DescriptionItem/index.d.ts +3 -0
  97. package/dist/typescript/src/lib/Components/DescriptionItem/index.d.ts.map +1 -0
  98. package/dist/typescript/src/lib/Components/DescriptionItem/types.d.ts +39 -0
  99. package/dist/typescript/src/lib/Components/DescriptionItem/types.d.ts.map +1 -0
  100. package/dist/typescript/src/lib/Components/DotIcon/DotIcon.d.ts +1 -1
  101. package/dist/typescript/src/lib/Components/DotIcon/DotIcon.d.ts.map +1 -1
  102. package/dist/typescript/src/lib/Components/DotIcon/types.d.ts +6 -0
  103. package/dist/typescript/src/lib/Components/DotIcon/types.d.ts.map +1 -1
  104. package/dist/typescript/src/lib/Components/DotSymbol/DotSymbol.d.ts +1 -1
  105. package/dist/typescript/src/lib/Components/DotSymbol/DotSymbol.d.ts.map +1 -1
  106. package/dist/typescript/src/lib/Components/DotSymbol/types.d.ts +6 -0
  107. package/dist/typescript/src/lib/Components/DotSymbol/types.d.ts.map +1 -1
  108. package/dist/typescript/src/lib/Components/MediaButton/MediaButton.d.ts +3 -3
  109. package/dist/typescript/src/lib/Components/MediaButton/MediaButton.d.ts.map +1 -1
  110. package/dist/typescript/src/lib/Components/MediaButton/types.d.ts +6 -6
  111. package/dist/typescript/src/lib/Components/MediaButton/types.d.ts.map +1 -1
  112. package/dist/typescript/src/lib/Components/MediaImage/MediaImage.d.ts +1 -1
  113. package/dist/typescript/src/lib/Components/MediaImage/MediaImage.d.ts.map +1 -1
  114. package/dist/typescript/src/lib/Components/MediaImage/types.d.ts +6 -0
  115. package/dist/typescript/src/lib/Components/MediaImage/types.d.ts.map +1 -1
  116. package/dist/typescript/src/lib/Components/NavBar/CoinCapsule.d.ts +1 -1
  117. package/dist/typescript/src/lib/Components/NavBar/CoinCapsule.d.ts.map +1 -1
  118. package/dist/typescript/src/lib/Components/NavBar/NavBar.d.ts +1 -1
  119. package/dist/typescript/src/lib/Components/NavBar/NavBar.d.ts.map +1 -1
  120. package/dist/typescript/src/lib/Components/NavBar/types.d.ts +3 -3
  121. package/dist/typescript/src/lib/Components/NavBar/types.d.ts.map +1 -1
  122. package/dist/typescript/src/lib/Components/PageIndicator/PageIndicator.d.ts.map +1 -1
  123. package/dist/typescript/src/lib/Components/index.d.ts +1 -0
  124. package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
  125. package/package.json +3 -3
  126. package/src/lib/Animations/Pulse/Pulse.mdx +1 -1
  127. package/src/lib/Animations/Pulse/Pulse.tsx +6 -3
  128. package/src/lib/Animations/Spin/Spin.mdx +1 -1
  129. package/src/lib/Components/AddressInput/AddressInput.mdx +1 -1
  130. package/src/lib/Components/AmountDisplay/AmountDisplay.mdx +7 -1
  131. package/src/lib/Components/AmountDisplay/AmountDisplay.stories.tsx +18 -0
  132. package/src/lib/Components/AmountDisplay/AmountDisplay.tsx +71 -40
  133. package/src/lib/Components/AmountDisplay/index.ts +5 -1
  134. package/src/lib/Components/AmountDisplay/types.ts +12 -3
  135. package/src/lib/Components/AmountInput/AmountInput.mdx +1 -1
  136. package/src/lib/Components/Avatar/Avatar.mdx +1 -1
  137. package/src/lib/Components/Banner/Banner.mdx +1 -1
  138. package/src/lib/Components/BottomSheet/BottomSheet.mdx +1 -1
  139. package/src/lib/Components/Button/Button.mdx +1 -1
  140. package/src/lib/Components/Card/Card.stories.tsx +1 -3
  141. package/src/lib/Components/CardButton/CardButton.mdx +1 -1
  142. package/src/lib/Components/Checkbox/Checkbox.mdx +1 -1
  143. package/src/lib/Components/DescriptionItem/DescriptionItem.mdx +139 -0
  144. package/src/lib/Components/DescriptionItem/DescriptionItem.stories.tsx +234 -0
  145. package/src/lib/Components/DescriptionItem/DescriptionItem.test.tsx +112 -0
  146. package/src/lib/Components/DescriptionItem/DescriptionItem.tsx +224 -0
  147. package/src/lib/Components/DescriptionItem/index.ts +2 -0
  148. package/src/lib/Components/DescriptionItem/types.ts +44 -0
  149. package/src/lib/Components/Divider/Divider.mdx +1 -1
  150. package/src/lib/Components/DotIcon/DotIcon.mdx +92 -0
  151. package/src/lib/Components/DotIcon/DotIcon.stories.tsx +35 -0
  152. package/src/lib/Components/DotIcon/DotIcon.tsx +31 -14
  153. package/src/lib/Components/DotIcon/types.ts +6 -0
  154. package/src/lib/Components/DotSymbol/DotSymbol.mdx +79 -2
  155. package/src/lib/Components/DotSymbol/DotSymbol.stories.tsx +17 -0
  156. package/src/lib/Components/DotSymbol/DotSymbol.tsx +42 -24
  157. package/src/lib/Components/DotSymbol/types.ts +6 -0
  158. package/src/lib/Components/IconButton/IconButton.mdx +1 -1
  159. package/src/lib/Components/InteractiveIcon/InteractiveIcon.mdx +1 -1
  160. package/src/lib/Components/Link/Link.mdx +1 -1
  161. package/src/lib/Components/MediaBanner/MediaBanner.mdx +1 -1
  162. package/src/lib/Components/MediaButton/MediaButton.mdx +3 -3
  163. package/src/lib/Components/MediaButton/MediaButton.stories.tsx +29 -15
  164. package/src/lib/Components/MediaButton/MediaButton.test.tsx +4 -4
  165. package/src/lib/Components/MediaButton/MediaButton.tsx +33 -20
  166. package/src/lib/Components/MediaButton/types.ts +6 -6
  167. package/src/lib/Components/MediaImage/MediaImage.stories.tsx +18 -0
  168. package/src/lib/Components/MediaImage/MediaImage.tsx +12 -2
  169. package/src/lib/Components/MediaImage/types.ts +6 -0
  170. package/src/lib/Components/NavBar/CoinCapsule.tsx +3 -2
  171. package/src/lib/Components/NavBar/NavBar.mdx +2 -2
  172. package/src/lib/Components/NavBar/NavBar.stories.tsx +3 -1
  173. package/src/lib/Components/NavBar/NavBar.test.tsx +3 -3
  174. package/src/lib/Components/NavBar/NavBar.tsx +2 -2
  175. package/src/lib/Components/NavBar/types.ts +3 -3
  176. package/src/lib/Components/OptionList/OptionList.stories.tsx +4 -4
  177. package/src/lib/Components/PageIndicator/PageIndicator.test.tsx +78 -0
  178. package/src/lib/Components/PageIndicator/PageIndicator.tsx +15 -7
  179. package/src/lib/Components/SearchInput/SearchInput.mdx +1 -1
  180. package/src/lib/Components/SegmentedControl/SegmentedControl.mdx +1 -1
  181. package/src/lib/Components/Select/Select.mdx +1 -1
  182. package/src/lib/Components/Spinner/Spinner.mdx +1 -1
  183. package/src/lib/Components/Spot/Spot.mdx +1 -1
  184. package/src/lib/Components/Stepper/Stepper.mdx +1 -1
  185. package/src/lib/Components/Subheader/Subheader.mdx +1 -1
  186. package/src/lib/Components/Switch/Switch.mdx +1 -1
  187. package/src/lib/Components/TabBar/TabBar.mdx +1 -1
  188. package/src/lib/Components/TextInput/TextInput.mdx +1 -1
  189. package/src/lib/Components/Tile/Tile.mdx +1 -1
  190. package/src/lib/Components/Tooltip/Tooltip.mdx +1 -1
  191. package/src/lib/Components/index.ts +1 -0
@@ -0,0 +1,139 @@
1
+ import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as DescriptionItemStories from './DescriptionItem.stories';
3
+ import { CustomTabs, Tab } from '../../../../.storybook/components';
4
+
5
+ <Meta title='Containment/DescriptionItem' of={DescriptionItemStories} />
6
+
7
+ # DescriptionItem
8
+
9
+ > View in [Figma](https://www.figma.com/design/JxaLVMTWirCpU0rsbZ30k7/2.-Components-Library?node-id=10311-11837&m=dev).
10
+
11
+ <CustomTabs>
12
+ <Tab label="Overview">
13
+
14
+ ## Introduction
15
+
16
+ DescriptionItem is a compound component for displaying key-value rows — typically used inside a summary panel, transaction detail, or settings screen. It composes a leading label side and a trailing value side, with the trailing always taking priority over the leading when space is constrained.
17
+
18
+ ## Anatomy
19
+
20
+ <Canvas of={DescriptionItemStories.Base} />
21
+
22
+ - **DescriptionItem**: Root row container. Accepts an optional `size` prop (`md` | `sm`).
23
+ - **DescriptionItemLeading**: Left section holding the label and any optional sibling (e.g. a tooltip icon).
24
+ - **DescriptionItemLabel**: Muted text label. Wraps to a second line before truncating (`numberOfLines={2}`).
25
+ - **DescriptionItemTrailing**: Right section holding the value or any inline element (Tag, Link, etc.). Never shrinks — always has layout priority.
26
+ - **DescriptionItemValue**: Semi-bold value text. Truncates to a single line (`numberOfLines={1}`).
27
+
28
+ ## Properties
29
+
30
+ <Canvas of={DescriptionItemStories.Base} />
31
+ <Controls of={DescriptionItemStories.Base} />
32
+
33
+ ### Trailing variants
34
+
35
+ The trailing section accepts any content — a plain value, a `Tag`, multiple `Tag`s, or a custom element. Use `DescriptionItemValue` for plain text; place other components directly in `DescriptionItemTrailing`.
36
+
37
+ <Canvas of={DescriptionItemStories.TrailingVariants} />
38
+
39
+ ### Size
40
+
41
+ Two sizes are available — `md` (default) and `sm`.
42
+
43
+ - The size of `DescriptionItemValue` is inherited from the parent `DescriptionItem`.
44
+ - The size of additional trailing content (e.g. `Tag`) needs to be set explicitly.
45
+
46
+ <Canvas of={DescriptionItemStories.SizeShowcase} />
47
+
48
+ ### With info tooltip
49
+
50
+ Pair a `Tooltip` with an info icon inside `DescriptionItemLeading` to surface additional context without cluttering the label.
51
+
52
+ <Canvas of={DescriptionItemStories.WithInfoIcon} />
53
+
54
+ ### Truncation
55
+
56
+ The label can wrap to two lines before truncating. The trailing value truncates after one line. When both sides have long content, the trailing always gets layout priority.
57
+
58
+ <Canvas of={DescriptionItemStories.TruncationBehavior} />
59
+
60
+ </Tab>
61
+ <Tab label="Implementation">
62
+
63
+ ## Setup
64
+
65
+ Install and set up the library with our [Setup Guide →](?path=/docs/getting-started-setup--docs).
66
+
67
+ ## Basic Usage
68
+
69
+ ```tsx
70
+ import {
71
+ DescriptionItem,
72
+ DescriptionItemLabel,
73
+ DescriptionItemLeading,
74
+ DescriptionItemTrailing,
75
+ DescriptionItemValue,
76
+ } from '@ledgerhq/lumen-ui-rnative';
77
+
78
+ function MyComponent() {
79
+ return (
80
+ <DescriptionItem size='md'>
81
+ <DescriptionItemLeading>
82
+ <DescriptionItemLabel>Fees</DescriptionItemLabel>
83
+ </DescriptionItemLeading>
84
+ <DescriptionItemTrailing>
85
+ <DescriptionItemValue>0.001 BTC</DescriptionItemValue>
86
+ </DescriptionItemTrailing>
87
+ </DescriptionItem>
88
+ );
89
+ }
90
+ ```
91
+
92
+ ### With info tooltip
93
+
94
+ Use a `Tooltip` alongside the label to add contextual information:
95
+
96
+ ```tsx
97
+ import {
98
+ DescriptionItem,
99
+ DescriptionItemLabel,
100
+ DescriptionItemLeading,
101
+ DescriptionItemTrailing,
102
+ DescriptionItemValue,
103
+ InteractiveIcon,
104
+ Tooltip,
105
+ TooltipTrigger,
106
+ TooltipContent,
107
+ } from '@ledgerhq/lumen-ui-rnative';
108
+ import { Information } from '@ledgerhq/lumen-ui-rnative/symbols';
109
+ import { Text } from 'react-native';
110
+
111
+ function MyComponent() {
112
+ return (
113
+ <DescriptionItem>
114
+ <DescriptionItemLeading>
115
+ <DescriptionItemLabel>Fees</DescriptionItemLabel>
116
+ <Tooltip>
117
+ <TooltipTrigger>
118
+ <InteractiveIcon
119
+ icon={Information}
120
+ size={16}
121
+ iconType='stroked'
122
+ accessibilityLabel='More information'
123
+ />
124
+ </TooltipTrigger>
125
+ <TooltipContent
126
+ content={<Text>Network fee paid to miners</Text>}
127
+ />
128
+ </Tooltip>
129
+ </DescriptionItemLeading>
130
+ <DescriptionItemTrailing>
131
+ <DescriptionItemValue>0.001 BTC</DescriptionItemValue>
132
+ </DescriptionItemTrailing>
133
+ </DescriptionItem>
134
+ );
135
+ }
136
+ ```
137
+
138
+ </Tab>
139
+ </CustomTabs>
@@ -0,0 +1,234 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-native';
2
+ import { Text, View } from 'react-native';
3
+ import { Information } from '../../Symbols';
4
+ import { InteractiveIcon } from '../InteractiveIcon';
5
+ import { Tag } from '../Tag';
6
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../Tooltip';
7
+ import {
8
+ DescriptionItem,
9
+ DescriptionItemLabel,
10
+ DescriptionItemLeading,
11
+ DescriptionItemTrailing,
12
+ DescriptionItemValue,
13
+ } from './DescriptionItem';
14
+
15
+ const Container = ({ children }: { children: React.ReactNode }) => (
16
+ <View style={{ padding: 8, backgroundColor: '#ffffff', width: 320 }}>
17
+ {children}
18
+ </View>
19
+ );
20
+
21
+ const meta: Meta<typeof DescriptionItem> = {
22
+ component: DescriptionItem,
23
+ title: 'Containment/DescriptionItem',
24
+ subcomponents: {
25
+ DescriptionItemLeading,
26
+ DescriptionItemLabel,
27
+ DescriptionItemTrailing,
28
+ DescriptionItemValue,
29
+ },
30
+ parameters: {
31
+ layout: 'centered',
32
+ docs: {
33
+ source: {
34
+ language: 'tsx',
35
+ format: true,
36
+ type: 'code',
37
+ },
38
+ },
39
+ },
40
+ decorators: [
41
+ (Story) => (
42
+ <Container>
43
+ <Story />
44
+ </Container>
45
+ ),
46
+ ],
47
+ };
48
+
49
+ export default meta;
50
+ type Story = StoryObj<typeof DescriptionItem>;
51
+
52
+ export const Base: Story = {
53
+ args: {
54
+ size: 'md',
55
+ },
56
+ render: (args) => (
57
+ <DescriptionItem {...args}>
58
+ <DescriptionItemLeading>
59
+ <DescriptionItemLabel>Fees</DescriptionItemLabel>
60
+ </DescriptionItemLeading>
61
+ <DescriptionItemTrailing>
62
+ <DescriptionItemValue>0.001 BTC</DescriptionItemValue>
63
+ </DescriptionItemTrailing>
64
+ </DescriptionItem>
65
+ ),
66
+ };
67
+
68
+ export const SizeShowcase: Story = {
69
+ args: {
70
+ size: 'md',
71
+ },
72
+ render: () => (
73
+ <View style={{ gap: 16 }}>
74
+ <DescriptionItem size='md'>
75
+ <DescriptionItemLeading>
76
+ <DescriptionItemLabel>Network</DescriptionItemLabel>
77
+ </DescriptionItemLeading>
78
+ <DescriptionItemTrailing>
79
+ <DescriptionItemValue>Ethereum</DescriptionItemValue>
80
+ </DescriptionItemTrailing>
81
+ </DescriptionItem>
82
+
83
+ <DescriptionItem size='md'>
84
+ <DescriptionItemLeading>
85
+ <DescriptionItemLabel>Network</DescriptionItemLabel>
86
+ </DescriptionItemLeading>
87
+ <DescriptionItemTrailing>
88
+ <Tag size='md' label='Ethereum' appearance='base' />
89
+ </DescriptionItemTrailing>
90
+ </DescriptionItem>
91
+ <br />
92
+ <DescriptionItem size='sm'>
93
+ <DescriptionItemLeading>
94
+ <DescriptionItemLabel>Network</DescriptionItemLabel>
95
+ </DescriptionItemLeading>
96
+ <DescriptionItemTrailing>
97
+ <DescriptionItemValue>Ethereum</DescriptionItemValue>
98
+ </DescriptionItemTrailing>
99
+ </DescriptionItem>
100
+
101
+ <DescriptionItem size='sm'>
102
+ <DescriptionItemLeading>
103
+ <DescriptionItemLabel>Network</DescriptionItemLabel>
104
+ </DescriptionItemLeading>
105
+ <DescriptionItemTrailing>
106
+ <Tag size='sm' label='Ethereum' appearance='base' />
107
+ </DescriptionItemTrailing>
108
+ </DescriptionItem>
109
+ </View>
110
+ ),
111
+ };
112
+
113
+ export const TrailingVariants: Story = {
114
+ render: () => (
115
+ <View style={{ gap: 16 }}>
116
+ <DescriptionItem>
117
+ <DescriptionItemLeading>
118
+ <DescriptionItemLabel>Plain value</DescriptionItemLabel>
119
+ </DescriptionItemLeading>
120
+ <DescriptionItemTrailing>
121
+ <DescriptionItemValue>Ethereum</DescriptionItemValue>
122
+ </DescriptionItemTrailing>
123
+ </DescriptionItem>
124
+
125
+ <DescriptionItem>
126
+ <DescriptionItemLeading>
127
+ <DescriptionItemLabel>Tag</DescriptionItemLabel>
128
+ </DescriptionItemLeading>
129
+ <DescriptionItemTrailing>
130
+ <Tag size='md' label='Confirmed' appearance='success' />
131
+ </DescriptionItemTrailing>
132
+ </DescriptionItem>
133
+
134
+ <DescriptionItem>
135
+ <DescriptionItemLeading>
136
+ <DescriptionItemLabel>Multiple tags</DescriptionItemLabel>
137
+ </DescriptionItemLeading>
138
+ <DescriptionItemTrailing>
139
+ <Tag size='md' label='ERC-20' appearance='base' />
140
+ <Tag size='md' label='New' appearance='accent' />
141
+ </DescriptionItemTrailing>
142
+ </DescriptionItem>
143
+ </View>
144
+ ),
145
+ };
146
+
147
+ export const WithInfoIcon: Story = {
148
+ render: () => (
149
+ <View style={{ gap: 16 }}>
150
+ <DescriptionItem>
151
+ <DescriptionItemLeading>
152
+ <DescriptionItemLabel>Fees</DescriptionItemLabel>
153
+ <Tooltip>
154
+ <TooltipTrigger>
155
+ <InteractiveIcon
156
+ icon={Information}
157
+ size={16}
158
+ iconType='stroked'
159
+ accessibilityLabel='More information'
160
+ />
161
+ </TooltipTrigger>
162
+ <TooltipContent content={<Text>Network fee paid to miners</Text>} />
163
+ </Tooltip>
164
+ </DescriptionItemLeading>
165
+ <DescriptionItemTrailing>
166
+ <DescriptionItemValue>0.001 BTC</DescriptionItemValue>
167
+ </DescriptionItemTrailing>
168
+ </DescriptionItem>
169
+
170
+ <DescriptionItem>
171
+ <DescriptionItemLeading>
172
+ <DescriptionItemLabel>Estimated time</DescriptionItemLabel>
173
+ <Tooltip>
174
+ <TooltipTrigger>
175
+ <InteractiveIcon
176
+ icon={Information}
177
+ size={16}
178
+ iconType='stroked'
179
+ accessibilityLabel='More information'
180
+ />
181
+ </TooltipTrigger>
182
+ <TooltipContent
183
+ content={<Text>Time may vary based on network congestion</Text>}
184
+ />
185
+ </Tooltip>
186
+ </DescriptionItemLeading>
187
+ <DescriptionItemTrailing>
188
+ <DescriptionItemValue>~30 min</DescriptionItemValue>
189
+ </DescriptionItemTrailing>
190
+ </DescriptionItem>
191
+ </View>
192
+ ),
193
+ };
194
+
195
+ export const TruncationBehavior: Story = {
196
+ render: () => (
197
+ <View style={{ gap: 16 }}>
198
+ <DescriptionItem>
199
+ <DescriptionItemLeading>
200
+ <DescriptionItemLabel>
201
+ This is a very long label that should go to a second line.
202
+ </DescriptionItemLabel>
203
+ </DescriptionItemLeading>
204
+ <DescriptionItemTrailing>
205
+ <DescriptionItemValue>0.001 BTC</DescriptionItemValue>
206
+ </DescriptionItemTrailing>
207
+ </DescriptionItem>
208
+
209
+ <DescriptionItem>
210
+ <DescriptionItemLeading>
211
+ <DescriptionItemLabel>Label</DescriptionItemLabel>
212
+ </DescriptionItemLeading>
213
+ <DescriptionItemTrailing>
214
+ <DescriptionItemValue>
215
+ 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
216
+ </DescriptionItemValue>
217
+ </DescriptionItemTrailing>
218
+ </DescriptionItem>
219
+
220
+ <DescriptionItem>
221
+ <DescriptionItemLeading>
222
+ <DescriptionItemLabel>
223
+ Destination address on network
224
+ </DescriptionItemLabel>
225
+ </DescriptionItemLeading>
226
+ <DescriptionItemTrailing>
227
+ <DescriptionItemValue>
228
+ 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
229
+ </DescriptionItemValue>
230
+ </DescriptionItemTrailing>
231
+ </DescriptionItem>
232
+ </View>
233
+ ),
234
+ };
@@ -0,0 +1,112 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
3
+ import { render, screen } from '@testing-library/react-native';
4
+
5
+ import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
6
+ import {
7
+ DescriptionItem,
8
+ DescriptionItemLabel,
9
+ DescriptionItemLeading,
10
+ DescriptionItemTrailing,
11
+ DescriptionItemValue,
12
+ } from './DescriptionItem';
13
+ import type { DescriptionItemProps, DescriptionItemSize } from './types';
14
+
15
+ const typographyTokens = ledgerLiveThemes.dark.typographies.sm;
16
+ const typographies = {
17
+ ...typographyTokens.heading,
18
+ ...typographyTokens.body,
19
+ };
20
+
21
+ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
22
+ <ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
23
+ {children}
24
+ </ThemeProvider>
25
+ );
26
+
27
+ const renderDescriptionItem = (props: Partial<DescriptionItemProps> = {}) =>
28
+ render(
29
+ <TestWrapper>
30
+ <DescriptionItem testID='description-item' {...props}>
31
+ <DescriptionItemLeading testID='leading'>
32
+ <DescriptionItemLabel>Label</DescriptionItemLabel>
33
+ </DescriptionItemLeading>
34
+ <DescriptionItemTrailing testID='trailing'>
35
+ <DescriptionItemValue>Value</DescriptionItemValue>
36
+ </DescriptionItemTrailing>
37
+ </DescriptionItem>
38
+ </TestWrapper>,
39
+ );
40
+
41
+ describe('DescriptionItem', () => {
42
+ describe('Rendering', () => {
43
+ it('renders label and value', () => {
44
+ renderDescriptionItem();
45
+
46
+ expect(screen.getByText('Label')).toBeTruthy();
47
+ expect(screen.getByText('Value')).toBeTruthy();
48
+ });
49
+
50
+ it('forwards testID to sub-components', () => {
51
+ renderDescriptionItem();
52
+
53
+ expect(screen.getByTestId('description-item')).toBeTruthy();
54
+ expect(screen.getByTestId('leading')).toBeTruthy();
55
+ expect(screen.getByTestId('trailing')).toBeTruthy();
56
+ });
57
+ });
58
+
59
+ describe('DescriptionItemLabel', () => {
60
+ it.each([
61
+ ['md', typographies.body2],
62
+ ['sm', typographies.body3],
63
+ ])('applies %s size typography', (size, expectedTypography) => {
64
+ renderDescriptionItem({ size: size as DescriptionItemSize });
65
+
66
+ const labelStyle = screen.getByText('Label').props.style;
67
+ expect(labelStyle.fontSize).toBe(expectedTypography.fontSize);
68
+ expect(labelStyle.fontWeight).toBe(expectedTypography.fontWeight);
69
+ });
70
+
71
+ it('defaults to md typography when no size is provided', () => {
72
+ renderDescriptionItem();
73
+
74
+ const labelStyle = screen.getByText('Label').props.style;
75
+ expect(labelStyle.fontSize).toBe(typographies.body2.fontSize);
76
+ expect(labelStyle.fontWeight).toBe(typographies.body2.fontWeight);
77
+ });
78
+
79
+ it('truncates label to two lines by default', () => {
80
+ renderDescriptionItem();
81
+
82
+ expect(screen.getByText('Label').props.numberOfLines).toBe(2);
83
+ });
84
+ });
85
+
86
+ describe('DescriptionItemValue', () => {
87
+ it.each([
88
+ ['md', typographies.body2SemiBold],
89
+ ['sm', typographies.body3SemiBold],
90
+ ])('applies %s size typography', (size, expectedTypography) => {
91
+ renderDescriptionItem({ size: size as DescriptionItemSize });
92
+
93
+ const valueStyle = screen.getByText('Value').props.style;
94
+ expect(valueStyle.fontSize).toBe(expectedTypography.fontSize);
95
+ expect(valueStyle.fontWeight).toBe(expectedTypography.fontWeight);
96
+ });
97
+
98
+ it('defaults to md typography when no size is provided', () => {
99
+ renderDescriptionItem();
100
+
101
+ const valueStyle = screen.getByText('Value').props.style;
102
+ expect(valueStyle.fontSize).toBe(typographies.body2SemiBold.fontSize);
103
+ expect(valueStyle.fontWeight).toBe(typographies.body2SemiBold.fontWeight);
104
+ });
105
+
106
+ it('truncates value to one line by default', () => {
107
+ renderDescriptionItem();
108
+
109
+ expect(screen.getByText('Value').props.numberOfLines).toBe(1);
110
+ });
111
+ });
112
+ });
@@ -0,0 +1,224 @@
1
+ import { createSafeContext } from '@ledgerhq/lumen-utils-shared';
2
+ import { StyleSheet } from 'react-native';
3
+ import { useStyleSheet } from '../../../styles';
4
+ import { Box, Text } from '../Utility';
5
+ import type {
6
+ DescriptionItemLabelProps,
7
+ DescriptionItemLeadingProps,
8
+ DescriptionItemProps,
9
+ DescriptionItemSize,
10
+ DescriptionItemTrailingProps,
11
+ DescriptionItemValueProps,
12
+ } from './types';
13
+
14
+ type DescriptionItemSizeContextValue = {
15
+ size: DescriptionItemSize;
16
+ };
17
+
18
+ const [DescriptionItemSizeProvider, useDescriptionItemSizeContext] =
19
+ createSafeContext<DescriptionItemSizeContextValue>('DescriptionItemSize', {
20
+ size: 'md',
21
+ });
22
+
23
+ /**
24
+ * A compound component for displaying a key-value description row.
25
+ * Composed with DescriptionItemLeading / DescriptionItemLabel (key side)
26
+ * and DescriptionItemTrailing / DescriptionItemValue (value side).
27
+ *
28
+ * @see {@link https://ldls.vercel.app/?path=/docs/containment-descriptionitem--docs Storybook}
29
+ *
30
+ * @example
31
+ * <DescriptionItem size="md">
32
+ * <DescriptionItemLeading>
33
+ * <DescriptionItemLabel>Fees</DescriptionItemLabel>
34
+ * </DescriptionItemLeading>
35
+ * <DescriptionItemTrailing>
36
+ * <DescriptionItemValue>0.001 BTC</DescriptionItemValue>
37
+ * </DescriptionItemTrailing>
38
+ * </DescriptionItem>
39
+ */
40
+ export const DescriptionItem = ({
41
+ children,
42
+ lx = {},
43
+ style,
44
+ size = 'md',
45
+ ...props
46
+ }: DescriptionItemProps) => {
47
+ const styles = useStyleSheet(
48
+ (t) => ({
49
+ root: {
50
+ width: '100%',
51
+ flexDirection: 'row',
52
+ alignItems: 'flex-start',
53
+ gap: t.spacings.s12,
54
+ },
55
+ }),
56
+ [],
57
+ );
58
+
59
+ return (
60
+ <DescriptionItemSizeProvider value={{ size }}>
61
+ <Box lx={lx} style={StyleSheet.flatten([styles.root, style])} {...props}>
62
+ {children}
63
+ </Box>
64
+ </DescriptionItemSizeProvider>
65
+ );
66
+ };
67
+
68
+ /**
69
+ * Layout container for the leading (left) side of the description item.
70
+ * Contains DescriptionItemLabel and an optional info icon sibling.
71
+ */
72
+ export const DescriptionItemLeading = ({
73
+ children,
74
+ lx = {},
75
+ style,
76
+ ...props
77
+ }: DescriptionItemLeadingProps) => {
78
+ const styles = useStyleSheet(
79
+ (t) => ({
80
+ leading: {
81
+ flex: 1,
82
+ minWidth: 0,
83
+ flexDirection: 'row',
84
+ alignItems: 'center',
85
+ gap: t.spacings.s4,
86
+ },
87
+ }),
88
+ [],
89
+ );
90
+
91
+ return (
92
+ <Box lx={lx} style={StyleSheet.flatten([styles.leading, style])} {...props}>
93
+ {children}
94
+ </Box>
95
+ );
96
+ };
97
+
98
+ /**
99
+ * Typography-bearing label for the leading side.
100
+ * Reads size from DescriptionItemSizeContext to apply the correct typography
101
+ * based on the size of the parent `DescriptionItem`.
102
+ */
103
+ export const DescriptionItemLabel = ({
104
+ children,
105
+ lx = {},
106
+ style,
107
+ numberOfLines = 2,
108
+ ellipsizeMode = 'tail',
109
+ ...props
110
+ }: DescriptionItemLabelProps) => {
111
+ const { size } = useDescriptionItemSizeContext({
112
+ consumerName: 'DescriptionItemLabel',
113
+ contextRequired: false,
114
+ });
115
+
116
+ const styles = useStyleSheet(
117
+ (t) => ({
118
+ label: StyleSheet.flatten([
119
+ size === 'md' ? t.typographies.body2 : t.typographies.body3,
120
+ {
121
+ flexShrink: 1,
122
+ minWidth: 0,
123
+ color: t.colors.text.muted,
124
+ },
125
+ ]),
126
+ }),
127
+ [size],
128
+ );
129
+
130
+ return (
131
+ <Text
132
+ lx={lx}
133
+ style={StyleSheet.flatten([styles.label, style])}
134
+ numberOfLines={numberOfLines}
135
+ ellipsizeMode={ellipsizeMode}
136
+ allowFontScaling={false}
137
+ {...props}
138
+ >
139
+ {children}
140
+ </Text>
141
+ );
142
+ };
143
+
144
+ /**
145
+ * Layout container for the trailing (right) side of the description item.
146
+ * Accepts DescriptionItemValue, Tag, Link, or any custom content.
147
+ */
148
+ export const DescriptionItemTrailing = ({
149
+ children,
150
+ lx = {},
151
+ style,
152
+ ...props
153
+ }: DescriptionItemTrailingProps) => {
154
+ const styles = useStyleSheet(
155
+ (t) => ({
156
+ trailing: {
157
+ flexShrink: 0,
158
+ maxWidth: '80%',
159
+ flexDirection: 'row',
160
+ alignItems: 'center',
161
+ gap: t.spacings.s4,
162
+ },
163
+ }),
164
+ [],
165
+ );
166
+
167
+ return (
168
+ <Box
169
+ lx={lx}
170
+ style={StyleSheet.flatten([styles.trailing, style])}
171
+ {...props}
172
+ >
173
+ {children}
174
+ </Box>
175
+ );
176
+ };
177
+
178
+ /**
179
+ * Typography-bearing value for the trailing side.
180
+ * Reads size from DescriptionItemSizeContext to apply the correct typography
181
+ * based on the size of the parent `DescriptionItem`.
182
+ */
183
+ export const DescriptionItemValue = ({
184
+ children,
185
+ lx = {},
186
+ style,
187
+ numberOfLines = 1,
188
+ ellipsizeMode = 'tail',
189
+ ...props
190
+ }: DescriptionItemValueProps) => {
191
+ const { size } = useDescriptionItemSizeContext({
192
+ consumerName: 'DescriptionItemValue',
193
+ contextRequired: false,
194
+ });
195
+
196
+ const styles = useStyleSheet(
197
+ (t) => ({
198
+ value: StyleSheet.flatten([
199
+ size === 'md'
200
+ ? t.typographies.body2SemiBold
201
+ : t.typographies.body3SemiBold,
202
+ {
203
+ flexShrink: 1,
204
+ minWidth: 0,
205
+ color: t.colors.text.base,
206
+ },
207
+ ]),
208
+ }),
209
+ [size],
210
+ );
211
+
212
+ return (
213
+ <Text
214
+ lx={lx}
215
+ style={StyleSheet.flatten([styles.value, style])}
216
+ numberOfLines={numberOfLines}
217
+ ellipsizeMode={ellipsizeMode}
218
+ allowFontScaling={false}
219
+ {...props}
220
+ >
221
+ {children}
222
+ </Text>
223
+ );
224
+ };