@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,44 +0,0 @@
1
- import React, { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../../native/ThemeContext';
4
- import { Radio } from './Radio.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/Form/Radio',
9
- component: Radio,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'padded' },
12
- } satisfies Meta<typeof Radio>;
13
-
14
- export default meta;
15
- type Story = StoryObj<typeof meta>;
16
-
17
- export const Unchecked: Story = { args: { label: 'Option A', name: 'option', checked: false } };
18
- export const Checked: Story = { args: { label: 'Option A', name: 'option', checked: true } };
19
- export const Disabled: Story = { args: { label: 'Option A', name: 'option', disabled: true } };
20
- export const WithError: Story = {
21
- args: { label: 'Option A', name: 'option', error: 'Please select an option.' },
22
- };
23
- export const WithHint: Story = {
24
- args: { label: 'Option A', name: 'option', hint: 'This is the recommended option.' },
25
- };
26
- export const Group: Story = {
27
- render: () => {
28
- const [selected, setSelected] = useState<string | null>(null);
29
- const options = ['Option A', 'Option B', 'Option C'];
30
- return (
31
- <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
32
- {options.map(o => (
33
- <Radio
34
- key={o}
35
- label={o}
36
- name="group"
37
- checked={selected === o}
38
- onChange={() => setSelected(o)}
39
- />
40
- ))}
41
- </div>
42
- );
43
- },
44
- };
@@ -1,79 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { StyleSheet, View, Pressable, StyleProp, ViewStyle } from 'react-native';
3
- import { spacing } from '../../../../native/tokens';
4
- import { useTheme } from '../../../../native/ThemeContext';
5
- import { Label } from '../atoms/Label.native';
6
- import { Message } from '../atoms/Message.native';
7
-
8
- interface RadioProps {
9
- label: ReactNode;
10
- name: string;
11
- checked?: boolean;
12
- onChange?: () => void;
13
- disabled?: boolean;
14
- error?: string;
15
- hint?: string;
16
- style?: StyleProp<ViewStyle>;
17
- }
18
-
19
- const DOT = 22;
20
- const INNER = 10;
21
-
22
- const styles = StyleSheet.create({
23
- container: {
24
- gap: spacing.xxs,
25
- },
26
- row: {
27
- flexDirection: 'row',
28
- alignItems: 'center',
29
- gap: spacing.xs,
30
- minHeight: 44,
31
- },
32
- outer: {
33
- width: DOT,
34
- height: DOT,
35
- borderRadius: DOT / 2,
36
- borderWidth: 2,
37
- alignItems: 'center',
38
- justifyContent: 'center',
39
- },
40
- inner: {
41
- width: INNER,
42
- height: INNER,
43
- borderRadius: INNER / 2,
44
- },
45
- });
46
-
47
- export function Radio({
48
- label,
49
- name,
50
- checked = false,
51
- onChange,
52
- disabled = false,
53
- error,
54
- hint,
55
- style,
56
- }: RadioProps) {
57
- const { colors } = useTheme();
58
- const borderColor = error ? colors.colorError : checked ? colors.colorInteractive : colors.colorOnBg;
59
-
60
- return (
61
- <View style={[styles.container, style]}>
62
- <Pressable
63
- onPress={() => !disabled && onChange?.()}
64
- accessibilityRole="radio"
65
- accessibilityLabel={typeof label === 'string' ? label : name}
66
- accessibilityState={{ checked, disabled }}
67
- disabled={disabled}
68
- style={({ pressed }) => [styles.row, pressed && { opacity: 0.7 }, disabled && { opacity: 0.4 }]}
69
- >
70
- <View style={[styles.outer, { borderColor }]}>
71
- {checked && <View style={[styles.inner, { backgroundColor: colors.colorInteractive }]} />}
72
- </View>
73
- <Label label={label} />
74
- </Pressable>
75
- <Message type="error" message={error} />
76
- <Message type="hint" message={hint} />
77
- </View>
78
- );
79
- }
@@ -1,79 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './RadioGroup.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # RadioGroup (React Native)
7
-
8
- Groups related radio buttons under a common legend with optional hint and error messages. Uses `accessibilityRole="radiogroup"` for proper screen reader semantics.
9
-
10
- > **Status: alpha** -- API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { RadioGroup, Radio } from '@boostdev/design-system-components';
16
- import { useState } from 'react';
17
-
18
- function Example() {
19
- const [selected, setSelected] = useState('standard');
20
-
21
- return (
22
- <RadioGroup legend="Shipping method">
23
- <Radio
24
- label="Standard"
25
- name="shipping"
26
- checked={selected === 'standard'}
27
- onChange={() => setSelected('standard')}
28
- />
29
- <Radio
30
- label="Express"
31
- name="shipping"
32
- checked={selected === 'express'}
33
- onChange={() => setSelected('express')}
34
- />
35
- </RadioGroup>
36
- );
37
- }
38
- ```
39
-
40
- ## Examples
41
-
42
- ### Default
43
- <Canvas of={Stories.Default} />
44
-
45
- ### Required
46
- <Canvas of={Stories.Required} />
47
-
48
- ### With hint
49
- <Canvas of={Stories.WithHint} />
50
-
51
- ### With error
52
- <Canvas of={Stories.WithError} />
53
-
54
- ### Disabled
55
- <Canvas of={Stories.Disabled} />
56
-
57
- ### Interactive
58
- <Canvas of={Stories.Interactive} />
59
-
60
- ## Props
61
-
62
- <ArgTypes of={Stories} />
63
-
64
- ## Props reference
65
-
66
- | Prop | Type | Default | Description |
67
- |------|------|---------|-------------|
68
- | `legend` | string | -- | Group label displayed above the radio buttons |
69
- | `children` | ReactNode | -- | Radio components |
70
- | `error` | string | -- | Error message displayed below the group |
71
- | `hint` | string | -- | Helper text displayed below the group |
72
- | `required` | boolean | -- | Appends asterisk to legend |
73
- | `disabled` | boolean | `false` | Reduces opacity of the entire group |
74
-
75
- ## Accessibility
76
-
77
- - Uses `accessibilityRole="radiogroup"` for proper grouping semantics
78
- - The group is labelled via `accessibilityLabel` matching the legend text
79
- - Error messages use `accessibilityRole="alert"` for screen reader announcement
@@ -1,66 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../../native/ThemeContext';
3
- import { RadioGroup } from './RadioGroup.native';
4
- import { Radio } from '../Radio/Radio.native';
5
-
6
- function renderWithTheme(ui: React.ReactElement) {
7
- return render(<ThemeProvider>{ui}</ThemeProvider>);
8
- }
9
-
10
- describe('RadioGroup (native)', () => {
11
- it('renders the legend text', () => {
12
- renderWithTheme(
13
- <RadioGroup legend="Shipping method">
14
- <Radio label="Standard" name="shipping" />
15
- </RadioGroup>,
16
- );
17
- expect(screen.getByText('Shipping method')).toBeInTheDocument();
18
- });
19
-
20
- it('has radiogroup role', () => {
21
- renderWithTheme(
22
- <RadioGroup legend="Shipping">
23
- <Radio label="Standard" name="shipping" />
24
- </RadioGroup>,
25
- );
26
- expect(screen.getByRole('radiogroup')).toBeInTheDocument();
27
- });
28
-
29
- it('renders children', () => {
30
- renderWithTheme(
31
- <RadioGroup legend="Options">
32
- <Radio label="Option A" name="opt" />
33
- <Radio label="Option B" name="opt" />
34
- </RadioGroup>,
35
- );
36
- expect(screen.getByText('Option A')).toBeInTheDocument();
37
- expect(screen.getByText('Option B')).toBeInTheDocument();
38
- });
39
-
40
- it('appends asterisk when required', () => {
41
- renderWithTheme(
42
- <RadioGroup legend="Payment" required>
43
- <Radio label="Card" name="payment" />
44
- </RadioGroup>,
45
- );
46
- expect(screen.getByText('*', { exact: false })).toBeInTheDocument();
47
- });
48
-
49
- it('renders error message', () => {
50
- renderWithTheme(
51
- <RadioGroup legend="Shipping" error="Please select one.">
52
- <Radio label="Standard" name="shipping" />
53
- </RadioGroup>,
54
- );
55
- expect(screen.getByRole('alert')).toHaveTextContent('Please select one.');
56
- });
57
-
58
- it('renders hint message', () => {
59
- renderWithTheme(
60
- <RadioGroup legend="Shipping" hint="Choose your preferred method.">
61
- <Radio label="Standard" name="shipping" />
62
- </RadioGroup>,
63
- );
64
- expect(screen.getByText('Choose your preferred method.')).toBeInTheDocument();
65
- });
66
- });
@@ -1,96 +0,0 @@
1
- import React, { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../../native/ThemeContext';
4
- import { RadioGroup } from './RadioGroup.native';
5
- import { Radio } from '../Radio/Radio.native';
6
-
7
- const meta = {
8
- tags: ['!stable', 'alpha'],
9
- title: 'React Native/Form/RadioGroup',
10
- component: RadioGroup,
11
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
12
- parameters: { layout: 'padded' },
13
- } satisfies Meta<typeof RadioGroup>;
14
-
15
- export default meta;
16
- type Story = StoryObj<typeof meta>;
17
-
18
- export const Default: Story = {
19
- args: {
20
- legend: 'Shipping method',
21
- children: (
22
- <>
23
- <Radio label="Standard" name="shipping" />
24
- <Radio label="Express" name="shipping" />
25
- <Radio label="Next day" name="shipping" />
26
- </>
27
- ),
28
- },
29
- };
30
-
31
- export const Required: Story = {
32
- args: {
33
- legend: 'Payment method',
34
- required: true,
35
- children: (
36
- <>
37
- <Radio label="Credit card" name="payment" />
38
- <Radio label="iDEAL" name="payment" />
39
- </>
40
- ),
41
- },
42
- };
43
-
44
- export const Disabled: Story = {
45
- args: {
46
- legend: 'Shipping method',
47
- disabled: true,
48
- children: (
49
- <>
50
- <Radio label="Standard" name="shipping" checked disabled />
51
- <Radio label="Express" name="shipping" disabled />
52
- </>
53
- ),
54
- },
55
- };
56
-
57
- export const WithError: Story = {
58
- args: {
59
- legend: 'Select one',
60
- error: 'Please select a shipping method.',
61
- children: (
62
- <>
63
- <Radio label="Standard" name="shipping" />
64
- <Radio label="Express" name="shipping" />
65
- </>
66
- ),
67
- },
68
- };
69
-
70
- export const WithHint: Story = {
71
- args: {
72
- legend: 'Delivery speed',
73
- hint: 'Express adds a surcharge.',
74
- children: (
75
- <>
76
- <Radio label="Standard" name="delivery" />
77
- <Radio label="Express" name="delivery" />
78
- </>
79
- ),
80
- },
81
- };
82
-
83
- function InteractiveComponent() {
84
- const [selected, setSelected] = useState('standard');
85
- return (
86
- <RadioGroup legend="Shipping method">
87
- <Radio label="Standard" name="shipping" checked={selected === 'standard'} onChange={() => setSelected('standard')} />
88
- <Radio label="Express" name="shipping" checked={selected === 'express'} onChange={() => setSelected('express')} />
89
- <Radio label="Next day" name="shipping" checked={selected === 'nextday'} onChange={() => setSelected('nextday')} />
90
- </RadioGroup>
91
- );
92
- }
93
-
94
- export const Interactive: Story = {
95
- render: () => <InteractiveComponent />,
96
- };
@@ -1,61 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
3
- import { spacing, font, lineHeight } from '../../../../native/tokens';
4
- import { useTheme } from '../../../../native/ThemeContext';
5
- import { Message } from '../atoms/Message.native';
6
-
7
- interface RadioGroupProps {
8
- legend: string;
9
- children: ReactNode;
10
- error?: string;
11
- hint?: string;
12
- required?: boolean;
13
- disabled?: boolean;
14
- style?: StyleProp<ViewStyle>;
15
- }
16
-
17
- const styles = StyleSheet.create({
18
- container: {
19
- gap: spacing.xs,
20
- },
21
- legend: {
22
- fontFamily: font.family.body,
23
- fontSize: font.size.body,
24
- lineHeight: lineHeight.body,
25
- fontWeight: font.weight.semibold,
26
- },
27
- required: {
28
- fontWeight: font.weight.body,
29
- },
30
- items: {
31
- gap: spacing.xxs,
32
- },
33
- });
34
-
35
- export function RadioGroup({
36
- legend,
37
- children,
38
- error,
39
- hint,
40
- required,
41
- disabled = false,
42
- style,
43
- }: Readonly<RadioGroupProps>) {
44
- const { colors } = useTheme();
45
-
46
- return (
47
- <View
48
- style={[styles.container, disabled && { opacity: 0.4 }, style]}
49
- accessibilityRole="radiogroup"
50
- accessibilityLabel={legend}
51
- >
52
- <Text style={[styles.legend, { color: colors.colorOnBg }]}>
53
- {legend}
54
- {required && <Text style={[styles.required, { color: colors.colorOnBgSubtle }]}> *</Text>}
55
- </Text>
56
- <View style={styles.items}>{children}</View>
57
- <Message type="error" message={error} />
58
- <Message type="hint" message={hint} />
59
- </View>
60
- );
61
- }
@@ -1,78 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './SegmentedControl.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # SegmentedControl (React Native)
7
-
8
- A horizontal control with multiple segments, where each segment acts as a tab. Supports `outline` and `filled` variants with three size options.
9
-
10
- > **Status: alpha** -- API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { SegmentedControl } from '@boostdev/design-system-components';
16
- import { useState } from 'react';
17
-
18
- function Example() {
19
- const [index, setIndex] = useState(0);
20
-
21
- return (
22
- <SegmentedControl
23
- selectedIndex={index}
24
- onValueChange={setIndex}
25
- accessibilityLabel="View mode"
26
- >
27
- {'Day'}
28
- {'Week'}
29
- {'Month'}
30
- </SegmentedControl>
31
- );
32
- }
33
- ```
34
-
35
- ## Examples
36
-
37
- ### Outline (default)
38
- <Canvas of={Stories.Outline} />
39
-
40
- ### Filled
41
- <Canvas of={Stories.Filled} />
42
-
43
- ### Small
44
- <Canvas of={Stories.Small} />
45
-
46
- ### Large
47
- <Canvas of={Stories.Large} />
48
-
49
- ### Disabled
50
- <Canvas of={Stories.Disabled} />
51
-
52
- ### Interactive
53
- <Canvas of={Stories.Interactive} />
54
-
55
- ### Interactive filled
56
- <Canvas of={Stories.InteractiveFilled} />
57
-
58
- ## Props
59
-
60
- <ArgTypes of={Stories} />
61
-
62
- ## Props reference
63
-
64
- | Prop | Type | Default | Description |
65
- |------|------|---------|-------------|
66
- | `selectedIndex` | number | `0` | Zero-based index of the active segment |
67
- | `disabled` | boolean | `false` | Disables all segments |
68
- | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Controls height and font size |
69
- | `variant` | `'outline' \| 'filled'` | `'outline'` | Visual style of the control |
70
- | `children` | ReactNode | -- | Segment labels (strings or elements) |
71
- | `accessibilityLabel` | string | -- | Label for the tab group |
72
- | `onValueChange` | `(index: number) => void` | -- | Called with the tapped segment index |
73
-
74
- ## Accessibility
75
-
76
- - Uses `accessibilityRole="tablist"` on the container
77
- - Each segment uses `accessibilityRole="tab"` with `accessibilityState.selected`
78
- - Disabled state is communicated via `accessibilityState`
@@ -1,82 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../../native/ThemeContext';
3
- import { SegmentedControl } from './SegmentedControl.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('SegmentedControl (native)', () => {
10
- it('renders all segments', () => {
11
- renderWithTheme(
12
- <SegmentedControl accessibilityLabel="View">
13
- {'Day'}
14
- {'Week'}
15
- {'Month'}
16
- </SegmentedControl>,
17
- );
18
- expect(screen.getByText('Day')).toBeInTheDocument();
19
- expect(screen.getByText('Week')).toBeInTheDocument();
20
- expect(screen.getByText('Month')).toBeInTheDocument();
21
- });
22
-
23
- it('has tablist role', () => {
24
- renderWithTheme(
25
- <SegmentedControl accessibilityLabel="View">
26
- {'Day'}
27
- {'Week'}
28
- </SegmentedControl>,
29
- );
30
- expect(screen.getByRole('tablist')).toBeInTheDocument();
31
- });
32
-
33
- it('renders tab roles for each segment', () => {
34
- renderWithTheme(
35
- <SegmentedControl accessibilityLabel="View">
36
- {'Day'}
37
- {'Week'}
38
- </SegmentedControl>,
39
- );
40
- const tabs = screen.getAllByRole('tab');
41
- expect(tabs).toHaveLength(2);
42
- });
43
-
44
- it('calls onValueChange with the clicked index', () => {
45
- const onValueChange = vi.fn();
46
- renderWithTheme(
47
- <SegmentedControl accessibilityLabel="View" onValueChange={onValueChange}>
48
- {'Day'}
49
- {'Week'}
50
- {'Month'}
51
- </SegmentedControl>,
52
- );
53
- fireEvent.click(screen.getByText('Week'));
54
- expect(onValueChange).toHaveBeenCalledWith(1);
55
- });
56
-
57
- it('does not call onValueChange when disabled', () => {
58
- const onValueChange = vi.fn();
59
- renderWithTheme(
60
- <SegmentedControl accessibilityLabel="View" onValueChange={onValueChange} disabled>
61
- {'Day'}
62
- {'Week'}
63
- </SegmentedControl>,
64
- );
65
- fireEvent.click(screen.getByText('Week'));
66
- expect(onValueChange).not.toHaveBeenCalled();
67
- });
68
-
69
- it('renders the selected segment with distinct styling', () => {
70
- renderWithTheme(
71
- <SegmentedControl accessibilityLabel="View" selectedIndex={1}>
72
- {'Day'}
73
- {'Week'}
74
- </SegmentedControl>,
75
- );
76
- // Verify both tabs render and the selected one exists
77
- const tabs = screen.getAllByRole('tab');
78
- expect(tabs).toHaveLength(2);
79
- // The selected tab text is rendered
80
- expect(screen.getByText('Week')).toBeInTheDocument();
81
- });
82
- });
@@ -1,99 +0,0 @@
1
- import React, { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../../native/ThemeContext';
4
- import { SegmentedControl } from './SegmentedControl.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/Form/SegmentedControl',
9
- component: SegmentedControl,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'padded' },
12
- } satisfies Meta<typeof SegmentedControl>;
13
-
14
- export default meta;
15
- type Story = StoryObj<typeof meta>;
16
-
17
- export const Outline: Story = {
18
- args: {
19
- variant: 'outline',
20
- selectedIndex: 0,
21
- children: ['Day', 'Week', 'Month'],
22
- accessibilityLabel: 'View mode',
23
- },
24
- };
25
-
26
- export const Filled: Story = {
27
- args: {
28
- variant: 'filled',
29
- selectedIndex: 1,
30
- children: ['Day', 'Week', 'Month'],
31
- accessibilityLabel: 'View mode',
32
- },
33
- };
34
-
35
- export const Small: Story = {
36
- args: {
37
- size: 'small',
38
- selectedIndex: 0,
39
- children: ['On', 'Off'],
40
- accessibilityLabel: 'Toggle',
41
- },
42
- };
43
-
44
- export const Large: Story = {
45
- args: {
46
- size: 'large',
47
- selectedIndex: 2,
48
- children: ['First', 'Second', 'Third'],
49
- accessibilityLabel: 'Selection',
50
- },
51
- };
52
-
53
- export const Disabled: Story = {
54
- args: {
55
- selectedIndex: 0,
56
- disabled: true,
57
- children: ['Day', 'Week', 'Month'],
58
- accessibilityLabel: 'View mode',
59
- },
60
- };
61
-
62
- function InteractiveComponent() {
63
- const [index, setIndex] = useState(0);
64
- return (
65
- <SegmentedControl
66
- selectedIndex={index}
67
- onValueChange={setIndex}
68
- accessibilityLabel="View mode"
69
- >
70
- {'Day'}
71
- {'Week'}
72
- {'Month'}
73
- </SegmentedControl>
74
- );
75
- }
76
-
77
- export const Interactive: Story = {
78
- render: () => <InteractiveComponent />,
79
- };
80
-
81
- function InteractiveFilledComponent() {
82
- const [index, setIndex] = useState(0);
83
- return (
84
- <SegmentedControl
85
- variant="filled"
86
- selectedIndex={index}
87
- onValueChange={setIndex}
88
- accessibilityLabel="Category"
89
- >
90
- {'All'}
91
- {'Active'}
92
- {'Completed'}
93
- </SegmentedControl>
94
- );
95
- }
96
-
97
- export const InteractiveFilled: Story = {
98
- render: () => <InteractiveFilledComponent />,
99
- };