@boostdev/design-system-components 2.4.2 → 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.
- package/AGENTS.md +4 -20
- package/README.md +3 -23
- package/dist/client.cjs +115 -61
- package/dist/client.css +537 -537
- package/dist/client.d.cts +11 -1
- package/dist/client.d.ts +11 -1
- package/dist/client.js +115 -61
- package/dist/index.cjs +115 -61
- package/dist/index.css +537 -537
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +115 -61
- package/dist/web-components/index.js +23 -3
- package/package.json +2 -10
- package/src/components/layout/Grid/Grid.mdx +20 -2
- package/src/components/layout/Grid/Grid.spec.tsx +175 -4
- package/src/components/layout/Grid/Grid.tsx +15 -1
- package/src/components/layout/Grid/masonry.ts +120 -9
- package/dist/native/index.cjs +0 -4337
- package/dist/native/index.d.cts +0 -574
- package/dist/native/index.d.ts +0 -574
- package/dist/native/index.js +0 -4421
- package/src/components/interaction/Button/Button.native.mdx +0 -91
- package/src/components/interaction/Button/Button.native.spec.tsx +0 -55
- package/src/components/interaction/Button/Button.native.stories.tsx +0 -141
- package/src/components/interaction/Button/Button.native.tsx +0 -105
- package/src/components/interaction/Dialog/Dialog.native.mdx +0 -61
- package/src/components/interaction/Dialog/Dialog.native.spec.tsx +0 -73
- package/src/components/interaction/Dialog/Dialog.native.stories.tsx +0 -53
- package/src/components/interaction/Dialog/Dialog.native.tsx +0 -128
- package/src/components/interaction/Drawer/Drawer.native.mdx +0 -58
- package/src/components/interaction/Drawer/Drawer.native.spec.tsx +0 -81
- package/src/components/interaction/Drawer/Drawer.native.stories.tsx +0 -33
- package/src/components/interaction/Drawer/Drawer.native.tsx +0 -175
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.mdx +0 -74
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.spec.tsx +0 -78
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.stories.tsx +0 -51
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.tsx +0 -254
- package/src/components/interaction/Popover/Popover.native.mdx +0 -61
- package/src/components/interaction/Popover/Popover.native.spec.tsx +0 -73
- package/src/components/interaction/Popover/Popover.native.stories.tsx +0 -44
- package/src/components/interaction/Popover/Popover.native.tsx +0 -87
- package/src/components/interaction/Rating/Rating.native.mdx +0 -55
- package/src/components/interaction/Rating/Rating.native.spec.tsx +0 -38
- package/src/components/interaction/Rating/Rating.native.stories.tsx +0 -37
- package/src/components/interaction/Rating/Rating.native.tsx +0 -50
- package/src/components/interaction/Toast/Toast.native.mdx +0 -81
- package/src/components/interaction/Toast/Toast.native.spec.tsx +0 -80
- package/src/components/interaction/Toast/Toast.native.stories.tsx +0 -47
- package/src/components/interaction/Toast/Toast.native.tsx +0 -202
- package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +0 -74
- package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +0 -42
- package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +0 -34
- package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +0 -80
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.mdx +0 -78
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.spec.tsx +0 -57
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.stories.tsx +0 -96
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.tsx +0 -61
- package/src/components/interaction/form/Combobox/Combobox.native.mdx +0 -86
- package/src/components/interaction/form/Combobox/Combobox.native.spec.tsx +0 -60
- package/src/components/interaction/form/Combobox/Combobox.native.stories.tsx +0 -99
- package/src/components/interaction/form/Combobox/Combobox.native.tsx +0 -211
- package/src/components/interaction/form/FileInput/FileInput.native.mdx +0 -86
- package/src/components/interaction/form/FileInput/FileInput.native.spec.tsx +0 -73
- package/src/components/interaction/form/FileInput/FileInput.native.stories.tsx +0 -69
- package/src/components/interaction/form/FileInput/FileInput.native.tsx +0 -129
- package/src/components/interaction/form/FormInput/FormInput.native.mdx +0 -81
- package/src/components/interaction/form/FormInput/FormInput.native.spec.tsx +0 -51
- package/src/components/interaction/form/FormInput/FormInput.native.stories.tsx +0 -54
- package/src/components/interaction/form/FormInput/FormInput.native.tsx +0 -122
- package/src/components/interaction/form/NumberInput/NumberInput.native.mdx +0 -87
- package/src/components/interaction/form/NumberInput/NumberInput.native.spec.tsx +0 -63
- package/src/components/interaction/form/NumberInput/NumberInput.native.stories.tsx +0 -63
- package/src/components/interaction/form/NumberInput/NumberInput.native.tsx +0 -198
- package/src/components/interaction/form/Radio/Radio.native.spec.tsx +0 -79
- package/src/components/interaction/form/Radio/Radio.native.stories.tsx +0 -44
- package/src/components/interaction/form/Radio/Radio.native.tsx +0 -79
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.mdx +0 -79
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.spec.tsx +0 -66
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.stories.tsx +0 -96
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.tsx +0 -61
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.mdx +0 -78
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.spec.tsx +0 -82
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.stories.tsx +0 -99
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.tsx +0 -121
- package/src/components/interaction/form/Select/Select.native.mdx +0 -85
- package/src/components/interaction/form/Select/Select.native.spec.tsx +0 -64
- package/src/components/interaction/form/Select/Select.native.stories.tsx +0 -75
- package/src/components/interaction/form/Select/Select.native.tsx +0 -252
- package/src/components/interaction/form/Slider/Slider.native.mdx +0 -83
- package/src/components/interaction/form/Slider/Slider.native.spec.tsx +0 -51
- package/src/components/interaction/form/Slider/Slider.native.stories.tsx +0 -58
- package/src/components/interaction/form/Slider/Slider.native.tsx +0 -188
- package/src/components/interaction/form/Switch/Switch.native.spec.tsx +0 -60
- package/src/components/interaction/form/Switch/Switch.native.stories.tsx +0 -33
- package/src/components/interaction/form/Switch/Switch.native.tsx +0 -59
- package/src/components/interaction/form/Textarea/Textarea.native.mdx +0 -76
- package/src/components/interaction/form/Textarea/Textarea.native.spec.tsx +0 -50
- package/src/components/interaction/form/Textarea/Textarea.native.stories.tsx +0 -50
- package/src/components/interaction/form/Textarea/Textarea.native.tsx +0 -108
- package/src/components/interaction/form/atoms/Label.native.spec.tsx +0 -39
- package/src/components/interaction/form/atoms/Label.native.stories.tsx +0 -18
- package/src/components/interaction/form/atoms/Label.native.tsx +0 -29
- package/src/components/interaction/form/atoms/Message.native.spec.tsx +0 -30
- package/src/components/interaction/form/atoms/Message.native.stories.tsx +0 -22
- package/src/components/interaction/form/atoms/Message.native.tsx +0 -34
- package/src/components/layout/ButtonGroup/ButtonGroup.native.spec.tsx +0 -83
- package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +0 -44
- package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +0 -44
- package/src/components/layout/Card/Card.native.spec.tsx +0 -102
- package/src/components/layout/Card/Card.native.stories.tsx +0 -51
- package/src/components/layout/Card/Card.native.tsx +0 -89
- package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +0 -39
- package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +0 -37
- package/src/components/layout/IconWrapper/IconWrapper.native.tsx +0 -20
- package/src/components/layout/SectionHeader/SectionHeader.native.spec.tsx +0 -51
- package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +0 -36
- package/src/components/layout/SectionHeader/SectionHeader.native.tsx +0 -79
- package/src/components/ui/Accordion/Accordion.native.mdx +0 -65
- package/src/components/ui/Accordion/Accordion.native.spec.tsx +0 -69
- package/src/components/ui/Accordion/Accordion.native.stories.tsx +0 -52
- package/src/components/ui/Accordion/Accordion.native.tsx +0 -141
- package/src/components/ui/Alert/Alert.native.mdx +0 -62
- package/src/components/ui/Alert/Alert.native.spec.tsx +0 -76
- package/src/components/ui/Alert/Alert.native.stories.tsx +0 -41
- package/src/components/ui/Alert/Alert.native.tsx +0 -94
- package/src/components/ui/Avatar/Avatar.native.mdx +0 -50
- package/src/components/ui/Avatar/Avatar.native.spec.tsx +0 -47
- package/src/components/ui/Avatar/Avatar.native.stories.tsx +0 -32
- package/src/components/ui/Avatar/Avatar.native.tsx +0 -85
- package/src/components/ui/Badge/Badge.native.mdx +0 -54
- package/src/components/ui/Badge/Badge.native.spec.tsx +0 -45
- package/src/components/ui/Badge/Badge.native.stories.tsx +0 -34
- package/src/components/ui/Badge/Badge.native.tsx +0 -50
- package/src/components/ui/Breadcrumb/Breadcrumb.native.mdx +0 -52
- package/src/components/ui/Breadcrumb/Breadcrumb.native.spec.tsx +0 -49
- package/src/components/ui/Breadcrumb/Breadcrumb.native.stories.tsx +0 -46
- package/src/components/ui/Breadcrumb/Breadcrumb.native.tsx +0 -83
- package/src/components/ui/Calendar/Calendar.native.mdx +0 -58
- package/src/components/ui/Calendar/Calendar.native.spec.tsx +0 -92
- package/src/components/ui/Calendar/Calendar.native.stories.tsx +0 -45
- package/src/components/ui/Calendar/Calendar.native.tsx +0 -294
- package/src/components/ui/Carousel/Carousel.native.mdx +0 -55
- package/src/components/ui/Carousel/Carousel.native.spec.tsx +0 -55
- package/src/components/ui/Carousel/Carousel.native.stories.tsx +0 -59
- package/src/components/ui/Carousel/Carousel.native.tsx +0 -184
- package/src/components/ui/Collapsible/Collapsible.native.mdx +0 -53
- package/src/components/ui/Collapsible/Collapsible.native.spec.tsx +0 -68
- package/src/components/ui/Collapsible/Collapsible.native.stories.tsx +0 -48
- package/src/components/ui/Collapsible/Collapsible.native.tsx +0 -139
- package/src/components/ui/DescriptionList/DescriptionList.native.mdx +0 -50
- package/src/components/ui/DescriptionList/DescriptionList.native.spec.tsx +0 -37
- package/src/components/ui/DescriptionList/DescriptionList.native.stories.tsx +0 -48
- package/src/components/ui/DescriptionList/DescriptionList.native.tsx +0 -54
- package/src/components/ui/Link/Link.native.mdx +0 -53
- package/src/components/ui/Link/Link.native.spec.tsx +0 -35
- package/src/components/ui/Link/Link.native.stories.tsx +0 -46
- package/src/components/ui/Link/Link.native.tsx +0 -48
- package/src/components/ui/Loading/Loading.native.spec.tsx +0 -24
- package/src/components/ui/Loading/Loading.native.stories.tsx +0 -31
- package/src/components/ui/Loading/Loading.native.tsx +0 -29
- package/src/components/ui/NotificationBanner/NotificationBanner.native.spec.tsx +0 -85
- package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +0 -37
- package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +0 -76
- package/src/components/ui/Pagination/Pagination.native.mdx +0 -62
- package/src/components/ui/Pagination/Pagination.native.spec.tsx +0 -86
- package/src/components/ui/Pagination/Pagination.native.stories.tsx +0 -47
- package/src/components/ui/Pagination/Pagination.native.tsx +0 -157
- package/src/components/ui/Progress/Progress.native.spec.tsx +0 -56
- package/src/components/ui/Progress/Progress.native.stories.tsx +0 -34
- package/src/components/ui/Progress/Progress.native.tsx +0 -84
- package/src/components/ui/ProgressCircle/ProgressCircle.native.mdx +0 -63
- package/src/components/ui/ProgressCircle/ProgressCircle.native.spec.tsx +0 -39
- package/src/components/ui/ProgressCircle/ProgressCircle.native.stories.tsx +0 -36
- package/src/components/ui/ProgressCircle/ProgressCircle.native.tsx +0 -176
- package/src/components/ui/Separator/Separator.native.spec.tsx +0 -39
- package/src/components/ui/Separator/Separator.native.stories.tsx +0 -38
- package/src/components/ui/Separator/Separator.native.tsx +0 -32
- package/src/components/ui/Skeleton/Skeleton.native.spec.tsx +0 -39
- package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +0 -31
- package/src/components/ui/Skeleton/Skeleton.native.tsx +0 -42
- package/src/components/ui/Table/Table.native.mdx +0 -72
- package/src/components/ui/Table/Table.native.spec.tsx +0 -83
- package/src/components/ui/Table/Table.native.stories.tsx +0 -55
- package/src/components/ui/Table/Table.native.tsx +0 -196
- package/src/components/ui/Tabs/Tabs.native.mdx +0 -65
- package/src/components/ui/Tabs/Tabs.native.spec.tsx +0 -65
- package/src/components/ui/Tabs/Tabs.native.stories.tsx +0 -57
- package/src/components/ui/Tabs/Tabs.native.tsx +0 -133
- package/src/components/ui/Tooltip/Tooltip.native.mdx +0 -47
- package/src/components/ui/Tooltip/Tooltip.native.spec.tsx +0 -37
- package/src/components/ui/Tooltip/Tooltip.native.stories.tsx +0 -38
- package/src/components/ui/Tooltip/Tooltip.native.tsx +0 -57
- package/src/components/ui/Typography/Typography.native.mdx +0 -56
- package/src/components/ui/Typography/Typography.native.spec.tsx +0 -49
- package/src/components/ui/Typography/Typography.native.stories.tsx +0 -36
- package/src/components/ui/Typography/Typography.native.tsx +0 -65
- package/src/native/ThemeContext.tsx +0 -28
- package/src/native/tokens.ts +0 -13
- package/src/native.ts +0 -76
- 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
|
-
}
|