@boostdev/design-system-components 2.4.1 → 2.5.0

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 +128 -62
  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 +128 -62
  8. package/dist/index.cjs +128 -62
  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 +128 -62
  13. package/dist/web-components/index.js +36 -4
  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 +206 -4
  17. package/src/components/layout/Grid/Grid.tsx +15 -1
  18. package/src/components/layout/Grid/masonry.ts +148 -10
  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,84 +0,0 @@
1
- import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
2
- import { spacing, font, border, lineHeight } from '../../../native/tokens';
3
- import { useTheme } from '../../../native/ThemeContext';
4
-
5
- type ProgressSize = 'small' | 'medium' | 'large';
6
-
7
- interface ProgressProps {
8
- value: number;
9
- max?: number;
10
- label: string;
11
- showLabel?: boolean;
12
- size?: ProgressSize;
13
- style?: StyleProp<ViewStyle>;
14
- }
15
-
16
- const trackHeight: Record<ProgressSize, number> = {
17
- small: 4,
18
- medium: 8,
19
- large: 12,
20
- };
21
-
22
- const styles = StyleSheet.create({
23
- container: {
24
- width: '100%',
25
- },
26
- labelRow: {
27
- flexDirection: 'row',
28
- justifyContent: 'space-between',
29
- marginBottom: spacing.xxs,
30
- },
31
- labelText: {
32
- fontSize: font.size.bodyS,
33
- fontFamily: font.family.body,
34
- lineHeight: lineHeight.bodyS,
35
- },
36
- track: {
37
- width: '100%',
38
- borderRadius: border.radius.full,
39
- overflow: 'hidden',
40
- },
41
- fill: {
42
- height: '100%',
43
- borderRadius: border.radius.full,
44
- },
45
- });
46
-
47
- export function Progress({
48
- value,
49
- max = 100,
50
- label,
51
- showLabel = false,
52
- size = 'medium',
53
- style,
54
- }: Readonly<ProgressProps>) {
55
- const { colors } = useTheme();
56
- const percentage = Math.min(100, Math.max(0, (value / max) * 100));
57
- const height = trackHeight[size];
58
-
59
- return (
60
- <View style={[styles.container, style]}>
61
- {showLabel && (
62
- <View style={styles.labelRow}>
63
- <Text style={[styles.labelText, { color: colors.colorOnBg }]}>{label}</Text>
64
- <Text style={[styles.labelText, { color: colors.colorOnBgSubtle }]}>
65
- {Math.round(percentage)}%
66
- </Text>
67
- </View>
68
- )}
69
- <View
70
- style={[styles.track, { height, backgroundColor: colors.colorBgSubtle }]}
71
- accessibilityRole="progressbar"
72
- accessibilityLabel={label}
73
- accessibilityValue={{ min: 0, max, now: value }}
74
- >
75
- <View
76
- style={[
77
- styles.fill,
78
- { width: `${percentage}%`, backgroundColor: colors.colorInteractive },
79
- ]}
80
- />
81
- </View>
82
- </View>
83
- );
84
- }
@@ -1,63 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './ProgressCircle.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # ProgressCircle (React Native)
7
-
8
- React Native circular progress indicator. Displays progress as a ring with an optional percentage label in the center.
9
-
10
- > **Status: alpha** — API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { ProgressCircle } from '@boostdev/design-system-components';
16
-
17
- <ProgressCircle value={75} label="Upload progress" showValue />
18
- <ProgressCircle value={50} label="Loading" size="large" />
19
- ```
20
-
21
- ## Examples
22
-
23
- ### Default
24
- <Canvas of={Stories.Default} />
25
-
26
- ### With value label
27
- <Canvas of={Stories.WithValue} />
28
-
29
- ### Small
30
- <Canvas of={Stories.Small} />
31
-
32
- ### Large
33
- <Canvas of={Stories.Large} />
34
-
35
- ### Complete
36
- <Canvas of={Stories.Complete} />
37
-
38
- ### Empty
39
- <Canvas of={Stories.Empty} />
40
-
41
- ### All sizes
42
- <Canvas of={Stories.AllSizes} />
43
-
44
- ## Props
45
-
46
- <ArgTypes of={Stories} />
47
-
48
- ## Props reference
49
-
50
- | Prop | Type | Default | Description |
51
- |------|------|---------|-------------|
52
- | `value` | `number` | — | Current progress value |
53
- | `max` | `number` | `100` | Maximum value |
54
- | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Circle dimensions (40, 64, 96px) |
55
- | `label` | `string` | — | Accessible label for the progress indicator |
56
- | `showValue` | `boolean` | `false` | Show percentage text in the center |
57
- | `style` | `StyleProp<ViewStyle>` | — | Override container styles |
58
-
59
- ## Accessibility
60
-
61
- - Uses `accessibilityRole="progressbar"` with `accessibilityValue` for min, max, and current
62
- - `label` prop provides the screen reader description
63
- - Percentage text is hidden from assistive technologies when `showValue` is true
@@ -1,39 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { ProgressCircle } from './ProgressCircle.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('ProgressCircle (native)', () => {
10
- it('has progressbar role with accessible label', () => {
11
- renderWithTheme(<ProgressCircle value={60} label="Upload progress" />);
12
- expect(screen.getByRole('progressbar', { name: 'Upload progress' })).toBeInTheDocument();
13
- });
14
-
15
- it('renders the progressbar element', () => {
16
- renderWithTheme(<ProgressCircle value={75} max={100} label="Loading" />);
17
- expect(screen.getByRole('progressbar')).toBeInTheDocument();
18
- });
19
-
20
- it('shows percentage text when showValue is true', () => {
21
- renderWithTheme(<ProgressCircle value={60} label="Progress" showValue />);
22
- expect(screen.getByText('60%')).toBeInTheDocument();
23
- });
24
-
25
- it('does not show percentage text by default', () => {
26
- renderWithTheme(<ProgressCircle value={60} label="Progress" />);
27
- expect(screen.queryByText('60%')).not.toBeInTheDocument();
28
- });
29
-
30
- it('clamps value at 100%', () => {
31
- renderWithTheme(<ProgressCircle value={150} max={100} label="Overflow" showValue />);
32
- expect(screen.getByText('100%')).toBeInTheDocument();
33
- });
34
-
35
- it('clamps value at 0%', () => {
36
- renderWithTheme(<ProgressCircle value={-10} max={100} label="Underflow" showValue />);
37
- expect(screen.getByText('0%')).toBeInTheDocument();
38
- });
39
- });
@@ -1,36 +0,0 @@
1
- import React from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { ProgressCircle } from './ProgressCircle.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/UI/ProgressCircle',
9
- component: ProgressCircle,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'centered' },
12
- argTypes: {
13
- size: { control: 'select', options: ['small', 'medium', 'large'] },
14
- value: { control: { type: 'range', min: 0, max: 100, step: 1 } },
15
- },
16
- } satisfies Meta<typeof ProgressCircle>;
17
-
18
- export default meta;
19
- type Story = StoryObj<typeof meta>;
20
-
21
- export const Default: Story = { args: { value: 60, label: 'Progress' } };
22
- export const WithValue: Story = { args: { value: 75, label: 'Upload progress', showValue: true } };
23
- export const Small: Story = { args: { value: 50, label: 'Small progress', size: 'small' } };
24
- export const Large: Story = { args: { value: 80, label: 'Large progress', size: 'large', showValue: true } };
25
- export const Complete: Story = { args: { value: 100, label: 'Complete', showValue: true } };
26
- export const Empty: Story = { args: { value: 0, label: 'Not started', showValue: true } };
27
-
28
- export const AllSizes: Story = {
29
- render: () => (
30
- <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
31
- {(['small', 'medium', 'large'] as const).map(s => (
32
- <ProgressCircle key={s} value={65} label={`${s} progress`} size={s} showValue={s !== 'small'} />
33
- ))}
34
- </div>
35
- ),
36
- };
@@ -1,176 +0,0 @@
1
- import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
2
- import { font } from '../../../native/tokens';
3
- import { useTheme } from '../../../native/ThemeContext';
4
-
5
- type ProgressCircleSize = 'small' | 'medium' | 'large';
6
-
7
- interface ProgressCircleProps {
8
- value: number;
9
- max?: number;
10
- size?: ProgressCircleSize;
11
- label: string;
12
- showValue?: boolean;
13
- style?: StyleProp<ViewStyle>;
14
- }
15
-
16
- const SIZE_PX: Record<ProgressCircleSize, number> = {
17
- small: 40,
18
- medium: 64,
19
- large: 96,
20
- };
21
-
22
- const STROKE_WIDTH: Record<ProgressCircleSize, number> = {
23
- small: 3,
24
- medium: 4,
25
- large: 5,
26
- };
27
-
28
- const FONT_SIZE: Record<ProgressCircleSize, number> = {
29
- small: font.size.bodyS - 2,
30
- medium: font.size.bodyS,
31
- large: font.size.body,
32
- };
33
-
34
- const styles = StyleSheet.create({
35
- wrapper: {
36
- alignItems: 'center',
37
- justifyContent: 'center',
38
- },
39
- valueText: {
40
- position: 'absolute',
41
- fontFamily: font.family.body,
42
- fontWeight: font.weight.semibold,
43
- },
44
- });
45
-
46
- function HalfCircle({
47
- size,
48
- stroke,
49
- color,
50
- rotation,
51
- }: {
52
- size: number;
53
- stroke: number;
54
- color: string;
55
- rotation: number;
56
- }) {
57
- return (
58
- <View
59
- style={{
60
- width: size / 2,
61
- height: size,
62
- overflow: 'hidden',
63
- }}
64
- >
65
- <View
66
- style={{
67
- width: size,
68
- height: size,
69
- borderRadius: size / 2,
70
- borderWidth: stroke,
71
- borderColor: color,
72
- transform: [{ rotate: `${rotation}deg` }],
73
- }}
74
- />
75
- </View>
76
- );
77
- }
78
-
79
- export function ProgressCircle({
80
- value,
81
- max = 100,
82
- size = 'medium',
83
- label,
84
- showValue = false,
85
- style,
86
- }: Readonly<ProgressCircleProps>) {
87
- const { colors } = useTheme();
88
- const percentage = Math.min(100, Math.max(0, (value / max) * 100));
89
- const px = SIZE_PX[size];
90
- const stroke = STROKE_WIDTH[size];
91
- const fillColor = colors.colorInteractive;
92
- const trackColor = colors.colorBgSubtle;
93
-
94
- // Convert percentage to degrees (0-360)
95
- const degrees = (percentage / 100) * 360;
96
-
97
- return (
98
- <View
99
- style={[
100
- styles.wrapper,
101
- { width: px, height: px },
102
- style,
103
- ]}
104
- accessibilityRole="progressbar"
105
- accessibilityLabel={label}
106
- accessibilityValue={{ min: 0, max, now: value }}
107
- >
108
- {/* Track: full circle background */}
109
- <View
110
- style={{
111
- position: 'absolute',
112
- width: px,
113
- height: px,
114
- borderRadius: px / 2,
115
- borderWidth: stroke,
116
- borderColor: trackColor,
117
- }}
118
- />
119
-
120
- {/* Fill: right half (0-180°) */}
121
- <View
122
- style={{
123
- position: 'absolute',
124
- width: px / 2,
125
- height: px,
126
- start: px / 2,
127
- overflow: 'hidden',
128
- }}
129
- >
130
- <HalfCircle
131
- size={px}
132
- stroke={stroke}
133
- color={percentage > 0 ? fillColor : 'transparent'}
134
- rotation={Math.min(degrees, 180) - 180}
135
- />
136
- </View>
137
-
138
- {/* Fill: left half (180-360°) */}
139
- {degrees > 180 && (
140
- <View
141
- style={{
142
- position: 'absolute',
143
- width: px / 2,
144
- height: px,
145
- start: 0,
146
- overflow: 'hidden',
147
- }}
148
- >
149
- <View
150
- style={{
151
- position: 'absolute',
152
- end: 0,
153
- }}
154
- >
155
- <HalfCircle
156
- size={px}
157
- stroke={stroke}
158
- color={fillColor}
159
- rotation={Math.min(degrees - 180, 180)}
160
- />
161
- </View>
162
- </View>
163
- )}
164
-
165
- {showValue && (
166
- <Text
167
- style={[styles.valueText, { fontSize: FONT_SIZE[size], color: colors.colorOnBg }]}
168
- importantForAccessibility="no"
169
- accessibilityElementsHidden={true}
170
- >
171
- {Math.round(percentage)}%
172
- </Text>
173
- )}
174
- </View>
175
- );
176
- }
@@ -1,39 +0,0 @@
1
- import { render } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Separator } from './Separator.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('Separator (native)', () => {
10
- it('renders with presentation role', () => {
11
- const { container } = renderWithTheme(<Separator />);
12
- const element = container.firstChild as HTMLElement;
13
- // react-native-web maps accessibilityRole="none" to role="presentation"
14
- expect(element).toHaveAttribute('role', 'presentation');
15
- });
16
-
17
- it('renders a single element', () => {
18
- const { container } = renderWithTheme(<Separator />);
19
- expect(container.firstChild).toBeInTheDocument();
20
- expect(container.childElementCount).toBe(1);
21
- });
22
-
23
- it('renders differently for vertical orientation', () => {
24
- const { container: hContainer } = renderWithTheme(<Separator />);
25
- const { container: vContainer } = renderWithTheme(<Separator orientation="vertical" />);
26
- const hEl = hContainer.firstChild as HTMLElement;
27
- const vEl = vContainer.firstChild as HTMLElement;
28
- // Both render but with distinct class/style sets
29
- expect(hEl.className).not.toBe(vEl.className);
30
- });
31
-
32
- it('applies custom style', () => {
33
- const { container } = renderWithTheme(
34
- <Separator style={{ marginTop: 16 }} />,
35
- );
36
- const element = container.firstChild as HTMLElement;
37
- expect(element.style.marginTop).toBe('16px');
38
- });
39
- });
@@ -1,38 +0,0 @@
1
- import React from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Separator } from './Separator.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/UI/Separator',
9
- component: Separator,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'padded' },
12
- argTypes: {
13
- orientation: { control: 'select', options: ['horizontal', 'vertical'] },
14
- },
15
- } satisfies Meta<typeof Separator>;
16
-
17
- export default meta;
18
- type Story = StoryObj<typeof meta>;
19
-
20
- export const Horizontal: Story = {
21
- render: (args) => (
22
- <div style={{ width: 300 }}>
23
- <Separator {...args} orientation="horizontal" />
24
- </div>
25
- ),
26
- };
27
-
28
- export const Vertical: Story = {
29
- render: (args) => (
30
- <div style={{ display: 'flex', height: 60, alignItems: 'stretch' }}>
31
- <span>Left</span>
32
- <div style={{ display: 'flex', margin: '0 8px' }}>
33
- <Separator {...args} orientation="vertical" />
34
- </div>
35
- <span>Right</span>
36
- </div>
37
- ),
38
- };
@@ -1,32 +0,0 @@
1
- import { StyleSheet, View, StyleProp, ViewStyle } from 'react-native';
2
- import { useTheme } from '../../../native/ThemeContext';
3
-
4
- interface SeparatorProps {
5
- orientation?: 'horizontal' | 'vertical';
6
- style?: StyleProp<ViewStyle>;
7
- }
8
-
9
- const styles = StyleSheet.create({
10
- horizontal: {
11
- height: StyleSheet.hairlineWidth,
12
- width: '100%',
13
- },
14
- vertical: {
15
- width: StyleSheet.hairlineWidth,
16
- alignSelf: 'stretch',
17
- },
18
- });
19
-
20
- export function Separator({ orientation = 'horizontal', style }: Readonly<SeparatorProps>) {
21
- const { colors } = useTheme();
22
- return (
23
- <View
24
- accessibilityRole="none"
25
- style={[
26
- orientation === 'vertical' ? styles.vertical : styles.horizontal,
27
- { backgroundColor: colors.colorBgSubtle },
28
- style,
29
- ]}
30
- />
31
- );
32
- }
@@ -1,39 +0,0 @@
1
- import { render } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Skeleton } from './Skeleton.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- describe('Skeleton (native)', () => {
10
- it('renders without crashing', () => {
11
- const { container } = renderWithTheme(<Skeleton />);
12
- expect(container.firstChild).toBeInTheDocument();
13
- });
14
-
15
- it('marks content as not important for accessibility', () => {
16
- const { container } = renderWithTheme(<Skeleton />);
17
- const element = container.firstChild as HTMLElement;
18
- // importantForAccessibility="no-hide-descendants" hides descendants from a11y tree
19
- // In react-native-web this renders as aria-hidden on the element
20
- expect(element).toBeTruthy();
21
- // The element should not have any accessible role
22
- expect(element.getAttribute('role')).toBeNull();
23
- });
24
-
25
- it('applies custom style', () => {
26
- const { container } = renderWithTheme(
27
- <Skeleton style={{ width: 200, height: 20 }} />,
28
- );
29
- const element = container.firstChild as HTMLElement;
30
- expect(element.style.width).toBe('200px');
31
- expect(element.style.height).toBe('20px');
32
- });
33
-
34
- it('has a background color from the theme', () => {
35
- const { container } = renderWithTheme(<Skeleton />);
36
- const element = container.firstChild as HTMLElement;
37
- expect(element.style.backgroundColor).toBeTruthy();
38
- });
39
- });
@@ -1,31 +0,0 @@
1
- import React from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Skeleton } from './Skeleton.native';
5
-
6
- const meta = {
7
- tags: ['!stable', 'alpha'],
8
- title: 'React Native/UI/Skeleton',
9
- component: Skeleton,
10
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
- parameters: { layout: 'padded' },
12
- } satisfies Meta<typeof Skeleton>;
13
-
14
- export default meta;
15
- type Story = StoryObj<typeof meta>;
16
-
17
- export const Text: Story = {
18
- render: (args) => <Skeleton {...args} style={{ width: 200, height: 16 }} />,
19
- };
20
- export const Avatar: Story = {
21
- render: (args) => <Skeleton {...args} style={{ width: 48, height: 48, borderRadius: 999 }} />,
22
- };
23
- export const Card: Story = {
24
- render: () => (
25
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', width: 280 }}>
26
- <Skeleton style={{ width: 280, height: 160 }} />
27
- <Skeleton style={{ width: 200, height: 16 }} />
28
- <Skeleton style={{ width: 140, height: 14 }} />
29
- </div>
30
- ),
31
- };
@@ -1,42 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import { Animated, StyleSheet, StyleProp, ViewStyle } from 'react-native';
3
- import { border } from '../../../native/tokens';
4
- import { useTheme } from '../../../native/ThemeContext';
5
-
6
- interface SkeletonProps {
7
- style?: StyleProp<ViewStyle>;
8
- }
9
-
10
- const styles = StyleSheet.create({
11
- skeleton: {
12
- borderRadius: border.radius.xs,
13
- overflow: 'hidden',
14
- },
15
- });
16
-
17
- export function Skeleton({ style }: SkeletonProps) {
18
- const { colors } = useTheme();
19
- const opacity = useRef(new Animated.Value(1)).current;
20
-
21
- useEffect(() => {
22
- const anim = Animated.loop(
23
- Animated.sequence([
24
- Animated.timing(opacity, { toValue: 0.4, duration: 700, useNativeDriver: true }),
25
- Animated.timing(opacity, { toValue: 1, duration: 700, useNativeDriver: true }),
26
- ]),
27
- );
28
- anim.start();
29
- return () => anim.stop();
30
- }, [opacity]);
31
-
32
- return (
33
- <Animated.View
34
- importantForAccessibility="no-hide-descendants"
35
- style={[
36
- styles.skeleton,
37
- { backgroundColor: colors.colorBgSubtle, opacity },
38
- style,
39
- ]}
40
- />
41
- );
42
- }