@ledgerhq/lumen-ui-rnative 0.1.34 → 0.1.36

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 (120) hide show
  1. package/dist/module/index.js +1 -0
  2. package/dist/module/index.js.map +1 -1
  3. package/dist/module/lib/Animations/Pulse/Pulse.js +16 -6
  4. package/dist/module/lib/Animations/Pulse/Pulse.js.map +1 -1
  5. package/dist/module/lib/Components/BottomSheet/BottomSheet.js +12 -7
  6. package/dist/module/lib/Components/BottomSheet/BottomSheet.js.map +1 -1
  7. package/dist/module/lib/Components/BottomSheet/BottomSheet.stories.js +220 -1
  8. package/dist/module/lib/Components/BottomSheet/BottomSheet.stories.js.map +1 -1
  9. package/dist/module/lib/Components/BottomSheet/BottomSheet.test.js +73 -0
  10. package/dist/module/lib/Components/BottomSheet/BottomSheet.test.js.map +1 -1
  11. package/dist/module/lib/Components/BottomSheet/BottomSheetHeader.js +1 -1
  12. package/dist/module/lib/Components/BottomSheet/BottomSheetHeader.js.map +1 -1
  13. package/dist/module/lib/Components/BottomSheet/CustomHandle.js +15 -2
  14. package/dist/module/lib/Components/BottomSheet/CustomHandle.js.map +1 -1
  15. package/dist/module/lib/Components/Card/Card.js.map +1 -1
  16. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.js +184 -0
  17. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.js.map +1 -0
  18. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.mdx +139 -0
  19. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.stories.js +258 -0
  20. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.stories.js.map +1 -0
  21. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.test.js +94 -0
  22. package/dist/module/lib/Components/DescriptionItem/DescriptionItem.test.js.map +1 -0
  23. package/dist/module/lib/Components/DescriptionItem/index.js +5 -0
  24. package/dist/module/lib/Components/DescriptionItem/index.js.map +1 -0
  25. package/dist/module/lib/Components/DescriptionItem/types.js +4 -0
  26. package/dist/module/lib/Components/DescriptionItem/types.js.map +1 -0
  27. package/dist/module/lib/Components/ListItem/ListItem.js.map +1 -1
  28. package/dist/module/lib/Components/MediaImage/MediaImage.js +5 -1
  29. package/dist/module/lib/Components/MediaImage/MediaImage.js.map +1 -1
  30. package/dist/module/lib/Components/NavBar/CoinCapsule.js +1 -0
  31. package/dist/module/lib/Components/NavBar/CoinCapsule.js.map +1 -1
  32. package/dist/module/lib/Components/OptionList/OptionList.js +45 -4
  33. package/dist/module/lib/Components/OptionList/OptionList.js.map +1 -1
  34. package/dist/module/lib/Components/OptionList/OptionList.mdx +19 -0
  35. package/dist/module/lib/Components/OptionList/OptionList.stories.js +254 -1
  36. package/dist/module/lib/Components/OptionList/OptionList.stories.js.map +1 -1
  37. package/dist/module/lib/Components/OptionList/OptionList.test.js +136 -1
  38. package/dist/module/lib/Components/OptionList/OptionList.test.js.map +1 -1
  39. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.js +39 -13
  40. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.js.map +1 -1
  41. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.test.js +117 -2
  42. package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.test.js.map +1 -1
  43. package/dist/module/lib/Components/PageIndicator/PageIndicator.test.js.map +1 -1
  44. package/dist/module/lib/Components/Skeleton/Skeleton.js +10 -3
  45. package/dist/module/lib/Components/Skeleton/Skeleton.js.map +1 -1
  46. package/dist/module/lib/Components/TabBar/TabBar.js +7 -6
  47. package/dist/module/lib/Components/TabBar/TabBar.js.map +1 -1
  48. package/dist/module/lib/Components/index.js +1 -0
  49. package/dist/module/lib/Components/index.js.map +1 -1
  50. package/dist/module/styles/lx/resolveStyle.js.map +1 -1
  51. package/dist/typescript/src/index.d.ts +1 -0
  52. package/dist/typescript/src/index.d.ts.map +1 -1
  53. package/dist/typescript/src/lib/Animations/Pulse/Pulse.d.ts +1 -1
  54. package/dist/typescript/src/lib/Animations/Pulse/Pulse.d.ts.map +1 -1
  55. package/dist/typescript/src/lib/Animations/Pulse/types.d.ts +2 -1
  56. package/dist/typescript/src/lib/Animations/Pulse/types.d.ts.map +1 -1
  57. package/dist/typescript/src/lib/Components/BottomSheet/BottomSheet.d.ts +1 -1
  58. package/dist/typescript/src/lib/Components/BottomSheet/BottomSheet.d.ts.map +1 -1
  59. package/dist/typescript/src/lib/Components/BottomSheet/CustomHandle.d.ts +5 -2
  60. package/dist/typescript/src/lib/Components/BottomSheet/CustomHandle.d.ts.map +1 -1
  61. package/dist/typescript/src/lib/Components/BottomSheet/types.d.ts +16 -3
  62. package/dist/typescript/src/lib/Components/BottomSheet/types.d.ts.map +1 -1
  63. package/dist/typescript/src/lib/Components/Card/Card.d.ts.map +1 -1
  64. package/dist/typescript/src/lib/Components/DescriptionItem/DescriptionItem.d.ts +42 -0
  65. package/dist/typescript/src/lib/Components/DescriptionItem/DescriptionItem.d.ts.map +1 -0
  66. package/dist/typescript/src/lib/Components/DescriptionItem/index.d.ts +3 -0
  67. package/dist/typescript/src/lib/Components/DescriptionItem/index.d.ts.map +1 -0
  68. package/dist/typescript/src/lib/Components/DescriptionItem/types.d.ts +39 -0
  69. package/dist/typescript/src/lib/Components/DescriptionItem/types.d.ts.map +1 -0
  70. package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts +3 -3
  71. package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts.map +1 -1
  72. package/dist/typescript/src/lib/Components/MediaImage/MediaImage.d.ts.map +1 -1
  73. package/dist/typescript/src/lib/Components/OptionList/OptionList.d.ts +3 -2
  74. package/dist/typescript/src/lib/Components/OptionList/OptionList.d.ts.map +1 -1
  75. package/dist/typescript/src/lib/Components/OptionList/types.d.ts +42 -5
  76. package/dist/typescript/src/lib/Components/OptionList/types.d.ts.map +1 -1
  77. package/dist/typescript/src/lib/Components/OptionList/useOptionList/useOptionListItems.d.ts +9 -1
  78. package/dist/typescript/src/lib/Components/OptionList/useOptionList/useOptionListItems.d.ts.map +1 -1
  79. package/dist/typescript/src/lib/Components/Skeleton/Skeleton.d.ts +1 -1
  80. package/dist/typescript/src/lib/Components/Skeleton/Skeleton.d.ts.map +1 -1
  81. package/dist/typescript/src/lib/Components/TabBar/TabBar.d.ts.map +1 -1
  82. package/dist/typescript/src/lib/Components/index.d.ts +1 -0
  83. package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
  84. package/dist/typescript/src/lib/types/index.d.ts +3 -3
  85. package/dist/typescript/src/lib/types/index.d.ts.map +1 -1
  86. package/dist/typescript/src/styles/lx/resolveStyle.d.ts +3 -3
  87. package/dist/typescript/src/styles/lx/resolveStyle.d.ts.map +1 -1
  88. package/package.json +1 -1
  89. package/src/index.ts +1 -0
  90. package/src/lib/Animations/Pulse/Pulse.tsx +38 -30
  91. package/src/lib/Animations/Pulse/types.ts +2 -1
  92. package/src/lib/Components/BottomSheet/BottomSheet.stories.tsx +174 -1
  93. package/src/lib/Components/BottomSheet/BottomSheet.test.tsx +59 -0
  94. package/src/lib/Components/BottomSheet/BottomSheet.tsx +19 -7
  95. package/src/lib/Components/BottomSheet/BottomSheetHeader.tsx +1 -1
  96. package/src/lib/Components/BottomSheet/CustomHandle.tsx +26 -5
  97. package/src/lib/Components/BottomSheet/types.ts +24 -3
  98. package/src/lib/Components/Card/Card.tsx +3 -3
  99. package/src/lib/Components/DescriptionItem/DescriptionItem.mdx +139 -0
  100. package/src/lib/Components/DescriptionItem/DescriptionItem.stories.tsx +234 -0
  101. package/src/lib/Components/DescriptionItem/DescriptionItem.test.tsx +112 -0
  102. package/src/lib/Components/DescriptionItem/DescriptionItem.tsx +224 -0
  103. package/src/lib/Components/DescriptionItem/index.ts +2 -0
  104. package/src/lib/Components/DescriptionItem/types.ts +44 -0
  105. package/src/lib/Components/ListItem/ListItem.tsx +3 -3
  106. package/src/lib/Components/MediaImage/MediaImage.tsx +5 -1
  107. package/src/lib/Components/NavBar/CoinCapsule.tsx +1 -0
  108. package/src/lib/Components/OptionList/OptionList.mdx +19 -0
  109. package/src/lib/Components/OptionList/OptionList.stories.tsx +254 -0
  110. package/src/lib/Components/OptionList/OptionList.test.tsx +143 -0
  111. package/src/lib/Components/OptionList/OptionList.tsx +49 -3
  112. package/src/lib/Components/OptionList/types.ts +46 -5
  113. package/src/lib/Components/OptionList/useOptionList/useOptionListItems.test.ts +124 -2
  114. package/src/lib/Components/OptionList/useOptionList/useOptionListItems.ts +53 -10
  115. package/src/lib/Components/PageIndicator/PageIndicator.test.tsx +2 -1
  116. package/src/lib/Components/Skeleton/Skeleton.tsx +9 -5
  117. package/src/lib/Components/TabBar/TabBar.tsx +3 -2
  118. package/src/lib/Components/index.ts +1 -0
  119. package/src/lib/types/index.ts +3 -3
  120. package/src/styles/lx/resolveStyle.ts +4 -3
@@ -1,8 +1,10 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import type { FC } from 'react';
2
3
  import { useState } from 'react';
3
4
  import { Button } from '../Button';
4
5
  import { SearchInput } from '../SearchInput';
5
- import { Box, Text } from '../Utility';
6
+ import { Spot } from '../Spot';
7
+ import { Box, RadialGradient, Text } from '../Utility';
6
8
  import { BottomSheet } from './BottomSheet';
7
9
  import { BottomSheetHeader } from './BottomSheetHeader';
8
10
  import {
@@ -11,6 +13,7 @@ import {
11
13
  BottomSheetView,
12
14
  BottomSheetVirtualizedList,
13
15
  } from './Scrollables';
16
+ import type { BottomSheetBackgroundProps } from './types';
14
17
  import { useBottomSheetRef } from './useBottomSheetRef';
15
18
 
16
19
  const meta = {
@@ -654,3 +657,173 @@ export const VirtualizedList: Story = {
654
657
  );
655
658
  },
656
659
  };
660
+
661
+ type StrongBackgroundColor = 'errorStrong' | 'successStrong' | 'mutedStrong';
662
+
663
+ const createGradientBackground =
664
+ (color: StrongBackgroundColor): FC<BottomSheetBackgroundProps> =>
665
+ ({ style }) => (
666
+ <Box
667
+ style={style}
668
+ lx={{ backgroundColor: 'canvasSheet', overflow: 'hidden' }}
669
+ >
670
+ <RadialGradient
671
+ center={{ x: 0.5, y: 0 }}
672
+ stops={[
673
+ { color, offset: 0, opacity: 0.3 },
674
+ { color, offset: 1, opacity: 0 },
675
+ ]}
676
+ lx={{
677
+ position: 'absolute',
678
+ top: 's0',
679
+ left: 's0',
680
+ right: 's0',
681
+ height: 's320',
682
+ }}
683
+ />
684
+ </Box>
685
+ );
686
+
687
+ const ErrorBackground = createGradientBackground('errorStrong');
688
+ const SuccessBackground = createGradientBackground('successStrong');
689
+ const MutedBackground = createGradientBackground('mutedStrong');
690
+
691
+ export const InfoStateVariants: Story = {
692
+ args: {
693
+ snapPoints: 'full',
694
+ hideCloseButton: false,
695
+ onBack: undefined,
696
+ onClose: undefined,
697
+ enableHandlePanningGesture: true,
698
+ enablePanDownToClose: true,
699
+ enableBlurKeyboardOnGesture: true,
700
+ enableDynamicSizing: false,
701
+ detached: false,
702
+ backdropPressBehavior: 'close',
703
+ hideHandle: true,
704
+ },
705
+ render: (args) => {
706
+ const errorBottomSheetRef = useBottomSheetRef();
707
+ const successBottomSheetRef = useBottomSheetRef();
708
+ const mutedBottomSheetRef = useBottomSheetRef();
709
+
710
+ return (
711
+ <Box
712
+ lx={{
713
+ height: 's320',
714
+ width: 'full',
715
+ alignItems: 'center',
716
+ justifyContent: 'center',
717
+ paddingTop: 's32',
718
+ gap: 's12',
719
+ }}
720
+ >
721
+ <Button
722
+ size='sm'
723
+ onPress={() => errorBottomSheetRef.current?.present()}
724
+ >
725
+ Error
726
+ </Button>
727
+ <Button
728
+ size='sm'
729
+ onPress={() => successBottomSheetRef.current?.present()}
730
+ >
731
+ Success
732
+ </Button>
733
+ <Button
734
+ size='sm'
735
+ onPress={() => mutedBottomSheetRef.current?.present()}
736
+ >
737
+ Muted
738
+ </Button>
739
+
740
+ <BottomSheet
741
+ {...args}
742
+ ref={errorBottomSheetRef}
743
+ backgroundComponent={ErrorBackground}
744
+ >
745
+ <BottomSheetView>
746
+ <BottomSheetHeader density='compact' />
747
+ <Box lx={{ alignItems: 'center', gap: 's24' }}>
748
+ <Spot appearance='error' size={72} />
749
+ <Box lx={{ alignItems: 'center', gap: 's12' }}>
750
+ <Text typography='heading4SemiBold' lx={{ color: 'base' }}>
751
+ Title
752
+ </Text>
753
+ <Text
754
+ typography='body2'
755
+ lx={{ color: 'muted', textAlign: 'center' }}
756
+ >
757
+ Description
758
+ </Text>
759
+ </Box>
760
+ </Box>
761
+ <Box lx={{ marginTop: 's24' }}>
762
+ <Button appearance='base' size='lg' isFull>
763
+ Label
764
+ </Button>
765
+ </Box>
766
+ </BottomSheetView>
767
+ </BottomSheet>
768
+
769
+ <BottomSheet
770
+ {...args}
771
+ ref={successBottomSheetRef}
772
+ backgroundComponent={SuccessBackground}
773
+ >
774
+ <BottomSheetView>
775
+ <BottomSheetHeader density='compact' />
776
+ <Box lx={{ alignItems: 'center', gap: 's24' }}>
777
+ <Spot appearance='check' size={72} />
778
+ <Box lx={{ alignItems: 'center', gap: 's12' }}>
779
+ <Text typography='heading4SemiBold' lx={{ color: 'base' }}>
780
+ Title
781
+ </Text>
782
+ <Text
783
+ typography='body2'
784
+ lx={{ color: 'muted', textAlign: 'center' }}
785
+ >
786
+ Description
787
+ </Text>
788
+ </Box>
789
+ </Box>
790
+ <Box lx={{ marginTop: 's24' }}>
791
+ <Button appearance='base' size='lg' isFull>
792
+ Label
793
+ </Button>
794
+ </Box>
795
+ </BottomSheetView>
796
+ </BottomSheet>
797
+
798
+ <BottomSheet
799
+ {...args}
800
+ ref={mutedBottomSheetRef}
801
+ backgroundComponent={MutedBackground}
802
+ >
803
+ <BottomSheetView>
804
+ <BottomSheetHeader density='compact' />
805
+ <Box lx={{ alignItems: 'center', gap: 's24' }}>
806
+ <Spot appearance='info' size={72} />
807
+ <Box lx={{ alignItems: 'center', gap: 's12' }}>
808
+ <Text typography='heading4SemiBold' lx={{ color: 'base' }}>
809
+ Title
810
+ </Text>
811
+ <Text
812
+ typography='body2'
813
+ lx={{ color: 'muted', textAlign: 'center' }}
814
+ >
815
+ Description
816
+ </Text>
817
+ </Box>
818
+ </Box>
819
+ <Box lx={{ marginTop: 's24' }}>
820
+ <Button appearance='base' size='lg' isFull>
821
+ Label
822
+ </Button>
823
+ </Box>
824
+ </BottomSheetView>
825
+ </BottomSheet>
826
+ </Box>
827
+ );
828
+ },
829
+ };
@@ -184,6 +184,65 @@ describe('BottomSheet', () => {
184
184
  expect(element.props['data-detached']).toBe('true');
185
185
  });
186
186
 
187
+ it('uses default background style when no backgroundComponent is provided', () => {
188
+ const { BottomSheet } = require('./BottomSheet');
189
+ const { getByTestId } = renderWithTheme(
190
+ <BottomSheet testID='bottom-sheet'>
191
+ <Text>Content</Text>
192
+ </BottomSheet>,
193
+ );
194
+
195
+ const element = getByTestId('bottom-sheet');
196
+ expect(element.props['data-has-background-component']).toBe('false');
197
+ expect(element.props['data-has-background-style']).toBe('true');
198
+ });
199
+
200
+ it('renders the default handle when hideHandle is not set', () => {
201
+ const { BottomSheet } = require('./BottomSheet');
202
+ const { getByTestId, queryByTestId } = renderWithTheme(
203
+ <BottomSheet testID='bottom-sheet'>
204
+ <Text>Content</Text>
205
+ </BottomSheet>,
206
+ );
207
+
208
+ expect(getByTestId('bottom-sheet-handle')).toBeTruthy();
209
+ expect(queryByTestId('bottom-sheet-handle-hidden')).toBeNull();
210
+ });
211
+
212
+ it('renders the hidden handle placeholder when hideHandle is true', () => {
213
+ const { BottomSheet } = require('./BottomSheet');
214
+ const { getByTestId, queryByTestId } = renderWithTheme(
215
+ <BottomSheet hideHandle testID='bottom-sheet'>
216
+ <Text>Content</Text>
217
+ </BottomSheet>,
218
+ );
219
+
220
+ expect(getByTestId('bottom-sheet-handle-hidden')).toBeTruthy();
221
+ expect(queryByTestId('bottom-sheet-handle')).toBeNull();
222
+ });
223
+
224
+ it('forwards backgroundComponent and skips default background style', () => {
225
+ const { BottomSheet } = require('./BottomSheet');
226
+ const CustomBackground = () => (
227
+ <View testID='custom-bg'>
228
+ <Text>BG</Text>
229
+ </View>
230
+ );
231
+ const { getByTestId } = renderWithTheme(
232
+ <BottomSheet
233
+ backgroundComponent={CustomBackground}
234
+ testID='bottom-sheet'
235
+ >
236
+ <Text>Content</Text>
237
+ </BottomSheet>,
238
+ );
239
+
240
+ const element = getByTestId('bottom-sheet');
241
+ expect(element.props['data-has-background-component']).toBe('true');
242
+ expect(element.props['data-has-background-style']).toBe('false');
243
+ expect(getByTestId('custom-bg')).toBeTruthy();
244
+ });
245
+
187
246
  it('respects enableHandlePanningGesture prop', () => {
188
247
  const { BottomSheet } = require('./BottomSheet');
189
248
  const { getByTestId } = renderWithTheme(
@@ -6,7 +6,7 @@ import { StyleSheet } from 'react-native';
6
6
  import { useStyleSheet } from '../../../styles';
7
7
  import { RuntimeConstants } from '../../utils';
8
8
  import { CustomBackdrop } from './CustomBackdrop';
9
- import { CustomHandle } from './CustomHandle';
9
+ import { CustomHandle, HiddenHandle } from './CustomHandle';
10
10
  import type { BottomSheetProps } from './types';
11
11
 
12
12
  const OFFSET_TOP = 25;
@@ -25,7 +25,13 @@ const MAX_DYNAMIC_CONTENT_SIZE = {
25
25
  fullWithOffset: FULL_WITH_OFFSET,
26
26
  };
27
27
 
28
- const useStyles = ({ shadow }: { shadow: boolean }) => {
28
+ const useStyles = ({
29
+ shadow,
30
+ hasCustomBackground,
31
+ }: {
32
+ shadow: boolean;
33
+ hasCustomBackground: boolean;
34
+ }) => {
29
35
  return useStyleSheet(
30
36
  (t) => ({
31
37
  root: StyleSheet.flatten([
@@ -35,7 +41,7 @@ const useStyles = ({ shadow }: { shadow: boolean }) => {
35
41
  flex: 1,
36
42
  borderTopLeftRadius: t.borderRadius.xl,
37
43
  borderTopRightRadius: t.borderRadius.xl,
38
- backgroundColor: t.colors.bg.canvasSheet,
44
+ overflow: 'hidden',
39
45
  },
40
46
  shadow && {
41
47
  boxShadow: t.shadows.lg,
@@ -46,7 +52,7 @@ const useStyles = ({ shadow }: { shadow: boolean }) => {
46
52
  backgroundColor: t.colors.bg.canvasSheet,
47
53
  },
48
54
  }),
49
- [shadow],
55
+ [shadow, hasCustomBackground],
50
56
  );
51
57
  };
52
58
 
@@ -74,6 +80,8 @@ export const BottomSheet = ({
74
80
  onBackdropPress,
75
81
  onChange,
76
82
  snapPoints = 'fullWithOffset',
83
+ backgroundComponent,
84
+ hideHandle = false,
77
85
  ref,
78
86
  ...props
79
87
  }: BottomSheetProps) => {
@@ -82,7 +90,10 @@ export const BottomSheet = ({
82
90
  const mergedRefs = useMergedRef<GorhomBottomSheetModal>(ref, innerRef);
83
91
  const [isOpen, setIsOpen] = useState(false);
84
92
 
85
- const styles = useStyles({ shadow: hideBackdrop && isOpen });
93
+ const styles = useStyles({
94
+ shadow: hideBackdrop && isOpen,
95
+ hasCustomBackground: Boolean(backgroundComponent),
96
+ });
86
97
 
87
98
  /**
88
99
  * Match the snap points to the preset or the custom snap points array
@@ -163,7 +174,8 @@ export const BottomSheet = ({
163
174
  {...props}
164
175
  ref={mergedRefs}
165
176
  style={styles.root}
166
- backgroundStyle={styles.background}
177
+ backgroundStyle={backgroundComponent ? undefined : styles.background}
178
+ backgroundComponent={backgroundComponent}
167
179
  onChange={handleChange}
168
180
  onAnimate={handleAnimate}
169
181
  /**
@@ -188,7 +200,7 @@ export const BottomSheet = ({
188
200
  /**
189
201
  * Components
190
202
  */
191
- handleComponent={CustomHandle}
203
+ handleComponent={hideHandle ? HiddenHandle : CustomHandle}
192
204
  backdropComponent={hideBackdrop ? undefined : renderBackdrop}
193
205
  >
194
206
  <BottomSheetProvider value={{ onBack, hideCloseButton }}>
@@ -28,7 +28,7 @@ const useStyles = ({
28
28
  {
29
29
  position: 'relative',
30
30
  zIndex: Z_INDEX_DIALOG_CONTENT,
31
- backgroundColor: t.colors.bg.canvasSheet,
31
+ backgroundColor: 'transparent',
32
32
  paddingBottom: t.spacings.s12,
33
33
  },
34
34
  spacing && {
@@ -1,5 +1,5 @@
1
1
  import type { BottomSheetVariables } from '@gorhom/bottom-sheet/lib/typescript/types';
2
- import type { Ref } from 'react';
2
+ import type { ComponentRef, Ref } from 'react';
3
3
  import { View } from 'react-native';
4
4
  import { useStyleSheet } from '../../../styles';
5
5
 
@@ -12,13 +12,13 @@ const useStyles = () => {
12
12
  alignItems: 'center',
13
13
  justifyContent: 'center',
14
14
  alignSelf: 'center',
15
- backgroundColor: t.colors.bg.canvasSheet,
15
+ backgroundColor: 'transparent',
16
16
  },
17
17
  handle: {
18
18
  height: t.spacings.s4,
19
19
  width: t.sizes.s36,
20
20
  borderRadius: t.borderRadius.full,
21
- backgroundColor: t.colors.bg.mutedPressed,
21
+ backgroundColor: t.colors.bg.mutedTransparentPressed,
22
22
  },
23
23
  }),
24
24
  [],
@@ -28,12 +28,33 @@ const useStyles = () => {
28
28
  export const CustomHandle = ({
29
29
  ref,
30
30
  ...props
31
- }: BottomSheetVariables & { ref?: Ref<React.ElementRef<typeof View>> }) => {
31
+ }: BottomSheetVariables & { ref?: Ref<ComponentRef<typeof View>> }) => {
32
32
  const styles = useStyles();
33
33
 
34
34
  return (
35
- <View {...props} ref={ref} style={styles.container}>
35
+ <View
36
+ {...props}
37
+ ref={ref}
38
+ style={styles.container}
39
+ testID='bottom-sheet-handle'
40
+ >
36
41
  <View style={styles.handle} />
37
42
  </View>
38
43
  );
39
44
  };
45
+
46
+ export const HiddenHandle = ({
47
+ ref,
48
+ ...props
49
+ }: BottomSheetVariables & { ref?: Ref<ComponentRef<typeof View>> }) => {
50
+ const styles = useStyles();
51
+
52
+ return (
53
+ <View
54
+ {...props}
55
+ ref={ref}
56
+ style={styles.container}
57
+ testID='bottom-sheet-handle-hidden'
58
+ />
59
+ );
60
+ };
@@ -5,16 +5,23 @@ import type {
5
5
  BottomSheetSectionList as GorhomBottomSheetSectionList,
6
6
  BottomSheetScrollView as GorhomBottomSheetScrollView,
7
7
  BottomSheetVirtualizedList as GorhomBottomSheetVirtualizedList,
8
+ BottomSheetBackgroundProps,
8
9
  } from '@gorhom/bottom-sheet';
9
-
10
10
  import type { Density } from '@ledgerhq/lumen-utils-shared';
11
- import type { PropsWithChildren, ReactNode, Ref } from 'react';
11
+ import type {
12
+ ComponentRef,
13
+ FC,
14
+ PropsWithChildren,
15
+ ReactNode,
16
+ Ref,
17
+ } from 'react';
18
+
12
19
  import type { StyledViewProps } from '../../../styles';
13
20
  export type BottomSheetProps = PropsWithChildren & {
14
21
  /**
15
22
  * Ref to the bottom sheet component.
16
23
  */
17
- ref?: Ref<React.ElementRef<typeof GorhomBottomSheetModal>>;
24
+ ref?: Ref<ComponentRef<typeof GorhomBottomSheetModal>>;
18
25
  /**
19
26
  * Used to locate this view in end-to-end tests.
20
27
  */
@@ -131,8 +138,22 @@ export type BottomSheetProps = PropsWithChildren & {
131
138
  * @default true
132
139
  */
133
140
  enableBlurKeyboardOnGesture?: boolean;
141
+ /**
142
+ * Custom background component rendered behind the sheet content and handle.
143
+ * Use to render gradients or other effects that should span the full sheet area,
144
+ * including the handle strip. When provided, the default sheet background is removed.
145
+ * @default undefined
146
+ */
147
+ backgroundComponent?: FC<BottomSheetBackgroundProps> | null;
148
+ /**
149
+ * If true, the drag handle (grabber) at the top of the sheet is hidden.
150
+ * @default false
151
+ */
152
+ hideHandle?: boolean;
134
153
  };
135
154
 
155
+ export type { BottomSheetBackgroundProps };
156
+
136
157
  export type BottomSheetHeaderProps = {
137
158
  /**
138
159
  * The density of the header.
@@ -3,7 +3,7 @@ import {
3
3
  DisabledProvider,
4
4
  isTextChildren,
5
5
  } from '@ledgerhq/lumen-utils-shared';
6
- import type { ReactNode, Ref } from 'react';
6
+ import type { ComponentRef, ReactNode, Ref } from 'react';
7
7
  import { useCallback, useEffect, useMemo } from 'react';
8
8
  import type { LayoutChangeEvent, ViewStyle } from 'react-native';
9
9
  import { StyleSheet, View } from 'react-native';
@@ -486,7 +486,7 @@ export const CardContentTitle = ({
486
486
  if (isTextChildren(children)) {
487
487
  return (
488
488
  <Text
489
- ref={ref as Ref<React.ElementRef<typeof Text>>}
489
+ ref={ref as Ref<ComponentRef<typeof Text>>}
490
490
  lx={lx}
491
491
  style={StyleSheet.flatten([styles.asText, style])}
492
492
  numberOfLines={1}
@@ -553,7 +553,7 @@ export const CardContentDescription = ({
553
553
  if (isTextChildren(children)) {
554
554
  return (
555
555
  <Text
556
- ref={ref as Ref<React.ElementRef<typeof Text>>}
556
+ ref={ref as Ref<ComponentRef<typeof Text>>}
557
557
  lx={lx}
558
558
  style={StyleSheet.flatten([styles.asText, style])}
559
559
  numberOfLines={1}
@@ -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>