@boostdev/design-system-components 1.0.3 → 1.1.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 (262) hide show
  1. package/AGENTS.md +51 -10
  2. package/dist/client.cjs +350 -174
  3. package/dist/client.css +751 -647
  4. package/dist/client.d.cts +62 -97
  5. package/dist/client.d.ts +62 -97
  6. package/dist/client.js +377 -211
  7. package/dist/index.cjs +350 -174
  8. package/dist/index.css +751 -647
  9. package/dist/index.d.cts +62 -97
  10. package/dist/index.d.ts +62 -97
  11. package/dist/index.js +377 -211
  12. package/dist/native/index.cjs +991 -0
  13. package/dist/native/index.d.cts +208 -0
  14. package/dist/native/index.d.ts +208 -0
  15. package/dist/native/index.js +968 -0
  16. package/dist/web-components.d.ts +304 -0
  17. package/dist/web-components.js +1978 -0
  18. package/package.json +27 -5
  19. package/src/components/interaction/Button/Button.module.css +11 -8
  20. package/src/components/interaction/Button/Button.native.mdx +72 -0
  21. package/src/components/interaction/Button/Button.native.spec.tsx +35 -0
  22. package/src/components/interaction/Button/Button.native.stories.tsx +42 -0
  23. package/src/components/interaction/Button/Button.native.tsx +95 -0
  24. package/src/components/interaction/Button/Button.stories.tsx +1 -1
  25. package/src/components/interaction/Button/Button.tsx +2 -2
  26. package/src/components/interaction/Command/Command.mdx +16 -0
  27. package/src/components/interaction/Command/Command.module.css +7 -7
  28. package/src/components/interaction/Command/Command.spec.tsx +37 -0
  29. package/src/components/interaction/Command/Command.stories.tsx +1 -1
  30. package/src/components/interaction/Command/Command.tsx +39 -4
  31. package/src/components/interaction/Dialog/Dialog.module.css +10 -13
  32. package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
  33. package/src/components/interaction/Dialog/Dialog.stories.tsx +18 -1
  34. package/src/components/interaction/Dialog/Dialog.tsx +52 -18
  35. package/src/components/interaction/Drawer/Drawer.mdx +14 -0
  36. package/src/components/interaction/Drawer/Drawer.module.css +11 -11
  37. package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
  38. package/src/components/interaction/Drawer/Drawer.stories.tsx +1 -1
  39. package/src/components/interaction/Drawer/Drawer.tsx +40 -6
  40. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
  41. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +1 -1
  42. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +2 -2
  43. package/src/components/interaction/Popover/Popover.mdx +17 -0
  44. package/src/components/interaction/Popover/Popover.module.css +132 -16
  45. package/src/components/interaction/Popover/Popover.spec.tsx +119 -20
  46. package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
  47. package/src/components/interaction/Popover/Popover.tsx +69 -29
  48. package/src/components/interaction/Rating/Rating.stories.tsx +1 -1
  49. package/src/components/interaction/Rating/Rating.tsx +2 -2
  50. package/src/components/interaction/Toast/Toast.module.css +13 -8
  51. package/src/components/interaction/Toast/Toast.spec.tsx +21 -0
  52. package/src/components/interaction/Toast/Toast.stories.tsx +1 -1
  53. package/src/components/interaction/Toast/Toast.tsx +22 -2
  54. package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
  55. package/src/components/interaction/form/Checkbox/Checkbox.module.css +9 -9
  56. package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
  57. package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +42 -0
  58. package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +36 -0
  59. package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +80 -0
  60. package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +1 -1
  61. package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
  62. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +44 -0
  63. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
  64. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +2 -2
  65. package/src/components/interaction/form/Combobox/Combobox.mdx +21 -0
  66. package/src/components/interaction/form/Combobox/Combobox.module.css +8 -9
  67. package/src/components/interaction/form/Combobox/Combobox.stories.tsx +1 -1
  68. package/src/components/interaction/form/Combobox/Combobox.tsx +2 -2
  69. package/src/components/interaction/form/FileInput/FileInput.mdx +14 -0
  70. package/src/components/interaction/form/FileInput/FileInput.module.css +5 -5
  71. package/src/components/interaction/form/FileInput/FileInput.stories.tsx +1 -1
  72. package/src/components/interaction/form/FileInput/FileInput.tsx +2 -2
  73. package/src/components/interaction/form/FormInput/FormInput.stories.tsx +1 -1
  74. package/src/components/interaction/form/FormInput/FormInput.tsx +2 -2
  75. package/src/components/interaction/form/NumberInput/NumberInput.mdx +16 -0
  76. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
  77. package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +1 -1
  78. package/src/components/interaction/form/NumberInput/NumberInput.tsx +2 -2
  79. package/src/components/interaction/form/Radio/Radio.mdx +3 -3
  80. package/src/components/interaction/form/Radio/Radio.module.css +9 -9
  81. package/src/components/interaction/form/Radio/Radio.native.stories.tsx +46 -0
  82. package/src/components/interaction/form/Radio/Radio.native.tsx +79 -0
  83. package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
  84. package/src/components/interaction/form/Radio/Radio.tsx +2 -2
  85. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +45 -0
  86. package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +1 -1
  87. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +2 -2
  88. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
  89. package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +1 -1
  90. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +2 -2
  91. package/src/components/interaction/form/Select/Select.module.css +3 -3
  92. package/src/components/interaction/form/Select/Select.stories.tsx +1 -1
  93. package/src/components/interaction/form/Select/Select.tsx +2 -2
  94. package/src/components/interaction/form/Slider/Slider.mdx +1 -1
  95. package/src/components/interaction/form/Slider/Slider.module.css +10 -10
  96. package/src/components/interaction/form/Slider/Slider.stories.tsx +1 -1
  97. package/src/components/interaction/form/Slider/Slider.tsx +2 -2
  98. package/src/components/interaction/form/Switch/Switch.mdx +4 -4
  99. package/src/components/interaction/form/Switch/Switch.module.css +11 -11
  100. package/src/components/interaction/form/Switch/Switch.native.spec.tsx +60 -0
  101. package/src/components/interaction/form/Switch/Switch.native.stories.tsx +35 -0
  102. package/src/components/interaction/form/Switch/Switch.native.tsx +59 -0
  103. package/src/components/interaction/form/Switch/Switch.stories.tsx +1 -1
  104. package/src/components/interaction/form/Switch/Switch.tsx +2 -2
  105. package/src/components/interaction/form/Textarea/Textarea.mdx +17 -0
  106. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
  107. package/src/components/interaction/form/Textarea/Textarea.stories.tsx +1 -1
  108. package/src/components/interaction/form/Textarea/Textarea.tsx +2 -2
  109. package/src/components/interaction/form/atoms/InputContainer.tsx +2 -1
  110. package/src/components/interaction/form/atoms/Label.native.stories.tsx +18 -0
  111. package/src/components/interaction/form/atoms/Label.native.tsx +29 -0
  112. package/src/components/interaction/form/atoms/Label.tsx +5 -3
  113. package/src/components/interaction/form/atoms/Message.native.spec.tsx +30 -0
  114. package/src/components/interaction/form/atoms/Message.native.stories.tsx +22 -0
  115. package/src/components/interaction/form/atoms/Message.native.tsx +34 -0
  116. package/src/components/interaction/form/atoms/Message.tsx +5 -3
  117. package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +50 -0
  118. package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +34 -0
  119. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -1
  120. package/src/components/layout/ButtonGroup/ButtonGroup.tsx +2 -2
  121. package/src/components/layout/Card/Card.native.stories.tsx +53 -0
  122. package/src/components/layout/Card/Card.native.tsx +89 -0
  123. package/src/components/layout/Card/Card.stories.tsx +1 -1
  124. package/src/components/layout/Card/Card.tsx +2 -2
  125. package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
  126. package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
  127. package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +39 -0
  128. package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +41 -0
  129. package/src/components/layout/IconWrapper/IconWrapper.native.tsx +20 -0
  130. package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +1 -1
  131. package/src/components/layout/IconWrapper/IconWrapper.tsx +2 -2
  132. package/src/components/layout/SectionHeader/SectionHeader.mdx +13 -0
  133. package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +38 -0
  134. package/src/components/layout/SectionHeader/SectionHeader.native.tsx +79 -0
  135. package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +1 -1
  136. package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
  137. package/src/components/ui/Accordion/Accordion.mdx +14 -0
  138. package/src/components/ui/Accordion/Accordion.module.css +5 -5
  139. package/src/components/ui/Accordion/Accordion.stories.tsx +1 -1
  140. package/src/components/ui/Accordion/Accordion.tsx +2 -2
  141. package/src/components/ui/Alert/Alert.native.mdx +62 -0
  142. package/src/components/ui/Alert/Alert.native.stories.tsx +43 -0
  143. package/src/components/ui/Alert/Alert.native.tsx +94 -0
  144. package/src/components/ui/Alert/Alert.stories.tsx +1 -1
  145. package/src/components/ui/Alert/Alert.tsx +2 -2
  146. package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
  147. package/src/components/ui/Avatar/Avatar.native.spec.tsx +47 -0
  148. package/src/components/ui/Avatar/Avatar.native.stories.tsx +34 -0
  149. package/src/components/ui/Avatar/Avatar.native.tsx +85 -0
  150. package/src/components/ui/Avatar/Avatar.stories.tsx +1 -1
  151. package/src/components/ui/Avatar/Avatar.tsx +2 -2
  152. package/src/components/ui/Badge/Badge.mdx +2 -2
  153. package/src/components/ui/Badge/Badge.native.mdx +54 -0
  154. package/src/components/ui/Badge/Badge.native.stories.tsx +36 -0
  155. package/src/components/ui/Badge/Badge.native.tsx +50 -0
  156. package/src/components/ui/Badge/Badge.stories.tsx +1 -1
  157. package/src/components/ui/Badge/Badge.tsx +2 -2
  158. package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +1 -1
  159. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +2 -2
  160. package/src/components/ui/Calendar/Calendar.mdx +16 -0
  161. package/src/components/ui/Calendar/Calendar.module.css +7 -7
  162. package/src/components/ui/Calendar/Calendar.stories.tsx +1 -1
  163. package/src/components/ui/Calendar/Calendar.tsx +2 -2
  164. package/src/components/ui/Carousel/Carousel.module.css +5 -5
  165. package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
  166. package/src/components/ui/Carousel/Carousel.tsx +2 -2
  167. package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
  168. package/src/components/ui/Collapsible/Collapsible.stories.tsx +1 -1
  169. package/src/components/ui/Collapsible/Collapsible.tsx +2 -2
  170. package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +1 -1
  171. package/src/components/ui/DescriptionList/DescriptionList.tsx +2 -2
  172. package/src/components/ui/Link/Link.mdx +14 -0
  173. package/src/components/ui/Link/Link.module.css +2 -2
  174. package/src/components/ui/Link/Link.stories.tsx +1 -1
  175. package/src/components/ui/Link/Link.tsx +2 -2
  176. package/src/components/ui/Loading/Loading.module.css +7 -7
  177. package/src/components/ui/Loading/Loading.native.spec.tsx +24 -0
  178. package/src/components/ui/Loading/Loading.native.stories.tsx +33 -0
  179. package/src/components/ui/Loading/Loading.native.tsx +29 -0
  180. package/src/components/ui/Loading/Loading.stories.tsx +1 -1
  181. package/src/components/ui/Loading/Loading.tsx +2 -2
  182. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
  183. package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +39 -0
  184. package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +76 -0
  185. package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +1 -1
  186. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +2 -2
  187. package/src/components/ui/Pagination/Pagination.module.css +7 -7
  188. package/src/components/ui/Pagination/Pagination.stories.tsx +1 -1
  189. package/src/components/ui/Pagination/Pagination.tsx +2 -2
  190. package/src/components/ui/Progress/Progress.mdx +1 -1
  191. package/src/components/ui/Progress/Progress.module.css +6 -6
  192. package/src/components/ui/Progress/Progress.native.stories.tsx +34 -0
  193. package/src/components/ui/Progress/Progress.native.tsx +84 -0
  194. package/src/components/ui/Progress/Progress.stories.tsx +1 -1
  195. package/src/components/ui/Progress/Progress.tsx +2 -2
  196. package/src/components/ui/ProgressCircle/ProgressCircle.module.css +1 -1
  197. package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +1 -1
  198. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +2 -2
  199. package/src/components/ui/Separator/Separator.mdx +14 -0
  200. package/src/components/ui/Separator/Separator.module.css +3 -3
  201. package/src/components/ui/Separator/Separator.native.stories.tsx +42 -0
  202. package/src/components/ui/Separator/Separator.native.tsx +32 -0
  203. package/src/components/ui/Separator/Separator.stories.tsx +1 -1
  204. package/src/components/ui/Separator/Separator.tsx +2 -2
  205. package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
  206. package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +33 -0
  207. package/src/components/ui/Skeleton/Skeleton.native.tsx +41 -0
  208. package/src/components/ui/Skeleton/Skeleton.stories.tsx +1 -1
  209. package/src/components/ui/Skeleton/Skeleton.tsx +2 -3
  210. package/src/components/ui/SkipLink/SkipLink.stories.tsx +1 -1
  211. package/src/components/ui/SkipLink/SkipLink.tsx +5 -3
  212. package/src/components/ui/Table/Table.mdx +14 -0
  213. package/src/components/ui/Table/Table.module.css +9 -9
  214. package/src/components/ui/Table/Table.stories.tsx +1 -1
  215. package/src/components/ui/Table/Table.tsx +2 -2
  216. package/src/components/ui/Tabs/Tabs.module.css +3 -3
  217. package/src/components/ui/Tabs/Tabs.stories.tsx +1 -1
  218. package/src/components/ui/Tabs/Tabs.tsx +2 -2
  219. package/src/components/ui/Tooltip/Tooltip.mdx +14 -0
  220. package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
  221. package/src/components/ui/Tooltip/Tooltip.stories.tsx +1 -1
  222. package/src/components/ui/Tooltip/Tooltip.tsx +2 -2
  223. package/src/components/ui/Typography/Typography.mdx +13 -0
  224. package/src/components/ui/Typography/Typography.native.mdx +56 -0
  225. package/src/components/ui/Typography/Typography.native.stories.tsx +38 -0
  226. package/src/components/ui/Typography/Typography.native.tsx +65 -0
  227. package/src/components/ui/Typography/Typography.stories.tsx +1 -1
  228. package/src/components/ui/Typography/Typography.tsx +2 -2
  229. package/src/css/bdc.css +8 -0
  230. package/src/index.ts +1 -0
  231. package/src/native/ThemeContext.tsx +28 -0
  232. package/src/native/tokens.ts +13 -0
  233. package/src/native.ts +39 -0
  234. package/src/polyfill-invoker-commands.ts +68 -0
  235. package/src/react-augment.d.ts +32 -0
  236. package/src/stories/DesignSystem/DarkMode.mdx +130 -0
  237. package/src/stories/ReactNative.mdx +121 -0
  238. package/src/types.ts +2 -0
  239. package/src/typings.d.ts +3 -0
  240. package/src/web-components/globals.ts +61 -0
  241. package/src/web-components/index.ts +12 -0
  242. package/src/web-components/interaction/BdsButton.mdx +106 -0
  243. package/src/web-components/interaction/BdsButton.stories.tsx +60 -0
  244. package/src/web-components/interaction/BdsPopover.mdx +120 -0
  245. package/src/web-components/interaction/BdsPopover.stories.tsx +70 -0
  246. package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
  247. package/src/web-components/interaction/BdsToastProvider.stories.tsx +73 -0
  248. package/src/web-components/interaction/bds-button.spec.ts +95 -0
  249. package/src/web-components/interaction/bds-button.ts +293 -0
  250. package/src/web-components/interaction/bds-popover.spec.ts +174 -0
  251. package/src/web-components/interaction/bds-popover.ts +229 -0
  252. package/src/web-components/interaction/bds-toast-provider.spec.ts +122 -0
  253. package/src/web-components/interaction/bds-toast-provider.ts +211 -0
  254. package/src/web-components/test/helpers.ts +22 -0
  255. package/src/web-components/ui/BdsAlert.mdx +90 -0
  256. package/src/web-components/ui/BdsAlert.stories.tsx +60 -0
  257. package/src/web-components/ui/BdsBadge.mdx +74 -0
  258. package/src/web-components/ui/BdsBadge.stories.tsx +37 -0
  259. package/src/web-components/ui/bds-alert.spec.ts +109 -0
  260. package/src/web-components/ui/bds-alert.ts +209 -0
  261. package/src/web-components/ui/bds-badge.spec.ts +51 -0
  262. package/src/web-components/ui/bds-badge.ts +88 -0
@@ -0,0 +1,94 @@
1
+ import { ReactNode } from 'react';
2
+ import { StyleSheet, View, Text, Pressable, StyleProp, ViewStyle } from 'react-native';
3
+ import { spacing, font, border, lineHeight } from '../../../native/tokens';
4
+ import { useTheme } from '../../../native/ThemeContext';
5
+
6
+ type AlertVariant = 'info' | 'success' | 'warning' | 'error';
7
+
8
+ interface AlertProps {
9
+ variant?: AlertVariant;
10
+ icon?: ReactNode;
11
+ title?: string;
12
+ children: ReactNode;
13
+ onDismiss?: () => void;
14
+ style?: StyleProp<ViewStyle>;
15
+ }
16
+
17
+ const styles = StyleSheet.create({
18
+ alert: {
19
+ flexDirection: 'row',
20
+ alignItems: 'flex-start',
21
+ padding: spacing.m,
22
+ borderRadius: border.radius.s,
23
+ gap: spacing.xs,
24
+ },
25
+ icon: {
26
+ flexShrink: 0,
27
+ marginTop: 2,
28
+ },
29
+ content: {
30
+ flex: 1,
31
+ gap: spacing.xxs,
32
+ },
33
+ title: {
34
+ fontSize: font.size.body,
35
+ fontFamily: font.family.body,
36
+ fontWeight: font.weight.semibold,
37
+ lineHeight: lineHeight.body,
38
+ },
39
+ body: {
40
+ fontSize: font.size.bodyS,
41
+ fontFamily: font.family.body,
42
+ lineHeight: lineHeight.bodyS,
43
+ },
44
+ dismiss: {
45
+ padding: spacing.xxs,
46
+ flexShrink: 0,
47
+ },
48
+ });
49
+
50
+ export function Alert({
51
+ variant = 'info',
52
+ icon,
53
+ title,
54
+ children,
55
+ onDismiss,
56
+ style,
57
+ }: Readonly<AlertProps>) {
58
+ const { colors } = useTheme();
59
+ const isUrgent = variant === 'error' || variant === 'warning';
60
+
61
+ const variantColors: Record<AlertVariant, { bg: string; text: string }> = {
62
+ info: { bg: colors.colorBlueSubtle, text: colors.colorOnBlueSubtle },
63
+ success: { bg: colors.colorSuccessSubtle, text: colors.colorOnSuccessSubtle },
64
+ warning: { bg: colors.colorOrangeSubtle, text: colors.colorOnOrangeSubtle },
65
+ error: { bg: colors.colorRedSubtle, text: colors.colorOnRedSubtle },
66
+ };
67
+
68
+ const { bg, text } = variantColors[variant];
69
+
70
+ return (
71
+ <View
72
+ accessibilityRole={isUrgent ? 'alert' : 'none'}
73
+ accessibilityLiveRegion={isUrgent ? 'assertive' : 'polite'}
74
+ style={[styles.alert, { backgroundColor: bg }, style]}
75
+ >
76
+ {icon && <View style={styles.icon}>{icon}</View>}
77
+ <View style={styles.content}>
78
+ {title && <Text style={[styles.title, { color: text }]}>{title}</Text>}
79
+ <Text style={[styles.body, { color: text }]}>{children}</Text>
80
+ </View>
81
+ {onDismiss && (
82
+ <Pressable
83
+ onPress={onDismiss}
84
+ style={styles.dismiss}
85
+ accessibilityLabel="Dismiss alert"
86
+ accessibilityRole="button"
87
+ hitSlop={8}
88
+ >
89
+ <Text style={{ color: text, fontSize: 16 }}>✕</Text>
90
+ </Pressable>
91
+ )}
92
+ </View>
93
+ );
94
+ }
@@ -3,7 +3,7 @@ import { useState } from 'react';
3
3
  import { Alert } from './Alert';
4
4
 
5
5
  const meta = {
6
- title: 'UI/Alert',
6
+ title: 'React/UI/Alert',
7
7
  component: Alert,
8
8
  argTypes: {
9
9
  variant: { control: 'select', options: ['info', 'success', 'warning', 'error'] },
@@ -1,14 +1,14 @@
1
1
  import { ReactNode } from 'react';
2
2
  import css from './Alert.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../types';
4
5
 
5
- interface AlertProps {
6
+ interface AlertProps extends WithClassName {
6
7
  variant?: 'info' | 'success' | 'warning' | 'error';
7
8
  icon?: ReactNode;
8
9
  title?: string;
9
10
  children: ReactNode;
10
11
  onDismiss?: () => void;
11
- className?: string;
12
12
  }
13
13
 
14
14
  export function Alert({
@@ -0,0 +1,50 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Avatar.native.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Avatar (React Native)
7
+
8
+ React Native avatar component displaying user initials on a coloured background. Derives initials from the `name` prop (first letter of each word, max 2).
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Avatar } from '@boostdev/components';
16
+
17
+ <Avatar name="John Doe" size="medium" />
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### All sizes
23
+ <Canvas of={Stories.AllSizes} />
24
+
25
+ ### With initials
26
+ <Canvas of={Stories.WithInitials} />
27
+
28
+ ### Small
29
+ <Canvas of={Stories.Small} />
30
+
31
+ ### Large
32
+ <Canvas of={Stories.Large} />
33
+
34
+ ### Single name
35
+ <Canvas of={Stories.SingleName} />
36
+
37
+ ## Props
38
+
39
+ <ArgTypes of={Stories} />
40
+
41
+ ## Props reference
42
+
43
+ | Prop | Type | Default | Description |
44
+ |------|------|---------|-------------|
45
+ | `name` | string | — | Full name — initials are derived from this |
46
+ | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Circle diameter |
47
+
48
+ ## Accessibility
49
+
50
+ - `accessibilityLabel` defaults to the `name` prop so screen readers announce the user's name
@@ -0,0 +1,47 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { ThemeProvider } from '../../../native/ThemeContext';
3
+ import { Avatar } from './Avatar.native';
4
+
5
+ function renderWithTheme(ui: React.ReactElement) {
6
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
7
+ }
8
+
9
+ describe('Avatar (native)', () => {
10
+ describe('initials fallback', () => {
11
+ it('has image role and accessible label on the container', () => {
12
+ renderWithTheme(<Avatar name="John Doe" />);
13
+ expect(screen.getByRole('img', { name: 'John Doe' })).toBeInTheDocument();
14
+ });
15
+
16
+ it('derives initials from a two-word name', () => {
17
+ renderWithTheme(<Avatar name="John Doe" />);
18
+ expect(screen.getByText('JD')).toBeInTheDocument();
19
+ });
20
+
21
+ it('uses a single-word name as one initial', () => {
22
+ renderWithTheme(<Avatar name="Alice" />);
23
+ expect(screen.getByText('A')).toBeInTheDocument();
24
+ });
25
+
26
+ it('uses the alt prop as accessible label when provided', () => {
27
+ renderWithTheme(<Avatar name="John Doe" alt="Profile picture" />);
28
+ expect(screen.getByRole('img', { name: 'Profile picture' })).toBeInTheDocument();
29
+ });
30
+ });
31
+
32
+ describe('image variant', () => {
33
+ it('has image role and accessible label on the container', () => {
34
+ renderWithTheme(<Avatar src="https://example.com/avatar.jpg" alt="Jane Smith" />);
35
+ expect(screen.getByRole('img', { name: 'Jane Smith' })).toBeInTheDocument();
36
+ });
37
+
38
+ it('does not duplicate the accessible label (regression: duplicate label bug)', () => {
39
+ // Previously, both the outer View and the inner Image had accessibilityLabel={label},
40
+ // causing screen readers to announce the name twice.
41
+ renderWithTheme(<Avatar src="https://example.com/avatar.jpg" alt="Jane Smith" />);
42
+ // Only one element should have aria-label="Jane Smith".
43
+ const labeled = screen.getAllByLabelText('Jane Smith');
44
+ expect(labeled).toHaveLength(1);
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { ThemeProvider } from '../../../native/ThemeContext';
4
+ import { Avatar } from './Avatar.native';
5
+
6
+ const meta = {
7
+ tags: ['!stable', 'alpha'],
8
+ title: 'React Native/UI/Avatar',
9
+ component: Avatar,
10
+ decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
11
+ parameters: { layout: 'centered' },
12
+ argTypes: {
13
+ size: { control: 'select', options: ['small', 'medium', 'large'] },
14
+ },
15
+ } satisfies Meta<typeof Avatar>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const WithInitials: Story = { args: { name: 'John Doe', size: 'medium' } };
21
+ export const Small: Story = { args: { name: 'Jane Smith', size: 'small' } };
22
+ export const Large: Story = { args: { name: 'Alex Brown', size: 'large' } };
23
+ export const SingleName: Story = { args: { name: 'Alice', size: 'medium' } };
24
+ export const AllSizes: Story = {
25
+ render: () => (
26
+ <ThemeProvider>
27
+ <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
28
+ {(['small', 'medium', 'large'] as const).map(s => (
29
+ <Avatar key={s} name="John Doe" size={s} />
30
+ ))}
31
+ </div>
32
+ </ThemeProvider>
33
+ ),
34
+ };
@@ -0,0 +1,85 @@
1
+ import { StyleSheet, View, Text, Image, StyleProp, ViewStyle } from 'react-native';
2
+ import { font } from '../../../native/tokens';
3
+ import { useTheme } from '../../../native/ThemeContext';
4
+
5
+ type AvatarSize = 'small' | 'medium' | 'large';
6
+
7
+ interface AvatarProps {
8
+ src?: string;
9
+ alt?: string;
10
+ name?: string;
11
+ size?: AvatarSize;
12
+ style?: StyleProp<ViewStyle>;
13
+ }
14
+
15
+ const sizeMap: Record<AvatarSize, { container: number; fontSize: number }> = {
16
+ small: { container: 32, fontSize: font.size.bodyS },
17
+ medium: { container: 48, fontSize: font.size.body },
18
+ large: { container: 72, fontSize: font.size.heading3 },
19
+ };
20
+
21
+ function getInitials(name: string): string {
22
+ return name
23
+ .split(' ')
24
+ .filter(Boolean)
25
+ .slice(0, 2)
26
+ .map(word => word[0].toUpperCase())
27
+ .join('');
28
+ }
29
+
30
+ const styles = StyleSheet.create({
31
+ container: {
32
+ borderRadius: 999,
33
+ overflow: 'hidden',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ },
37
+ image: {
38
+ width: '100%',
39
+ height: '100%',
40
+ },
41
+ initials: {
42
+ fontFamily: font.family.body,
43
+ fontWeight: font.weight.semibold,
44
+ },
45
+ });
46
+
47
+ export function Avatar({ src, alt, name, size = 'medium', style }: Readonly<AvatarProps>) {
48
+ const { colors } = useTheme();
49
+ const { container, fontSize } = sizeMap[size];
50
+ const label = alt ?? name ?? 'User avatar';
51
+
52
+ if (src) {
53
+ return (
54
+ <View
55
+ style={[styles.container, { width: container, height: container }, style]}
56
+ accessibilityLabel={label}
57
+ accessibilityRole="image"
58
+ >
59
+ <Image source={{ uri: src }} style={styles.image} />
60
+ </View>
61
+ );
62
+ }
63
+
64
+ const initials = name ? getInitials(name) : '';
65
+
66
+ return (
67
+ <View
68
+ style={[
69
+ styles.container,
70
+ { width: container, height: container, backgroundColor: colors.colorBlue },
71
+ style,
72
+ ]}
73
+ accessibilityLabel={label}
74
+ accessibilityRole="image"
75
+ >
76
+ <Text
77
+ style={[styles.initials, { fontSize, color: colors.colorOnBlue }]}
78
+ importantForAccessibility="no"
79
+ accessibilityElementsHidden={true}
80
+ >
81
+ {initials}
82
+ </Text>
83
+ </View>
84
+ );
85
+ }
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Avatar } from './Avatar';
3
3
 
4
4
  const meta = {
5
- title: 'UI/Avatar',
5
+ title: 'React/UI/Avatar',
6
6
  component: Avatar,
7
7
  argTypes: {
8
8
  size: { control: 'radio', options: ['small', 'medium', 'large'] },
@@ -1,12 +1,12 @@
1
1
  import css from './Avatar.module.css';
2
2
  import { cn } from '@boostdev/design-system-foundation';
3
+ import type { WithClassName } from '../../../types';
3
4
 
4
- interface AvatarProps {
5
+ interface AvatarProps extends WithClassName {
5
6
  src?: string;
6
7
  alt?: string;
7
8
  name?: string;
8
9
  size?: 'small' | 'medium' | 'large';
9
- className?: string;
10
10
  }
11
11
 
12
12
  function getInitials(name: string): string {
@@ -50,8 +50,8 @@ Override these on a parent element to customise the badge without touching globa
50
50
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
51
51
  </thead>
52
52
  <tbody>
53
- <tr><td>`--badge_color`</td><td>`var(--bds-cta--subtle)`</td><td>Background colour</td></tr>
54
- <tr><td>`--badge_on-color`</td><td>`var(--bds-on-cta--subtle)`</td><td>Text colour</td></tr>
53
+ <tr><td>`--badge_bg`</td><td>`var(--bds-color_blue)`</td><td>Background colour</td></tr>
54
+ <tr><td>`--badge_text`</td><td>`var(--bds-color_on-blue)`</td><td>Text colour</td></tr>
55
55
  </tbody>
56
56
  </table>
57
57
 
@@ -0,0 +1,54 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Badge.native.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Badge (React Native)
7
+
8
+ React Native badge component for short status labels and counts. Five semantic variants matching the web `Badge` API.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Badge } from '@boostdev/components';
16
+
17
+ <Badge variant="success">Active</Badge>
18
+ <Badge variant="error">3</Badge>
19
+ ```
20
+
21
+ ## Examples
22
+
23
+ ### All variants
24
+ <Canvas of={Stories.AllVariants} />
25
+
26
+ ### Primary
27
+ <Canvas of={Stories.Primary} />
28
+
29
+ ### Secondary
30
+ <Canvas of={Stories.Secondary} />
31
+
32
+ ### Success
33
+ <Canvas of={Stories.Success} />
34
+
35
+ ### Error
36
+ <Canvas of={Stories.Error} />
37
+
38
+ ### Warning
39
+ <Canvas of={Stories.Warning} />
40
+
41
+ ## Props
42
+
43
+ <ArgTypes of={Stories} />
44
+
45
+ ## Props reference
46
+
47
+ | Prop | Type | Default | Description |
48
+ |------|------|---------|-------------|
49
+ | `variant` | `"primary" \| "secondary" \| "success" \| "error" \| "warning"` | `"primary"` | Semantic colour |
50
+ | `children` | `ReactNode` | — | Badge label |
51
+
52
+ ## Accessibility
53
+
54
+ - Badge is decorative — wrap in a component with `accessibilityLabel` when colour alone conveys status (e.g. `accessibilityLabel="3 notifications"`)
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { ThemeProvider } from '../../../native/ThemeContext';
4
+ import { Badge } from './Badge.native';
5
+
6
+ const meta = {
7
+ tags: ['!stable', 'alpha'],
8
+ title: 'React Native/UI/Badge',
9
+ component: Badge,
10
+ decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
11
+ parameters: { layout: 'centered' },
12
+ argTypes: {
13
+ variant: { control: 'select', options: ['primary', 'secondary', 'success', 'error', 'warning'] },
14
+ },
15
+ } satisfies Meta<typeof Badge>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Primary: Story = { args: { children: 'Primary', variant: 'primary' } };
21
+ export const Secondary: Story = { args: { children: 'Secondary', variant: 'secondary' } };
22
+ export const Success: Story = { args: { children: 'Success', variant: 'success' } };
23
+ export const Error: Story = { args: { children: 'Error', variant: 'error' } };
24
+ export const Warning: Story = { args: { children: 'Warning', variant: 'warning' } };
25
+ export const AllVariants: Story = {
26
+ args: { children: '' },
27
+ render: () => (
28
+ <ThemeProvider>
29
+ <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
30
+ {(['primary', 'secondary', 'success', 'error', 'warning'] as const).map(v => (
31
+ <Badge key={v} variant={v}>{v}</Badge>
32
+ ))}
33
+ </div>
34
+ </ThemeProvider>
35
+ ),
36
+ };
@@ -0,0 +1,50 @@
1
+ import { ReactNode } from 'react';
2
+ import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
3
+ import { spacing, font } from '../../../native/tokens';
4
+ import { useTheme } from '../../../native/ThemeContext';
5
+
6
+ type BadgeVariant = 'primary' | 'secondary' | 'success' | 'error' | 'warning';
7
+
8
+ interface BadgeProps {
9
+ children: ReactNode;
10
+ variant?: BadgeVariant;
11
+ style?: StyleProp<ViewStyle>;
12
+ }
13
+
14
+ const styles = StyleSheet.create({
15
+ badge: {
16
+ alignSelf: 'flex-start',
17
+ flexDirection: 'row',
18
+ alignItems: 'center',
19
+ justifyContent: 'center',
20
+ paddingVertical: spacing.xxs,
21
+ paddingHorizontal: spacing.xs,
22
+ borderRadius: 999,
23
+ },
24
+ text: {
25
+ fontSize: font.size.bodyS,
26
+ fontFamily: font.family.body,
27
+ fontWeight: font.weight.semibold,
28
+ lineHeight: font.size.bodyS,
29
+ },
30
+ });
31
+
32
+ export function Badge({ children, variant = 'primary', style }: BadgeProps) {
33
+ const { colors } = useTheme();
34
+
35
+ const variantColors: Record<BadgeVariant, { bg: string; text: string }> = {
36
+ primary: { bg: colors.colorInteractive, text: colors.colorOnInteractive },
37
+ secondary: { bg: colors.colorBgSubtle, text: colors.colorOnBgSubtle },
38
+ success: { bg: colors.colorSuccess, text: colors.colorOnSuccess },
39
+ error: { bg: colors.colorError, text: colors.colorOnError },
40
+ warning: { bg: colors.colorWarning, text: colors.colorOnWarning },
41
+ };
42
+
43
+ const { bg, text } = variantColors[variant];
44
+
45
+ return (
46
+ <View style={[styles.badge, { backgroundColor: bg }, style]}>
47
+ <Text style={[styles.text, { color: text }]}>{children}</Text>
48
+ </View>
49
+ );
50
+ }
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Badge } from './Badge';
3
3
 
4
4
  const meta = {
5
- title: 'UI/Badge',
5
+ title: 'React/UI/Badge',
6
6
  component: Badge,
7
7
  argTypes: {
8
8
  variant: { control: 'select', options: ['primary', 'secondary', 'success', 'error', 'warning'] },
@@ -1,11 +1,11 @@
1
1
  import { ReactNode } from 'react';
2
2
  import css from './Badge.module.css';
3
3
  import { cn } from '@boostdev/design-system-foundation';
4
+ import type { WithClassName } from '../../../types';
4
5
 
5
- interface BadgeProps {
6
+ interface BadgeProps extends WithClassName {
6
7
  children: ReactNode;
7
8
  variant?: 'primary' | 'secondary' | 'success' | 'error' | 'warning';
8
- className?: string;
9
9
  }
10
10
 
11
11
  export function Badge({ children, variant = 'primary', className }: BadgeProps) {
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Breadcrumb } from './Breadcrumb';
3
3
 
4
4
  const meta = {
5
- title: 'UI/Breadcrumb',
5
+ title: 'React/UI/Breadcrumb',
6
6
  component: Breadcrumb,
7
7
  } satisfies Meta<typeof Breadcrumb>;
8
8
 
@@ -1,14 +1,14 @@
1
1
  import css from './Breadcrumb.module.css';
2
2
  import { cn } from '@boostdev/design-system-foundation';
3
+ import type { WithClassName } from '../../../types';
3
4
 
4
5
  export interface BreadcrumbItem {
5
6
  label: string;
6
7
  href?: string;
7
8
  }
8
9
 
9
- interface BreadcrumbProps {
10
+ interface BreadcrumbProps extends WithClassName {
10
11
  items: BreadcrumbItem[];
11
- className?: string;
12
12
  }
13
13
 
14
14
  export function Breadcrumb({ items, className }: Readonly<BreadcrumbProps>) {
@@ -26,6 +26,22 @@ Date picker calendar grid. Use standalone or as the popover content inside a dat
26
26
 
27
27
  <ArgTypes of={Stories} />
28
28
 
29
+ ## CSS variables
30
+
31
+
32
+ <table>
33
+ <thead>
34
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
35
+ </thead>
36
+ <tbody>
37
+ <tr><td>`--calendar_border`</td><td>`none`</td><td>Container border</td></tr>
38
+ <tr><td>`--calendar_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Container corner radius</td></tr>
39
+ <tr><td>`--calendar_shadow`</td><td>`var(--bds-shadow_s)`</td><td>Container box shadow</td></tr>
40
+ <tr><td>`--calendar_today-border`</td><td>`none`</td><td>Border on today's date cell</td></tr>
41
+ </tbody>
42
+ </table>
43
+
44
+
29
45
  ## Accessibility
30
46
 
31
47
  - Grid uses `role="grid"` with `role="gridcell"` for each day
@@ -13,7 +13,7 @@
13
13
  border-radius: var(--calendar_radius, var(--bdc-outline_radius));
14
14
  padding: var(--bds-space_m);
15
15
  box-shadow: var(--calendar_shadow, var(--bds-shadow_s));
16
- width: 20rem;
16
+ inline-size: 20rem;
17
17
  }
18
18
 
19
19
  .header {
@@ -33,8 +33,8 @@
33
33
  display: flex;
34
34
  align-items: center;
35
35
  justify-content: center;
36
- width: 2rem;
37
- height: 2rem;
36
+ inline-size: 2rem;
37
+ block-size: 2rem;
38
38
  border-radius: 50%;
39
39
  cursor: pointer;
40
40
  color: var(--bds-color_on-bg);
@@ -42,8 +42,8 @@
42
42
  }
43
43
 
44
44
  .navBtn svg {
45
- width: 1rem;
46
- height: 1rem;
45
+ inline-size: 1rem;
46
+ block-size: 1rem;
47
47
  }
48
48
 
49
49
  .navBtn:focus-visible {
@@ -59,7 +59,7 @@
59
59
 
60
60
  .grid {
61
61
  border-collapse: collapse;
62
- width: 100%;
62
+ inline-size: 100%;
63
63
  table-layout: fixed;
64
64
  }
65
65
 
@@ -80,7 +80,7 @@
80
80
  display: flex;
81
81
  align-items: center;
82
82
  justify-content: center;
83
- width: 100%;
83
+ inline-size: 100%;
84
84
  aspect-ratio: 1;
85
85
  border-radius: 50%;
86
86
  font-size: var(--bds-font_size--body--s);
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { Calendar } from './Calendar';
4
4
 
5
5
  const meta = {
6
- title: 'UI/Calendar',
6
+ title: 'React/UI/Calendar',
7
7
  component: Calendar,
8
8
  decorators: [
9
9
  Story => (