@ledgerhq/lumen-ui-rnative 0.1.33 → 0.1.35
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.
- package/dist/module/index.js +1 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/lib/Animations/Pulse/Pulse.js +17 -7
- package/dist/module/lib/Animations/Pulse/Pulse.js.map +1 -1
- package/dist/module/lib/Components/BaseTag/BaseTag.js +122 -0
- package/dist/module/lib/Components/BaseTag/BaseTag.js.map +1 -0
- package/dist/module/lib/Components/BaseTag/BaseTag.test.js +144 -0
- package/dist/module/lib/Components/BaseTag/BaseTag.test.js.map +1 -0
- package/dist/module/lib/Components/BaseTag/index.js +5 -0
- package/dist/module/lib/Components/BaseTag/index.js.map +1 -0
- package/dist/module/lib/Components/BaseTag/types.js +4 -0
- package/dist/module/lib/Components/BaseTag/types.js.map +1 -0
- package/dist/module/lib/Components/BottomSheet/BottomSheet.js +12 -7
- package/dist/module/lib/Components/BottomSheet/BottomSheet.js.map +1 -1
- package/dist/module/lib/Components/BottomSheet/BottomSheet.stories.js +220 -1
- package/dist/module/lib/Components/BottomSheet/BottomSheet.stories.js.map +1 -1
- package/dist/module/lib/Components/BottomSheet/BottomSheet.test.js +73 -0
- package/dist/module/lib/Components/BottomSheet/BottomSheet.test.js.map +1 -1
- package/dist/module/lib/Components/BottomSheet/BottomSheetHeader.js +1 -1
- package/dist/module/lib/Components/BottomSheet/BottomSheetHeader.js.map +1 -1
- package/dist/module/lib/Components/BottomSheet/CustomHandle.js +15 -2
- package/dist/module/lib/Components/BottomSheet/CustomHandle.js.map +1 -1
- package/dist/module/lib/Components/Card/Card.js.map +1 -1
- package/dist/module/lib/Components/ListItem/ListItem.js.map +1 -1
- package/dist/module/lib/Components/MediaImage/MediaImage.js +10 -2
- package/dist/module/lib/Components/MediaImage/MediaImage.js.map +1 -1
- package/dist/module/lib/Components/MediaTag/MediaTag.js +39 -0
- package/dist/module/lib/Components/MediaTag/MediaTag.js.map +1 -0
- package/dist/module/lib/Components/MediaTag/MediaTag.mdx +161 -0
- package/dist/module/lib/Components/MediaTag/MediaTag.stories.js +122 -0
- package/dist/module/lib/Components/MediaTag/MediaTag.stories.js.map +1 -0
- package/dist/module/lib/Components/MediaTag/MediaTag.test.js +30 -0
- package/dist/module/lib/Components/MediaTag/MediaTag.test.js.map +1 -0
- package/dist/module/lib/Components/MediaTag/index.js +5 -0
- package/dist/module/lib/Components/MediaTag/index.js.map +1 -0
- package/dist/module/lib/Components/MediaTag/types.js +4 -0
- package/dist/module/lib/Components/MediaTag/types.js.map +1 -0
- package/dist/module/lib/Components/OptionList/OptionList.js +45 -4
- package/dist/module/lib/Components/OptionList/OptionList.js.map +1 -1
- package/dist/module/lib/Components/OptionList/OptionList.mdx +19 -0
- package/dist/module/lib/Components/OptionList/OptionList.stories.js +254 -1
- package/dist/module/lib/Components/OptionList/OptionList.stories.js.map +1 -1
- package/dist/module/lib/Components/OptionList/OptionList.test.js +136 -1
- package/dist/module/lib/Components/OptionList/OptionList.test.js.map +1 -1
- package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.js +39 -13
- package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.js.map +1 -1
- package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.test.js +117 -2
- package/dist/module/lib/Components/OptionList/useOptionList/useOptionListItems.test.js.map +1 -1
- package/dist/module/lib/Components/PageIndicator/PageIndicator.test.js.map +1 -1
- package/dist/module/lib/Components/Skeleton/Skeleton.js +10 -3
- package/dist/module/lib/Components/Skeleton/Skeleton.js.map +1 -1
- package/dist/module/lib/Components/TabBar/TabBar.js +7 -6
- package/dist/module/lib/Components/TabBar/TabBar.js.map +1 -1
- package/dist/module/lib/Components/Tag/Tag.js +10 -95
- package/dist/module/lib/Components/Tag/Tag.js.map +1 -1
- package/dist/module/lib/Components/Tag/Tag.mdx +1 -79
- package/dist/module/lib/Components/Tag/Tag.stories.js +8 -1
- package/dist/module/lib/Components/Tag/Tag.stories.js.map +1 -1
- package/dist/module/lib/Components/Tag/Tag.test.js +69 -0
- package/dist/module/lib/Components/Tag/Tag.test.js.map +1 -0
- package/dist/module/lib/Components/index.js +1 -0
- package/dist/module/lib/Components/index.js.map +1 -1
- package/dist/module/styles/lx/resolveStyle.js.map +1 -1
- package/dist/typescript/src/index.d.ts +1 -0
- package/dist/typescript/src/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/Animations/Pulse/Pulse.d.ts +1 -1
- package/dist/typescript/src/lib/Animations/Pulse/Pulse.d.ts.map +1 -1
- package/dist/typescript/src/lib/Animations/Pulse/types.d.ts +2 -1
- package/dist/typescript/src/lib/Animations/Pulse/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/BaseTag/BaseTag.d.ts +3 -0
- package/dist/typescript/src/lib/Components/BaseTag/BaseTag.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/BaseTag/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/BaseTag/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/BaseTag/types.d.ts +10 -0
- package/dist/typescript/src/lib/Components/BaseTag/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/BottomSheet/BottomSheet.d.ts +1 -1
- package/dist/typescript/src/lib/Components/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/BottomSheet/CustomHandle.d.ts +5 -2
- package/dist/typescript/src/lib/Components/BottomSheet/CustomHandle.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/BottomSheet/types.d.ts +16 -3
- package/dist/typescript/src/lib/Components/BottomSheet/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Card/Card.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts +3 -3
- package/dist/typescript/src/lib/Components/ListItem/ListItem.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/MediaImage/MediaImage.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/MediaTag/MediaTag.d.ts +26 -0
- package/dist/typescript/src/lib/Components/MediaTag/MediaTag.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaTag/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/MediaTag/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaTag/types.d.ts +10 -0
- package/dist/typescript/src/lib/Components/MediaTag/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/OptionList/OptionList.d.ts +3 -2
- package/dist/typescript/src/lib/Components/OptionList/OptionList.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/OptionList/types.d.ts +42 -5
- package/dist/typescript/src/lib/Components/OptionList/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/OptionList/useOptionList/useOptionListItems.d.ts +9 -1
- package/dist/typescript/src/lib/Components/OptionList/useOptionList/useOptionListItems.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Skeleton/Skeleton.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/TabBar/TabBar.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Tag/Tag.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Tag/Tag.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Tag/types.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Tag/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/index.d.ts +1 -0
- package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/types/index.d.ts +3 -3
- package/dist/typescript/src/lib/types/index.d.ts.map +1 -1
- package/dist/typescript/src/styles/lx/resolveStyle.d.ts +3 -3
- package/dist/typescript/src/styles/lx/resolveStyle.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/Animations/Pulse/Pulse.tsx +34 -29
- package/src/lib/Animations/Pulse/types.ts +2 -1
- package/src/lib/Components/BaseTag/BaseTag.test.tsx +137 -0
- package/src/lib/Components/BaseTag/BaseTag.tsx +152 -0
- package/src/lib/Components/BaseTag/index.ts +2 -0
- package/src/lib/Components/BaseTag/types.ts +11 -0
- package/src/lib/Components/BottomSheet/BottomSheet.stories.tsx +174 -1
- package/src/lib/Components/BottomSheet/BottomSheet.test.tsx +59 -0
- package/src/lib/Components/BottomSheet/BottomSheet.tsx +19 -7
- package/src/lib/Components/BottomSheet/BottomSheetHeader.tsx +1 -1
- package/src/lib/Components/BottomSheet/CustomHandle.tsx +26 -5
- package/src/lib/Components/BottomSheet/types.ts +24 -3
- package/src/lib/Components/Card/Card.tsx +3 -3
- package/src/lib/Components/ListItem/ListItem.tsx +3 -3
- package/src/lib/Components/MediaImage/MediaImage.tsx +10 -2
- package/src/lib/Components/MediaTag/MediaTag.mdx +161 -0
- package/src/lib/Components/MediaTag/MediaTag.stories.tsx +112 -0
- package/src/lib/Components/MediaTag/MediaTag.test.tsx +27 -0
- package/src/lib/Components/MediaTag/MediaTag.tsx +36 -0
- package/src/lib/Components/MediaTag/index.ts +2 -0
- package/src/lib/Components/MediaTag/types.ts +10 -0
- package/src/lib/Components/OptionList/OptionList.mdx +19 -0
- package/src/lib/Components/OptionList/OptionList.stories.tsx +254 -0
- package/src/lib/Components/OptionList/OptionList.test.tsx +143 -0
- package/src/lib/Components/OptionList/OptionList.tsx +49 -3
- package/src/lib/Components/OptionList/types.ts +46 -5
- package/src/lib/Components/OptionList/useOptionList/useOptionListItems.test.ts +124 -2
- package/src/lib/Components/OptionList/useOptionList/useOptionListItems.ts +53 -10
- package/src/lib/Components/PageIndicator/PageIndicator.test.tsx +2 -1
- package/src/lib/Components/Skeleton/Skeleton.tsx +9 -5
- package/src/lib/Components/TabBar/TabBar.tsx +3 -2
- package/src/lib/Components/Tag/Tag.mdx +1 -79
- package/src/lib/Components/Tag/Tag.stories.tsx +3 -0
- package/src/lib/Components/Tag/Tag.test.tsx +51 -0
- package/src/lib/Components/Tag/Tag.tsx +12 -119
- package/src/lib/Components/Tag/types.ts +2 -1
- package/src/lib/Components/index.ts +1 -0
- package/src/lib/types/index.ts +3 -3
- package/src/styles/lx/resolveStyle.ts +4 -3
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { describe, it, expect, jest } from '@jest/globals';
|
|
2
|
+
import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
|
|
3
|
+
import { render, screen } from '@testing-library/react-native';
|
|
4
|
+
import type { ViewStyle } from 'react-native';
|
|
5
|
+
import { Text } from 'react-native';
|
|
6
|
+
import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
|
|
7
|
+
import { BaseTag } from './BaseTag';
|
|
8
|
+
import type { BaseTagProps } from './types';
|
|
9
|
+
|
|
10
|
+
const { colors } = ledgerLiveThemes.dark;
|
|
11
|
+
|
|
12
|
+
const renderWithProvider = (component: React.ReactElement) =>
|
|
13
|
+
render(
|
|
14
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
|
|
15
|
+
{component}
|
|
16
|
+
</ThemeProvider>,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const baseProps = {
|
|
20
|
+
consumerName: 'BaseTagTest',
|
|
21
|
+
variant: 'tag',
|
|
22
|
+
label: 'Label',
|
|
23
|
+
testID: 'base-tag',
|
|
24
|
+
} satisfies BaseTagProps;
|
|
25
|
+
|
|
26
|
+
describe('BaseTag Component', () => {
|
|
27
|
+
describe('Rendering', () => {
|
|
28
|
+
it('should render label text', () => {
|
|
29
|
+
renderWithProvider(<BaseTag {...baseProps} label='Bitcoin' />);
|
|
30
|
+
expect(screen.getByText('Bitcoin')).toBeTruthy();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should render testID on root element', () => {
|
|
34
|
+
renderWithProvider(<BaseTag {...baseProps} />);
|
|
35
|
+
expect(screen.getByTestId('base-tag')).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should not render any icon when renderIcon is not provided', () => {
|
|
39
|
+
renderWithProvider(<BaseTag {...baseProps} />);
|
|
40
|
+
expect(screen.queryByTestId('test-icon')).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should call renderIcon with the computed icon style', () => {
|
|
44
|
+
const renderIcon = jest.fn(() => (
|
|
45
|
+
<Text testID='test-icon'>icon</Text>
|
|
46
|
+
)) as BaseTagProps['renderIcon'];
|
|
47
|
+
renderWithProvider(<BaseTag {...baseProps} renderIcon={renderIcon} />);
|
|
48
|
+
expect(screen.getByTestId('test-icon')).toBeTruthy();
|
|
49
|
+
expect(renderIcon).toHaveBeenCalledWith(
|
|
50
|
+
expect.objectContaining({ color: expect.any(String) }),
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('Appearances', () => {
|
|
56
|
+
const cases: [NonNullable<BaseTagProps['appearance']>, string][] = [
|
|
57
|
+
['base', colors.bg.mutedTransparent],
|
|
58
|
+
['gray', colors.bg.mutedTransparent],
|
|
59
|
+
['accent', colors.bg.accent],
|
|
60
|
+
['accent-subtle', colors.bg.activeSubtle],
|
|
61
|
+
['success', colors.bg.success],
|
|
62
|
+
['error', colors.bg.error],
|
|
63
|
+
['warning', colors.bg.warning],
|
|
64
|
+
['white', colors.bg.white],
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
it.each(cases)(
|
|
68
|
+
'should render with %s background color',
|
|
69
|
+
(appearance, expected) => {
|
|
70
|
+
renderWithProvider(<BaseTag {...baseProps} appearance={appearance} />);
|
|
71
|
+
expect(screen.getByTestId('base-tag').props.style.backgroundColor).toBe(
|
|
72
|
+
expected,
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Sizes', () => {
|
|
79
|
+
it.each(['sm', 'md'] as const)('should render with %s size', (size) => {
|
|
80
|
+
renderWithProvider(<BaseTag {...baseProps} size={size} />);
|
|
81
|
+
expect(screen.getByTestId('base-tag')).toBeTruthy();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should use smaller padding for sm than md (tag variant)', () => {
|
|
85
|
+
const { rerender } = renderWithProvider(
|
|
86
|
+
<BaseTag {...baseProps} size='md' />,
|
|
87
|
+
);
|
|
88
|
+
const mdPadding =
|
|
89
|
+
screen.getByTestId('base-tag').props.style.paddingHorizontal;
|
|
90
|
+
|
|
91
|
+
rerender(
|
|
92
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
|
|
93
|
+
<BaseTag {...baseProps} size='sm' />
|
|
94
|
+
</ThemeProvider>,
|
|
95
|
+
);
|
|
96
|
+
const smPadding =
|
|
97
|
+
screen.getByTestId('base-tag').props.style.paddingHorizontal;
|
|
98
|
+
|
|
99
|
+
expect(smPadding).toBeLessThan(mdPadding);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('Variants', () => {
|
|
104
|
+
it('should use paddingHorizontal for tag variant', () => {
|
|
105
|
+
renderWithProvider(<BaseTag {...baseProps} variant='tag' />);
|
|
106
|
+
const style = screen.getByTestId('base-tag').props.style;
|
|
107
|
+
expect(style.paddingHorizontal).toBeDefined();
|
|
108
|
+
expect(style.paddingLeft).toBeUndefined();
|
|
109
|
+
expect(style.paddingRight).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should use asymmetric paddingLeft / paddingRight for media variant', () => {
|
|
113
|
+
renderWithProvider(<BaseTag {...baseProps} variant='media' />);
|
|
114
|
+
const style = screen.getByTestId('base-tag').props.style;
|
|
115
|
+
expect(style.paddingLeft).toBeDefined();
|
|
116
|
+
expect(style.paddingRight).toBeDefined();
|
|
117
|
+
expect(style.paddingHorizontal).toBeUndefined();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('Disabled', () => {
|
|
122
|
+
it('should apply disabled background when disabled', () => {
|
|
123
|
+
renderWithProvider(<BaseTag {...baseProps} disabled />);
|
|
124
|
+
expect(screen.getByTestId('base-tag').props.style.backgroundColor).toBe(
|
|
125
|
+
colors.bg.disabled,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('Styling', () => {
|
|
131
|
+
it('should merge custom style with computed root style', () => {
|
|
132
|
+
const custom: ViewStyle = { marginTop: 16 };
|
|
133
|
+
renderWithProvider(<BaseTag {...baseProps} style={custom} />);
|
|
134
|
+
expect(screen.getByTestId('base-tag').props.style.marginTop).toBe(16);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useDisabledContext } from '@ledgerhq/lumen-utils-shared';
|
|
2
|
+
import { StyleSheet, Text } from 'react-native';
|
|
3
|
+
import { useStyleSheet } from '../../../styles';
|
|
4
|
+
import type { TagProps } from '../Tag/types';
|
|
5
|
+
import { Box } from '../Utility';
|
|
6
|
+
import type { BaseTagProps, BaseTagVariant } from './types';
|
|
7
|
+
|
|
8
|
+
type Appearance = NonNullable<TagProps['appearance']>;
|
|
9
|
+
type Size = NonNullable<TagProps['size']>;
|
|
10
|
+
|
|
11
|
+
const useBaseTagStyles = ({
|
|
12
|
+
appearance,
|
|
13
|
+
size,
|
|
14
|
+
disabled,
|
|
15
|
+
variant,
|
|
16
|
+
}: {
|
|
17
|
+
appearance: Appearance;
|
|
18
|
+
size: Size;
|
|
19
|
+
disabled: boolean;
|
|
20
|
+
variant: BaseTagVariant;
|
|
21
|
+
}) => {
|
|
22
|
+
return useStyleSheet(
|
|
23
|
+
(t) => {
|
|
24
|
+
const bgColors: Record<Appearance, string> = {
|
|
25
|
+
base: t.colors.bg.mutedTransparent,
|
|
26
|
+
gray: t.colors.bg.mutedTransparent,
|
|
27
|
+
accent: t.colors.bg.accent,
|
|
28
|
+
'accent-subtle': t.colors.bg.activeSubtle,
|
|
29
|
+
success: t.colors.bg.success,
|
|
30
|
+
error: t.colors.bg.error,
|
|
31
|
+
warning: t.colors.bg.warning,
|
|
32
|
+
white: t.colors.bg.white,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const textColors: Record<Appearance, string> = {
|
|
36
|
+
base: t.colors.text.base,
|
|
37
|
+
gray: t.colors.text.muted,
|
|
38
|
+
accent: t.colors.text.onAccent,
|
|
39
|
+
'accent-subtle': t.colors.text.active,
|
|
40
|
+
success: t.colors.text.success,
|
|
41
|
+
error: t.colors.text.error,
|
|
42
|
+
warning: t.colors.text.warning,
|
|
43
|
+
white: t.colors.text.black,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const tagPadding = {
|
|
47
|
+
md: {
|
|
48
|
+
paddingHorizontal: t.spacings.s8,
|
|
49
|
+
paddingVertical: t.spacings.s4,
|
|
50
|
+
},
|
|
51
|
+
sm: {
|
|
52
|
+
paddingHorizontal: t.spacings.s4,
|
|
53
|
+
paddingVertical: t.spacings.s2,
|
|
54
|
+
},
|
|
55
|
+
} satisfies Record<Size, object>;
|
|
56
|
+
|
|
57
|
+
const mediaPadding = {
|
|
58
|
+
md: {
|
|
59
|
+
paddingLeft: t.spacings.s4,
|
|
60
|
+
paddingRight: t.spacings.s8,
|
|
61
|
+
paddingVertical: t.spacings.s4,
|
|
62
|
+
},
|
|
63
|
+
sm: {
|
|
64
|
+
paddingLeft: t.spacings.s4,
|
|
65
|
+
paddingRight: t.spacings.s4,
|
|
66
|
+
paddingVertical: t.spacings.s2,
|
|
67
|
+
},
|
|
68
|
+
} satisfies Record<Size, object>;
|
|
69
|
+
|
|
70
|
+
const padding =
|
|
71
|
+
variant === 'media' ? mediaPadding[size] : tagPadding[size];
|
|
72
|
+
|
|
73
|
+
const textTypography =
|
|
74
|
+
size === 'md' ? t.typographies.body3 : t.typographies.body4;
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
root: StyleSheet.flatten([
|
|
78
|
+
{
|
|
79
|
+
flexDirection: 'row',
|
|
80
|
+
alignItems: 'center',
|
|
81
|
+
justifyContent: 'center',
|
|
82
|
+
gap: t.spacings.s4,
|
|
83
|
+
borderRadius: t.borderRadius.xs,
|
|
84
|
+
backgroundColor: bgColors[appearance],
|
|
85
|
+
...padding,
|
|
86
|
+
},
|
|
87
|
+
disabled && {
|
|
88
|
+
backgroundColor: t.colors.bg.disabled,
|
|
89
|
+
},
|
|
90
|
+
]),
|
|
91
|
+
text: StyleSheet.flatten([
|
|
92
|
+
textTypography,
|
|
93
|
+
{
|
|
94
|
+
color: textColors[appearance],
|
|
95
|
+
},
|
|
96
|
+
disabled && {
|
|
97
|
+
color: t.colors.text.disabled,
|
|
98
|
+
},
|
|
99
|
+
]),
|
|
100
|
+
icon: StyleSheet.flatten([
|
|
101
|
+
{
|
|
102
|
+
flexShrink: 0,
|
|
103
|
+
color: textColors[appearance],
|
|
104
|
+
},
|
|
105
|
+
disabled && {
|
|
106
|
+
color: t.colors.text.disabled,
|
|
107
|
+
},
|
|
108
|
+
]),
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
[appearance, size, disabled, variant],
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const BaseTag = ({
|
|
116
|
+
appearance = 'accent',
|
|
117
|
+
size = 'md',
|
|
118
|
+
variant,
|
|
119
|
+
consumerName,
|
|
120
|
+
label,
|
|
121
|
+
renderIcon,
|
|
122
|
+
disabled: disabledProp = false,
|
|
123
|
+
lx = {},
|
|
124
|
+
style,
|
|
125
|
+
ref,
|
|
126
|
+
...props
|
|
127
|
+
}: BaseTagProps) => {
|
|
128
|
+
const disabled = useDisabledContext({
|
|
129
|
+
consumerName,
|
|
130
|
+
mergeWith: { disabled: disabledProp },
|
|
131
|
+
});
|
|
132
|
+
const styles = useBaseTagStyles({
|
|
133
|
+
appearance,
|
|
134
|
+
size,
|
|
135
|
+
disabled: !!disabled,
|
|
136
|
+
variant,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<Box
|
|
141
|
+
ref={ref}
|
|
142
|
+
lx={lx}
|
|
143
|
+
style={StyleSheet.flatten([styles.root, style])}
|
|
144
|
+
{...props}
|
|
145
|
+
>
|
|
146
|
+
{renderIcon?.(styles.icon)}
|
|
147
|
+
<Text style={styles.text} numberOfLines={1}>
|
|
148
|
+
{label}
|
|
149
|
+
</Text>
|
|
150
|
+
</Box>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { StyleProp, TextStyle } from 'react-native';
|
|
3
|
+
import type { TagProps } from '../Tag/types';
|
|
4
|
+
|
|
5
|
+
export type BaseTagVariant = 'tag' | 'media';
|
|
6
|
+
|
|
7
|
+
export type BaseTagProps = Omit<TagProps, 'icon'> & {
|
|
8
|
+
variant: BaseTagVariant;
|
|
9
|
+
consumerName: string;
|
|
10
|
+
renderIcon?: (style: StyleProp<TextStyle>) => ReactNode;
|
|
11
|
+
};
|
|
@@ -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 {
|
|
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 = ({
|
|
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
|
-
|
|
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({
|
|
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 }}>
|