@ledgerhq/lumen-ui-rnative 0.1.11 → 0.1.13
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/i18n/locales/de.json +3 -0
- package/dist/module/i18n/locales/en.json +3 -0
- package/dist/module/i18n/locales/es.json +3 -0
- package/dist/module/i18n/locales/fr.json +3 -0
- package/dist/module/i18n/locales/ja.json +3 -0
- package/dist/module/i18n/locales/ko.json +3 -0
- package/dist/module/i18n/locales/pt.json +3 -0
- package/dist/module/i18n/locales/ru.json +3 -0
- package/dist/module/i18n/locales/th.json +3 -0
- package/dist/module/i18n/locales/tr.json +3 -0
- package/dist/module/i18n/locales/zh.json +3 -0
- package/dist/module/lib/Animations/Pulse/Pulse.js +1 -1
- package/dist/module/lib/Animations/Spin/Spin.js +1 -1
- package/dist/module/lib/Components/AmountDisplay/AmountDisplay.js +21 -21
- package/dist/module/lib/Components/AmountDisplay/AmountDisplay.js.map +1 -1
- package/dist/module/lib/Components/AmountInput/AmountInput.js +3 -3
- package/dist/module/lib/Components/BaseInput/BaseInput.js +1 -1
- package/dist/module/lib/Components/BottomSheet/BottomSheet.stories.js.map +1 -1
- package/dist/module/lib/Components/Card/Card.js +39 -29
- package/dist/module/lib/Components/Card/Card.js.map +1 -1
- package/dist/module/lib/Components/InteractiveIcon/InteractiveIcon.js +22 -2
- package/dist/module/lib/Components/InteractiveIcon/InteractiveIcon.js.map +1 -1
- package/dist/module/lib/Components/Link/Link.mdx +1 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.js +158 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.js.map +1 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.mdx +150 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.stories.js +135 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.stories.js.map +1 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.test.js +83 -0
- package/dist/module/lib/Components/MediaBanner/MediaBanner.test.js.map +1 -0
- package/dist/module/lib/Components/MediaBanner/index.js +5 -0
- package/dist/module/lib/Components/MediaBanner/index.js.map +1 -0
- package/dist/module/lib/Components/MediaBanner/types.js +4 -0
- package/dist/module/lib/Components/MediaBanner/types.js.map +1 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.js +183 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.js.map +1 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.mdx +111 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.stories.js +199 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.stories.js.map +1 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.test.js +140 -0
- package/dist/module/lib/Components/MediaCard/MediaCard.test.js.map +1 -0
- package/dist/module/lib/Components/MediaCard/index.js +5 -0
- package/dist/module/lib/Components/MediaCard/index.js.map +1 -0
- package/dist/module/lib/Components/MediaCard/types.js +4 -0
- package/dist/module/lib/Components/MediaCard/types.js.map +1 -0
- package/dist/module/lib/Components/PageIndicator/PageIndicator.js +2 -2
- package/dist/module/lib/Components/SegmentedControl/usePillLayout.js +1 -1
- package/dist/module/lib/Components/Stepper/Stepper.js +1 -1
- package/dist/module/lib/Components/Switch/BaseSwitch.js +1 -1
- package/dist/module/lib/Components/TabBar/TabBar.js +4 -4
- package/dist/module/lib/Components/ThemeProvider/ThemeProvider.test.js +22 -20
- package/dist/module/lib/Components/ThemeProvider/ThemeProvider.test.js.map +1 -1
- package/dist/module/lib/Components/TriggerButton/TriggerButton.js +197 -0
- package/dist/module/lib/Components/TriggerButton/TriggerButton.js.map +1 -0
- package/dist/module/lib/Components/TriggerButton/TriggerButton.mdx +44 -0
- package/dist/module/lib/Components/TriggerButton/TriggerButton.stories.js +170 -0
- package/dist/module/lib/Components/TriggerButton/TriggerButton.stories.js.map +1 -0
- package/dist/module/lib/Components/TriggerButton/TriggerButton.test.js +146 -0
- package/dist/module/lib/Components/TriggerButton/TriggerButton.test.js.map +1 -0
- package/dist/module/lib/Components/TriggerButton/index.js +5 -0
- package/dist/module/lib/Components/TriggerButton/index.js.map +1 -0
- package/dist/module/lib/Components/TriggerButton/types.js +4 -0
- package/dist/module/lib/Components/TriggerButton/types.js.map +1 -0
- package/dist/module/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.js.map +1 -1
- package/dist/module/lib/Components/index.js +3 -0
- package/dist/module/lib/Components/index.js.map +1 -1
- package/dist/module/lib/Symbols/Icons/NanoGen5.js +49 -0
- package/dist/module/lib/Symbols/Icons/NanoGen5.js.map +1 -0
- package/dist/module/lib/Symbols/index.js +1 -0
- package/dist/module/lib/Symbols/index.js.map +1 -1
- package/dist/typescript/src/lib/Components/Card/Card.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/InteractiveIcon/InteractiveIcon.d.ts +1 -1
- package/dist/typescript/src/lib/Components/InteractiveIcon/InteractiveIcon.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/InteractiveIcon/types.d.ts +8 -0
- package/dist/typescript/src/lib/Components/InteractiveIcon/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/MediaBanner/MediaBanner.d.ts +16 -0
- package/dist/typescript/src/lib/Components/MediaBanner/MediaBanner.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaBanner/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/MediaBanner/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaBanner/types.d.ts +42 -0
- package/dist/typescript/src/lib/Components/MediaBanner/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaCard/MediaCard.d.ts +32 -0
- package/dist/typescript/src/lib/Components/MediaCard/MediaCard.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaCard/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/MediaCard/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/MediaCard/types.d.ts +38 -0
- package/dist/typescript/src/lib/Components/MediaCard/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/TriggerButton/TriggerButton.d.ts +26 -0
- package/dist/typescript/src/lib/Components/TriggerButton/TriggerButton.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/TriggerButton/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/TriggerButton/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/TriggerButton/types.d.ts +38 -0
- package/dist/typescript/src/lib/Components/TriggerButton/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/Symbols/Icons/NanoGen5.d.ts +35 -0
- package/dist/typescript/src/lib/Symbols/Icons/NanoGen5.d.ts.map +1 -0
- package/dist/typescript/src/lib/Symbols/index.d.ts +1 -0
- package/dist/typescript/src/lib/Symbols/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/i18n/locales/de.json +3 -0
- package/src/i18n/locales/en.json +3 -0
- package/src/i18n/locales/es.json +3 -0
- package/src/i18n/locales/fr.json +3 -0
- package/src/i18n/locales/ja.json +3 -0
- package/src/i18n/locales/ko.json +3 -0
- package/src/i18n/locales/pt.json +3 -0
- package/src/i18n/locales/ru.json +3 -0
- package/src/i18n/locales/th.json +3 -0
- package/src/i18n/locales/tr.json +3 -0
- package/src/i18n/locales/zh.json +3 -0
- package/src/lib/Components/AmountDisplay/AmountDisplay.tsx +20 -20
- package/src/lib/Components/BottomSheet/BottomSheet.stories.tsx +9 -9
- package/src/lib/Components/Card/Card.tsx +38 -33
- package/src/lib/Components/InteractiveIcon/InteractiveIcon.tsx +26 -4
- package/src/lib/Components/InteractiveIcon/types.ts +8 -0
- package/src/lib/Components/Link/Link.mdx +1 -0
- package/src/lib/Components/MediaBanner/MediaBanner.mdx +150 -0
- package/src/lib/Components/MediaBanner/MediaBanner.stories.tsx +143 -0
- package/src/lib/Components/MediaBanner/MediaBanner.test.tsx +77 -0
- package/src/lib/Components/MediaBanner/MediaBanner.tsx +172 -0
- package/src/lib/Components/MediaBanner/index.ts +2 -0
- package/src/lib/Components/MediaBanner/types.ts +44 -0
- package/src/lib/Components/MediaCard/MediaCard.mdx +111 -0
- package/src/lib/Components/MediaCard/MediaCard.stories.tsx +190 -0
- package/src/lib/Components/MediaCard/MediaCard.test.tsx +125 -0
- package/src/lib/Components/MediaCard/MediaCard.tsx +203 -0
- package/src/lib/Components/MediaCard/index.ts +2 -0
- package/src/lib/Components/MediaCard/types.ts +39 -0
- package/src/lib/Components/ThemeProvider/ThemeProvider.test.tsx +16 -18
- package/src/lib/Components/TriggerButton/TriggerButton.mdx +44 -0
- package/src/lib/Components/TriggerButton/TriggerButton.stories.tsx +132 -0
- package/src/lib/Components/TriggerButton/TriggerButton.test.tsx +157 -0
- package/src/lib/Components/TriggerButton/TriggerButton.tsx +228 -0
- package/src/lib/Components/TriggerButton/index.ts +2 -0
- package/src/lib/Components/TriggerButton/types.ts +38 -0
- package/src/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.tsx +1 -1
- package/src/lib/Components/index.ts +3 -0
- package/src/lib/Symbols/Icons/NanoGen5.tsx +44 -0
- package/src/lib/Symbols/index.ts +1 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { CryptoIcon } from '@ledgerhq/crypto-icons';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
|
|
3
|
+
import { Settings, Star } from '../../Symbols';
|
|
4
|
+
import { Box } from '../Utility';
|
|
5
|
+
import { TriggerButton } from './TriggerButton';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof TriggerButton> = {
|
|
8
|
+
component: TriggerButton,
|
|
9
|
+
title: 'Action/TriggerButton',
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
docs: {
|
|
13
|
+
source: {
|
|
14
|
+
language: 'tsx',
|
|
15
|
+
format: true,
|
|
16
|
+
type: 'code',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof TriggerButton>;
|
|
24
|
+
|
|
25
|
+
export const Base: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
children: 'All accounts',
|
|
28
|
+
appearance: 'gray',
|
|
29
|
+
},
|
|
30
|
+
render: (args) => <TriggerButton {...args} />,
|
|
31
|
+
parameters: {
|
|
32
|
+
docs: {
|
|
33
|
+
source: {
|
|
34
|
+
code: `
|
|
35
|
+
<TriggerButton appearance="gray">
|
|
36
|
+
All accounts
|
|
37
|
+
</TriggerButton>
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const SizeShowcase: Story = {
|
|
45
|
+
render: () => (
|
|
46
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's16' }}>
|
|
47
|
+
<TriggerButton size='sm' icon={<Star size={20} />} iconType='flat'>
|
|
48
|
+
Small
|
|
49
|
+
</TriggerButton>
|
|
50
|
+
<TriggerButton size='md' icon={<Star size={20} />} iconType='flat'>
|
|
51
|
+
Medium
|
|
52
|
+
</TriggerButton>
|
|
53
|
+
</Box>
|
|
54
|
+
),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const IconTypeShowcase: Story = {
|
|
58
|
+
render: () => (
|
|
59
|
+
<Box lx={{ flexDirection: 'column', gap: 's16' }}>
|
|
60
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's16' }}>
|
|
61
|
+
<TriggerButton
|
|
62
|
+
icon={<Settings size={20} />}
|
|
63
|
+
iconType='flat'
|
|
64
|
+
appearance='gray'
|
|
65
|
+
>
|
|
66
|
+
Flat icon (md)
|
|
67
|
+
</TriggerButton>
|
|
68
|
+
<TriggerButton
|
|
69
|
+
icon={<CryptoIcon ledgerId='bitcoin' ticker='BTC' size='32px' />}
|
|
70
|
+
iconType='rounded'
|
|
71
|
+
appearance='gray'
|
|
72
|
+
>
|
|
73
|
+
Rounded icon (md)
|
|
74
|
+
</TriggerButton>
|
|
75
|
+
<TriggerButton appearance='gray'>No icon (md)</TriggerButton>
|
|
76
|
+
</Box>
|
|
77
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's16' }}>
|
|
78
|
+
<TriggerButton
|
|
79
|
+
icon={<Settings size={20} />}
|
|
80
|
+
iconType='flat'
|
|
81
|
+
appearance='gray'
|
|
82
|
+
size='sm'
|
|
83
|
+
>
|
|
84
|
+
Flat icon (sm)
|
|
85
|
+
</TriggerButton>
|
|
86
|
+
<TriggerButton
|
|
87
|
+
icon={<CryptoIcon ledgerId='bitcoin' ticker='BTC' size='24px' />}
|
|
88
|
+
iconType='rounded'
|
|
89
|
+
appearance='gray'
|
|
90
|
+
size='sm'
|
|
91
|
+
>
|
|
92
|
+
Rounded icon (sm)
|
|
93
|
+
</TriggerButton>
|
|
94
|
+
<TriggerButton appearance='gray' size='sm'>
|
|
95
|
+
No icon (sm)
|
|
96
|
+
</TriggerButton>
|
|
97
|
+
</Box>
|
|
98
|
+
</Box>
|
|
99
|
+
),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const AppearanceShowcase: Story = {
|
|
103
|
+
render: () => {
|
|
104
|
+
const appearances = ['gray', 'transparent', 'no-background'] as const;
|
|
105
|
+
return (
|
|
106
|
+
<Box lx={{ flexDirection: 'column', gap: 's16', padding: 's16' }}>
|
|
107
|
+
{appearances.map((appearance) => (
|
|
108
|
+
<Box
|
|
109
|
+
key={appearance}
|
|
110
|
+
lx={{ flexDirection: 'row', alignItems: 'center', gap: 's16' }}
|
|
111
|
+
>
|
|
112
|
+
<TriggerButton appearance={appearance}>{appearance}</TriggerButton>
|
|
113
|
+
<TriggerButton
|
|
114
|
+
appearance={appearance}
|
|
115
|
+
icon={<Settings size={20} />}
|
|
116
|
+
iconType='flat'
|
|
117
|
+
>
|
|
118
|
+
{appearance}
|
|
119
|
+
</TriggerButton>
|
|
120
|
+
<TriggerButton
|
|
121
|
+
appearance={appearance}
|
|
122
|
+
icon={<CryptoIcon ledgerId='bitcoin' ticker='BTC' size='32px' />}
|
|
123
|
+
iconType='rounded'
|
|
124
|
+
>
|
|
125
|
+
{appearance}
|
|
126
|
+
</TriggerButton>
|
|
127
|
+
</Box>
|
|
128
|
+
))}
|
|
129
|
+
</Box>
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { describe, it, expect, jest } from '@jest/globals';
|
|
2
|
+
import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
|
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react-native';
|
|
4
|
+
import React, { createRef } from 'react';
|
|
5
|
+
import { View, ViewStyle } from 'react-native';
|
|
6
|
+
|
|
7
|
+
import { Settings } from '../../Symbols';
|
|
8
|
+
import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
|
|
9
|
+
import { TriggerButton } from './TriggerButton';
|
|
10
|
+
|
|
11
|
+
const renderWithProvider = (component: React.ReactElement) => {
|
|
12
|
+
return render(
|
|
13
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
|
|
14
|
+
{component}
|
|
15
|
+
</ThemeProvider>,
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('TriggerButton', () => {
|
|
20
|
+
describe('Rendering', () => {
|
|
21
|
+
it('should render with label text and correct accessibility role', () => {
|
|
22
|
+
renderWithProvider(
|
|
23
|
+
<TriggerButton testID='trigger'>All accounts</TriggerButton>,
|
|
24
|
+
);
|
|
25
|
+
const trigger = screen.getByTestId('trigger');
|
|
26
|
+
expect(trigger).toBeTruthy();
|
|
27
|
+
expect(screen.getByText('All accounts')).toBeTruthy();
|
|
28
|
+
expect(trigger.props.accessibilityRole).toBe('button');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should always render a chevron icon', () => {
|
|
32
|
+
renderWithProvider(<TriggerButton testID='trigger'>Label</TriggerButton>);
|
|
33
|
+
expect(screen.getByTestId('button-trigger-chevron')).toBeTruthy();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it.each(['gray', 'transparent', 'no-background'] as const)(
|
|
37
|
+
'should render without errors for appearance "%s"',
|
|
38
|
+
(appearance) => {
|
|
39
|
+
renderWithProvider(
|
|
40
|
+
<TriggerButton testID='trigger' appearance={appearance}>
|
|
41
|
+
Label
|
|
42
|
+
</TriggerButton>,
|
|
43
|
+
);
|
|
44
|
+
expect(screen.getByTestId('trigger')).toBeTruthy();
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
it.each(['sm', 'md'] as const)(
|
|
49
|
+
'should render without errors for size "%s"',
|
|
50
|
+
(size) => {
|
|
51
|
+
renderWithProvider(
|
|
52
|
+
<TriggerButton testID='trigger' size={size}>
|
|
53
|
+
Label
|
|
54
|
+
</TriggerButton>,
|
|
55
|
+
);
|
|
56
|
+
expect(screen.getByTestId('trigger')).toBeTruthy();
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Icons', () => {
|
|
62
|
+
it('should render with a flat interface icon', () => {
|
|
63
|
+
renderWithProvider(
|
|
64
|
+
<TriggerButton
|
|
65
|
+
testID='trigger'
|
|
66
|
+
icon={<Settings size={20} testID='icon' />}
|
|
67
|
+
iconType='flat'
|
|
68
|
+
>
|
|
69
|
+
Network
|
|
70
|
+
</TriggerButton>,
|
|
71
|
+
);
|
|
72
|
+
expect(screen.getByTestId('icon')).toBeTruthy();
|
|
73
|
+
expect(screen.getByText('Network')).toBeTruthy();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should render with a rounded icon', () => {
|
|
77
|
+
renderWithProvider(
|
|
78
|
+
<TriggerButton
|
|
79
|
+
testID='trigger'
|
|
80
|
+
icon={<View testID='crypto-icon' />}
|
|
81
|
+
iconType='rounded'
|
|
82
|
+
>
|
|
83
|
+
Bitcoin
|
|
84
|
+
</TriggerButton>,
|
|
85
|
+
);
|
|
86
|
+
expect(screen.getByTestId('crypto-icon')).toBeTruthy();
|
|
87
|
+
expect(screen.getByText('Bitcoin')).toBeTruthy();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('States', () => {
|
|
92
|
+
it('should be disabled when disabled prop is true', () => {
|
|
93
|
+
renderWithProvider(
|
|
94
|
+
<TriggerButton testID='trigger' disabled>
|
|
95
|
+
Label
|
|
96
|
+
</TriggerButton>,
|
|
97
|
+
);
|
|
98
|
+
const trigger = screen.getByTestId('trigger');
|
|
99
|
+
expect(trigger.props.accessibilityState.disabled).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('Interactions', () => {
|
|
104
|
+
it('should call onPress when pressed', () => {
|
|
105
|
+
const handlePress = jest.fn();
|
|
106
|
+
renderWithProvider(
|
|
107
|
+
<TriggerButton testID='trigger' onPress={handlePress}>
|
|
108
|
+
Press me
|
|
109
|
+
</TriggerButton>,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
fireEvent.press(screen.getByTestId('trigger'));
|
|
113
|
+
expect(handlePress).toHaveBeenCalledTimes(1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should not call onPress when disabled', () => {
|
|
117
|
+
const handlePress = jest.fn();
|
|
118
|
+
renderWithProvider(
|
|
119
|
+
<TriggerButton testID='trigger' onPress={handlePress} disabled>
|
|
120
|
+
Disabled
|
|
121
|
+
</TriggerButton>,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
fireEvent.press(screen.getByTestId('trigger'));
|
|
125
|
+
expect(handlePress).not.toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('Ref Forwarding', () => {
|
|
130
|
+
it('should forward ref', () => {
|
|
131
|
+
const ref = createRef<View>();
|
|
132
|
+
renderWithProvider(
|
|
133
|
+
<TriggerButton ref={ref} testID='trigger'>
|
|
134
|
+
Label
|
|
135
|
+
</TriggerButton>,
|
|
136
|
+
);
|
|
137
|
+
expect(ref.current).toBeTruthy();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('Styling', () => {
|
|
142
|
+
it('should apply custom style and lx props', () => {
|
|
143
|
+
const customStyle: ViewStyle = { marginTop: 16 };
|
|
144
|
+
renderWithProvider(
|
|
145
|
+
<TriggerButton
|
|
146
|
+
testID='trigger'
|
|
147
|
+
style={customStyle}
|
|
148
|
+
lx={{ padding: 's8' }}
|
|
149
|
+
>
|
|
150
|
+
Styled
|
|
151
|
+
</TriggerButton>,
|
|
152
|
+
);
|
|
153
|
+
const trigger = screen.getByTestId('trigger');
|
|
154
|
+
expect(trigger.props.style).toBeDefined();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import { useStyleSheet } from '../../../styles';
|
|
4
|
+
import { ChevronDown } from '../../Symbols';
|
|
5
|
+
import { Pressable } from '../Utility';
|
|
6
|
+
import type { TriggerButtonProps } from './types';
|
|
7
|
+
|
|
8
|
+
type Appearance = NonNullable<TriggerButtonProps['appearance']>;
|
|
9
|
+
type Size = NonNullable<TriggerButtonProps['size']>;
|
|
10
|
+
type IconType = 'flat' | 'rounded' | 'none';
|
|
11
|
+
|
|
12
|
+
const useStyles = ({
|
|
13
|
+
appearance,
|
|
14
|
+
size,
|
|
15
|
+
disabled,
|
|
16
|
+
pressed,
|
|
17
|
+
iconType,
|
|
18
|
+
}: {
|
|
19
|
+
appearance: Appearance;
|
|
20
|
+
size: Size;
|
|
21
|
+
disabled: boolean;
|
|
22
|
+
pressed: boolean;
|
|
23
|
+
iconType: IconType;
|
|
24
|
+
}) => {
|
|
25
|
+
return useStyleSheet(
|
|
26
|
+
(t) => {
|
|
27
|
+
const bgColors: Record<Appearance, string> = {
|
|
28
|
+
gray: t.colors.bg.muted,
|
|
29
|
+
transparent: t.colors.bg.mutedTransparent,
|
|
30
|
+
'no-background': 'transparent',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const pressedBgColors: Record<Appearance, string> = {
|
|
34
|
+
gray: t.colors.bg.mutedPressed,
|
|
35
|
+
transparent: t.colors.bg.mutedTransparentPressed,
|
|
36
|
+
'no-background': t.colors.bg.baseTransparentPressed,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const textColor = disabled ? t.colors.text.disabled : t.colors.text.base;
|
|
40
|
+
|
|
41
|
+
type PaddingStyle = {
|
|
42
|
+
paddingTop: number;
|
|
43
|
+
paddingBottom: number;
|
|
44
|
+
paddingLeft: number;
|
|
45
|
+
paddingRight: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const paddingMap: Record<Size, Record<IconType, PaddingStyle>> = {
|
|
49
|
+
md: {
|
|
50
|
+
flat: {
|
|
51
|
+
paddingTop: t.spacings.s12,
|
|
52
|
+
paddingBottom: t.spacings.s12,
|
|
53
|
+
paddingLeft: t.spacings.s16,
|
|
54
|
+
paddingRight: t.spacings.s16,
|
|
55
|
+
},
|
|
56
|
+
rounded: {
|
|
57
|
+
paddingTop: t.spacings.s8,
|
|
58
|
+
paddingBottom: t.spacings.s8,
|
|
59
|
+
paddingLeft: t.spacings.s8,
|
|
60
|
+
paddingRight: t.spacings.s16,
|
|
61
|
+
},
|
|
62
|
+
none: {
|
|
63
|
+
paddingTop: t.spacings.s14,
|
|
64
|
+
paddingBottom: t.spacings.s14,
|
|
65
|
+
paddingLeft: t.spacings.s16,
|
|
66
|
+
paddingRight: t.spacings.s16,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
sm: {
|
|
70
|
+
flat: {
|
|
71
|
+
paddingTop: t.spacings.s10,
|
|
72
|
+
paddingBottom: t.spacings.s10,
|
|
73
|
+
paddingLeft: t.spacings.s12,
|
|
74
|
+
paddingRight: t.spacings.s12,
|
|
75
|
+
},
|
|
76
|
+
rounded: {
|
|
77
|
+
paddingTop: t.spacings.s8,
|
|
78
|
+
paddingBottom: t.spacings.s8,
|
|
79
|
+
paddingLeft: t.spacings.s8,
|
|
80
|
+
paddingRight: t.spacings.s10,
|
|
81
|
+
},
|
|
82
|
+
none: {
|
|
83
|
+
paddingTop: t.spacings.s10,
|
|
84
|
+
paddingBottom: t.spacings.s10,
|
|
85
|
+
paddingLeft: t.spacings.s12,
|
|
86
|
+
paddingRight: t.spacings.s12,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
container: StyleSheet.flatten([
|
|
93
|
+
{
|
|
94
|
+
flexDirection: 'row',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
borderRadius: t.borderRadius.full,
|
|
97
|
+
backgroundColor: bgColors[appearance],
|
|
98
|
+
gap: t.spacings.s8,
|
|
99
|
+
},
|
|
100
|
+
paddingMap[size][iconType],
|
|
101
|
+
pressed && { backgroundColor: pressedBgColors[appearance] },
|
|
102
|
+
disabled && { backgroundColor: t.colors.bg.disabled },
|
|
103
|
+
appearance === 'no-background' &&
|
|
104
|
+
disabled && { backgroundColor: 'transparent' },
|
|
105
|
+
]),
|
|
106
|
+
label: StyleSheet.flatten([
|
|
107
|
+
t.typographies.body2SemiBold,
|
|
108
|
+
{
|
|
109
|
+
color: textColor,
|
|
110
|
+
textAlign: 'left',
|
|
111
|
+
},
|
|
112
|
+
]),
|
|
113
|
+
labelWrapper: {
|
|
114
|
+
flexDirection: 'row',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
gap: t.spacings.s2,
|
|
117
|
+
},
|
|
118
|
+
icon: {
|
|
119
|
+
flexShrink: 0,
|
|
120
|
+
},
|
|
121
|
+
chevron: {
|
|
122
|
+
flexShrink: 0,
|
|
123
|
+
color: textColor,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
[appearance, size, disabled, pressed, iconType],
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Trigger button for select/dropdown components. Displays a label with an optional
|
|
133
|
+
* leading icon and a trailing chevron indicator.
|
|
134
|
+
*
|
|
135
|
+
* This component is intended to be used exclusively as the trigger inside a Select or
|
|
136
|
+
* dropdown pattern. It should not be used as a standalone action button — use `Button`
|
|
137
|
+
* or `IconButton` instead.
|
|
138
|
+
*
|
|
139
|
+
* @see {@link https://www.figma.com/design/JxaLVMTWirCpU0rsbZ30k7/2.-Components-Library?node-id=6389-45680 Figma}
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* import { TriggerButton } from '@ledgerhq/lumen-ui-rnative';
|
|
143
|
+
* import { Settings } from '@ledgerhq/lumen-ui-rnative/symbols';
|
|
144
|
+
*
|
|
145
|
+
* <TriggerButton icon={<Settings size={20} />} iconType="flat">
|
|
146
|
+
* Network
|
|
147
|
+
* </TriggerButton>
|
|
148
|
+
*
|
|
149
|
+
* <TriggerButton>All accounts</TriggerButton>
|
|
150
|
+
*/
|
|
151
|
+
export const TriggerButton = ({
|
|
152
|
+
lx,
|
|
153
|
+
style,
|
|
154
|
+
appearance = 'gray',
|
|
155
|
+
size = 'md',
|
|
156
|
+
disabled = false,
|
|
157
|
+
icon,
|
|
158
|
+
iconType = 'flat',
|
|
159
|
+
children: label,
|
|
160
|
+
ref,
|
|
161
|
+
...props
|
|
162
|
+
}: TriggerButtonProps) => {
|
|
163
|
+
const effectiveIconType: IconType = icon ? iconType : 'none';
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Pressable
|
|
167
|
+
ref={ref}
|
|
168
|
+
lx={lx}
|
|
169
|
+
style={style}
|
|
170
|
+
disabled={disabled}
|
|
171
|
+
accessibilityRole='button'
|
|
172
|
+
accessibilityState={{ disabled }}
|
|
173
|
+
{...props}
|
|
174
|
+
>
|
|
175
|
+
{({ pressed }) => (
|
|
176
|
+
<TriggerButtonContent
|
|
177
|
+
appearance={appearance}
|
|
178
|
+
size={size}
|
|
179
|
+
disabled={disabled}
|
|
180
|
+
pressed={pressed}
|
|
181
|
+
iconType={effectiveIconType}
|
|
182
|
+
icon={icon}
|
|
183
|
+
>
|
|
184
|
+
{label}
|
|
185
|
+
</TriggerButtonContent>
|
|
186
|
+
)}
|
|
187
|
+
</Pressable>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
type TriggerButtonContentProps = PropsWithChildren<{
|
|
192
|
+
appearance: Appearance;
|
|
193
|
+
size: Size;
|
|
194
|
+
disabled: boolean;
|
|
195
|
+
pressed: boolean;
|
|
196
|
+
iconType: IconType;
|
|
197
|
+
icon?: TriggerButtonProps['icon'];
|
|
198
|
+
}>;
|
|
199
|
+
|
|
200
|
+
const TriggerButtonContent = ({
|
|
201
|
+
appearance,
|
|
202
|
+
size,
|
|
203
|
+
disabled,
|
|
204
|
+
pressed,
|
|
205
|
+
iconType,
|
|
206
|
+
icon,
|
|
207
|
+
children,
|
|
208
|
+
}: TriggerButtonContentProps) => {
|
|
209
|
+
const styles = useStyles({ appearance, size, disabled, pressed, iconType });
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<View style={styles.container} testID='button-trigger-content'>
|
|
213
|
+
{icon && <View style={styles.icon}>{icon}</View>}
|
|
214
|
+
<View style={styles.labelWrapper}>
|
|
215
|
+
<Text style={styles.label} numberOfLines={1} ellipsizeMode='tail'>
|
|
216
|
+
{children}
|
|
217
|
+
</Text>
|
|
218
|
+
<ChevronDown
|
|
219
|
+
size={20}
|
|
220
|
+
style={styles.chevron}
|
|
221
|
+
testID='button-trigger-chevron'
|
|
222
|
+
/>
|
|
223
|
+
</View>
|
|
224
|
+
</View>
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
TriggerButton.displayName = 'TriggerButton';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyledPressableProps } from '../../../styles';
|
|
3
|
+
|
|
4
|
+
export type TriggerButtonProps = {
|
|
5
|
+
/**
|
|
6
|
+
* The visual style of the trigger button.
|
|
7
|
+
* @default 'gray'
|
|
8
|
+
*/
|
|
9
|
+
appearance?: 'gray' | 'transparent' | 'no-background';
|
|
10
|
+
/**
|
|
11
|
+
* The size variant of the trigger button.
|
|
12
|
+
* @default 'md'
|
|
13
|
+
*/
|
|
14
|
+
size?: 'sm' | 'md';
|
|
15
|
+
/**
|
|
16
|
+
* An optional pre-rendered icon element to display as leading content.
|
|
17
|
+
* Consumer is responsible for sizing the icon.
|
|
18
|
+
*/
|
|
19
|
+
icon?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Determines the padding scheme when an icon is present.
|
|
22
|
+
* - `'flat'`: Standard padding for line/interface icons.
|
|
23
|
+
* - `'rounded'`: Tighter left padding for circular icons with their own background (e.g., crypto icons).
|
|
24
|
+
*
|
|
25
|
+
* Only relevant when `icon` is provided.
|
|
26
|
+
* @default 'flat'
|
|
27
|
+
*/
|
|
28
|
+
iconType?: 'flat' | 'rounded';
|
|
29
|
+
/**
|
|
30
|
+
* Whether the trigger button is disabled.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The label content of the trigger button.
|
|
36
|
+
*/
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
} & Omit<StyledPressableProps, 'children'>;
|
|
@@ -5,6 +5,7 @@ export * from './Avatar';
|
|
|
5
5
|
export * from './Banner';
|
|
6
6
|
export * from './BottomSheet';
|
|
7
7
|
export * from './Button';
|
|
8
|
+
export * from './TriggerButton';
|
|
8
9
|
export * from './Card';
|
|
9
10
|
export * from './CardButton';
|
|
10
11
|
export * from './ContentBanner';
|
|
@@ -15,6 +16,8 @@ export * from './IconButton';
|
|
|
15
16
|
export * from './InteractiveIcon';
|
|
16
17
|
export * from './Link';
|
|
17
18
|
export * from './ListItem';
|
|
19
|
+
export * from './MediaBanner';
|
|
20
|
+
export * from './MediaCard';
|
|
18
21
|
export * from './NavBar';
|
|
19
22
|
export * from './PageIndicator';
|
|
20
23
|
export * from './SearchInput';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import Svg, { Path } from 'react-native-svg';
|
|
2
|
+
import createIcon from '../../Components/Icon/createIcon';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* NanoGen5 icon component for React Native.
|
|
6
|
+
*
|
|
7
|
+
* This icon component is automatically generated from SVG files and uses the createIcon utility
|
|
8
|
+
* to create a consistent icon interface. It supports all standard SVG props (from react-native-svg)
|
|
9
|
+
* and additional size variants defined in the Icon component.
|
|
10
|
+
*
|
|
11
|
+
* @component
|
|
12
|
+
* @param {16 | 20 | 24 | 40 | 48 | 56} [size=24] - The size of the icon in pixels.
|
|
13
|
+
* @param {string} [color] - The color of the icon.
|
|
14
|
+
* @param {SVGProps} [...props] - All standard SVG element props (from react-native-svg).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Basic usage with default size (24px)
|
|
18
|
+
* import { NanoGen5 } from '@ledgerhq/lumen-ui-rnative/symbols';
|
|
19
|
+
*
|
|
20
|
+
* <NanoGen5 />
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // With custom size and style
|
|
24
|
+
* <NanoGen5 size={40} color="warning" lx={{ marginTop: 's4' }} />
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Used within a Button component
|
|
28
|
+
* import { Button } from '@ledgerhq/lumen-ui-rnative';
|
|
29
|
+
*
|
|
30
|
+
* <Button icon={NanoGen5} size="md">
|
|
31
|
+
* Click me
|
|
32
|
+
* </Button>
|
|
33
|
+
*/
|
|
34
|
+
export const NanoGen5 = createIcon(
|
|
35
|
+
'NanoGen5',
|
|
36
|
+
<Svg width={24} height={24} fill='currentColor' viewBox='0 0 16 16'>
|
|
37
|
+
<Path
|
|
38
|
+
fill='currentColor'
|
|
39
|
+
fillRule='evenodd'
|
|
40
|
+
d='M11 1.333c.92 0 1.667.747 1.667 1.667v.333c.184 0 .333.15.333.334v1.666a.334.334 0 0 1-.266.327l-.067.007V13c0 .92-.746 1.667-1.667 1.667H5c-.92 0-1.667-.746-1.667-1.667V3c0-.92.747-1.667 1.667-1.667zM9.667 12.667a.667.667 0 0 0 0 1.333H11a.667.667 0 0 0 0-1.333zM5 2.333A.667.667 0 0 0 4.333 3v8c0 .368.299.667.667.667h6a.667.667 0 0 0 .667-.667V3A.667.667 0 0 0 11 2.333z'
|
|
41
|
+
clipRule='evenodd'
|
|
42
|
+
/>
|
|
43
|
+
</Svg>,
|
|
44
|
+
);
|
package/src/lib/Symbols/index.ts
CHANGED
|
@@ -164,6 +164,7 @@ export { Moon } from './Icons/Moon';
|
|
|
164
164
|
export { MoreHorizontal } from './Icons/MoreHorizontal';
|
|
165
165
|
export { MoreVertical } from './Icons/MoreVertical';
|
|
166
166
|
export { Nano } from './Icons/Nano';
|
|
167
|
+
export { NanoGen5 } from './Icons/NanoGen5';
|
|
167
168
|
export { NetworkWarning } from './Icons/NetworkWarning';
|
|
168
169
|
export { Nfc } from './Icons/Nfc';
|
|
169
170
|
export { Nft } from './Icons/Nft';
|