@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.
- 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 +194 -4
- package/src/components/layout/Grid/Grid.tsx +15 -1
- package/src/components/layout/Grid/masonry.ts +127 -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,62 +0,0 @@
|
|
|
1
|
-
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
-
import * as Stories from './Alert.native.stories';
|
|
3
|
-
|
|
4
|
-
<Meta of={Stories} />
|
|
5
|
-
|
|
6
|
-
# Alert (React Native)
|
|
7
|
-
|
|
8
|
-
React Native alert component for inline status messages. Supports four semantic variants, an optional title, and a dismissible mode — matching the web `Alert` API.
|
|
9
|
-
|
|
10
|
-
> **Status: alpha** — API may change before stable release.
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
```tsx
|
|
15
|
-
import { Alert } from '@boostdev/design-system-components';
|
|
16
|
-
|
|
17
|
-
<Alert variant="success" title="Saved">
|
|
18
|
-
Your changes have been saved successfully.
|
|
19
|
-
</Alert>
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Examples
|
|
23
|
-
|
|
24
|
-
### All variants
|
|
25
|
-
<Canvas of={Stories.AllVariants} />
|
|
26
|
-
|
|
27
|
-
### Info
|
|
28
|
-
<Canvas of={Stories.Info} />
|
|
29
|
-
|
|
30
|
-
### Success
|
|
31
|
-
<Canvas of={Stories.Success} />
|
|
32
|
-
|
|
33
|
-
### Warning
|
|
34
|
-
<Canvas of={Stories.Warning} />
|
|
35
|
-
|
|
36
|
-
### Error
|
|
37
|
-
<Canvas of={Stories.Error} />
|
|
38
|
-
|
|
39
|
-
### With title
|
|
40
|
-
<Canvas of={Stories.WithTitle} />
|
|
41
|
-
|
|
42
|
-
### Dismissible
|
|
43
|
-
<Canvas of={Stories.Dismissible} />
|
|
44
|
-
|
|
45
|
-
## Props
|
|
46
|
-
|
|
47
|
-
<ArgTypes of={Stories} />
|
|
48
|
-
|
|
49
|
-
## Props reference
|
|
50
|
-
|
|
51
|
-
| Prop | Type | Default | Description |
|
|
52
|
-
|------|------|---------|-------------|
|
|
53
|
-
| `variant` | `"info" \| "success" \| "warning" \| "error"` | `"info"` | Semantic colour and icon |
|
|
54
|
-
| `title` | string | — | Optional heading above body content |
|
|
55
|
-
| `onDismiss` | `() => void` | — | When set, a dismiss button is shown; called on press |
|
|
56
|
-
| `children` | `ReactNode` | — | Alert body content |
|
|
57
|
-
|
|
58
|
-
## Accessibility
|
|
59
|
-
|
|
60
|
-
- `error` and `warning` variants announce content as `assertive`
|
|
61
|
-
- `info` and `success` variants announce content as `polite`
|
|
62
|
-
- The dismiss button has a descriptive accessibility label
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { Text } from 'react-native';
|
|
3
|
-
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
|
-
import { Alert } from './Alert.native';
|
|
5
|
-
|
|
6
|
-
function renderWithTheme(ui: React.ReactElement) {
|
|
7
|
-
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
describe('Alert (native)', () => {
|
|
11
|
-
it('renders children text', () => {
|
|
12
|
-
renderWithTheme(<Alert>Something happened</Alert>);
|
|
13
|
-
expect(screen.getByText('Something happened')).toBeInTheDocument();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('renders the title when provided', () => {
|
|
17
|
-
renderWithTheme(<Alert title="Heads up">Details here</Alert>);
|
|
18
|
-
expect(screen.getByText('Heads up')).toBeInTheDocument();
|
|
19
|
-
expect(screen.getByText('Details here')).toBeInTheDocument();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('does not render a title element when title is omitted', () => {
|
|
23
|
-
renderWithTheme(<Alert>No title alert</Alert>);
|
|
24
|
-
expect(screen.getByText('No title alert')).toBeInTheDocument();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('renders an icon when provided', () => {
|
|
28
|
-
renderWithTheme(
|
|
29
|
-
<Alert icon={<Text>!</Text>}>With icon</Alert>,
|
|
30
|
-
);
|
|
31
|
-
expect(screen.getByText('!')).toBeInTheDocument();
|
|
32
|
-
expect(screen.getByText('With icon')).toBeInTheDocument();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('has alert role for error variant', () => {
|
|
36
|
-
renderWithTheme(<Alert variant="error">Error message</Alert>);
|
|
37
|
-
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('has alert role for warning variant', () => {
|
|
41
|
-
renderWithTheme(<Alert variant="warning">Warning message</Alert>);
|
|
42
|
-
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('does not have alert role for info variant', () => {
|
|
46
|
-
renderWithTheme(<Alert variant="info">Info message</Alert>);
|
|
47
|
-
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('does not have alert role for success variant', () => {
|
|
51
|
-
renderWithTheme(<Alert variant="success">Success message</Alert>);
|
|
52
|
-
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('renders a dismiss button when onDismiss is provided', () => {
|
|
56
|
-
renderWithTheme(<Alert onDismiss={() => {}}>Dismissable</Alert>);
|
|
57
|
-
expect(screen.getByRole('button', { name: 'Dismiss alert' })).toBeInTheDocument();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('calls onDismiss when dismiss button is pressed', () => {
|
|
61
|
-
const onDismiss = vi.fn();
|
|
62
|
-
renderWithTheme(<Alert onDismiss={onDismiss}>Dismissable</Alert>);
|
|
63
|
-
fireEvent.click(screen.getByRole('button', { name: 'Dismiss alert' }));
|
|
64
|
-
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('does not render a dismiss button when onDismiss is omitted', () => {
|
|
68
|
-
renderWithTheme(<Alert>No dismiss</Alert>);
|
|
69
|
-
expect(screen.queryByRole('button', { name: 'Dismiss alert' })).not.toBeInTheDocument();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('accepts custom style', () => {
|
|
73
|
-
renderWithTheme(<Alert style={{ marginTop: 10 }}>Styled</Alert>);
|
|
74
|
-
expect(screen.getByText('Styled')).toBeInTheDocument();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
|
-
import { Alert } from './Alert.native';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
tags: ['!stable', 'alpha'],
|
|
8
|
-
title: 'React Native/UI/Alert',
|
|
9
|
-
component: Alert,
|
|
10
|
-
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
|
-
parameters: { layout: 'padded' },
|
|
12
|
-
argTypes: {
|
|
13
|
-
variant: { control: 'select', options: ['info', 'success', 'warning', 'error'] },
|
|
14
|
-
},
|
|
15
|
-
} satisfies Meta<typeof Alert>;
|
|
16
|
-
|
|
17
|
-
export default meta;
|
|
18
|
-
type Story = StoryObj<typeof meta>;
|
|
19
|
-
|
|
20
|
-
export const Info: Story = { args: { children: 'This is an informational message.', variant: 'info' } };
|
|
21
|
-
export const Success: Story = { args: { children: 'Your changes have been saved.', variant: 'success' } };
|
|
22
|
-
export const Warning: Story = { args: { children: 'Please review before continuing.', variant: 'warning' } };
|
|
23
|
-
export const Error: Story = { args: { children: 'Something went wrong. Please try again.', variant: 'error' } };
|
|
24
|
-
export const WithTitle: Story = {
|
|
25
|
-
args: { children: 'More details about the alert.', variant: 'info', title: 'Heads up' },
|
|
26
|
-
};
|
|
27
|
-
export const Dismissible: Story = {
|
|
28
|
-
args: { children: 'You can dismiss this alert.', variant: 'success', onDismiss: () => {} },
|
|
29
|
-
};
|
|
30
|
-
export const AllVariants: Story = {
|
|
31
|
-
args: { children: '' },
|
|
32
|
-
render: () => (
|
|
33
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px', maxWidth: 400 }}>
|
|
34
|
-
{(['info', 'success', 'warning', 'error'] as const).map(v => (
|
|
35
|
-
<Alert key={v} variant={v} title={v.charAt(0).toUpperCase() + v.slice(1)}>
|
|
36
|
-
This is a {v} alert.
|
|
37
|
-
</Alert>
|
|
38
|
-
))}
|
|
39
|
-
</div>
|
|
40
|
-
),
|
|
41
|
-
};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { StyleSheet, View, Text, Pressable, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
-
import { spacing, font, border, lineHeight } from '../../../native/tokens';
|
|
4
|
-
import { useTheme } from '../../../native/ThemeContext';
|
|
5
|
-
|
|
6
|
-
type AlertVariant = 'info' | 'success' | 'warning' | 'error';
|
|
7
|
-
|
|
8
|
-
interface AlertProps {
|
|
9
|
-
variant?: AlertVariant;
|
|
10
|
-
icon?: ReactNode;
|
|
11
|
-
title?: string;
|
|
12
|
-
children: ReactNode;
|
|
13
|
-
onDismiss?: () => void;
|
|
14
|
-
style?: StyleProp<ViewStyle>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const styles = StyleSheet.create({
|
|
18
|
-
alert: {
|
|
19
|
-
flexDirection: 'row',
|
|
20
|
-
alignItems: 'flex-start',
|
|
21
|
-
padding: spacing.m,
|
|
22
|
-
borderRadius: border.radius.s,
|
|
23
|
-
gap: spacing.xs,
|
|
24
|
-
},
|
|
25
|
-
icon: {
|
|
26
|
-
flexShrink: 0,
|
|
27
|
-
marginTop: 2,
|
|
28
|
-
},
|
|
29
|
-
content: {
|
|
30
|
-
flex: 1,
|
|
31
|
-
gap: spacing.xxs,
|
|
32
|
-
},
|
|
33
|
-
title: {
|
|
34
|
-
fontSize: font.size.body,
|
|
35
|
-
fontFamily: font.family.body,
|
|
36
|
-
fontWeight: font.weight.semibold,
|
|
37
|
-
lineHeight: lineHeight.body,
|
|
38
|
-
},
|
|
39
|
-
body: {
|
|
40
|
-
fontSize: font.size.bodyS,
|
|
41
|
-
fontFamily: font.family.body,
|
|
42
|
-
lineHeight: lineHeight.bodyS,
|
|
43
|
-
},
|
|
44
|
-
dismiss: {
|
|
45
|
-
padding: spacing.xxs,
|
|
46
|
-
flexShrink: 0,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
export function Alert({
|
|
51
|
-
variant = 'info',
|
|
52
|
-
icon,
|
|
53
|
-
title,
|
|
54
|
-
children,
|
|
55
|
-
onDismiss,
|
|
56
|
-
style,
|
|
57
|
-
}: Readonly<AlertProps>) {
|
|
58
|
-
const { colors } = useTheme();
|
|
59
|
-
const isUrgent = variant === 'error' || variant === 'warning';
|
|
60
|
-
|
|
61
|
-
const variantColors: Record<AlertVariant, { bg: string; text: string; border: string }> = {
|
|
62
|
-
info: { bg: colors.colorBlueSubtle, text: colors.colorOnBlueSubtle, border: colors.colorBlue },
|
|
63
|
-
success: { bg: colors.colorSuccessSubtle, text: colors.colorOnSuccessSubtle, border: colors.colorSuccess },
|
|
64
|
-
warning: { bg: colors.colorOrangeSubtle, text: colors.colorOnOrangeSubtle, border: colors.colorWarning },
|
|
65
|
-
error: { bg: colors.colorRedSubtle, text: colors.colorOnBg, border: colors.colorError },
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const { bg, text, border: borderColor } = variantColors[variant];
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<View
|
|
72
|
-
accessibilityRole={isUrgent ? 'alert' : 'none'}
|
|
73
|
-
accessibilityLiveRegion={isUrgent ? 'assertive' : 'polite'}
|
|
74
|
-
style={[styles.alert, { backgroundColor: bg, borderStartWidth: spacing.s, borderStartColor: borderColor }, style]}
|
|
75
|
-
>
|
|
76
|
-
{icon && <View style={styles.icon}>{icon}</View>}
|
|
77
|
-
<View style={styles.content}>
|
|
78
|
-
{title && <Text style={[styles.title, { color: text }]}>{title}</Text>}
|
|
79
|
-
<Text style={[styles.body, { color: text }]}>{children}</Text>
|
|
80
|
-
</View>
|
|
81
|
-
{onDismiss && (
|
|
82
|
-
<Pressable
|
|
83
|
-
onPress={onDismiss}
|
|
84
|
-
style={styles.dismiss}
|
|
85
|
-
accessibilityLabel="Dismiss alert"
|
|
86
|
-
accessibilityRole="button"
|
|
87
|
-
hitSlop={8}
|
|
88
|
-
>
|
|
89
|
-
<Text style={{ color: text, fontSize: 16 }}>✕</Text>
|
|
90
|
-
</Pressable>
|
|
91
|
-
)}
|
|
92
|
-
</View>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
-
import * as Stories from './Avatar.native.stories';
|
|
3
|
-
|
|
4
|
-
<Meta of={Stories} />
|
|
5
|
-
|
|
6
|
-
# Avatar (React Native)
|
|
7
|
-
|
|
8
|
-
React Native avatar component displaying user initials on a coloured background. Derives initials from the `name` prop (first letter of each word, max 2).
|
|
9
|
-
|
|
10
|
-
> **Status: alpha** — API may change before stable release.
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
```tsx
|
|
15
|
-
import { Avatar } from '@boostdev/design-system-components';
|
|
16
|
-
|
|
17
|
-
<Avatar name="John Doe" size="medium" />
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Examples
|
|
21
|
-
|
|
22
|
-
### All sizes
|
|
23
|
-
<Canvas of={Stories.AllSizes} />
|
|
24
|
-
|
|
25
|
-
### With initials
|
|
26
|
-
<Canvas of={Stories.WithInitials} />
|
|
27
|
-
|
|
28
|
-
### Small
|
|
29
|
-
<Canvas of={Stories.Small} />
|
|
30
|
-
|
|
31
|
-
### Large
|
|
32
|
-
<Canvas of={Stories.Large} />
|
|
33
|
-
|
|
34
|
-
### Single name
|
|
35
|
-
<Canvas of={Stories.SingleName} />
|
|
36
|
-
|
|
37
|
-
## Props
|
|
38
|
-
|
|
39
|
-
<ArgTypes of={Stories} />
|
|
40
|
-
|
|
41
|
-
## Props reference
|
|
42
|
-
|
|
43
|
-
| Prop | Type | Default | Description |
|
|
44
|
-
|------|------|---------|-------------|
|
|
45
|
-
| `name` | string | — | Full name — initials are derived from this |
|
|
46
|
-
| `size` | `"small" \| "medium" \| "large"` | `"medium"` | Circle diameter |
|
|
47
|
-
|
|
48
|
-
## Accessibility
|
|
49
|
-
|
|
50
|
-
- `accessibilityLabel` defaults to the `name` prop so screen readers announce the user's name
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
3
|
-
import { Avatar } from './Avatar.native';
|
|
4
|
-
|
|
5
|
-
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
-
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
describe('Avatar (native)', () => {
|
|
10
|
-
describe('initials fallback', () => {
|
|
11
|
-
it('has image role and accessible label on the container', () => {
|
|
12
|
-
renderWithTheme(<Avatar name="John Doe" />);
|
|
13
|
-
expect(screen.getByRole('img', { name: 'John Doe' })).toBeInTheDocument();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('derives initials from a two-word name', () => {
|
|
17
|
-
renderWithTheme(<Avatar name="John Doe" />);
|
|
18
|
-
expect(screen.getByText('JD')).toBeInTheDocument();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('uses a single-word name as one initial', () => {
|
|
22
|
-
renderWithTheme(<Avatar name="Alice" />);
|
|
23
|
-
expect(screen.getByText('A')).toBeInTheDocument();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('uses the alt prop as accessible label when provided', () => {
|
|
27
|
-
renderWithTheme(<Avatar name="John Doe" alt="Profile picture" />);
|
|
28
|
-
expect(screen.getByRole('img', { name: 'Profile picture' })).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('image variant', () => {
|
|
33
|
-
it('has image role and accessible label on the container', () => {
|
|
34
|
-
renderWithTheme(<Avatar src="https://example.com/avatar.jpg" alt="Jane Smith" />);
|
|
35
|
-
expect(screen.getByRole('img', { name: 'Jane Smith' })).toBeInTheDocument();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('does not duplicate the accessible label (regression: duplicate label bug)', () => {
|
|
39
|
-
// Previously, both the outer View and the inner Image had accessibilityLabel={label},
|
|
40
|
-
// causing screen readers to announce the name twice.
|
|
41
|
-
renderWithTheme(<Avatar src="https://example.com/avatar.jpg" alt="Jane Smith" />);
|
|
42
|
-
// Only one element should have aria-label="Jane Smith".
|
|
43
|
-
const labeled = screen.getAllByLabelText('Jane Smith');
|
|
44
|
-
expect(labeled).toHaveLength(1);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
|
-
import { Avatar } from './Avatar.native';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
tags: ['!stable', 'alpha'],
|
|
8
|
-
title: 'React Native/UI/Avatar',
|
|
9
|
-
component: Avatar,
|
|
10
|
-
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
|
-
parameters: { layout: 'centered' },
|
|
12
|
-
argTypes: {
|
|
13
|
-
size: { control: 'select', options: ['small', 'medium', 'large'] },
|
|
14
|
-
},
|
|
15
|
-
} satisfies Meta<typeof Avatar>;
|
|
16
|
-
|
|
17
|
-
export default meta;
|
|
18
|
-
type Story = StoryObj<typeof meta>;
|
|
19
|
-
|
|
20
|
-
export const WithInitials: Story = { args: { name: 'John Doe', size: 'medium' } };
|
|
21
|
-
export const Small: Story = { args: { name: 'Jane Smith', size: 'small' } };
|
|
22
|
-
export const Large: Story = { args: { name: 'Alex Brown', size: 'large' } };
|
|
23
|
-
export const SingleName: Story = { args: { name: 'Alice', size: 'medium' } };
|
|
24
|
-
export const AllSizes: Story = {
|
|
25
|
-
render: () => (
|
|
26
|
-
<div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
|
|
27
|
-
{(['small', 'medium', 'large'] as const).map(s => (
|
|
28
|
-
<Avatar key={s} name="John Doe" size={s} />
|
|
29
|
-
))}
|
|
30
|
-
</div>
|
|
31
|
-
),
|
|
32
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { StyleSheet, View, Text, Image, StyleProp, ViewStyle } from 'react-native';
|
|
2
|
-
import { font, border } from '../../../native/tokens';
|
|
3
|
-
import { useTheme } from '../../../native/ThemeContext';
|
|
4
|
-
|
|
5
|
-
type AvatarSize = 'small' | 'medium' | 'large';
|
|
6
|
-
|
|
7
|
-
interface AvatarProps {
|
|
8
|
-
src?: string;
|
|
9
|
-
alt?: string;
|
|
10
|
-
name?: string;
|
|
11
|
-
size?: AvatarSize;
|
|
12
|
-
style?: StyleProp<ViewStyle>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const sizeMap: Record<AvatarSize, { container: number; fontSize: number }> = {
|
|
16
|
-
small: { container: 32, fontSize: font.size.bodyS },
|
|
17
|
-
medium: { container: 48, fontSize: font.size.body },
|
|
18
|
-
large: { container: 72, fontSize: font.size.heading3 },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function getInitials(name: string): string {
|
|
22
|
-
return name
|
|
23
|
-
.split(' ')
|
|
24
|
-
.filter(Boolean)
|
|
25
|
-
.slice(0, 2)
|
|
26
|
-
.map(word => word[0].toUpperCase())
|
|
27
|
-
.join('');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const styles = StyleSheet.create({
|
|
31
|
-
container: {
|
|
32
|
-
borderRadius: border.radius.full,
|
|
33
|
-
overflow: 'hidden',
|
|
34
|
-
alignItems: 'center',
|
|
35
|
-
justifyContent: 'center',
|
|
36
|
-
},
|
|
37
|
-
image: {
|
|
38
|
-
width: '100%',
|
|
39
|
-
height: '100%',
|
|
40
|
-
},
|
|
41
|
-
initials: {
|
|
42
|
-
fontFamily: font.family.body,
|
|
43
|
-
fontWeight: font.weight.semibold,
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
export function Avatar({ src, alt, name, size = 'medium', style }: Readonly<AvatarProps>) {
|
|
48
|
-
const { colors } = useTheme();
|
|
49
|
-
const { container, fontSize } = sizeMap[size];
|
|
50
|
-
const label = alt ?? name ?? 'User avatar';
|
|
51
|
-
|
|
52
|
-
if (src) {
|
|
53
|
-
return (
|
|
54
|
-
<View
|
|
55
|
-
style={[styles.container, { width: container, height: container }, style]}
|
|
56
|
-
accessibilityLabel={label}
|
|
57
|
-
accessibilityRole="image"
|
|
58
|
-
>
|
|
59
|
-
<Image source={{ uri: src }} style={styles.image} />
|
|
60
|
-
</View>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const initials = name ? getInitials(name) : '';
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<View
|
|
68
|
-
style={[
|
|
69
|
-
styles.container,
|
|
70
|
-
{ width: container, height: container, backgroundColor: colors.colorBlue },
|
|
71
|
-
style,
|
|
72
|
-
]}
|
|
73
|
-
accessibilityLabel={label}
|
|
74
|
-
accessibilityRole="image"
|
|
75
|
-
>
|
|
76
|
-
<Text
|
|
77
|
-
style={[styles.initials, { fontSize, color: colors.colorOnBlue }]}
|
|
78
|
-
importantForAccessibility="no"
|
|
79
|
-
accessibilityElementsHidden={true}
|
|
80
|
-
>
|
|
81
|
-
{initials}
|
|
82
|
-
</Text>
|
|
83
|
-
</View>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
-
import * as Stories from './Badge.native.stories';
|
|
3
|
-
|
|
4
|
-
<Meta of={Stories} />
|
|
5
|
-
|
|
6
|
-
# Badge (React Native)
|
|
7
|
-
|
|
8
|
-
React Native badge component for short status labels and counts. Five semantic variants matching the web `Badge` API.
|
|
9
|
-
|
|
10
|
-
> **Status: alpha** — API may change before stable release.
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
```tsx
|
|
15
|
-
import { Badge } from '@boostdev/design-system-components';
|
|
16
|
-
|
|
17
|
-
<Badge variant="success">Active</Badge>
|
|
18
|
-
<Badge variant="error">3</Badge>
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Examples
|
|
22
|
-
|
|
23
|
-
### All variants
|
|
24
|
-
<Canvas of={Stories.AllVariants} />
|
|
25
|
-
|
|
26
|
-
### Primary
|
|
27
|
-
<Canvas of={Stories.Primary} />
|
|
28
|
-
|
|
29
|
-
### Secondary
|
|
30
|
-
<Canvas of={Stories.Secondary} />
|
|
31
|
-
|
|
32
|
-
### Success
|
|
33
|
-
<Canvas of={Stories.Success} />
|
|
34
|
-
|
|
35
|
-
### Error
|
|
36
|
-
<Canvas of={Stories.Error} />
|
|
37
|
-
|
|
38
|
-
### Warning
|
|
39
|
-
<Canvas of={Stories.Warning} />
|
|
40
|
-
|
|
41
|
-
## Props
|
|
42
|
-
|
|
43
|
-
<ArgTypes of={Stories} />
|
|
44
|
-
|
|
45
|
-
## Props reference
|
|
46
|
-
|
|
47
|
-
| Prop | Type | Default | Description |
|
|
48
|
-
|------|------|---------|-------------|
|
|
49
|
-
| `variant` | `"primary" \| "secondary" \| "success" \| "error" \| "warning"` | `"primary"` | Semantic colour |
|
|
50
|
-
| `children` | `ReactNode` | — | Badge label |
|
|
51
|
-
|
|
52
|
-
## Accessibility
|
|
53
|
-
|
|
54
|
-
- Badge is decorative — wrap in a component with `accessibilityLabel` when colour alone conveys status (e.g. `accessibilityLabel="3 notifications"`)
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
3
|
-
import { Badge } from './Badge.native';
|
|
4
|
-
|
|
5
|
-
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
-
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
describe('Badge (native)', () => {
|
|
10
|
-
it('renders children text', () => {
|
|
11
|
-
renderWithTheme(<Badge>New</Badge>);
|
|
12
|
-
expect(screen.getByText('New')).toBeInTheDocument();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('defaults to primary variant', () => {
|
|
16
|
-
const { container } = renderWithTheme(<Badge>Default</Badge>);
|
|
17
|
-
expect(screen.getByText('Default')).toBeInTheDocument();
|
|
18
|
-
expect(container).toBeTruthy();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('renders with secondary variant', () => {
|
|
22
|
-
renderWithTheme(<Badge variant="secondary">Info</Badge>);
|
|
23
|
-
expect(screen.getByText('Info')).toBeInTheDocument();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('renders with success variant', () => {
|
|
27
|
-
renderWithTheme(<Badge variant="success">Active</Badge>);
|
|
28
|
-
expect(screen.getByText('Active')).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('renders with error variant', () => {
|
|
32
|
-
renderWithTheme(<Badge variant="error">Failed</Badge>);
|
|
33
|
-
expect(screen.getByText('Failed')).toBeInTheDocument();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('renders with warning variant', () => {
|
|
37
|
-
renderWithTheme(<Badge variant="warning">Pending</Badge>);
|
|
38
|
-
expect(screen.getByText('Pending')).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('accepts custom style', () => {
|
|
42
|
-
renderWithTheme(<Badge style={{ marginTop: 10 }}>Styled</Badge>);
|
|
43
|
-
expect(screen.getByText('Styled')).toBeInTheDocument();
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
|
-
import { Badge } from './Badge.native';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
tags: ['!stable', 'alpha'],
|
|
8
|
-
title: 'React Native/UI/Badge',
|
|
9
|
-
component: Badge,
|
|
10
|
-
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
|
-
parameters: { layout: 'centered' },
|
|
12
|
-
argTypes: {
|
|
13
|
-
variant: { control: 'select', options: ['primary', 'secondary', 'success', 'error', 'warning'] },
|
|
14
|
-
},
|
|
15
|
-
} satisfies Meta<typeof Badge>;
|
|
16
|
-
|
|
17
|
-
export default meta;
|
|
18
|
-
type Story = StoryObj<typeof meta>;
|
|
19
|
-
|
|
20
|
-
export const Primary: Story = { args: { children: 'Primary', variant: 'primary' } };
|
|
21
|
-
export const Secondary: Story = { args: { children: 'Secondary', variant: 'secondary' } };
|
|
22
|
-
export const Success: Story = { args: { children: 'Success', variant: 'success' } };
|
|
23
|
-
export const Error: Story = { args: { children: 'Error', variant: 'error' } };
|
|
24
|
-
export const Warning: Story = { args: { children: 'Warning', variant: 'warning' } };
|
|
25
|
-
export const AllVariants: Story = {
|
|
26
|
-
args: { children: '' },
|
|
27
|
-
render: () => (
|
|
28
|
-
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
|
29
|
-
{(['primary', 'secondary', 'success', 'error', 'warning'] as const).map(v => (
|
|
30
|
-
<Badge key={v} variant={v}>{v}</Badge>
|
|
31
|
-
))}
|
|
32
|
-
</div>
|
|
33
|
-
),
|
|
34
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
-
import { spacing, font, border } from '../../../native/tokens';
|
|
4
|
-
import { useTheme } from '../../../native/ThemeContext';
|
|
5
|
-
|
|
6
|
-
type BadgeVariant = 'primary' | 'secondary' | 'success' | 'error' | 'warning';
|
|
7
|
-
|
|
8
|
-
interface BadgeProps {
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
variant?: BadgeVariant;
|
|
11
|
-
style?: StyleProp<ViewStyle>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const styles = StyleSheet.create({
|
|
15
|
-
badge: {
|
|
16
|
-
alignSelf: 'flex-start',
|
|
17
|
-
flexDirection: 'row',
|
|
18
|
-
alignItems: 'center',
|
|
19
|
-
justifyContent: 'center',
|
|
20
|
-
paddingVertical: spacing.xxs,
|
|
21
|
-
paddingHorizontal: spacing.xs,
|
|
22
|
-
borderRadius: border.radius.full,
|
|
23
|
-
},
|
|
24
|
-
text: {
|
|
25
|
-
fontSize: font.size.bodyS,
|
|
26
|
-
fontFamily: font.family.body,
|
|
27
|
-
fontWeight: font.weight.semibold,
|
|
28
|
-
lineHeight: font.size.bodyS,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
export function Badge({ children, variant = 'primary', style }: BadgeProps) {
|
|
33
|
-
const { colors } = useTheme();
|
|
34
|
-
|
|
35
|
-
const variantColors: Record<BadgeVariant, { bg: string; text: string }> = {
|
|
36
|
-
primary: { bg: colors.colorInteractive, text: colors.colorOnInteractive },
|
|
37
|
-
secondary: { bg: colors.colorBgSubtle, text: colors.colorOnBgSubtle },
|
|
38
|
-
success: { bg: colors.colorSuccess, text: colors.colorOnSuccess },
|
|
39
|
-
error: { bg: colors.colorError, text: colors.colorOnError },
|
|
40
|
-
warning: { bg: colors.colorWarning, text: colors.colorOnWarning },
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const { bg, text } = variantColors[variant];
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<View style={[styles.badge, { backgroundColor: bg }, style]}>
|
|
47
|
-
<Text style={[styles.text, { color: text }]}>{children}</Text>
|
|
48
|
-
</View>
|
|
49
|
-
);
|
|
50
|
-
}
|