@boostdev/design-system-components 2.4.2 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/AGENTS.md +4 -20
  2. package/README.md +3 -23
  3. package/dist/client.cjs +115 -61
  4. package/dist/client.css +537 -537
  5. package/dist/client.d.cts +11 -1
  6. package/dist/client.d.ts +11 -1
  7. package/dist/client.js +115 -61
  8. package/dist/index.cjs +115 -61
  9. package/dist/index.css +537 -537
  10. package/dist/index.d.cts +11 -1
  11. package/dist/index.d.ts +11 -1
  12. package/dist/index.js +115 -61
  13. package/dist/web-components/index.js +23 -3
  14. package/package.json +2 -10
  15. package/src/components/layout/Grid/Grid.mdx +20 -2
  16. package/src/components/layout/Grid/Grid.spec.tsx +194 -4
  17. package/src/components/layout/Grid/Grid.tsx +15 -1
  18. package/src/components/layout/Grid/masonry.ts +127 -9
  19. package/dist/native/index.cjs +0 -4337
  20. package/dist/native/index.d.cts +0 -574
  21. package/dist/native/index.d.ts +0 -574
  22. package/dist/native/index.js +0 -4421
  23. package/src/components/interaction/Button/Button.native.mdx +0 -91
  24. package/src/components/interaction/Button/Button.native.spec.tsx +0 -55
  25. package/src/components/interaction/Button/Button.native.stories.tsx +0 -141
  26. package/src/components/interaction/Button/Button.native.tsx +0 -105
  27. package/src/components/interaction/Dialog/Dialog.native.mdx +0 -61
  28. package/src/components/interaction/Dialog/Dialog.native.spec.tsx +0 -73
  29. package/src/components/interaction/Dialog/Dialog.native.stories.tsx +0 -53
  30. package/src/components/interaction/Dialog/Dialog.native.tsx +0 -128
  31. package/src/components/interaction/Drawer/Drawer.native.mdx +0 -58
  32. package/src/components/interaction/Drawer/Drawer.native.spec.tsx +0 -81
  33. package/src/components/interaction/Drawer/Drawer.native.stories.tsx +0 -33
  34. package/src/components/interaction/Drawer/Drawer.native.tsx +0 -175
  35. package/src/components/interaction/DropdownMenu/DropdownMenu.native.mdx +0 -74
  36. package/src/components/interaction/DropdownMenu/DropdownMenu.native.spec.tsx +0 -78
  37. package/src/components/interaction/DropdownMenu/DropdownMenu.native.stories.tsx +0 -51
  38. package/src/components/interaction/DropdownMenu/DropdownMenu.native.tsx +0 -254
  39. package/src/components/interaction/Popover/Popover.native.mdx +0 -61
  40. package/src/components/interaction/Popover/Popover.native.spec.tsx +0 -73
  41. package/src/components/interaction/Popover/Popover.native.stories.tsx +0 -44
  42. package/src/components/interaction/Popover/Popover.native.tsx +0 -87
  43. package/src/components/interaction/Rating/Rating.native.mdx +0 -55
  44. package/src/components/interaction/Rating/Rating.native.spec.tsx +0 -38
  45. package/src/components/interaction/Rating/Rating.native.stories.tsx +0 -37
  46. package/src/components/interaction/Rating/Rating.native.tsx +0 -50
  47. package/src/components/interaction/Toast/Toast.native.mdx +0 -81
  48. package/src/components/interaction/Toast/Toast.native.spec.tsx +0 -80
  49. package/src/components/interaction/Toast/Toast.native.stories.tsx +0 -47
  50. package/src/components/interaction/Toast/Toast.native.tsx +0 -202
  51. package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +0 -74
  52. package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +0 -42
  53. package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +0 -34
  54. package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +0 -80
  55. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.mdx +0 -78
  56. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.spec.tsx +0 -57
  57. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.stories.tsx +0 -96
  58. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.tsx +0 -61
  59. package/src/components/interaction/form/Combobox/Combobox.native.mdx +0 -86
  60. package/src/components/interaction/form/Combobox/Combobox.native.spec.tsx +0 -60
  61. package/src/components/interaction/form/Combobox/Combobox.native.stories.tsx +0 -99
  62. package/src/components/interaction/form/Combobox/Combobox.native.tsx +0 -211
  63. package/src/components/interaction/form/FileInput/FileInput.native.mdx +0 -86
  64. package/src/components/interaction/form/FileInput/FileInput.native.spec.tsx +0 -73
  65. package/src/components/interaction/form/FileInput/FileInput.native.stories.tsx +0 -69
  66. package/src/components/interaction/form/FileInput/FileInput.native.tsx +0 -129
  67. package/src/components/interaction/form/FormInput/FormInput.native.mdx +0 -81
  68. package/src/components/interaction/form/FormInput/FormInput.native.spec.tsx +0 -51
  69. package/src/components/interaction/form/FormInput/FormInput.native.stories.tsx +0 -54
  70. package/src/components/interaction/form/FormInput/FormInput.native.tsx +0 -122
  71. package/src/components/interaction/form/NumberInput/NumberInput.native.mdx +0 -87
  72. package/src/components/interaction/form/NumberInput/NumberInput.native.spec.tsx +0 -63
  73. package/src/components/interaction/form/NumberInput/NumberInput.native.stories.tsx +0 -63
  74. package/src/components/interaction/form/NumberInput/NumberInput.native.tsx +0 -198
  75. package/src/components/interaction/form/Radio/Radio.native.spec.tsx +0 -79
  76. package/src/components/interaction/form/Radio/Radio.native.stories.tsx +0 -44
  77. package/src/components/interaction/form/Radio/Radio.native.tsx +0 -79
  78. package/src/components/interaction/form/RadioGroup/RadioGroup.native.mdx +0 -79
  79. package/src/components/interaction/form/RadioGroup/RadioGroup.native.spec.tsx +0 -66
  80. package/src/components/interaction/form/RadioGroup/RadioGroup.native.stories.tsx +0 -96
  81. package/src/components/interaction/form/RadioGroup/RadioGroup.native.tsx +0 -61
  82. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.mdx +0 -78
  83. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.spec.tsx +0 -82
  84. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.stories.tsx +0 -99
  85. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.tsx +0 -121
  86. package/src/components/interaction/form/Select/Select.native.mdx +0 -85
  87. package/src/components/interaction/form/Select/Select.native.spec.tsx +0 -64
  88. package/src/components/interaction/form/Select/Select.native.stories.tsx +0 -75
  89. package/src/components/interaction/form/Select/Select.native.tsx +0 -252
  90. package/src/components/interaction/form/Slider/Slider.native.mdx +0 -83
  91. package/src/components/interaction/form/Slider/Slider.native.spec.tsx +0 -51
  92. package/src/components/interaction/form/Slider/Slider.native.stories.tsx +0 -58
  93. package/src/components/interaction/form/Slider/Slider.native.tsx +0 -188
  94. package/src/components/interaction/form/Switch/Switch.native.spec.tsx +0 -60
  95. package/src/components/interaction/form/Switch/Switch.native.stories.tsx +0 -33
  96. package/src/components/interaction/form/Switch/Switch.native.tsx +0 -59
  97. package/src/components/interaction/form/Textarea/Textarea.native.mdx +0 -76
  98. package/src/components/interaction/form/Textarea/Textarea.native.spec.tsx +0 -50
  99. package/src/components/interaction/form/Textarea/Textarea.native.stories.tsx +0 -50
  100. package/src/components/interaction/form/Textarea/Textarea.native.tsx +0 -108
  101. package/src/components/interaction/form/atoms/Label.native.spec.tsx +0 -39
  102. package/src/components/interaction/form/atoms/Label.native.stories.tsx +0 -18
  103. package/src/components/interaction/form/atoms/Label.native.tsx +0 -29
  104. package/src/components/interaction/form/atoms/Message.native.spec.tsx +0 -30
  105. package/src/components/interaction/form/atoms/Message.native.stories.tsx +0 -22
  106. package/src/components/interaction/form/atoms/Message.native.tsx +0 -34
  107. package/src/components/layout/ButtonGroup/ButtonGroup.native.spec.tsx +0 -83
  108. package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +0 -44
  109. package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +0 -44
  110. package/src/components/layout/Card/Card.native.spec.tsx +0 -102
  111. package/src/components/layout/Card/Card.native.stories.tsx +0 -51
  112. package/src/components/layout/Card/Card.native.tsx +0 -89
  113. package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +0 -39
  114. package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +0 -37
  115. package/src/components/layout/IconWrapper/IconWrapper.native.tsx +0 -20
  116. package/src/components/layout/SectionHeader/SectionHeader.native.spec.tsx +0 -51
  117. package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +0 -36
  118. package/src/components/layout/SectionHeader/SectionHeader.native.tsx +0 -79
  119. package/src/components/ui/Accordion/Accordion.native.mdx +0 -65
  120. package/src/components/ui/Accordion/Accordion.native.spec.tsx +0 -69
  121. package/src/components/ui/Accordion/Accordion.native.stories.tsx +0 -52
  122. package/src/components/ui/Accordion/Accordion.native.tsx +0 -141
  123. package/src/components/ui/Alert/Alert.native.mdx +0 -62
  124. package/src/components/ui/Alert/Alert.native.spec.tsx +0 -76
  125. package/src/components/ui/Alert/Alert.native.stories.tsx +0 -41
  126. package/src/components/ui/Alert/Alert.native.tsx +0 -94
  127. package/src/components/ui/Avatar/Avatar.native.mdx +0 -50
  128. package/src/components/ui/Avatar/Avatar.native.spec.tsx +0 -47
  129. package/src/components/ui/Avatar/Avatar.native.stories.tsx +0 -32
  130. package/src/components/ui/Avatar/Avatar.native.tsx +0 -85
  131. package/src/components/ui/Badge/Badge.native.mdx +0 -54
  132. package/src/components/ui/Badge/Badge.native.spec.tsx +0 -45
  133. package/src/components/ui/Badge/Badge.native.stories.tsx +0 -34
  134. package/src/components/ui/Badge/Badge.native.tsx +0 -50
  135. package/src/components/ui/Breadcrumb/Breadcrumb.native.mdx +0 -52
  136. package/src/components/ui/Breadcrumb/Breadcrumb.native.spec.tsx +0 -49
  137. package/src/components/ui/Breadcrumb/Breadcrumb.native.stories.tsx +0 -46
  138. package/src/components/ui/Breadcrumb/Breadcrumb.native.tsx +0 -83
  139. package/src/components/ui/Calendar/Calendar.native.mdx +0 -58
  140. package/src/components/ui/Calendar/Calendar.native.spec.tsx +0 -92
  141. package/src/components/ui/Calendar/Calendar.native.stories.tsx +0 -45
  142. package/src/components/ui/Calendar/Calendar.native.tsx +0 -294
  143. package/src/components/ui/Carousel/Carousel.native.mdx +0 -55
  144. package/src/components/ui/Carousel/Carousel.native.spec.tsx +0 -55
  145. package/src/components/ui/Carousel/Carousel.native.stories.tsx +0 -59
  146. package/src/components/ui/Carousel/Carousel.native.tsx +0 -184
  147. package/src/components/ui/Collapsible/Collapsible.native.mdx +0 -53
  148. package/src/components/ui/Collapsible/Collapsible.native.spec.tsx +0 -68
  149. package/src/components/ui/Collapsible/Collapsible.native.stories.tsx +0 -48
  150. package/src/components/ui/Collapsible/Collapsible.native.tsx +0 -139
  151. package/src/components/ui/DescriptionList/DescriptionList.native.mdx +0 -50
  152. package/src/components/ui/DescriptionList/DescriptionList.native.spec.tsx +0 -37
  153. package/src/components/ui/DescriptionList/DescriptionList.native.stories.tsx +0 -48
  154. package/src/components/ui/DescriptionList/DescriptionList.native.tsx +0 -54
  155. package/src/components/ui/Link/Link.native.mdx +0 -53
  156. package/src/components/ui/Link/Link.native.spec.tsx +0 -35
  157. package/src/components/ui/Link/Link.native.stories.tsx +0 -46
  158. package/src/components/ui/Link/Link.native.tsx +0 -48
  159. package/src/components/ui/Loading/Loading.native.spec.tsx +0 -24
  160. package/src/components/ui/Loading/Loading.native.stories.tsx +0 -31
  161. package/src/components/ui/Loading/Loading.native.tsx +0 -29
  162. package/src/components/ui/NotificationBanner/NotificationBanner.native.spec.tsx +0 -85
  163. package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +0 -37
  164. package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +0 -76
  165. package/src/components/ui/Pagination/Pagination.native.mdx +0 -62
  166. package/src/components/ui/Pagination/Pagination.native.spec.tsx +0 -86
  167. package/src/components/ui/Pagination/Pagination.native.stories.tsx +0 -47
  168. package/src/components/ui/Pagination/Pagination.native.tsx +0 -157
  169. package/src/components/ui/Progress/Progress.native.spec.tsx +0 -56
  170. package/src/components/ui/Progress/Progress.native.stories.tsx +0 -34
  171. package/src/components/ui/Progress/Progress.native.tsx +0 -84
  172. package/src/components/ui/ProgressCircle/ProgressCircle.native.mdx +0 -63
  173. package/src/components/ui/ProgressCircle/ProgressCircle.native.spec.tsx +0 -39
  174. package/src/components/ui/ProgressCircle/ProgressCircle.native.stories.tsx +0 -36
  175. package/src/components/ui/ProgressCircle/ProgressCircle.native.tsx +0 -176
  176. package/src/components/ui/Separator/Separator.native.spec.tsx +0 -39
  177. package/src/components/ui/Separator/Separator.native.stories.tsx +0 -38
  178. package/src/components/ui/Separator/Separator.native.tsx +0 -32
  179. package/src/components/ui/Skeleton/Skeleton.native.spec.tsx +0 -39
  180. package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +0 -31
  181. package/src/components/ui/Skeleton/Skeleton.native.tsx +0 -42
  182. package/src/components/ui/Table/Table.native.mdx +0 -72
  183. package/src/components/ui/Table/Table.native.spec.tsx +0 -83
  184. package/src/components/ui/Table/Table.native.stories.tsx +0 -55
  185. package/src/components/ui/Table/Table.native.tsx +0 -196
  186. package/src/components/ui/Tabs/Tabs.native.mdx +0 -65
  187. package/src/components/ui/Tabs/Tabs.native.spec.tsx +0 -65
  188. package/src/components/ui/Tabs/Tabs.native.stories.tsx +0 -57
  189. package/src/components/ui/Tabs/Tabs.native.tsx +0 -133
  190. package/src/components/ui/Tooltip/Tooltip.native.mdx +0 -47
  191. package/src/components/ui/Tooltip/Tooltip.native.spec.tsx +0 -37
  192. package/src/components/ui/Tooltip/Tooltip.native.stories.tsx +0 -38
  193. package/src/components/ui/Tooltip/Tooltip.native.tsx +0 -57
  194. package/src/components/ui/Typography/Typography.native.mdx +0 -56
  195. package/src/components/ui/Typography/Typography.native.spec.tsx +0 -49
  196. package/src/components/ui/Typography/Typography.native.stories.tsx +0 -36
  197. package/src/components/ui/Typography/Typography.native.tsx +0 -65
  198. package/src/native/ThemeContext.tsx +0 -28
  199. package/src/native/tokens.ts +0 -13
  200. package/src/native.ts +0 -76
  201. package/src/stories/ReactNative.mdx +0 -156
@@ -1,254 +0,0 @@
1
- import React, { ReactElement, ReactNode, useState, useEffect, useRef } from 'react';
2
- import {
3
- Modal,
4
- View,
5
- Pressable,
6
- Text,
7
- FlatList,
8
- Animated,
9
- StyleSheet,
10
- Dimensions,
11
- SafeAreaView,
12
- StyleProp,
13
- ViewStyle,
14
- } from 'react-native';
15
- import { spacing, font, border, lineHeight } from '../../../native/tokens';
16
- import { useTheme } from '../../../native/ThemeContext';
17
-
18
- export interface DropdownMenuItem {
19
- id: string;
20
- label: string;
21
- onPress?: () => void;
22
- disabled?: boolean;
23
- icon?: ReactNode;
24
- separator?: boolean;
25
- }
26
-
27
- interface DropdownMenuProps {
28
- trigger: ReactElement;
29
- items: DropdownMenuItem[];
30
- title?: string;
31
- style?: StyleProp<ViewStyle>;
32
- }
33
-
34
- const styles = StyleSheet.create({
35
- overlay: {
36
- flex: 1,
37
- justifyContent: 'flex-end',
38
- },
39
- sheet: {
40
- maxHeight: '60%',
41
- borderTopStartRadius: border.radius.m,
42
- borderTopEndRadius: border.radius.m,
43
- paddingTop: spacing.m,
44
- paddingBottom: spacing.xxxl,
45
- },
46
- sheetHeader: {
47
- flexDirection: 'row',
48
- justifyContent: 'space-between',
49
- alignItems: 'center',
50
- paddingHorizontal: spacing.m,
51
- paddingBottom: spacing.s,
52
- borderBottomWidth: 1,
53
- marginBottom: spacing.xxs,
54
- },
55
- sheetTitle: {
56
- fontFamily: font.family.body,
57
- fontSize: font.size.heading3,
58
- fontWeight: font.weight.semibold,
59
- },
60
- closeText: {
61
- fontFamily: font.family.body,
62
- fontSize: font.size.body,
63
- fontWeight: font.weight.semibold,
64
- },
65
- separator: {
66
- height: 1,
67
- marginHorizontal: spacing.m,
68
- marginVertical: spacing.xxs,
69
- },
70
- item: {
71
- flexDirection: 'row',
72
- alignItems: 'center',
73
- paddingHorizontal: spacing.m,
74
- paddingVertical: spacing.s,
75
- minHeight: 48,
76
- gap: spacing.s,
77
- },
78
- itemText: {
79
- fontFamily: font.family.body,
80
- fontSize: font.size.body,
81
- lineHeight: lineHeight.body,
82
- },
83
- });
84
-
85
- export function DropdownMenu({
86
- trigger,
87
- items,
88
- title = 'Menu',
89
- style,
90
- }: Readonly<DropdownMenuProps>) {
91
- const { colors } = useTheme();
92
- const [open, setOpen] = useState(false);
93
- const screenHeight = Dimensions.get('window').height;
94
-
95
- const slideAnim = useRef(new Animated.Value(screenHeight)).current;
96
- const backdropAnim = useRef(new Animated.Value(0)).current;
97
-
98
- useEffect(() => {
99
- if (open) {
100
- Animated.parallel([
101
- Animated.timing(slideAnim, {
102
- toValue: 0,
103
- duration: 250,
104
- useNativeDriver: true,
105
- }),
106
- Animated.timing(backdropAnim, {
107
- toValue: 1,
108
- duration: 250,
109
- useNativeDriver: true,
110
- }),
111
- ]).start();
112
- } else {
113
- Animated.parallel([
114
- Animated.timing(slideAnim, {
115
- toValue: screenHeight,
116
- duration: 200,
117
- useNativeDriver: true,
118
- }),
119
- Animated.timing(backdropAnim, {
120
- toValue: 0,
121
- duration: 200,
122
- useNativeDriver: true,
123
- }),
124
- ]).start();
125
- }
126
- }, [open, slideAnim, backdropAnim, screenHeight]);
127
-
128
- const handleItemPress = (item: DropdownMenuItem) => {
129
- item.onPress?.();
130
- setOpen(false);
131
- };
132
-
133
- return (
134
- <View style={style}>
135
- {React.cloneElement(trigger as ReactElement<Record<string, unknown>>, {
136
- onPress: () => setOpen(true),
137
- accessibilityRole: 'button',
138
- accessibilityLabel: title,
139
- })}
140
-
141
- <Modal
142
- visible={open}
143
- transparent
144
- animationType="none"
145
- onRequestClose={() => setOpen(false)}
146
- >
147
- <View style={styles.overlay}>
148
- <Animated.View
149
- style={[
150
- StyleSheet.absoluteFill,
151
- { backgroundColor: 'rgba(0, 0, 0, 0.5)', opacity: backdropAnim },
152
- ]}
153
- >
154
- <Pressable
155
- style={StyleSheet.absoluteFill}
156
- onPress={() => setOpen(false)}
157
- accessibilityRole="button"
158
- accessibilityLabel="Close menu"
159
- />
160
- </Animated.View>
161
- <Animated.View
162
- style={[
163
- styles.sheet,
164
- {
165
- backgroundColor: colors.colorBg,
166
- transform: [{ translateY: slideAnim }],
167
- },
168
- ]}
169
- >
170
- <Pressable
171
- onPress={() => {
172
- /* prevent close when pressing sheet */
173
- }}
174
- >
175
- <SafeAreaView>
176
- <View
177
- style={[
178
- styles.sheetHeader,
179
- { borderBottomColor: colors.colorBgSubtle },
180
- ]}
181
- >
182
- <Text style={[styles.sheetTitle, { color: colors.colorOnBg }]}>
183
- {title}
184
- </Text>
185
- <Pressable
186
- accessibilityRole="button"
187
- accessibilityLabel="Close"
188
- onPress={() => setOpen(false)}
189
- hitSlop={8}
190
- >
191
- <Text
192
- style={[
193
- styles.closeText,
194
- { color: colors.colorInteractive },
195
- ]}
196
- >
197
- Done
198
- </Text>
199
- </Pressable>
200
- </View>
201
- <FlatList
202
- data={items}
203
- keyExtractor={(item) => item.id}
204
- renderItem={({ item }) => (
205
- <>
206
- {item.separator && (
207
- <View
208
- style={[
209
- styles.separator,
210
- { backgroundColor: colors.colorBgSubtle },
211
- ]}
212
- accessibilityRole="none"
213
- />
214
- )}
215
- <Pressable
216
- accessibilityRole="menuitem"
217
- accessibilityLabel={item.label}
218
- accessibilityState={{ disabled: item.disabled }}
219
- disabled={item.disabled}
220
- onPress={() => handleItemPress(item)}
221
- style={({ pressed }) => [
222
- styles.item,
223
- pressed && { backgroundColor: colors.colorBgSubtle },
224
- item.disabled && { opacity: 0.4 },
225
- ]}
226
- >
227
- {!!item.icon && (
228
- <View
229
- importantForAccessibility="no-hide-descendants"
230
- accessibilityElementsHidden={true}
231
- >
232
- {item.icon}
233
- </View>
234
- )}
235
- <Text
236
- style={[
237
- styles.itemText,
238
- { color: colors.colorOnBg },
239
- ]}
240
- >
241
- {item.label}
242
- </Text>
243
- </Pressable>
244
- </>
245
- )}
246
- />
247
- </SafeAreaView>
248
- </Pressable>
249
- </Animated.View>
250
- </View>
251
- </Modal>
252
- </View>
253
- );
254
- }
@@ -1,61 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './Popover.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Popover (React Native)
7
-
8
- A popover overlay that displays content in a centered modal. The trigger element opens the popover on press; tapping the backdrop dismisses it.
9
-
10
- > **Status: alpha** — API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Popover } from '@boostdev/design-system-components';
16
-
17
- <Popover
18
- content={<Text>Additional information here.</Text>}
19
- accessibilityLabel="More info"
20
- >
21
- <Button>Show Info</Button>
22
- </Popover>
23
- ```
24
-
25
- Wrap your tree in `ThemeProvider` (once, at the app root):
26
-
27
- ```tsx
28
- import { ThemeProvider } from '@boostdev/design-system-components/native/ThemeContext';
29
-
30
- export default function App() {
31
- return <ThemeProvider><YourApp /></ThemeProvider>;
32
- }
33
- ```
34
-
35
- ## Examples
36
-
37
- ### Default
38
- <Canvas of={Stories.Default} />
39
-
40
- ### Rich content
41
- <Canvas of={Stories.RichContent} />
42
-
43
- ## Props
44
-
45
- <ArgTypes of={Stories} />
46
-
47
- ## Props reference
48
-
49
- | Prop | Type | Default | Description |
50
- |------|------|---------|-------------|
51
- | `children` | `ReactNode` | — | Trigger element that opens the popover on press |
52
- | `content` | `ReactNode` | — | Content displayed inside the popover panel |
53
- | `accessibilityLabel` | `string` | `'Open popover'` | Label for the trigger and panel |
54
- | `style` | `StyleProp<ViewStyle>` | — | Override wrapper styles |
55
-
56
- ## Accessibility
57
-
58
- - Trigger has `accessibilityRole="button"` with `expanded` state
59
- - Backdrop press closes the popover
60
- - Android back button triggers `onRequestClose` to dismiss
61
- - Panel has shadow elevation for visual depth
@@ -1,73 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/react';
2
- import { Text, Pressable } from 'react-native';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Popover } from './Popover.native';
5
-
6
- function renderWithTheme(ui: React.ReactElement) {
7
- return render(<ThemeProvider>{ui}</ThemeProvider>);
8
- }
9
-
10
- function Trigger({ children, ...rest }: { children: string; onPress?: () => void }) {
11
- return <Pressable {...rest}><Text>{children}</Text></Pressable>;
12
- }
13
-
14
- describe('Popover (native)', () => {
15
- it('renders the trigger element', () => {
16
- renderWithTheme(
17
- <Popover content={<Text>Popover content</Text>}>
18
- <Trigger>Trigger</Trigger>
19
- </Popover>,
20
- );
21
- expect(screen.getByText('Trigger')).toBeInTheDocument();
22
- });
23
-
24
- it('does not show content initially', () => {
25
- renderWithTheme(
26
- <Popover content={<Text>Hidden content</Text>}>
27
- <Trigger>Trigger</Trigger>
28
- </Popover>,
29
- );
30
- expect(screen.queryByText('Hidden content')).not.toBeInTheDocument();
31
- });
32
-
33
- it('shows content when trigger is pressed', () => {
34
- renderWithTheme(
35
- <Popover content={<Text>Visible content</Text>} accessibilityLabel="Info">
36
- <Trigger>Trigger</Trigger>
37
- </Popover>,
38
- );
39
- fireEvent.click(screen.getByRole('button', { name: 'Info' }));
40
- expect(screen.getByText('Visible content')).toBeInTheDocument();
41
- });
42
-
43
- it('renders trigger with button role and custom accessibility label', () => {
44
- renderWithTheme(
45
- <Popover content={<Text>Content</Text>} accessibilityLabel="Details">
46
- <Trigger>Trigger</Trigger>
47
- </Popover>,
48
- );
49
- const trigger = screen.getByRole('button', { name: 'Details' });
50
- expect(trigger).toBeInTheDocument();
51
- });
52
-
53
- it('renders backdrop with close label when open', () => {
54
- renderWithTheme(
55
- <Popover content={<Text>Closable</Text>} accessibilityLabel="Test">
56
- <Trigger>Trigger</Trigger>
57
- </Popover>,
58
- );
59
- fireEvent.click(screen.getByRole('button', { name: 'Test' }));
60
- expect(screen.getByText('Closable')).toBeInTheDocument();
61
- // Verify the close backdrop button exists
62
- expect(screen.getByRole('button', { name: 'Close popover' })).toBeInTheDocument();
63
- });
64
-
65
- it('uses default accessibility label when none provided', () => {
66
- renderWithTheme(
67
- <Popover content={<Text>Content</Text>}>
68
- <Trigger>Trigger</Trigger>
69
- </Popover>,
70
- );
71
- expect(screen.getByRole('button', { name: 'Open popover' })).toBeInTheDocument();
72
- });
73
- });
@@ -1,44 +0,0 @@
1
- import React from 'react';
2
- import { Text, View } from 'react-native';
3
- import type { Meta, StoryObj } from '@storybook/react';
4
- import { ThemeProvider } from '../../../native/ThemeContext';
5
- import { Popover } from './Popover.native';
6
- import { Button } from '../Button/Button.native';
7
-
8
- const meta = {
9
- tags: ['!stable', 'alpha'],
10
- title: 'React Native/Interaction/Popover',
11
- component: Popover,
12
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
13
- parameters: { layout: 'centered' },
14
- } satisfies Meta<typeof Popover>;
15
-
16
- export default meta;
17
- type Story = StoryObj<typeof meta>;
18
-
19
- export const Default: Story = {
20
- render: () => (
21
- <Popover
22
- content={<Text>This is popover content displayed in a modal overlay.</Text>}
23
- accessibilityLabel="More info"
24
- >
25
- <Button>Show Popover</Button>
26
- </Popover>
27
- ),
28
- };
29
-
30
- export const RichContent: Story = {
31
- render: () => (
32
- <Popover
33
- content={
34
- <View style={{ gap: 8 }}>
35
- <Text style={{ fontWeight: '600', fontSize: 16 }}>Help</Text>
36
- <Text>Press the button below to submit your response. You can edit your answer before submitting.</Text>
37
- </View>
38
- }
39
- accessibilityLabel="Help information"
40
- >
41
- <Button variant="outline">Help</Button>
42
- </Popover>
43
- ),
44
- };
@@ -1,87 +0,0 @@
1
- import React, { ReactElement, ReactNode, useState } from 'react';
2
- import {
3
- Modal,
4
- View,
5
- Pressable,
6
- StyleSheet,
7
- StyleProp,
8
- ViewStyle,
9
- } from 'react-native';
10
- import { spacing, border } from '../../../native/tokens';
11
- import { useTheme } from '../../../native/ThemeContext';
12
-
13
- interface PopoverProps {
14
- children: ReactElement;
15
- content: ReactNode;
16
- accessibilityLabel?: string;
17
- style?: StyleProp<ViewStyle>;
18
- }
19
-
20
- const styles = StyleSheet.create({
21
- wrapper: {
22
- alignSelf: 'flex-start',
23
- },
24
- overlay: {
25
- flex: 1,
26
- justifyContent: 'center',
27
- alignItems: 'center',
28
- backgroundColor: 'rgba(0, 0, 0, 0.25)',
29
- padding: spacing.m,
30
- },
31
- panel: {
32
- width: '100%',
33
- maxWidth: 360,
34
- borderRadius: border.radius.s,
35
- padding: spacing.m,
36
- shadowColor: '#000',
37
- shadowOffset: { width: 0, height: 2 },
38
- shadowOpacity: 0.15,
39
- shadowRadius: 8,
40
- elevation: 4,
41
- },
42
- });
43
-
44
- export function Popover({
45
- children,
46
- content,
47
- accessibilityLabel,
48
- style,
49
- }: Readonly<PopoverProps>) {
50
- const { colors } = useTheme();
51
- const [isOpen, setIsOpen] = useState(false);
52
-
53
- return (
54
- <View style={[styles.wrapper, style]}>
55
- {React.cloneElement(children as ReactElement<Record<string, unknown>>, {
56
- onPress: () => setIsOpen(true),
57
- accessibilityRole: 'button',
58
- accessibilityLabel: accessibilityLabel ?? 'Open popover',
59
- })}
60
-
61
- <Modal
62
- visible={isOpen}
63
- transparent
64
- animationType="fade"
65
- onRequestClose={() => setIsOpen(false)}
66
- >
67
- <Pressable
68
- style={styles.overlay}
69
- onPress={() => setIsOpen(false)}
70
- accessibilityRole="button"
71
- accessibilityLabel="Close popover"
72
- >
73
- <Pressable
74
- style={[styles.panel, { backgroundColor: colors.colorBg }]}
75
- onPress={() => {
76
- /* prevent close when pressing panel */
77
- }}
78
- accessibilityRole="none"
79
- accessibilityLabel={accessibilityLabel}
80
- >
81
- {content}
82
- </Pressable>
83
- </Pressable>
84
- </Modal>
85
- </View>
86
- );
87
- }
@@ -1,55 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './Rating.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Rating (React Native)
7
-
8
- React Native read-only star rating display using text characters. Shows filled and empty stars based on the `value` prop.
9
-
10
- > **Status: alpha** — API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Rating } from '@boostdev/design-system-components';
16
-
17
- <Rating value={4} />
18
- <Rating value={7} max={10} />
19
- ```
20
-
21
- ## Examples
22
-
23
- ### Default (3 of 5)
24
- <Canvas of={Stories.Default} />
25
-
26
- ### Full rating
27
- <Canvas of={Stories.Full} />
28
-
29
- ### Empty rating
30
- <Canvas of={Stories.Empty} />
31
-
32
- ### Custom max
33
- <Canvas of={Stories.CustomMax} />
34
-
35
- ### All variants
36
- <Canvas of={Stories.AllVariants} />
37
-
38
- ## Props
39
-
40
- <ArgTypes of={Stories} />
41
-
42
- ## Props reference
43
-
44
- | Prop | Type | Default | Description |
45
- |------|------|---------|-------------|
46
- | `value` | `number` | — | Number of filled stars |
47
- | `max` | `number` | `5` | Total number of stars |
48
- | `accessibilityLabel` | `string` | `"{value} out of {max} stars"` | Screen reader label |
49
- | `style` | `StyleProp<ViewStyle>` | — | Override container styles |
50
-
51
- ## Accessibility
52
-
53
- - Uses `accessibilityRole="image"` since the rating is a visual representation
54
- - Auto-generates an accessibility label like "3 out of 5 stars" when not provided
55
- - Individual stars are hidden from assistive technologies
@@ -1,38 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Rating } from './Rating.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('Rating (native)', () => {
10
- it('has image role with auto-generated accessible label', () => {
11
- renderWithTheme(<Rating value={3} />);
12
- expect(screen.getByRole('img', { name: '3 out of 5 stars' })).toBeInTheDocument();
13
- });
14
-
15
- it('renders the correct number of filled stars', () => {
16
- renderWithTheme(<Rating value={3} />);
17
- const stars = screen.getAllByText('\u2605');
18
- expect(stars).toHaveLength(5);
19
- });
20
-
21
- it('renders all stars as filled characters', () => {
22
- renderWithTheme(<Rating value={2} />);
23
- const stars = screen.getAllByText('\u2605');
24
- expect(stars).toHaveLength(5);
25
- });
26
-
27
- it('uses custom max value', () => {
28
- renderWithTheme(<Rating value={7} max={10} />);
29
- expect(screen.getByRole('img', { name: '7 out of 10 stars' })).toBeInTheDocument();
30
- const stars = screen.getAllByText('\u2605');
31
- expect(stars).toHaveLength(10);
32
- });
33
-
34
- it('uses custom accessibilityLabel when provided', () => {
35
- renderWithTheme(<Rating value={4} accessibilityLabel="Excellent rating" />);
36
- expect(screen.getByRole('img', { name: 'Excellent rating' })).toBeInTheDocument();
37
- });
38
- });
@@ -1,37 +0,0 @@
1
- import React from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Rating } from './Rating.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/Interaction/Rating',
9
- component: Rating,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'centered' },
12
- argTypes: {
13
- value: { control: { type: 'range', min: 0, max: 5, step: 1 } },
14
- max: { control: { type: 'number', min: 1, max: 10 } },
15
- },
16
- } satisfies Meta<typeof Rating>;
17
-
18
- export default meta;
19
- type Story = StoryObj<typeof meta>;
20
-
21
- export const Default: Story = { args: { value: 3 } };
22
- export const Full: Story = { args: { value: 5 } };
23
- export const Empty: Story = { args: { value: 0 } };
24
- export const CustomMax: Story = { args: { value: 7, max: 10 } };
25
- export const WithLabel: Story = {
26
- args: { value: 4, accessibilityLabel: 'Customer rating: 4 out of 5' },
27
- };
28
-
29
- export const AllVariants: Story = {
30
- render: () => (
31
- <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
32
- {[0, 1, 2, 3, 4, 5].map(v => (
33
- <Rating key={v} value={v} />
34
- ))}
35
- </div>
36
- ),
37
- };