@boostdev/design-system-react-native 0.2.6 → 0.4.0
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/CHANGELOG.md +94 -0
- package/dist/index.cjs +166 -159
- package/dist/index.d.cts +13 -3
- package/dist/index.d.ts +13 -3
- package/dist/index.js +166 -159
- package/package.json +1 -1
- package/src/components/interaction/Dialog/Dialog.mdx +3 -2
- package/src/components/interaction/Dialog/Dialog.spec.tsx +42 -11
- package/src/components/interaction/Dialog/Dialog.tsx +19 -12
- package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +3 -1
- package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +73 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +79 -73
- package/src/components/interaction/Popover/Popover.mdx +3 -1
- package/src/components/interaction/Popover/Popover.spec.tsx +43 -3
- package/src/components/interaction/Popover/Popover.tsx +23 -13
- package/src/components/interaction/SideSheet/SideSheet.mdx +4 -3
- package/src/components/interaction/SideSheet/SideSheet.spec.tsx +8 -9
- package/src/components/interaction/SideSheet/SideSheet.tsx +9 -3
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +17 -3
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +15 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +9 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +2 -1
- package/src/components/ui/Alert/Alert.spec.tsx +32 -1
- package/src/components/ui/Alert/Alert.stories.tsx +45 -0
- package/src/components/ui/Alert/Alert.tsx +11 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +32 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +30 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +16 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { Text } from 'react-native';
|
|
2
|
+
import { Text, View, Pressable } from 'react-native';
|
|
3
3
|
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
4
|
import { NotificationBanner } from './NotificationBanner';
|
|
5
5
|
|
|
@@ -13,6 +13,37 @@ describe('NotificationBanner (native)', () => {
|
|
|
13
13
|
expect(screen.getByText('Update available')).toBeInTheDocument();
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
it('renders non-text children such as a view with an action', () => {
|
|
17
|
+
const onPress = vi.fn();
|
|
18
|
+
renderWithTheme(
|
|
19
|
+
<NotificationBanner variant="warning">
|
|
20
|
+
<View>
|
|
21
|
+
<Text>Session expiring</Text>
|
|
22
|
+
<Pressable accessibilityRole="button" onPress={onPress}>
|
|
23
|
+
<Text>Extend</Text>
|
|
24
|
+
</Pressable>
|
|
25
|
+
</View>
|
|
26
|
+
</NotificationBanner>,
|
|
27
|
+
);
|
|
28
|
+
expect(screen.getByText('Session expiring')).toBeInTheDocument();
|
|
29
|
+
fireEvent.click(screen.getByRole('button', { name: 'Extend' }));
|
|
30
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('passes the resolved text color to a function child', () => {
|
|
34
|
+
let receivedColor = '';
|
|
35
|
+
renderWithTheme(
|
|
36
|
+
<NotificationBanner variant="info">
|
|
37
|
+
{({ color }) => {
|
|
38
|
+
receivedColor = color;
|
|
39
|
+
return <Text style={{ color }}>Themed content</Text>;
|
|
40
|
+
}}
|
|
41
|
+
</NotificationBanner>,
|
|
42
|
+
);
|
|
43
|
+
expect(screen.getByText('Themed content')).toBeInTheDocument();
|
|
44
|
+
expect(receivedColor).not.toBe('');
|
|
45
|
+
});
|
|
46
|
+
|
|
16
47
|
it('has alert role for error variant', () => {
|
|
17
48
|
renderWithTheme(
|
|
18
49
|
<NotificationBanner variant="error">Something went wrong</NotificationBanner>,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { View, Text, Pressable } from 'react-native';
|
|
2
3
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
4
|
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
5
|
import { NotificationBanner } from './NotificationBanner';
|
|
@@ -35,3 +36,32 @@ export const AllVariants: Story = {
|
|
|
35
36
|
</div>
|
|
36
37
|
),
|
|
37
38
|
};
|
|
39
|
+
|
|
40
|
+
// A string child keeps the default body styling. A non-text child renders as-is.
|
|
41
|
+
export const WithRichContent: Story = {
|
|
42
|
+
args: { children: '', variant: 'warning' },
|
|
43
|
+
render: (args) => (
|
|
44
|
+
<NotificationBanner {...args}>
|
|
45
|
+
<View style={{ gap: 2 }}>
|
|
46
|
+
<Text style={{ fontSize: 13, fontWeight: '600' }}>Session expiring</Text>
|
|
47
|
+
<Text style={{ fontSize: 13 }}>You will be signed out in 2 minutes.</Text>
|
|
48
|
+
</View>
|
|
49
|
+
</NotificationBanner>
|
|
50
|
+
),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Pass a function child to receive the variant's resolved text color, so custom
|
|
54
|
+
// content matches the banner's theme automatically.
|
|
55
|
+
export const WithThemedContent: Story = {
|
|
56
|
+
args: { children: '', variant: 'error' },
|
|
57
|
+
render: (args) => (
|
|
58
|
+
<NotificationBanner {...args}>
|
|
59
|
+
{({ color }) => (
|
|
60
|
+
<View style={{ gap: 2 }}>
|
|
61
|
+
<Text style={{ color, fontSize: 13, fontWeight: '600' }}>Sync failed</Text>
|
|
62
|
+
<Text style={{ color, fontSize: 13 }}>Check your connection and try again.</Text>
|
|
63
|
+
</View>
|
|
64
|
+
)}
|
|
65
|
+
</NotificationBanner>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
@@ -5,9 +5,13 @@ import { useTheme } from '../../../native/ThemeContext';
|
|
|
5
5
|
|
|
6
6
|
type BannerVariant = 'info' | 'success' | 'warning' | 'error';
|
|
7
7
|
|
|
8
|
+
/** Function form of children: receives the variant's resolved text color so
|
|
9
|
+
* custom content can match the banner's theme. */
|
|
10
|
+
type BannerContentRenderProp = (props: { color: string }) => ReactNode;
|
|
11
|
+
|
|
8
12
|
interface NotificationBannerProps {
|
|
9
13
|
variant?: BannerVariant;
|
|
10
|
-
children: ReactNode;
|
|
14
|
+
children: ReactNode | BannerContentRenderProp;
|
|
11
15
|
action?: ReactNode;
|
|
12
16
|
onDismiss?: () => void;
|
|
13
17
|
style?: StyleProp<ViewStyle>;
|
|
@@ -21,8 +25,10 @@ const styles = StyleSheet.create({
|
|
|
21
25
|
paddingHorizontal: spacing.m,
|
|
22
26
|
gap: spacing.xs,
|
|
23
27
|
},
|
|
24
|
-
|
|
28
|
+
body: {
|
|
25
29
|
flex: 1,
|
|
30
|
+
},
|
|
31
|
+
content: {
|
|
26
32
|
fontSize: font.size.bodyS,
|
|
27
33
|
fontFamily: font.family.body,
|
|
28
34
|
lineHeight: lineHeight.bodyS,
|
|
@@ -51,6 +57,7 @@ export function NotificationBanner({
|
|
|
51
57
|
};
|
|
52
58
|
|
|
53
59
|
const { bg, text, border: borderColor } = variantColors[variant];
|
|
60
|
+
const resolvedChildren = typeof children === 'function' ? children({ color: text }) : children;
|
|
54
61
|
|
|
55
62
|
return (
|
|
56
63
|
<View
|
|
@@ -58,7 +65,13 @@ export function NotificationBanner({
|
|
|
58
65
|
accessibilityLiveRegion={isUrgent ? 'assertive' : 'polite'}
|
|
59
66
|
style={[styles.banner, { backgroundColor: bg, borderBottomWidth: 3, borderBottomColor: borderColor }, style]}
|
|
60
67
|
>
|
|
61
|
-
<
|
|
68
|
+
<View style={styles.body}>
|
|
69
|
+
{typeof resolvedChildren === 'string' || typeof resolvedChildren === 'number' ? (
|
|
70
|
+
<Text style={[styles.content, { color: text }]}>{resolvedChildren}</Text>
|
|
71
|
+
) : (
|
|
72
|
+
resolvedChildren
|
|
73
|
+
)}
|
|
74
|
+
</View>
|
|
62
75
|
{action}
|
|
63
76
|
{onDismiss && (
|
|
64
77
|
<Pressable
|