@ledgerhq/lumen-ui-rnative 0.0.76 → 0.0.78
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/package.json +2 -2
- package/dist/src/lib/Components/ContentBanner/ContentBanner.d.ts +48 -0
- package/dist/src/lib/Components/ContentBanner/ContentBanner.d.ts.map +1 -0
- package/dist/src/lib/Components/ContentBanner/ContentBanner.js +108 -0
- package/dist/src/lib/Components/ContentBanner/ContentBanner.stories.d.ts +11 -0
- package/dist/src/lib/Components/ContentBanner/ContentBanner.stories.d.ts.map +1 -0
- package/dist/src/lib/Components/ContentBanner/ContentBanner.stories.js +91 -0
- package/dist/src/lib/Components/ContentBanner/index.d.ts +3 -0
- package/dist/src/lib/Components/ContentBanner/index.d.ts.map +1 -0
- package/dist/src/lib/Components/ContentBanner/index.js +2 -0
- package/dist/src/lib/Components/ContentBanner/types.d.ts +47 -0
- package/dist/src/lib/Components/ContentBanner/types.d.ts.map +1 -0
- package/dist/src/lib/Components/ContentBanner/types.js +1 -0
- package/dist/src/lib/Components/index.d.ts +1 -0
- package/dist/src/lib/Components/index.d.ts.map +1 -1
- package/dist/src/lib/Components/index.js +1 -0
- package/dist/src/lib/Symbols/Icons/Csv.d.ts +35 -0
- package/dist/src/lib/Symbols/Icons/Csv.d.ts.map +1 -0
- package/dist/src/lib/Symbols/Icons/Csv.js +34 -0
- package/dist/src/lib/Symbols/Icons/Invoice.d.ts +35 -0
- package/dist/src/lib/Symbols/Icons/Invoice.d.ts.map +1 -0
- package/dist/src/lib/Symbols/Icons/Invoice.js +34 -0
- package/dist/src/lib/Symbols/Icons/Mailbox.d.ts +35 -0
- package/dist/src/lib/Symbols/Icons/Mailbox.d.ts.map +1 -0
- package/dist/src/lib/Symbols/Icons/Mailbox.js +34 -0
- package/dist/src/lib/Symbols/Icons/PenEditWriting.d.ts +35 -0
- package/dist/src/lib/Symbols/Icons/PenEditWriting.d.ts.map +1 -0
- package/dist/src/lib/Symbols/Icons/PenEditWriting.js +34 -0
- package/dist/src/lib/Symbols/Icons/Unlink.d.ts +35 -0
- package/dist/src/lib/Symbols/Icons/Unlink.d.ts.map +1 -0
- package/dist/src/lib/Symbols/Icons/Unlink.js +34 -0
- package/dist/src/lib/Symbols/index.d.ts +5 -0
- package/dist/src/lib/Symbols/index.d.ts.map +1 -1
- package/dist/src/lib/Symbols/index.js +5 -0
- package/package.json +2 -2
- package/src/lib/Components/ContentBanner/ContentBanner.mdx +246 -0
- package/src/lib/Components/ContentBanner/ContentBanner.stories.tsx +226 -0
- package/src/lib/Components/ContentBanner/ContentBanner.test.tsx +133 -0
- package/src/lib/Components/ContentBanner/ContentBanner.tsx +226 -0
- package/src/lib/Components/ContentBanner/index.ts +2 -0
- package/src/lib/Components/ContentBanner/types.ts +50 -0
- package/src/lib/Components/index.ts +1 -0
- package/src/lib/Symbols/Icons/Csv.tsx +49 -0
- package/src/lib/Symbols/Icons/Invoice.tsx +45 -0
- package/src/lib/Symbols/Icons/Mailbox.tsx +45 -0
- package/src/lib/Symbols/Icons/PenEditWriting.tsx +45 -0
- package/src/lib/Symbols/Icons/Unlink.tsx +45 -0
- package/src/lib/Symbols/index.ts +5 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Settings, Wallet } from '../../Symbols';
|
|
4
|
+
import { Button } from '../Button';
|
|
5
|
+
import { Spot } from '../Spot';
|
|
6
|
+
import { Stepper } from '../Stepper';
|
|
7
|
+
import { Box, Text } from '../Utility';
|
|
8
|
+
import {
|
|
9
|
+
ContentBanner,
|
|
10
|
+
ContentBannerContent,
|
|
11
|
+
ContentBannerDescription,
|
|
12
|
+
ContentBannerTitle,
|
|
13
|
+
} from './ContentBanner';
|
|
14
|
+
|
|
15
|
+
const meta: Meta<typeof ContentBanner> = {
|
|
16
|
+
component: ContentBanner,
|
|
17
|
+
title: 'Communication/ContentBanner',
|
|
18
|
+
parameters: {
|
|
19
|
+
docs: {
|
|
20
|
+
source: {
|
|
21
|
+
language: 'tsx',
|
|
22
|
+
format: true,
|
|
23
|
+
type: 'code',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
onClose: {
|
|
29
|
+
control: 'select',
|
|
30
|
+
description: 'Close action callback',
|
|
31
|
+
options: ['With Close', 'None'],
|
|
32
|
+
mapping: {
|
|
33
|
+
'With Close': () => {
|
|
34
|
+
console.log('Close clicked');
|
|
35
|
+
},
|
|
36
|
+
None: undefined,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
closeAccessibilityLabel: {
|
|
40
|
+
control: 'text',
|
|
41
|
+
description: 'Accessibility label for the close button',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default meta;
|
|
47
|
+
type Story = StoryObj<typeof ContentBanner>;
|
|
48
|
+
|
|
49
|
+
export const Base: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
closeAccessibilityLabel: 'Close content banner',
|
|
52
|
+
},
|
|
53
|
+
render: (args) => (
|
|
54
|
+
<Box lx={{ maxWidth: 's400' }}>
|
|
55
|
+
<ContentBanner {...args}>
|
|
56
|
+
<Spot appearance='icon' icon={Settings} size={48} />
|
|
57
|
+
<ContentBannerContent>
|
|
58
|
+
<ContentBannerTitle>Content Banner</ContentBannerTitle>
|
|
59
|
+
<ContentBannerDescription>
|
|
60
|
+
This is a description for the content banner.
|
|
61
|
+
</ContentBannerDescription>
|
|
62
|
+
</ContentBannerContent>
|
|
63
|
+
</ContentBanner>
|
|
64
|
+
</Box>
|
|
65
|
+
),
|
|
66
|
+
parameters: {
|
|
67
|
+
docs: {
|
|
68
|
+
source: {
|
|
69
|
+
code: `
|
|
70
|
+
<ContentBanner>
|
|
71
|
+
<Spot appearance="icon" icon={Settings} size={48} />
|
|
72
|
+
<ContentBannerContent>
|
|
73
|
+
<ContentBannerTitle>Content Banner</ContentBannerTitle>
|
|
74
|
+
<ContentBannerDescription>
|
|
75
|
+
This is a description for the content banner.
|
|
76
|
+
</ContentBannerDescription>
|
|
77
|
+
</ContentBannerContent>
|
|
78
|
+
</ContentBanner>
|
|
79
|
+
`,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const WithClose: Story = {
|
|
86
|
+
render: () => {
|
|
87
|
+
const [visible, setVisible] = React.useState(true);
|
|
88
|
+
|
|
89
|
+
if (!visible) {
|
|
90
|
+
return (
|
|
91
|
+
<Button
|
|
92
|
+
appearance='transparent'
|
|
93
|
+
size='sm'
|
|
94
|
+
onPress={() => setVisible(true)}
|
|
95
|
+
>
|
|
96
|
+
Show banner again
|
|
97
|
+
</Button>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Box lx={{ maxWidth: 's400' }}>
|
|
103
|
+
<ContentBanner
|
|
104
|
+
onClose={() => setVisible(false)}
|
|
105
|
+
closeAccessibilityLabel='Close content banner'
|
|
106
|
+
>
|
|
107
|
+
<Spot appearance='icon' icon={Settings} size={48} />
|
|
108
|
+
<ContentBannerContent>
|
|
109
|
+
<ContentBannerTitle>Dismissible Banner</ContentBannerTitle>
|
|
110
|
+
<ContentBannerDescription>
|
|
111
|
+
Press the close button to dismiss this banner.
|
|
112
|
+
</ContentBannerDescription>
|
|
113
|
+
</ContentBannerContent>
|
|
114
|
+
</ContentBanner>
|
|
115
|
+
</Box>
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const ContentVariationsShowcase: Story = {
|
|
121
|
+
render: () => (
|
|
122
|
+
<Box
|
|
123
|
+
lx={{
|
|
124
|
+
flexDirection: 'column',
|
|
125
|
+
maxWidth: 's400',
|
|
126
|
+
gap: 's16',
|
|
127
|
+
padding: 's8',
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
<ContentBanner>
|
|
131
|
+
<Spot appearance='icon' icon={Settings} size={48} />
|
|
132
|
+
<ContentBannerContent>
|
|
133
|
+
<ContentBannerTitle>Title Only</ContentBannerTitle>
|
|
134
|
+
</ContentBannerContent>
|
|
135
|
+
</ContentBanner>
|
|
136
|
+
|
|
137
|
+
<ContentBanner>
|
|
138
|
+
<Spot appearance='icon' icon={Wallet} size={48} />
|
|
139
|
+
<ContentBannerContent>
|
|
140
|
+
<ContentBannerTitle>Title with Description</ContentBannerTitle>
|
|
141
|
+
<ContentBannerDescription>
|
|
142
|
+
This is a description for the content banner.
|
|
143
|
+
</ContentBannerDescription>
|
|
144
|
+
</ContentBannerContent>
|
|
145
|
+
</ContentBanner>
|
|
146
|
+
|
|
147
|
+
<ContentBanner onClose={() => console.log('close')}>
|
|
148
|
+
<Spot appearance='icon' icon={Wallet} size={48} />
|
|
149
|
+
<ContentBannerContent>
|
|
150
|
+
<ContentBannerTitle>With Close Button</ContentBannerTitle>
|
|
151
|
+
<ContentBannerDescription>
|
|
152
|
+
A content banner with a leading icon and close button.
|
|
153
|
+
</ContentBannerDescription>
|
|
154
|
+
</ContentBannerContent>
|
|
155
|
+
</ContentBanner>
|
|
156
|
+
|
|
157
|
+
<ContentBanner onClose={() => console.log('close')}>
|
|
158
|
+
<Spot appearance='icon' icon={Settings} size={48} />
|
|
159
|
+
<ContentBannerContent>
|
|
160
|
+
<ContentBannerTitle>
|
|
161
|
+
Longer Title That Demonstrates Clamping Behavior With a Long
|
|
162
|
+
Description
|
|
163
|
+
</ContentBannerTitle>
|
|
164
|
+
<ContentBannerDescription>
|
|
165
|
+
This is a longer description that demonstrates how the content
|
|
166
|
+
banner handles overflow. It should be clamped at two lines.
|
|
167
|
+
</ContentBannerDescription>
|
|
168
|
+
</ContentBannerContent>
|
|
169
|
+
</ContentBanner>
|
|
170
|
+
</Box>
|
|
171
|
+
),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const WithStepper: Story = {
|
|
175
|
+
render: () => (
|
|
176
|
+
<Box lx={{ maxWidth: 's400' }}>
|
|
177
|
+
<ContentBanner onClose={() => console.log('close')}>
|
|
178
|
+
<Stepper currentStep={2} totalSteps={4} />
|
|
179
|
+
<ContentBannerContent>
|
|
180
|
+
<ContentBannerTitle>Setup your wallet</ContentBannerTitle>
|
|
181
|
+
<ContentBannerDescription>
|
|
182
|
+
Complete all steps to secure your wallet.
|
|
183
|
+
</ContentBannerDescription>
|
|
184
|
+
</ContentBannerContent>
|
|
185
|
+
</ContentBanner>
|
|
186
|
+
</Box>
|
|
187
|
+
),
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export const ResponsiveLayout: Story = {
|
|
191
|
+
render: () => (
|
|
192
|
+
<Box
|
|
193
|
+
lx={{
|
|
194
|
+
width: 's400',
|
|
195
|
+
flexDirection: 'column',
|
|
196
|
+
gap: 's16',
|
|
197
|
+
backgroundColor: 'mutedPressed',
|
|
198
|
+
padding: 's16',
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
<Text typography='body4SemiBold' lx={{ color: 'muted' }}>
|
|
202
|
+
Container with a fixed width
|
|
203
|
+
</Text>
|
|
204
|
+
<ContentBanner>
|
|
205
|
+
<Spot appearance='icon' icon={Settings} size={48} />
|
|
206
|
+
<ContentBannerContent>
|
|
207
|
+
<ContentBannerTitle>Short Title</ContentBannerTitle>
|
|
208
|
+
<ContentBannerDescription>Short description</ContentBannerDescription>
|
|
209
|
+
</ContentBannerContent>
|
|
210
|
+
</ContentBanner>
|
|
211
|
+
<ContentBanner onClose={() => console.log('close')}>
|
|
212
|
+
<Spot appearance='icon' icon={Wallet} size={48} />
|
|
213
|
+
<ContentBannerContent>
|
|
214
|
+
<ContentBannerTitle>
|
|
215
|
+
Longer Title That Might Overflow When Container is Smaller
|
|
216
|
+
</ContentBannerTitle>
|
|
217
|
+
<ContentBannerDescription>
|
|
218
|
+
This is a longer description that demonstrates how the content
|
|
219
|
+
banner handles longer content within its constraints. It should be
|
|
220
|
+
clamped at two lines with an ellipsis.
|
|
221
|
+
</ContentBannerDescription>
|
|
222
|
+
</ContentBannerContent>
|
|
223
|
+
</ContentBanner>
|
|
224
|
+
</Box>
|
|
225
|
+
),
|
|
226
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { describe, it, expect, jest } from '@jest/globals';
|
|
2
|
+
import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
|
|
3
|
+
import { fireEvent, render } from '@testing-library/react-native';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { Settings } from '../../Symbols';
|
|
6
|
+
import { Spot } from '../Spot';
|
|
7
|
+
import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
|
|
8
|
+
import {
|
|
9
|
+
ContentBanner,
|
|
10
|
+
ContentBannerContent,
|
|
11
|
+
ContentBannerTitle,
|
|
12
|
+
ContentBannerDescription,
|
|
13
|
+
} from './ContentBanner';
|
|
14
|
+
|
|
15
|
+
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
16
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
|
|
17
|
+
{children}
|
|
18
|
+
</ThemeProvider>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
describe('ContentBanner', () => {
|
|
22
|
+
it('should render children', () => {
|
|
23
|
+
const { getByText } = render(
|
|
24
|
+
<TestWrapper>
|
|
25
|
+
<ContentBanner>
|
|
26
|
+
<ContentBannerContent>
|
|
27
|
+
<ContentBannerTitle>Title</ContentBannerTitle>
|
|
28
|
+
</ContentBannerContent>
|
|
29
|
+
</ContentBanner>
|
|
30
|
+
</TestWrapper>,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
getByText('Title');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should render title and description', () => {
|
|
37
|
+
const { getByText } = render(
|
|
38
|
+
<TestWrapper>
|
|
39
|
+
<ContentBanner>
|
|
40
|
+
<ContentBannerContent>
|
|
41
|
+
<ContentBannerTitle>Banner Title</ContentBannerTitle>
|
|
42
|
+
<ContentBannerDescription>
|
|
43
|
+
Banner description
|
|
44
|
+
</ContentBannerDescription>
|
|
45
|
+
</ContentBannerContent>
|
|
46
|
+
</ContentBanner>
|
|
47
|
+
</TestWrapper>,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
getByText('Banner Title');
|
|
51
|
+
getByText('Banner description');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should render close button when onClose is provided', () => {
|
|
55
|
+
const handleClose = jest.fn();
|
|
56
|
+
const { getByTestId } = render(
|
|
57
|
+
<TestWrapper>
|
|
58
|
+
<ContentBanner onClose={handleClose}>
|
|
59
|
+
<ContentBannerContent>
|
|
60
|
+
<ContentBannerTitle>Title</ContentBannerTitle>
|
|
61
|
+
</ContentBannerContent>
|
|
62
|
+
</ContentBanner>
|
|
63
|
+
</TestWrapper>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const closeButton = getByTestId('content-banner-close-button');
|
|
67
|
+
expect(closeButton).toBeTruthy();
|
|
68
|
+
fireEvent.press(closeButton);
|
|
69
|
+
expect(handleClose).toHaveBeenCalledTimes(1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should not render close button when onClose is not provided', () => {
|
|
73
|
+
const { queryByTestId } = render(
|
|
74
|
+
<TestWrapper>
|
|
75
|
+
<ContentBanner>
|
|
76
|
+
<ContentBannerContent>
|
|
77
|
+
<ContentBannerTitle>Title</ContentBannerTitle>
|
|
78
|
+
</ContentBannerContent>
|
|
79
|
+
</ContentBanner>
|
|
80
|
+
</TestWrapper>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(queryByTestId('content-banner-close-button')).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should apply custom closeAccessibilityLabel', () => {
|
|
87
|
+
const handleClose = jest.fn();
|
|
88
|
+
const { getByLabelText } = render(
|
|
89
|
+
<TestWrapper>
|
|
90
|
+
<ContentBanner onClose={handleClose} closeAccessibilityLabel='Dismiss'>
|
|
91
|
+
<ContentBannerContent>
|
|
92
|
+
<ContentBannerTitle>Title</ContentBannerTitle>
|
|
93
|
+
</ContentBannerContent>
|
|
94
|
+
</ContentBanner>
|
|
95
|
+
</TestWrapper>,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(getByLabelText('Dismiss')).toBeTruthy();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should render with a leading element', () => {
|
|
102
|
+
const { getByText, getByTestId } = render(
|
|
103
|
+
<TestWrapper>
|
|
104
|
+
<ContentBanner testID='content-banner'>
|
|
105
|
+
<Spot appearance='icon' icon={Settings} />
|
|
106
|
+
<ContentBannerContent>
|
|
107
|
+
<ContentBannerTitle>With Spot</ContentBannerTitle>
|
|
108
|
+
</ContentBannerContent>
|
|
109
|
+
</ContentBanner>
|
|
110
|
+
</TestWrapper>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(getByTestId('content-banner')).toBeTruthy();
|
|
114
|
+
getByText('With Spot');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should apply surface background color', () => {
|
|
118
|
+
const { getByTestId } = render(
|
|
119
|
+
<TestWrapper>
|
|
120
|
+
<ContentBanner testID='content-banner'>
|
|
121
|
+
<ContentBannerContent>
|
|
122
|
+
<ContentBannerTitle>Title</ContentBannerTitle>
|
|
123
|
+
</ContentBannerContent>
|
|
124
|
+
</ContentBanner>
|
|
125
|
+
</TestWrapper>,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const banner = getByTestId('content-banner');
|
|
129
|
+
expect(banner.props.style.backgroundColor).toBe(
|
|
130
|
+
ledgerLiveThemes.dark.colors.bg.surface,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { isTextChildren } from '@ledgerhq/lumen-utils-shared';
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import { useCommonTranslation } from '../../../i18n';
|
|
4
|
+
import { useStyleSheet } from '../../../styles';
|
|
5
|
+
import { Close } from '../../Symbols';
|
|
6
|
+
import { InteractiveIcon } from '../InteractiveIcon';
|
|
7
|
+
import { Box, Text } from '../Utility';
|
|
8
|
+
import {
|
|
9
|
+
ContentBannerContentProps,
|
|
10
|
+
ContentBannerDescriptionProps,
|
|
11
|
+
ContentBannerProps,
|
|
12
|
+
ContentBannerTitleProps,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Container for the text content (title and description) within the content banner.
|
|
17
|
+
*/
|
|
18
|
+
export const ContentBannerContent = ({
|
|
19
|
+
children,
|
|
20
|
+
lx = {},
|
|
21
|
+
style,
|
|
22
|
+
ref,
|
|
23
|
+
...viewProps
|
|
24
|
+
}: ContentBannerContentProps) => {
|
|
25
|
+
const styles = useStyleSheet(
|
|
26
|
+
(t) => ({
|
|
27
|
+
content: {
|
|
28
|
+
flex: 1,
|
|
29
|
+
minWidth: 0,
|
|
30
|
+
flexDirection: 'column',
|
|
31
|
+
gap: t.spacings.s4,
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
[],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Box
|
|
39
|
+
ref={ref}
|
|
40
|
+
lx={lx}
|
|
41
|
+
style={StyleSheet.flatten([styles.content, style])}
|
|
42
|
+
{...viewProps}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</Box>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
ContentBannerContent.displayName = 'ContentBannerContent';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The main title of the content banner.
|
|
53
|
+
*/
|
|
54
|
+
export const ContentBannerTitle = ({
|
|
55
|
+
children,
|
|
56
|
+
lx = {},
|
|
57
|
+
style,
|
|
58
|
+
ref,
|
|
59
|
+
...textProps
|
|
60
|
+
}: ContentBannerTitleProps) => {
|
|
61
|
+
const styles = useStyleSheet(
|
|
62
|
+
(t) => ({
|
|
63
|
+
title: StyleSheet.flatten([
|
|
64
|
+
t.typographies.body2SemiBold,
|
|
65
|
+
{
|
|
66
|
+
color: t.colors.text.base,
|
|
67
|
+
},
|
|
68
|
+
]),
|
|
69
|
+
}),
|
|
70
|
+
[],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (isTextChildren(children)) {
|
|
74
|
+
return (
|
|
75
|
+
<Text
|
|
76
|
+
ref={ref}
|
|
77
|
+
lx={lx}
|
|
78
|
+
style={StyleSheet.flatten([styles.title, style])}
|
|
79
|
+
numberOfLines={1}
|
|
80
|
+
ellipsizeMode='tail'
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
</Text>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Box lx={lx} style={StyleSheet.flatten([style])} {...textProps}>
|
|
89
|
+
{children}
|
|
90
|
+
</Box>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
ContentBannerTitle.displayName = 'ContentBannerTitle';
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Optional description text below the title.
|
|
98
|
+
*/
|
|
99
|
+
export const ContentBannerDescription = ({
|
|
100
|
+
children,
|
|
101
|
+
lx = {},
|
|
102
|
+
style,
|
|
103
|
+
ref,
|
|
104
|
+
...textProps
|
|
105
|
+
}: ContentBannerDescriptionProps) => {
|
|
106
|
+
const styles = useStyleSheet(
|
|
107
|
+
(t) => ({
|
|
108
|
+
description: StyleSheet.flatten([
|
|
109
|
+
t.typographies.body3,
|
|
110
|
+
{
|
|
111
|
+
color: t.colors.text.muted,
|
|
112
|
+
},
|
|
113
|
+
]),
|
|
114
|
+
}),
|
|
115
|
+
[],
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (isTextChildren(children)) {
|
|
119
|
+
return (
|
|
120
|
+
<Text
|
|
121
|
+
ref={ref}
|
|
122
|
+
lx={lx}
|
|
123
|
+
style={StyleSheet.flatten([styles.description, style])}
|
|
124
|
+
numberOfLines={2}
|
|
125
|
+
ellipsizeMode='tail'
|
|
126
|
+
>
|
|
127
|
+
{children}
|
|
128
|
+
</Text>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<Box lx={lx} style={StyleSheet.flatten([style])} {...textProps}>
|
|
134
|
+
{children}
|
|
135
|
+
</Box>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
ContentBannerDescription.displayName = 'ContentBannerDescription';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A content banner component for displaying a composable banner with an optional
|
|
143
|
+
* leading visual, title, description, and close button.
|
|
144
|
+
*
|
|
145
|
+
* @see {@link https://ldls.vercel.app/?path=/docs/communication-contentbanner-overview--docs Storybook}
|
|
146
|
+
*
|
|
147
|
+
* @warning The `lx` prop should only be used for layout adjustments like margins or positioning.
|
|
148
|
+
* Do not use it to modify the banner's core appearance (colors, padding, etc).
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* import { ContentBanner, ContentBannerContent, ContentBannerTitle, ContentBannerDescription, Spot } from '@ledgerhq/lumen-ui-rnative';
|
|
152
|
+
* import { Wallet } from '@ledgerhq/lumen-ui-rnative/symbols';
|
|
153
|
+
*
|
|
154
|
+
* <ContentBanner onClose={() => {}}>
|
|
155
|
+
* <Spot appearance="icon" icon={Wallet} size={48} />
|
|
156
|
+
* <ContentBannerContent>
|
|
157
|
+
* <ContentBannerTitle>Title</ContentBannerTitle>
|
|
158
|
+
* <ContentBannerDescription>Description text</ContentBannerDescription>
|
|
159
|
+
* </ContentBannerContent>
|
|
160
|
+
* </ContentBanner>
|
|
161
|
+
*/
|
|
162
|
+
export const ContentBanner = ({
|
|
163
|
+
children,
|
|
164
|
+
lx = {},
|
|
165
|
+
style,
|
|
166
|
+
onClose,
|
|
167
|
+
closeAccessibilityLabel,
|
|
168
|
+
ref,
|
|
169
|
+
...viewProps
|
|
170
|
+
}: ContentBannerProps) => {
|
|
171
|
+
const { t } = useCommonTranslation();
|
|
172
|
+
|
|
173
|
+
const styles = useStyleSheet(
|
|
174
|
+
(t) => ({
|
|
175
|
+
root: {
|
|
176
|
+
flexDirection: 'row',
|
|
177
|
+
width: t.sizes.full,
|
|
178
|
+
alignItems: 'center',
|
|
179
|
+
gap: t.spacings.s12,
|
|
180
|
+
borderRadius: t.borderRadius.md,
|
|
181
|
+
padding: t.spacings.s12,
|
|
182
|
+
backgroundColor: t.colors.bg.surface,
|
|
183
|
+
},
|
|
184
|
+
rootWithClose: {
|
|
185
|
+
paddingRight: t.spacings.s48,
|
|
186
|
+
},
|
|
187
|
+
closeButton: {
|
|
188
|
+
position: 'absolute',
|
|
189
|
+
top: t.spacings.s8,
|
|
190
|
+
right: t.spacings.s8,
|
|
191
|
+
},
|
|
192
|
+
}),
|
|
193
|
+
[],
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<Box
|
|
198
|
+
ref={ref}
|
|
199
|
+
lx={lx}
|
|
200
|
+
style={StyleSheet.flatten([
|
|
201
|
+
styles.root,
|
|
202
|
+
onClose && styles.rootWithClose,
|
|
203
|
+
style,
|
|
204
|
+
])}
|
|
205
|
+
{...viewProps}
|
|
206
|
+
>
|
|
207
|
+
{children}
|
|
208
|
+
{onClose && (
|
|
209
|
+
<View style={styles.closeButton}>
|
|
210
|
+
<InteractiveIcon
|
|
211
|
+
iconType='stroked'
|
|
212
|
+
testID='content-banner-close-button'
|
|
213
|
+
onPress={() => onClose()}
|
|
214
|
+
accessibilityLabel={
|
|
215
|
+
closeAccessibilityLabel || t('components.banner.closeAriaLabel')
|
|
216
|
+
}
|
|
217
|
+
>
|
|
218
|
+
<Close size={16} />
|
|
219
|
+
</InteractiveIcon>
|
|
220
|
+
</View>
|
|
221
|
+
)}
|
|
222
|
+
</Box>
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
ContentBanner.displayName = 'ContentBanner';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyledTextProps, StyledViewProps } from '../../../styles';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props for the ContentBanner root component
|
|
6
|
+
*/
|
|
7
|
+
export type ContentBannerProps = {
|
|
8
|
+
/**
|
|
9
|
+
* The content of the content banner (ContentBannerContent, ContentBannerTitle, ContentBannerDescription, or any leading element)
|
|
10
|
+
*/
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Optional close action.
|
|
14
|
+
*/
|
|
15
|
+
onClose?: () => void;
|
|
16
|
+
/**
|
|
17
|
+
* Optional accessibility label for the close button.
|
|
18
|
+
*/
|
|
19
|
+
closeAccessibilityLabel?: string;
|
|
20
|
+
} & Omit<StyledViewProps, 'children'>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Props for the ContentBannerContent component
|
|
24
|
+
*/
|
|
25
|
+
export type ContentBannerContentProps = {
|
|
26
|
+
/**
|
|
27
|
+
* The content (ContentBannerTitle, ContentBannerDescription)
|
|
28
|
+
*/
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
} & Omit<StyledViewProps, 'children'>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Props for the ContentBannerTitle component
|
|
34
|
+
*/
|
|
35
|
+
export type ContentBannerTitleProps = {
|
|
36
|
+
/**
|
|
37
|
+
* The title text or custom content
|
|
38
|
+
*/
|
|
39
|
+
children: React.ReactNode;
|
|
40
|
+
} & Omit<StyledTextProps, 'children'>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Props for the ContentBannerDescription component
|
|
44
|
+
*/
|
|
45
|
+
export type ContentBannerDescriptionProps = {
|
|
46
|
+
/**
|
|
47
|
+
* The description text or custom content
|
|
48
|
+
*/
|
|
49
|
+
children: React.ReactNode;
|
|
50
|
+
} & Omit<StyledTextProps, 'children'>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Svg, { Path } from 'react-native-svg';
|
|
2
|
+
import createIcon from '../../Components/Icon/createIcon';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Csv 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 { Csv } from '@ledgerhq/lumen-ui-rnative/symbols';
|
|
19
|
+
*
|
|
20
|
+
* <Csv />
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // With custom size and style
|
|
24
|
+
* <Csv 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={Csv} size="md">
|
|
31
|
+
* Click me
|
|
32
|
+
* </Button>
|
|
33
|
+
*/
|
|
34
|
+
export const Csv = createIcon(
|
|
35
|
+
'Csv',
|
|
36
|
+
<Svg width={24} height={24} fill='currentColor' viewBox='0 0 16 16'>
|
|
37
|
+
<Path
|
|
38
|
+
stroke='currentColor'
|
|
39
|
+
strokeLinecap='round'
|
|
40
|
+
strokeLinejoin='round'
|
|
41
|
+
strokeWidth={1.3}
|
|
42
|
+
d='M10.333 14h1a2 2 0 0 0 2-2V5.673a2 2 0 0 0-.585-1.414l-1.673-1.673A2 2 0 0 0 9.66 2H4.667a2 2 0 0 0-2 2v4.667m10.666-3H11a1.333 1.333 0 0 1-1.333-1.334V2M8 6v3.333m0 0L9.333 8M8 9.333 6.667 8'
|
|
43
|
+
/>
|
|
44
|
+
<Path
|
|
45
|
+
fill='currentColor'
|
|
46
|
+
d='M1.86 13.656a.467.467 0 0 1 .641.156.13.13 0 0 0 .043.044q.015.01.04.01h.426a.08.08 0 0 0 .079-.078.08.08 0 0 0-.062-.078l-.728-.183a1.012 1.012 0 0 1 .247-1.994h.426c.39 0 .704.217.878.502a.467.467 0 0 1-.797.486.13.13 0 0 0-.042-.044.1.1 0 0 0-.039-.01h-.426a.079.079 0 0 0-.02.155l.73.183A1.012 1.012 0 0 1 3.01 14.8h-.426c-.391 0-.705-.217-.879-.503a.467.467 0 0 1 .156-.64m2.34.01v-1c0-.625.507-1.133 1.133-1.133H6a.467.467 0 0 1 0 .934h-.667a.2.2 0 0 0-.2.2v1c0 .11.09.2.2.2H6a.467.467 0 0 1 0 .933h-.667A1.134 1.134 0 0 1 4.2 13.667m5.01-2.115c.248.07.392.327.323.575l-.656 2.334a.47.47 0 0 1-.45.34H7.99a.47.47 0 0 1-.45-.34l-.656-2.334a.467.467 0 0 1 .899-.252l.425 1.514.426-1.514a.467.467 0 0 1 .576-.323'
|
|
47
|
+
/>
|
|
48
|
+
</Svg>,
|
|
49
|
+
);
|