@ledgerhq/lumen-ui-rnative 0.1.21 → 0.1.23

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 (99) hide show
  1. package/dist/module/lib/Components/ListItem/ListItem.js +57 -27
  2. package/dist/module/lib/Components/ListItem/ListItem.js.map +1 -1
  3. package/dist/module/lib/Components/ListItem/ListItem.mdx +15 -7
  4. package/dist/module/lib/Components/ListItem/ListItem.stories.js +497 -283
  5. package/dist/module/lib/Components/ListItem/ListItem.stories.js.map +1 -1
  6. package/dist/module/lib/Components/ListItem/ListItem.test.js +153 -0
  7. package/dist/module/lib/Components/ListItem/ListItem.test.js.map +1 -0
  8. package/dist/module/lib/Components/{TriggerButton/TriggerButton.js → MediaButton/MediaButton.js} +13 -10
  9. package/dist/module/lib/Components/MediaButton/MediaButton.js.map +1 -0
  10. package/{src/lib/Components/TriggerButton/TriggerButton.mdx → dist/module/lib/Components/MediaButton/MediaButton.mdx} +10 -10
  11. package/dist/module/lib/Components/{TriggerButton/TriggerButton.stories.js → MediaButton/MediaButton.stories.js} +18 -18
  12. package/dist/module/lib/Components/MediaButton/MediaButton.stories.js.map +1 -0
  13. package/dist/module/lib/Components/{TriggerButton/TriggerButton.test.js → MediaButton/MediaButton.test.js} +14 -14
  14. package/dist/module/lib/Components/MediaButton/MediaButton.test.js.map +1 -0
  15. package/dist/module/lib/Components/MediaButton/index.js +5 -0
  16. package/dist/module/lib/Components/MediaButton/index.js.map +1 -0
  17. package/dist/module/lib/Components/MediaButton/types.js.map +1 -0
  18. package/dist/module/lib/Components/NavBar/NavBar.js +0 -2
  19. package/dist/module/lib/Components/NavBar/NavBar.js.map +1 -1
  20. package/dist/module/lib/Components/OptionList/OptionList.figma.js +28 -0
  21. package/dist/module/lib/Components/OptionList/OptionList.figma.js.map +1 -0
  22. package/dist/module/lib/Components/OptionList/OptionList.js +452 -0
  23. package/dist/module/lib/Components/OptionList/OptionList.js.map +1 -0
  24. package/dist/module/lib/Components/OptionList/OptionList.mdx +304 -0
  25. package/dist/module/lib/Components/OptionList/OptionList.stories.js +735 -0
  26. package/dist/module/lib/Components/OptionList/OptionList.stories.js.map +1 -0
  27. package/dist/module/lib/Components/OptionList/OptionList.test.js +443 -0
  28. package/dist/module/lib/Components/OptionList/OptionList.test.js.map +1 -0
  29. package/dist/module/lib/Components/OptionList/index.js +5 -0
  30. package/dist/module/lib/Components/OptionList/index.js.map +1 -0
  31. package/dist/module/lib/Components/OptionList/types.js +4 -0
  32. package/dist/module/lib/Components/OptionList/types.js.map +1 -0
  33. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.js +36 -0
  34. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.js.map +1 -0
  35. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.test.js +84 -0
  36. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.test.js.map +1 -0
  37. package/dist/module/lib/Components/index.js +2 -1
  38. package/dist/module/lib/Components/index.js.map +1 -1
  39. package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts +8 -8
  40. package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts.map +1 -1
  41. package/dist/typescript/src/lib/Components/ListItem/types.d.ts +11 -7
  42. package/dist/typescript/src/lib/Components/ListItem/types.d.ts.map +1 -1
  43. package/dist/typescript/src/lib/Components/MediaButton/MediaButton.d.ts +23 -0
  44. package/dist/typescript/src/lib/Components/MediaButton/MediaButton.d.ts.map +1 -0
  45. package/dist/typescript/src/lib/Components/MediaButton/index.d.ts +3 -0
  46. package/dist/typescript/src/lib/Components/MediaButton/index.d.ts.map +1 -0
  47. package/dist/typescript/src/lib/Components/{TriggerButton → MediaButton}/types.d.ts +10 -5
  48. package/dist/typescript/src/lib/Components/MediaButton/types.d.ts.map +1 -0
  49. package/dist/typescript/src/lib/Components/OptionList/OptionList.d.ts +12 -0
  50. package/dist/typescript/src/lib/Components/OptionList/OptionList.d.ts.map +1 -0
  51. package/dist/typescript/src/lib/Components/OptionList/OptionList.figma.d.ts +2 -0
  52. package/dist/typescript/src/lib/Components/OptionList/OptionList.figma.d.ts.map +1 -0
  53. package/dist/typescript/src/lib/Components/OptionList/index.d.ts +3 -0
  54. package/dist/typescript/src/lib/Components/OptionList/index.d.ts.map +1 -0
  55. package/dist/typescript/src/lib/Components/OptionList/types.d.ts +97 -0
  56. package/dist/typescript/src/lib/Components/OptionList/types.d.ts.map +1 -0
  57. package/dist/typescript/src/lib/Components/OptionList/useOptionList/useOptionListItems.d.ts +12 -0
  58. package/dist/typescript/src/lib/Components/OptionList/useOptionList/useOptionListItems.d.ts.map +1 -0
  59. package/dist/typescript/src/lib/Components/index.d.ts +2 -1
  60. package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
  61. package/dist/typescript/src/styles/types/theme.types.d.ts +7 -6
  62. package/dist/typescript/src/styles/types/theme.types.d.ts.map +1 -1
  63. package/package.json +1 -1
  64. package/src/lib/Components/ListItem/ListItem.mdx +15 -7
  65. package/src/lib/Components/ListItem/ListItem.stories.tsx +354 -220
  66. package/src/lib/Components/ListItem/ListItem.test.tsx +152 -0
  67. package/src/lib/Components/ListItem/ListItem.tsx +63 -28
  68. package/src/lib/Components/ListItem/types.ts +11 -8
  69. package/{dist/module/lib/Components/TriggerButton/TriggerButton.mdx → src/lib/Components/MediaButton/MediaButton.mdx} +10 -10
  70. package/src/lib/Components/{TriggerButton/TriggerButton.stories.tsx → MediaButton/MediaButton.stories.tsx} +28 -28
  71. package/src/lib/Components/{TriggerButton/TriggerButton.test.tsx → MediaButton/MediaButton.test.tsx} +22 -22
  72. package/src/lib/Components/{TriggerButton/TriggerButton.tsx → MediaButton/MediaButton.tsx} +27 -21
  73. package/src/lib/Components/MediaButton/index.ts +2 -0
  74. package/src/lib/Components/{TriggerButton → MediaButton}/types.ts +10 -5
  75. package/src/lib/Components/NavBar/NavBar.tsx +0 -3
  76. package/src/lib/Components/OptionList/OptionList.figma.tsx +37 -0
  77. package/src/lib/Components/OptionList/OptionList.mdx +304 -0
  78. package/src/lib/Components/OptionList/OptionList.stories.tsx +755 -0
  79. package/src/lib/Components/OptionList/OptionList.test.tsx +412 -0
  80. package/src/lib/Components/OptionList/OptionList.tsx +532 -0
  81. package/src/lib/Components/OptionList/index.ts +2 -0
  82. package/src/lib/Components/OptionList/types.ts +115 -0
  83. package/src/lib/Components/OptionList/useOptionList/useOptionListItems.test.ts +73 -0
  84. package/src/lib/Components/OptionList/useOptionList/useOptionListItems.ts +49 -0
  85. package/src/lib/Components/index.ts +2 -1
  86. package/src/styles/types/theme.types.ts +8 -6
  87. package/dist/module/lib/Components/TriggerButton/TriggerButton.js.map +0 -1
  88. package/dist/module/lib/Components/TriggerButton/TriggerButton.stories.js.map +0 -1
  89. package/dist/module/lib/Components/TriggerButton/TriggerButton.test.js.map +0 -1
  90. package/dist/module/lib/Components/TriggerButton/index.js +0 -5
  91. package/dist/module/lib/Components/TriggerButton/index.js.map +0 -1
  92. package/dist/module/lib/Components/TriggerButton/types.js.map +0 -1
  93. package/dist/typescript/src/lib/Components/TriggerButton/TriggerButton.d.ts +0 -23
  94. package/dist/typescript/src/lib/Components/TriggerButton/TriggerButton.d.ts.map +0 -1
  95. package/dist/typescript/src/lib/Components/TriggerButton/index.d.ts +0 -3
  96. package/dist/typescript/src/lib/Components/TriggerButton/index.d.ts.map +0 -1
  97. package/dist/typescript/src/lib/Components/TriggerButton/types.d.ts.map +0 -1
  98. package/src/lib/Components/TriggerButton/index.ts +0 -2
  99. /package/dist/module/lib/Components/{TriggerButton → MediaButton}/types.js +0 -0
@@ -0,0 +1,412 @@
1
+ import { describe, it, expect, jest } from '@jest/globals';
2
+ import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
3
+ import { render, fireEvent } from '@testing-library/react-native';
4
+ import { Text } from 'react-native';
5
+ import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
6
+ import {
7
+ OptionList,
8
+ OptionListContent,
9
+ OptionListItem,
10
+ OptionListItemContent,
11
+ OptionListItemContentRow,
12
+ OptionListItemLeading,
13
+ OptionListItemText,
14
+ OptionListItemDescription,
15
+ OptionListEmptyState,
16
+ OptionListTrigger,
17
+ } from './OptionList';
18
+ import type { OptionListItemData } from './types';
19
+
20
+ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
21
+ <ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
22
+ {children}
23
+ </ThemeProvider>
24
+ );
25
+
26
+ const ITEMS: OptionListItemData[] = [
27
+ { value: 'a', label: 'Alpha' },
28
+ { value: 'b', label: 'Beta' },
29
+ { value: 'c', label: 'Gamma' },
30
+ ];
31
+
32
+ const GROUPED_ITEMS: OptionListItemData[] = [
33
+ { value: 'apple', label: 'Apple', group: 'Fruits' },
34
+ { value: 'banana', label: 'Banana', group: 'Fruits' },
35
+ { value: 'carrot', label: 'Carrot', group: 'Vegetables' },
36
+ ];
37
+
38
+ const renderOptionList = ({
39
+ items = ITEMS,
40
+ value,
41
+ defaultValue,
42
+ onValueChange,
43
+ disabled,
44
+ }: {
45
+ items?: OptionListItemData[];
46
+ value?: string | null;
47
+ defaultValue?: string | null;
48
+ onValueChange?: (v: string | null) => void;
49
+ disabled?: boolean;
50
+ } = {}) =>
51
+ render(
52
+ <TestWrapper>
53
+ <OptionList
54
+ items={items}
55
+ value={value}
56
+ defaultValue={defaultValue}
57
+ onValueChange={onValueChange}
58
+ disabled={disabled}
59
+ >
60
+ <OptionListContent
61
+ renderItem={(item) => (
62
+ <OptionListItem value={item.value} disabled={item.disabled}>
63
+ <OptionListItemContent>
64
+ <OptionListItemText>{item.label}</OptionListItemText>
65
+ </OptionListItemContent>
66
+ </OptionListItem>
67
+ )}
68
+ />
69
+ </OptionList>
70
+ </TestWrapper>,
71
+ );
72
+
73
+ describe('OptionList', () => {
74
+ it('renders all items', () => {
75
+ const { getByText } = renderOptionList();
76
+
77
+ expect(getByText('Alpha')).toBeTruthy();
78
+ expect(getByText('Beta')).toBeTruthy();
79
+ expect(getByText('Gamma')).toBeTruthy();
80
+ });
81
+
82
+ it('calls onValueChange when an item is pressed', () => {
83
+ const onValueChange = jest.fn();
84
+ const { getByText } = renderOptionList({ onValueChange });
85
+
86
+ fireEvent.press(getByText('Beta'));
87
+
88
+ expect(onValueChange).toHaveBeenCalledWith('b');
89
+ });
90
+
91
+ it('marks the selected item with accessibilityState', () => {
92
+ const { getAllByRole } = renderOptionList({ value: 'a' });
93
+
94
+ const items = getAllByRole('radio');
95
+ const selectedItem = items.find(
96
+ (i) => i.props.accessibilityState?.selected === true,
97
+ );
98
+
99
+ expect(selectedItem).toBeTruthy();
100
+ });
101
+
102
+ it('updates selection on press (uncontrolled)', () => {
103
+ const onValueChange = jest.fn();
104
+ const { getByText } = renderOptionList({
105
+ defaultValue: 'a',
106
+ onValueChange,
107
+ });
108
+
109
+ fireEvent.press(getByText('Gamma'));
110
+
111
+ expect(onValueChange).toHaveBeenCalledWith('c');
112
+ });
113
+
114
+ it('does not fire onValueChange when disabled item is pressed', () => {
115
+ const onValueChange = jest.fn();
116
+ const items: OptionListItemData[] = [
117
+ { value: 'a', label: 'Enabled' },
118
+ { value: 'b', label: 'Disabled', disabled: true },
119
+ ];
120
+
121
+ const { getByText } = renderOptionList({ items, onValueChange });
122
+
123
+ fireEvent.press(getByText('Disabled'));
124
+
125
+ expect(onValueChange).not.toHaveBeenCalled();
126
+ });
127
+
128
+ it('renders group labels for grouped items', () => {
129
+ const { getByText } = renderOptionList({ items: GROUPED_ITEMS });
130
+
131
+ expect(getByText('Fruits')).toBeTruthy();
132
+ expect(getByText('Vegetables')).toBeTruthy();
133
+ expect(getByText('Apple')).toBeTruthy();
134
+ expect(getByText('Carrot')).toBeTruthy();
135
+ });
136
+
137
+ it('passes selected state to renderItem', () => {
138
+ const renderItem = jest.fn(
139
+ (item: OptionListItemData, selected: boolean) => (
140
+ <Text testID={`item-${item.value}`}>
141
+ {item.label} {selected ? 'selected' : ''}
142
+ </Text>
143
+ ),
144
+ );
145
+
146
+ render(
147
+ <TestWrapper>
148
+ <OptionList
149
+ items={[
150
+ { value: 'a', label: 'A' },
151
+ { value: 'b', label: 'B', disabled: true },
152
+ ]}
153
+ value='a'
154
+ >
155
+ <OptionListContent renderItem={renderItem} />
156
+ </OptionList>
157
+ </TestWrapper>,
158
+ );
159
+
160
+ expect(renderItem).toHaveBeenCalledWith(
161
+ expect.objectContaining({ value: 'a' }),
162
+ true,
163
+ );
164
+ expect(renderItem).toHaveBeenCalledWith(
165
+ expect.objectContaining({ value: 'b' }),
166
+ false,
167
+ );
168
+ });
169
+
170
+ it('renders description sub-component', () => {
171
+ const { getByText } = render(
172
+ <TestWrapper>
173
+ <OptionList items={ITEMS} value={null}>
174
+ <OptionListContent
175
+ renderItem={(item) => (
176
+ <OptionListItem value={item.value}>
177
+ <OptionListItemContent>
178
+ <OptionListItemText>{item.label}</OptionListItemText>
179
+ <OptionListItemDescription>
180
+ Description for {item.label}
181
+ </OptionListItemDescription>
182
+ </OptionListItemContent>
183
+ </OptionListItem>
184
+ )}
185
+ />
186
+ </OptionList>
187
+ </TestWrapper>,
188
+ );
189
+
190
+ expect(getByText('Description for Alpha')).toBeTruthy();
191
+ });
192
+
193
+ describe('OptionList-level disabled', () => {
194
+ it('prevents all items from being pressed when OptionList is disabled', () => {
195
+ const onValueChange = jest.fn();
196
+ const { getByText } = renderOptionList({
197
+ disabled: true,
198
+ onValueChange,
199
+ });
200
+
201
+ fireEvent.press(getByText('Alpha'));
202
+ fireEvent.press(getByText('Beta'));
203
+
204
+ expect(onValueChange).not.toHaveBeenCalled();
205
+ });
206
+
207
+ it('sets disabled accessibilityState on all items', () => {
208
+ const { getAllByRole } = renderOptionList({ disabled: true });
209
+
210
+ const items = getAllByRole('radio');
211
+ for (const item of items) {
212
+ expect(item.props.accessibilityState?.disabled).toBe(true);
213
+ }
214
+ });
215
+ });
216
+
217
+ describe('OptionListItemLeading', () => {
218
+ it('renders leading content beside item text', () => {
219
+ const { getByTestId, getByText } = render(
220
+ <TestWrapper>
221
+ <OptionList items={ITEMS} value={null}>
222
+ <OptionListContent
223
+ renderItem={(item) => (
224
+ <OptionListItem value={item.value}>
225
+ <OptionListItemLeading>
226
+ <Text testID={`leading-${item.value}`}>icon</Text>
227
+ </OptionListItemLeading>
228
+ <OptionListItemContent>
229
+ <OptionListItemText>{item.label}</OptionListItemText>
230
+ </OptionListItemContent>
231
+ </OptionListItem>
232
+ )}
233
+ />
234
+ </OptionList>
235
+ </TestWrapper>,
236
+ );
237
+
238
+ expect(getByTestId('leading-a')).toBeTruthy();
239
+ expect(getByText('Alpha')).toBeTruthy();
240
+ });
241
+ });
242
+
243
+ describe('OptionListItemContentRow', () => {
244
+ it('renders children in a row layout', () => {
245
+ const singleItem: OptionListItemData[] = [{ value: 'a', label: 'Alpha' }];
246
+
247
+ const { getByText } = render(
248
+ <TestWrapper>
249
+ <OptionList items={singleItem} value={null}>
250
+ <OptionListContent
251
+ renderItem={(item) => (
252
+ <OptionListItem value={item.value}>
253
+ <OptionListItemContent>
254
+ <OptionListItemContentRow>
255
+ <OptionListItemText>{item.label}</OptionListItemText>
256
+ <Text>tag</Text>
257
+ </OptionListItemContentRow>
258
+ </OptionListItemContent>
259
+ </OptionListItem>
260
+ )}
261
+ />
262
+ </OptionList>
263
+ </TestWrapper>,
264
+ );
265
+
266
+ expect(getByText('Alpha')).toBeTruthy();
267
+ expect(getByText('tag')).toBeTruthy();
268
+ });
269
+ });
270
+
271
+ describe('OptionListEmptyState', () => {
272
+ it('renders title when item list is empty', () => {
273
+ const { getByText } = render(
274
+ <TestWrapper>
275
+ <OptionList items={[]} value={null}>
276
+ <OptionListContent renderItem={() => null} />
277
+ <OptionListEmptyState title='No results' />
278
+ </OptionList>
279
+ </TestWrapper>,
280
+ );
281
+
282
+ expect(getByText('No results')).toBeTruthy();
283
+ });
284
+
285
+ it('renders description when provided', () => {
286
+ const { getByText } = render(
287
+ <TestWrapper>
288
+ <OptionList items={[]} value={null}>
289
+ <OptionListContent renderItem={() => null} />
290
+ <OptionListEmptyState
291
+ title='No results'
292
+ description='Try a different search'
293
+ />
294
+ </OptionList>
295
+ </TestWrapper>,
296
+ );
297
+
298
+ expect(getByText('No results')).toBeTruthy();
299
+ expect(getByText('Try a different search')).toBeTruthy();
300
+ });
301
+
302
+ it('does not render when items are present', () => {
303
+ const { queryByText } = render(
304
+ <TestWrapper>
305
+ <OptionList items={ITEMS} value={null}>
306
+ <OptionListContent
307
+ renderItem={(item) => (
308
+ <OptionListItem value={item.value}>
309
+ <OptionListItemContent>
310
+ <OptionListItemText>{item.label}</OptionListItemText>
311
+ </OptionListItemContent>
312
+ </OptionListItem>
313
+ )}
314
+ />
315
+ <OptionListEmptyState title='No results' />
316
+ </OptionList>
317
+ </TestWrapper>,
318
+ );
319
+
320
+ expect(queryByText('No results')).toBeNull();
321
+ });
322
+ });
323
+
324
+ describe('OptionListTrigger', () => {
325
+ it('calls onPress when pressed', () => {
326
+ const onPress = jest.fn();
327
+ const { getByRole } = render(
328
+ <TestWrapper>
329
+ <OptionList items={ITEMS} value={null}>
330
+ <OptionListTrigger label='Choose' onPress={onPress} />
331
+ </OptionList>
332
+ </TestWrapper>,
333
+ );
334
+
335
+ fireEvent.press(getByRole('button'));
336
+
337
+ expect(onPress).toHaveBeenCalledTimes(1);
338
+ });
339
+
340
+ it('renders the label text', () => {
341
+ const { getByText } = render(
342
+ <TestWrapper>
343
+ <OptionList items={ITEMS} value={null}>
344
+ <OptionListTrigger label='Pick a value' onPress={jest.fn()} />
345
+ </OptionList>
346
+ </TestWrapper>,
347
+ );
348
+
349
+ expect(getByText('Pick a value')).toBeTruthy();
350
+ });
351
+
352
+ it('renders children as selected value content', () => {
353
+ const { getByText } = render(
354
+ <TestWrapper>
355
+ <OptionList items={ITEMS} value='a'>
356
+ <OptionListTrigger label='Currency' onPress={jest.fn()}>
357
+ <Text>Alpha</Text>
358
+ </OptionListTrigger>
359
+ </OptionList>
360
+ </TestWrapper>,
361
+ );
362
+
363
+ expect(getByText('Alpha')).toBeTruthy();
364
+ expect(getByText('Currency')).toBeTruthy();
365
+ });
366
+
367
+ it('does not call onPress when disabled', () => {
368
+ const onPress = jest.fn();
369
+ const { getByRole } = render(
370
+ <TestWrapper>
371
+ <OptionList items={ITEMS} value={null}>
372
+ <OptionListTrigger label='Choose' onPress={onPress} disabled />
373
+ </OptionList>
374
+ </TestWrapper>,
375
+ );
376
+
377
+ fireEvent.press(getByRole('button'));
378
+
379
+ expect(onPress).not.toHaveBeenCalled();
380
+ });
381
+
382
+ it('inherits disabled from OptionList', () => {
383
+ const onPress = jest.fn();
384
+ const { getByRole } = render(
385
+ <TestWrapper>
386
+ <OptionList items={ITEMS} value={null} disabled>
387
+ <OptionListTrigger label='Choose' onPress={onPress} />
388
+ </OptionList>
389
+ </TestWrapper>,
390
+ );
391
+
392
+ fireEvent.press(getByRole('button'));
393
+
394
+ expect(onPress).not.toHaveBeenCalled();
395
+ });
396
+
397
+ it('renders without a label', () => {
398
+ const { getByRole, queryByText } = render(
399
+ <TestWrapper>
400
+ <OptionList items={ITEMS} value={null}>
401
+ <OptionListTrigger onPress={jest.fn()}>
402
+ <Text>Selected value</Text>
403
+ </OptionListTrigger>
404
+ </OptionList>
405
+ </TestWrapper>,
406
+ );
407
+
408
+ expect(getByRole('button')).toBeTruthy();
409
+ expect(queryByText('Selected value')).toBeTruthy();
410
+ });
411
+ });
412
+ });