@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,62 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './Alert.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Alert (React Native)
7
-
8
- React Native alert component for inline status messages. Supports four semantic variants, an optional title, and a dismissible mode — matching the web `Alert` API.
9
-
10
- > **Status: alpha** — API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Alert } from '@boostdev/design-system-components';
16
-
17
- <Alert variant="success" title="Saved">
18
- Your changes have been saved successfully.
19
- </Alert>
20
- ```
21
-
22
- ## Examples
23
-
24
- ### All variants
25
- <Canvas of={Stories.AllVariants} />
26
-
27
- ### Info
28
- <Canvas of={Stories.Info} />
29
-
30
- ### Success
31
- <Canvas of={Stories.Success} />
32
-
33
- ### Warning
34
- <Canvas of={Stories.Warning} />
35
-
36
- ### Error
37
- <Canvas of={Stories.Error} />
38
-
39
- ### With title
40
- <Canvas of={Stories.WithTitle} />
41
-
42
- ### Dismissible
43
- <Canvas of={Stories.Dismissible} />
44
-
45
- ## Props
46
-
47
- <ArgTypes of={Stories} />
48
-
49
- ## Props reference
50
-
51
- | Prop | Type | Default | Description |
52
- |------|------|---------|-------------|
53
- | `variant` | `"info" \| "success" \| "warning" \| "error"` | `"info"` | Semantic colour and icon |
54
- | `title` | string | — | Optional heading above body content |
55
- | `onDismiss` | `() => void` | — | When set, a dismiss button is shown; called on press |
56
- | `children` | `ReactNode` | — | Alert body content |
57
-
58
- ## Accessibility
59
-
60
- - `error` and `warning` variants announce content as `assertive`
61
- - `info` and `success` variants announce content as `polite`
62
- - The dismiss button has a descriptive accessibility label
@@ -1,76 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/react';
2
- import { Text } from 'react-native';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Alert } from './Alert.native';
5
-
6
- function renderWithTheme(ui: React.ReactElement) {
7
- return render(<ThemeProvider>{ui}</ThemeProvider>);
8
- }
9
-
10
- describe('Alert (native)', () => {
11
- it('renders children text', () => {
12
- renderWithTheme(<Alert>Something happened</Alert>);
13
- expect(screen.getByText('Something happened')).toBeInTheDocument();
14
- });
15
-
16
- it('renders the title when provided', () => {
17
- renderWithTheme(<Alert title="Heads up">Details here</Alert>);
18
- expect(screen.getByText('Heads up')).toBeInTheDocument();
19
- expect(screen.getByText('Details here')).toBeInTheDocument();
20
- });
21
-
22
- it('does not render a title element when title is omitted', () => {
23
- renderWithTheme(<Alert>No title alert</Alert>);
24
- expect(screen.getByText('No title alert')).toBeInTheDocument();
25
- });
26
-
27
- it('renders an icon when provided', () => {
28
- renderWithTheme(
29
- <Alert icon={<Text>!</Text>}>With icon</Alert>,
30
- );
31
- expect(screen.getByText('!')).toBeInTheDocument();
32
- expect(screen.getByText('With icon')).toBeInTheDocument();
33
- });
34
-
35
- it('has alert role for error variant', () => {
36
- renderWithTheme(<Alert variant="error">Error message</Alert>);
37
- expect(screen.getByRole('alert')).toBeInTheDocument();
38
- });
39
-
40
- it('has alert role for warning variant', () => {
41
- renderWithTheme(<Alert variant="warning">Warning message</Alert>);
42
- expect(screen.getByRole('alert')).toBeInTheDocument();
43
- });
44
-
45
- it('does not have alert role for info variant', () => {
46
- renderWithTheme(<Alert variant="info">Info message</Alert>);
47
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
48
- });
49
-
50
- it('does not have alert role for success variant', () => {
51
- renderWithTheme(<Alert variant="success">Success message</Alert>);
52
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
53
- });
54
-
55
- it('renders a dismiss button when onDismiss is provided', () => {
56
- renderWithTheme(<Alert onDismiss={() => {}}>Dismissable</Alert>);
57
- expect(screen.getByRole('button', { name: 'Dismiss alert' })).toBeInTheDocument();
58
- });
59
-
60
- it('calls onDismiss when dismiss button is pressed', () => {
61
- const onDismiss = vi.fn();
62
- renderWithTheme(<Alert onDismiss={onDismiss}>Dismissable</Alert>);
63
- fireEvent.click(screen.getByRole('button', { name: 'Dismiss alert' }));
64
- expect(onDismiss).toHaveBeenCalledTimes(1);
65
- });
66
-
67
- it('does not render a dismiss button when onDismiss is omitted', () => {
68
- renderWithTheme(<Alert>No dismiss</Alert>);
69
- expect(screen.queryByRole('button', { name: 'Dismiss alert' })).not.toBeInTheDocument();
70
- });
71
-
72
- it('accepts custom style', () => {
73
- renderWithTheme(<Alert style={{ marginTop: 10 }}>Styled</Alert>);
74
- expect(screen.getByText('Styled')).toBeInTheDocument();
75
- });
76
- });
@@ -1,41 +0,0 @@
1
- import React from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Alert } from './Alert.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/UI/Alert',
9
- component: Alert,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'padded' },
12
- argTypes: {
13
- variant: { control: 'select', options: ['info', 'success', 'warning', 'error'] },
14
- },
15
- } satisfies Meta<typeof Alert>;
16
-
17
- export default meta;
18
- type Story = StoryObj<typeof meta>;
19
-
20
- export const Info: Story = { args: { children: 'This is an informational message.', variant: 'info' } };
21
- export const Success: Story = { args: { children: 'Your changes have been saved.', variant: 'success' } };
22
- export const Warning: Story = { args: { children: 'Please review before continuing.', variant: 'warning' } };
23
- export const Error: Story = { args: { children: 'Something went wrong. Please try again.', variant: 'error' } };
24
- export const WithTitle: Story = {
25
- args: { children: 'More details about the alert.', variant: 'info', title: 'Heads up' },
26
- };
27
- export const Dismissible: Story = {
28
- args: { children: 'You can dismiss this alert.', variant: 'success', onDismiss: () => {} },
29
- };
30
- export const AllVariants: Story = {
31
- args: { children: '' },
32
- render: () => (
33
- <div style={{ display: 'flex', flexDirection: 'column', gap: '12px', maxWidth: 400 }}>
34
- {(['info', 'success', 'warning', 'error'] as const).map(v => (
35
- <Alert key={v} variant={v} title={v.charAt(0).toUpperCase() + v.slice(1)}>
36
- This is a {v} alert.
37
- </Alert>
38
- ))}
39
- </div>
40
- ),
41
- };
@@ -1,94 +0,0 @@
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; border: string }> = {
62
- info: { bg: colors.colorBlueSubtle, text: colors.colorOnBlueSubtle, border: colors.colorBlue },
63
- success: { bg: colors.colorSuccessSubtle, text: colors.colorOnSuccessSubtle, border: colors.colorSuccess },
64
- warning: { bg: colors.colorOrangeSubtle, text: colors.colorOnOrangeSubtle, border: colors.colorWarning },
65
- error: { bg: colors.colorRedSubtle, text: colors.colorOnBg, border: colors.colorError },
66
- };
67
-
68
- const { bg, text, border: borderColor } = variantColors[variant];
69
-
70
- return (
71
- <View
72
- accessibilityRole={isUrgent ? 'alert' : 'none'}
73
- accessibilityLiveRegion={isUrgent ? 'assertive' : 'polite'}
74
- style={[styles.alert, { backgroundColor: bg, borderStartWidth: spacing.s, borderStartColor: borderColor }, 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
- }
@@ -1,50 +0,0 @@
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/design-system-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
@@ -1,47 +0,0 @@
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
- });
@@ -1,32 +0,0 @@
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 colorScheme="light"><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
- <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
27
- {(['small', 'medium', 'large'] as const).map(s => (
28
- <Avatar key={s} name="John Doe" size={s} />
29
- ))}
30
- </div>
31
- ),
32
- };
@@ -1,85 +0,0 @@
1
- import { StyleSheet, View, Text, Image, StyleProp, ViewStyle } from 'react-native';
2
- import { font, border } 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: border.radius.full,
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
- }
@@ -1,54 +0,0 @@
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/design-system-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"`)
@@ -1,45 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Badge } from './Badge.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('Badge (native)', () => {
10
- it('renders children text', () => {
11
- renderWithTheme(<Badge>New</Badge>);
12
- expect(screen.getByText('New')).toBeInTheDocument();
13
- });
14
-
15
- it('defaults to primary variant', () => {
16
- const { container } = renderWithTheme(<Badge>Default</Badge>);
17
- expect(screen.getByText('Default')).toBeInTheDocument();
18
- expect(container).toBeTruthy();
19
- });
20
-
21
- it('renders with secondary variant', () => {
22
- renderWithTheme(<Badge variant="secondary">Info</Badge>);
23
- expect(screen.getByText('Info')).toBeInTheDocument();
24
- });
25
-
26
- it('renders with success variant', () => {
27
- renderWithTheme(<Badge variant="success">Active</Badge>);
28
- expect(screen.getByText('Active')).toBeInTheDocument();
29
- });
30
-
31
- it('renders with error variant', () => {
32
- renderWithTheme(<Badge variant="error">Failed</Badge>);
33
- expect(screen.getByText('Failed')).toBeInTheDocument();
34
- });
35
-
36
- it('renders with warning variant', () => {
37
- renderWithTheme(<Badge variant="warning">Pending</Badge>);
38
- expect(screen.getByText('Pending')).toBeInTheDocument();
39
- });
40
-
41
- it('accepts custom style', () => {
42
- renderWithTheme(<Badge style={{ marginTop: 10 }}>Styled</Badge>);
43
- expect(screen.getByText('Styled')).toBeInTheDocument();
44
- });
45
- });
@@ -1,34 +0,0 @@
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 colorScheme="light"><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
- <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
29
- {(['primary', 'secondary', 'success', 'error', 'warning'] as const).map(v => (
30
- <Badge key={v} variant={v}>{v}</Badge>
31
- ))}
32
- </div>
33
- ),
34
- };
@@ -1,50 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
3
- import { spacing, font, border } 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: border.radius.full,
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
- }