@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,91 +0,0 @@
1
- import { Meta, Canvas, ArgTypes, Controls } from '@storybook/blocks';
2
- import * as Stories from './Button.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Button (React Native)
7
-
8
- React Native button component using `Pressable`. Matches the web `Button` API as closely as possible, replacing `onClick` with `onPress` and `aria-label` with `accessibilityLabel`.
9
-
10
- > **Status: alpha** — API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Button } from '@boostdev/design-system-components';
16
-
17
- <Button variant="default" size="medium" onPress={() => console.log('pressed')}>
18
- Save
19
- </Button>
20
- ```
21
-
22
- Wrap your tree in `ThemeProvider` (once, at the app root):
23
-
24
- ```tsx
25
- import { ThemeProvider } from '@boostdev/design-system-components/native/ThemeContext';
26
-
27
- export default function App() {
28
- return <ThemeProvider><YourApp /></ThemeProvider>;
29
- }
30
- ```
31
-
32
- ## Playground
33
-
34
- <Canvas of={Stories.Playground} />
35
- <Controls of={Stories.Playground} />
36
-
37
- ## Variants
38
-
39
- <Canvas of={Stories.Default} />
40
- <Canvas of={Stories.Outline} />
41
-
42
- ## Sizes
43
-
44
- <Canvas of={Stories.Small} />
45
- <Canvas of={Stories.Medium} />
46
- <Canvas of={Stories.Large} />
47
-
48
- ## Icons
49
-
50
- <Canvas of={Stories.WithIconStart} />
51
- <Canvas of={Stories.WithIconEnd} />
52
-
53
- ### Icon-only (1:1 aspect ratio)
54
-
55
- Set `isIconOnly` to render a square button with equal `spacing.xs` padding on both axes. Composable with any `variant`. Always pair with `accessibilityLabel` for assistive tech.
56
-
57
- <Canvas of={Stories.IconOnly} />
58
- <Canvas of={Stories.IconOnlyOutline} />
59
- <Canvas of={Stories.AllIconOnly} />
60
-
61
- ## All variants × sizes
62
-
63
- <Canvas of={Stories.AllVariants} />
64
-
65
- ## Disabled
66
-
67
- <Canvas of={Stories.Disabled} />
68
-
69
- ## Props
70
-
71
- <ArgTypes of={Stories} />
72
-
73
- ## Props reference
74
-
75
- | Prop | Type | Default | Description |
76
- |------|------|---------|-------------|
77
- | `variant` | `"default" \| "outline"` | `"default"` | Visual style (`"ghost"` is a deprecated alias for `"outline"`) |
78
- | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Height and horizontal padding |
79
- | `disabled` | boolean | `false` | Prevents interaction, reduces opacity |
80
- | `onPress` | `() => void` | — | Called when the button is pressed |
81
- | `iconStart` | `ReactNode` | — | Icon before the label |
82
- | `iconEnd` | `ReactNode` | — | Icon after the label |
83
- | `isIconOnly` | boolean | `false` | 1:1 aspect ratio with equal `spacing.xs` padding on both axes. Requires `accessibilityLabel`. |
84
- | `accessibilityLabel` | string | — | Screen-reader label (required for icon-only buttons) |
85
- | `style` | `StyleProp<ViewStyle>` | — | Override container styles |
86
-
87
- ## Accessibility
88
-
89
- - Provide `accessibilityLabel` for icon-only buttons
90
- - `disabled` communicates the disabled state to assistive technologies
91
- - Pressed state reduces opacity to 0.75 for visual feedback
@@ -1,55 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Button } from './Button.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('Button (native)', () => {
10
- it('has button role', () => {
11
- renderWithTheme(<Button>Save</Button>);
12
- expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument();
13
- });
14
-
15
- it('exposes disabled state', () => {
16
- renderWithTheme(<Button disabled>Save</Button>);
17
- expect(screen.getByRole('button', { name: 'Save' })).toHaveAttribute('aria-disabled', 'true');
18
- });
19
-
20
- it('renders with icon-start without throwing', () => {
21
- // Icon wrappers have importantForAccessibility="no-hide-descendants" (Android)
22
- // and accessibilityElementsHidden={true} (iOS) so decorative icons are hidden on device.
23
- const icon = <span data-testid="icon">★</span>;
24
- renderWithTheme(<Button iconStart={icon}>Save</Button>);
25
- expect(screen.getByRole('button')).toBeInTheDocument();
26
- expect(screen.getByTestId('icon')).toBeInTheDocument();
27
- });
28
-
29
- it('renders with icon-end without throwing', () => {
30
- const icon = <span data-testid="icon-end">→</span>;
31
- renderWithTheme(<Button iconEnd={icon}>Next</Button>);
32
- expect(screen.getByRole('button')).toBeInTheDocument();
33
- expect(screen.getByTestId('icon-end')).toBeInTheDocument();
34
- });
35
-
36
- it('renders icon-only button with accessibilityLabel and icon as children (not wrapped in Text)', () => {
37
- renderWithTheme(
38
- <Button isIconOnly accessibilityLabel="Confirm">
39
- <span data-testid="icon-only">✓</span>
40
- </Button>
41
- );
42
- expect(screen.getByRole('button', { name: 'Confirm' })).toBeInTheDocument();
43
- expect(screen.getByTestId('icon-only')).toBeInTheDocument();
44
- });
45
-
46
- it('renders icon-only with outline variant', () => {
47
- renderWithTheme(
48
- <Button isIconOnly variant="outline" accessibilityLabel="Confirm">
49
- <span data-testid="icon-only-outline">✓</span>
50
- </Button>
51
- );
52
- expect(screen.getByRole('button', { name: 'Confirm' })).toBeInTheDocument();
53
- expect(screen.getByTestId('icon-only-outline')).toBeInTheDocument();
54
- });
55
- });
@@ -1,141 +0,0 @@
1
- import React from 'react';
2
- import type { ReactNode } from 'react';
3
- import type { Meta, StoryObj } from '@storybook/react';
4
- import { ThemeProvider } from '../../../native/ThemeContext';
5
- import { Button, type ButtonProps } from './Button.native';
6
-
7
- const CheckIcon = (
8
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" aria-hidden>
9
- <path d="M12 0a12 12 0 1 0 12 12A12 12 0 0 0 12 0Zm6.93 8.2-6.85 9.29a1 1 0 0 1-1.43.19l-4.89-3.91a1 1 0 0 1-.15-1.41A1 1 0 0 1 7 12.21l4.08 3.26L17.32 7a1 1 0 0 1 1.39-.21 1 1 0 0 1 .22 1.41Z" fill="currentColor"/>
10
- </svg>
11
- );
12
-
13
- const ArrowRight = <span aria-hidden>→</span>;
14
- const ArrowLeft = <span aria-hidden>←</span>;
15
-
16
- const iconMap = {
17
- none: undefined,
18
- check: CheckIcon,
19
- arrowRight: ArrowRight,
20
- arrowLeft: ArrowLeft,
21
- };
22
-
23
- type IconKey = keyof typeof iconMap;
24
-
25
- type ButtonStoryArgs = Omit<ButtonProps, 'iconStart' | 'iconEnd' | 'children'> & {
26
- iconStart?: IconKey;
27
- iconEnd?: IconKey;
28
- children?: ReactNode;
29
- };
30
-
31
- const meta: Meta<ButtonStoryArgs> = {
32
- tags: ['!stable', 'alpha'],
33
- title: 'React Native/Interaction/Button',
34
- component: Button,
35
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
36
- parameters: { layout: 'centered' },
37
- argTypes: {
38
- variant: {
39
- control: 'select',
40
- options: ['default', 'outline'],
41
- description: 'Visual style. `default` is filled; `outline` is transparent with a border.',
42
- table: { defaultValue: { summary: 'default' } },
43
- },
44
- size: {
45
- control: 'select',
46
- options: ['small', 'medium', 'large'],
47
- description: 'Height + horizontal padding.',
48
- table: { defaultValue: { summary: 'medium' } },
49
- },
50
- iconStart: {
51
- control: 'select',
52
- options: Object.keys(iconMap) as IconKey[],
53
- mapping: iconMap,
54
- description: 'Icon rendered before the label.',
55
- },
56
- iconEnd: {
57
- control: 'select',
58
- options: Object.keys(iconMap) as IconKey[],
59
- mapping: iconMap,
60
- description: 'Icon rendered after the label.',
61
- },
62
- isIconOnly: {
63
- control: 'boolean',
64
- description: 'Forces 1:1 aspect ratio with minimal xs padding. Composable with any `variant`. Requires `accessibilityLabel`.',
65
- table: { defaultValue: { summary: 'false' } },
66
- },
67
- disabled: { control: 'boolean' },
68
- onPress: { action: 'pressed' },
69
- },
70
- args: { variant: 'default', size: 'medium' },
71
- };
72
-
73
- export default meta;
74
- type Story = StoryObj<ButtonStoryArgs>;
75
-
76
- export const Playground: Story = {
77
- args: { children: 'Button', iconStart: 'none', iconEnd: 'none' },
78
- };
79
-
80
- export const Default: Story = { args: { children: 'Default' } };
81
- export const Outline: Story = { args: { children: 'Outline', variant: 'outline' } };
82
- export const Small: Story = { args: { children: 'Small', size: 'small' } };
83
- export const Medium: Story = { args: { children: 'Medium', size: 'medium' } };
84
- export const Large: Story = { args: { children: 'Large', size: 'large' } };
85
- export const Disabled: Story = { args: { children: 'Disabled', disabled: true } };
86
-
87
- export const WithIconStart: Story = { args: { children: 'Confirm', iconStart: 'check' } };
88
- export const WithIconEnd: Story = { args: { children: 'Continue', iconEnd: 'arrowRight' } };
89
-
90
- export const IconOnly: Story = {
91
- args: {
92
- isIconOnly: true,
93
- accessibilityLabel: 'Confirm',
94
- children: CheckIcon,
95
- },
96
- };
97
-
98
- export const IconOnlyOutline: Story = {
99
- args: {
100
- variant: 'outline',
101
- isIconOnly: true,
102
- accessibilityLabel: 'Confirm',
103
- children: CheckIcon,
104
- },
105
- };
106
-
107
- export const AllVariants: Story = {
108
- render: () => (
109
- <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
110
- {(['default', 'outline'] as const).map(v => (
111
- <div key={v} style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
112
- {(['small', 'medium', 'large'] as const).map(s => (
113
- <Button key={s} variant={v} size={s}>{v} {s}</Button>
114
- ))}
115
- </div>
116
- ))}
117
- </div>
118
- ),
119
- };
120
-
121
- export const AllIconOnly: Story = {
122
- render: () => (
123
- <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
124
- {(['default', 'outline'] as const).map(v => (
125
- <div key={v} style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
126
- {(['small', 'medium', 'large'] as const).map(s => (
127
- <Button
128
- key={s}
129
- variant={v}
130
- size={s}
131
- isIconOnly
132
- accessibilityLabel={`${v} ${s}`}
133
- >
134
- {CheckIcon}
135
- </Button>
136
- ))}
137
- </div>
138
- ))}
139
- </div>
140
- ),
141
- };
@@ -1,105 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { StyleSheet, Pressable, Text, View, StyleProp, ViewStyle } from 'react-native';
3
- import { spacing, font, border } from '../../../native/tokens';
4
- import { useTheme } from '../../../native/ThemeContext';
5
-
6
- type ButtonVariant = 'default' | 'outline' | 'ghost'; // 'ghost' is deprecated, use 'outline'
7
- type ButtonSize = 'small' | 'medium' | 'large';
8
-
9
- export interface ButtonProps {
10
- children?: ReactNode;
11
- variant?: ButtonVariant;
12
- size?: ButtonSize;
13
- iconStart?: ReactNode;
14
- iconEnd?: ReactNode;
15
- disabled?: boolean;
16
- /** Forces a 1:1 aspect ratio with minimal padding for icon-only buttons. Requires `accessibilityLabel`. */
17
- isIconOnly?: boolean;
18
- onPress?: () => void;
19
- accessibilityLabel?: string;
20
- style?: StyleProp<ViewStyle>;
21
- }
22
-
23
- const sizeTokens: Record<ButtonSize, { height: number; px: number; fontSize: number; radius: number }> = {
24
- small: { height: 36, px: spacing.m, fontSize: font.size.bodyS, radius: border.radius.full },
25
- medium: { height: 48, px: spacing.m, fontSize: font.size.body, radius: border.radius.full },
26
- large: { height: 56, px: spacing.xxl, fontSize: font.size.heading3, radius: border.radius.full },
27
- };
28
-
29
- const styles = StyleSheet.create({
30
- pressable: {
31
- flexDirection: 'row',
32
- alignItems: 'center',
33
- justifyContent: 'center',
34
- gap: spacing.xs,
35
- alignSelf: 'flex-start',
36
- },
37
- label: {
38
- fontFamily: font.family.body,
39
- fontWeight: font.weight.semibold,
40
- includeFontPadding: false,
41
- },
42
- });
43
-
44
- export function Button({
45
- children,
46
- variant = 'default',
47
- size = 'medium',
48
- iconStart,
49
- iconEnd,
50
- disabled = false,
51
- isIconOnly = false,
52
- onPress,
53
- accessibilityLabel,
54
- style,
55
- }: Readonly<ButtonProps>) {
56
- const { colors } = useTheme();
57
- const { height, px, fontSize, radius } = sizeTokens[size];
58
-
59
- // Color model: filled = interactive bg, outline = transparent bg + interactive_on-bg border/text
60
- const primaryColor = colors.colorInteractive;
61
- const onPrimaryColor = colors.colorOnInteractive;
62
- // Outline uses colorInteractiveOnBg for text/border — colorInteractive may not meet 3:1 on white
63
- const outlineTextColor = colors.colorInteractiveOnBg;
64
-
65
- const isOutline = variant === 'outline' || variant === 'ghost';
66
- const containerStyle: ViewStyle = {
67
- height,
68
- paddingHorizontal: isIconOnly ? spacing.xs : px,
69
- borderRadius: radius,
70
- opacity: disabled ? 0.4 : 1,
71
- // Icon-only: square with equal xs padding on both axes.
72
- ...(isIconOnly && { aspectRatio: 1, paddingVertical: spacing.xs }),
73
- ...(variant === 'default' && { backgroundColor: primaryColor }),
74
- ...(isOutline && {
75
- backgroundColor: 'transparent',
76
- borderWidth: 1,
77
- borderColor: outlineTextColor,
78
- }),
79
- };
80
-
81
- const textColor = variant === 'default' ? onPrimaryColor : outlineTextColor;
82
-
83
- return (
84
- <Pressable
85
- onPress={onPress}
86
- disabled={disabled}
87
- accessibilityRole="button"
88
- accessibilityLabel={accessibilityLabel}
89
- accessibilityState={{ disabled }}
90
- style={({ pressed }) => [styles.pressable, containerStyle, pressed && { opacity: 0.75 }, style]}
91
- >
92
- {iconStart && <View importantForAccessibility="no-hide-descendants" accessibilityElementsHidden={true}>{iconStart}</View>}
93
- {children && (
94
- isIconOnly
95
- ? children
96
- : (
97
- <Text style={[styles.label, { fontSize, color: textColor }]}>
98
- {children}
99
- </Text>
100
- )
101
- )}
102
- {iconEnd && <View importantForAccessibility="no-hide-descendants">{iconEnd}</View>}
103
- </Pressable>
104
- );
105
- }
@@ -1,61 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './Dialog.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Dialog (React Native)
7
-
8
- A modal dialog overlay that centers content on screen with a semi-transparent backdrop. Built on React Native `Modal` with `transparent` background.
9
-
10
- > **Status: alpha** — API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Dialog } from '@boostdev/design-system-components';
16
-
17
- const [open, setOpen] = useState(false);
18
-
19
- <Button onPress={() => setOpen(true)}>Open</Button>
20
- <Dialog isOpen={open} onClose={() => setOpen(false)} title="Confirm">
21
- <Text>Are you sure?</Text>
22
- </Dialog>
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
- ### Without title
41
- <Canvas of={Stories.WithoutTitle} />
42
-
43
- ## Props
44
-
45
- <ArgTypes of={Stories} />
46
-
47
- ## Props reference
48
-
49
- | Prop | Type | Default | Description |
50
- |------|------|---------|-------------|
51
- | `isOpen` | `boolean` | `false` | Controls visibility of the dialog |
52
- | `onClose` | `() => void` | — | Called when backdrop or close button is pressed |
53
- | `title` | `string` | — | Optional title displayed at the top |
54
- | `children` | `ReactNode` | — | Dialog body content |
55
- | `style` | `StyleProp<ViewStyle>` | — | Override panel styles |
56
-
57
- ## Accessibility
58
-
59
- - Backdrop press and close button both call `onClose`
60
- - Close button has `accessibilityLabel="Close dialog"`
61
- - Android back button triggers `onRequestClose` which calls `onClose`
@@ -1,73 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Dialog } from './Dialog.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('Dialog (native)', () => {
10
- it('renders children when open', () => {
11
- renderWithTheme(
12
- <Dialog isOpen>
13
- <span>Dialog body</span>
14
- </Dialog>,
15
- );
16
- expect(screen.getByText('Dialog body')).toBeInTheDocument();
17
- });
18
-
19
- it('does not render children when closed', () => {
20
- renderWithTheme(
21
- <Dialog isOpen={false}>
22
- <span>Hidden content</span>
23
- </Dialog>,
24
- );
25
- expect(screen.queryByText('Hidden content')).not.toBeInTheDocument();
26
- });
27
-
28
- it('renders the title when provided', () => {
29
- renderWithTheme(
30
- <Dialog isOpen title="Confirm action">
31
- <span>Body</span>
32
- </Dialog>,
33
- );
34
- expect(screen.getByText('Confirm action')).toBeInTheDocument();
35
- });
36
-
37
- it('renders a close button with accessible label', () => {
38
- renderWithTheme(
39
- <Dialog isOpen onClose={() => {}}>
40
- <span>Body</span>
41
- </Dialog>,
42
- );
43
- const closeButtons = screen.getAllByRole('button', { name: 'Close dialog' });
44
- expect(closeButtons.length).toBeGreaterThanOrEqual(1);
45
- });
46
-
47
- it('calls onClose when close button is pressed', () => {
48
- const onClose = vi.fn();
49
- renderWithTheme(
50
- <Dialog isOpen onClose={onClose}>
51
- <span>Body</span>
52
- </Dialog>,
53
- );
54
- const closeButtons = screen.getAllByRole('button', { name: 'Close dialog' });
55
- // The inner close button (not the backdrop)
56
- const innerClose = closeButtons[closeButtons.length - 1];
57
- fireEvent.click(innerClose);
58
- expect(onClose).toHaveBeenCalledTimes(1);
59
- });
60
-
61
- it('calls onClose when backdrop is pressed', () => {
62
- const onClose = vi.fn();
63
- renderWithTheme(
64
- <Dialog isOpen onClose={onClose}>
65
- <span>Body</span>
66
- </Dialog>,
67
- );
68
- const closeButtons = screen.getAllByRole('button', { name: 'Close dialog' });
69
- // The first one is the backdrop
70
- fireEvent.click(closeButtons[0]);
71
- expect(onClose).toHaveBeenCalledTimes(1);
72
- });
73
- });
@@ -1,53 +0,0 @@
1
- import React, { useState } 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 { Dialog } from './Dialog.native';
6
- import { Button } from '../Button/Button.native';
7
-
8
- const meta = {
9
- tags: ['!stable', 'alpha'],
10
- title: 'React Native/Interaction/Dialog',
11
- component: Dialog,
12
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
13
- parameters: { layout: 'centered' },
14
- } satisfies Meta<typeof Dialog>;
15
-
16
- export default meta;
17
- type Story = StoryObj<typeof meta>;
18
-
19
- function DialogDemo() {
20
- const [open, setOpen] = useState(false);
21
- return (
22
- <>
23
- <Button onPress={() => setOpen(true)}>Open Dialog</Button>
24
- <Dialog isOpen={open} onClose={() => setOpen(false)} title="Confirmation">
25
- <Text>Are you sure you want to proceed with this action?</Text>
26
- <View style={{ flexDirection: 'row', gap: 8, marginTop: 16 }}>
27
- <Button variant="outline" onPress={() => setOpen(false)}>Cancel</Button>
28
- <Button onPress={() => setOpen(false)}>Confirm</Button>
29
- </View>
30
- </Dialog>
31
- </>
32
- );
33
- }
34
-
35
- function DialogWithoutTitleDemo() {
36
- const [open, setOpen] = useState(false);
37
- return (
38
- <>
39
- <Button onPress={() => setOpen(true)}>Open Dialog (no title)</Button>
40
- <Dialog isOpen={open} onClose={() => setOpen(false)}>
41
- <Text>This dialog has no title, just content.</Text>
42
- </Dialog>
43
- </>
44
- );
45
- }
46
-
47
- export const Default: Story = {
48
- render: () => <DialogDemo />,
49
- };
50
-
51
- export const WithoutTitle: Story = {
52
- render: () => <DialogWithoutTitleDemo />,
53
- };