@boostdev/design-system-components 1.2.2 → 1.2.4
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 +5 -0
- package/README.md +50 -5
- package/dist/client.cjs +50 -50
- package/dist/client.css +506 -503
- package/dist/client.js +50 -50
- package/dist/index.cjs +50 -50
- package/dist/index.css +506 -503
- package/dist/index.js +50 -50
- package/dist/native/index.cjs +3692 -352
- package/dist/native/index.d.cts +359 -3
- package/dist/native/index.d.ts +359 -3
- package/dist/native/index.js +3811 -364
- package/package.json +3 -3
- package/src/components/interaction/Button/Button.module.css +1 -4
- package/src/components/interaction/Button/Button.native.stories.tsx +10 -12
- package/src/components/interaction/Button/Button.native.tsx +3 -3
- package/src/components/interaction/Dialog/Dialog.module.css +1 -1
- package/src/components/interaction/Dialog/Dialog.native.mdx +61 -0
- package/src/components/interaction/Dialog/Dialog.native.spec.tsx +73 -0
- package/src/components/interaction/Dialog/Dialog.native.stories.tsx +53 -0
- package/src/components/interaction/Dialog/Dialog.native.tsx +128 -0
- package/src/components/interaction/Drawer/Drawer.module.css +1 -1
- package/src/components/interaction/Drawer/Drawer.native.mdx +58 -0
- package/src/components/interaction/Drawer/Drawer.native.spec.tsx +81 -0
- package/src/components/interaction/Drawer/Drawer.native.stories.tsx +33 -0
- package/src/components/interaction/Drawer/Drawer.native.tsx +175 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +4 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.mdx +74 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.spec.tsx +78 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.stories.tsx +51 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.tsx +254 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +4 -4
- package/src/components/interaction/Popover/Popover.native.mdx +61 -0
- package/src/components/interaction/Popover/Popover.native.spec.tsx +73 -0
- package/src/components/interaction/Popover/Popover.native.stories.tsx +44 -0
- package/src/components/interaction/Popover/Popover.native.tsx +87 -0
- package/src/components/interaction/Rating/Rating.native.mdx +55 -0
- package/src/components/interaction/Rating/Rating.native.spec.tsx +38 -0
- package/src/components/interaction/Rating/Rating.native.stories.tsx +37 -0
- package/src/components/interaction/Rating/Rating.native.tsx +50 -0
- package/src/components/interaction/Toast/Toast.native.mdx +81 -0
- package/src/components/interaction/Toast/Toast.native.spec.tsx +80 -0
- package/src/components/interaction/Toast/Toast.native.stories.tsx +47 -0
- package/src/components/interaction/Toast/Toast.native.tsx +202 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +2 -4
- package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.mdx +78 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.spec.tsx +57 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.stories.tsx +96 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.tsx +61 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +1 -1
- package/src/components/interaction/form/Combobox/Combobox.native.mdx +86 -0
- package/src/components/interaction/form/Combobox/Combobox.native.spec.tsx +60 -0
- package/src/components/interaction/form/Combobox/Combobox.native.stories.tsx +99 -0
- package/src/components/interaction/form/Combobox/Combobox.native.tsx +211 -0
- package/src/components/interaction/form/FileInput/FileInput.native.mdx +86 -0
- package/src/components/interaction/form/FileInput/FileInput.native.spec.tsx +73 -0
- package/src/components/interaction/form/FileInput/FileInput.native.stories.tsx +69 -0
- package/src/components/interaction/form/FileInput/FileInput.native.tsx +129 -0
- package/src/components/interaction/form/FormInput/FormInput.module.css +1 -1
- package/src/components/interaction/form/FormInput/FormInput.native.mdx +81 -0
- package/src/components/interaction/form/FormInput/FormInput.native.spec.tsx +51 -0
- package/src/components/interaction/form/FormInput/FormInput.native.stories.tsx +54 -0
- package/src/components/interaction/form/FormInput/FormInput.native.tsx +122 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.native.mdx +87 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.spec.tsx +63 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.stories.tsx +63 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.tsx +198 -0
- package/src/components/interaction/form/Radio/Radio.native.spec.tsx +79 -0
- package/src/components/interaction/form/Radio/Radio.native.stories.tsx +12 -14
- package/src/components/interaction/form/Radio/Radio.native.tsx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.mdx +79 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.spec.tsx +66 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.stories.tsx +96 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.tsx +61 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -5
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.mdx +78 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.spec.tsx +82 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.stories.tsx +99 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.tsx +121 -0
- package/src/components/interaction/form/Select/Select.module.css +1 -1
- package/src/components/interaction/form/Select/Select.native.mdx +85 -0
- package/src/components/interaction/form/Select/Select.native.spec.tsx +64 -0
- package/src/components/interaction/form/Select/Select.native.stories.tsx +75 -0
- package/src/components/interaction/form/Select/Select.native.tsx +252 -0
- package/src/components/interaction/form/Slider/Slider.module.css +4 -4
- package/src/components/interaction/form/Slider/Slider.native.mdx +83 -0
- package/src/components/interaction/form/Slider/Slider.native.spec.tsx +51 -0
- package/src/components/interaction/form/Slider/Slider.native.stories.tsx +58 -0
- package/src/components/interaction/form/Slider/Slider.native.tsx +188 -0
- package/src/components/interaction/form/Switch/Switch.module.css +3 -3
- package/src/components/interaction/form/Switch/Switch.native.stories.tsx +2 -4
- package/src/components/interaction/form/Textarea/Textarea.native.mdx +76 -0
- package/src/components/interaction/form/Textarea/Textarea.native.spec.tsx +50 -0
- package/src/components/interaction/form/Textarea/Textarea.native.stories.tsx +50 -0
- package/src/components/interaction/form/Textarea/Textarea.native.tsx +108 -0
- package/src/components/interaction/form/atoms/Label.native.spec.tsx +39 -0
- package/src/components/interaction/form/atoms/Label.native.stories.tsx +1 -1
- package/src/components/interaction/form/atoms/Message.native.stories.tsx +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.native.spec.tsx +83 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +13 -19
- package/src/components/layout/Card/Card.native.spec.tsx +102 -0
- package/src/components/layout/Card/Card.native.stories.tsx +9 -11
- package/src/components/layout/Card/Card.native.tsx +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.module.css +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +7 -11
- package/src/components/layout/SectionHeader/SectionHeader.native.spec.tsx +51 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +6 -8
- package/src/components/ui/Accordion/Accordion.native.mdx +65 -0
- package/src/components/ui/Accordion/Accordion.native.spec.tsx +69 -0
- package/src/components/ui/Accordion/Accordion.native.stories.tsx +52 -0
- package/src/components/ui/Accordion/Accordion.native.tsx +141 -0
- package/src/components/ui/Alert/Alert.module.css +1 -1
- package/src/components/ui/Alert/Alert.native.spec.tsx +76 -0
- package/src/components/ui/Alert/Alert.native.stories.tsx +8 -10
- package/src/components/ui/Alert/Alert.native.tsx +7 -7
- package/src/components/ui/Avatar/Avatar.module.css +1 -1
- package/src/components/ui/Avatar/Avatar.native.stories.tsx +6 -8
- package/src/components/ui/Avatar/Avatar.native.tsx +2 -2
- package/src/components/ui/Badge/Badge.module.css +1 -1
- package/src/components/ui/Badge/Badge.native.spec.tsx +45 -0
- package/src/components/ui/Badge/Badge.native.stories.tsx +6 -8
- package/src/components/ui/Badge/Badge.native.tsx +2 -2
- package/src/components/ui/Breadcrumb/Breadcrumb.native.mdx +52 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.native.spec.tsx +49 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.native.stories.tsx +46 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.native.tsx +83 -0
- package/src/components/ui/Calendar/Calendar.module.css +2 -2
- package/src/components/ui/Calendar/Calendar.native.mdx +58 -0
- package/src/components/ui/Calendar/Calendar.native.spec.tsx +92 -0
- package/src/components/ui/Calendar/Calendar.native.stories.tsx +45 -0
- package/src/components/ui/Calendar/Calendar.native.tsx +294 -0
- package/src/components/ui/Carousel/Carousel.module.css +1 -1
- package/src/components/ui/Carousel/Carousel.native.mdx +55 -0
- package/src/components/ui/Carousel/Carousel.native.spec.tsx +55 -0
- package/src/components/ui/Carousel/Carousel.native.stories.tsx +59 -0
- package/src/components/ui/Carousel/Carousel.native.tsx +184 -0
- package/src/components/ui/Collapsible/Collapsible.native.mdx +53 -0
- package/src/components/ui/Collapsible/Collapsible.native.spec.tsx +68 -0
- package/src/components/ui/Collapsible/Collapsible.native.stories.tsx +48 -0
- package/src/components/ui/Collapsible/Collapsible.native.tsx +139 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.mdx +50 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.spec.tsx +37 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.stories.tsx +48 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.tsx +54 -0
- package/src/components/ui/Link/Link.native.mdx +53 -0
- package/src/components/ui/Link/Link.native.spec.tsx +35 -0
- package/src/components/ui/Link/Link.native.stories.tsx +46 -0
- package/src/components/ui/Link/Link.native.tsx +48 -0
- package/src/components/ui/Loading/Loading.module.css +1 -1
- package/src/components/ui/Loading/Loading.native.stories.tsx +6 -8
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +1 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.native.spec.tsx +85 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +8 -10
- package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +7 -7
- package/src/components/ui/Pagination/Pagination.module.css +5 -1
- package/src/components/ui/Pagination/Pagination.native.mdx +62 -0
- package/src/components/ui/Pagination/Pagination.native.spec.tsx +86 -0
- package/src/components/ui/Pagination/Pagination.native.stories.tsx +47 -0
- package/src/components/ui/Pagination/Pagination.native.tsx +157 -0
- package/src/components/ui/Progress/Progress.module.css +2 -2
- package/src/components/ui/Progress/Progress.native.spec.tsx +56 -0
- package/src/components/ui/Progress/Progress.native.stories.tsx +1 -1
- package/src/components/ui/Progress/Progress.native.tsx +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.native.mdx +63 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.native.spec.tsx +39 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.native.stories.tsx +36 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.native.tsx +176 -0
- package/src/components/ui/Separator/Separator.native.spec.tsx +39 -0
- package/src/components/ui/Separator/Separator.native.stories.tsx +10 -14
- package/src/components/ui/Skeleton/Skeleton.native.spec.tsx +39 -0
- package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +6 -8
- package/src/components/ui/Skeleton/Skeleton.native.tsx +2 -1
- package/src/components/ui/Table/Table.native.mdx +72 -0
- package/src/components/ui/Table/Table.native.spec.tsx +83 -0
- package/src/components/ui/Table/Table.native.stories.tsx +55 -0
- package/src/components/ui/Table/Table.native.tsx +196 -0
- package/src/components/ui/Tabs/Tabs.native.mdx +65 -0
- package/src/components/ui/Tabs/Tabs.native.spec.tsx +65 -0
- package/src/components/ui/Tabs/Tabs.native.stories.tsx +57 -0
- package/src/components/ui/Tabs/Tabs.native.tsx +133 -0
- package/src/components/ui/Tooltip/Tooltip.module.css +1 -1
- package/src/components/ui/Tooltip/Tooltip.native.mdx +47 -0
- package/src/components/ui/Tooltip/Tooltip.native.spec.tsx +37 -0
- package/src/components/ui/Tooltip/Tooltip.native.stories.tsx +38 -0
- package/src/components/ui/Tooltip/Tooltip.native.tsx +57 -0
- package/src/components/ui/Typography/Typography.native.spec.tsx +49 -0
- package/src/components/ui/Typography/Typography.native.stories.tsx +8 -10
- package/src/native/ThemeContext.tsx +3 -3
- package/src/native.ts +37 -0
- package/src/stories/ReactNative.mdx +48 -13
- package/src/web-components/interaction/BdsAccordion.stories.tsx +1 -1
- package/src/web-components/interaction/BdsCollapsible.stories.tsx +1 -1
- package/src/web-components/interaction/BdsSkipLink.stories.tsx +1 -1
- package/src/web-components/interaction/BdsTabs.stories.tsx +1 -1
- package/src/web-components/interaction/BdsTooltip.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCombobox.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFileInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFormInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsRadio.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSelect.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSlider.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSwitch.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsTextarea.stories.tsx +1 -1
- package/src/web-components/ui/BdsButtonGroup.stories.tsx +1 -1
- package/src/web-components/ui/BdsCard.stories.tsx +1 -1
- package/src/web-components/ui/BdsIconWrapper.stories.tsx +1 -1
- package/src/web-components/ui/BdsRating.stories.tsx +1 -1
- package/src/web-components/ui/BdsSectionHeader.stories.tsx +1 -1
|
@@ -7,7 +7,7 @@ const meta = {
|
|
|
7
7
|
tags: ['!stable', 'alpha'],
|
|
8
8
|
title: 'React Native/Form/Switch',
|
|
9
9
|
component: Switch,
|
|
10
|
-
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
10
|
+
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
11
|
parameters: { layout: 'padded' },
|
|
12
12
|
} satisfies Meta<typeof Switch>;
|
|
13
13
|
|
|
@@ -27,9 +27,7 @@ export const Interactive: Story = {
|
|
|
27
27
|
render: () => {
|
|
28
28
|
const [value, setValue] = useState(false);
|
|
29
29
|
return (
|
|
30
|
-
<
|
|
31
|
-
<Switch label="Enable feature" name="feature" value={value} onChange={setValue} />
|
|
32
|
-
</ThemeProvider>
|
|
30
|
+
<Switch label="Enable feature" name="feature" value={value} onChange={setValue} />
|
|
33
31
|
);
|
|
34
32
|
},
|
|
35
33
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './Textarea.native.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Textarea (React Native)
|
|
7
|
+
|
|
8
|
+
Multiline text input with label, optional hint, and error state. Uses a `TextInput` with `multiline` enabled and `textAlignVertical: 'top'`.
|
|
9
|
+
|
|
10
|
+
> **Status: alpha** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Textarea } from '@boostdev/components';
|
|
16
|
+
import { useState } from 'react';
|
|
17
|
+
|
|
18
|
+
function Example() {
|
|
19
|
+
const [bio, setBio] = useState('');
|
|
20
|
+
return (
|
|
21
|
+
<Textarea
|
|
22
|
+
label="Bio"
|
|
23
|
+
name="bio"
|
|
24
|
+
placeholder="Tell us about yourself..."
|
|
25
|
+
value={bio}
|
|
26
|
+
onChange={setBio}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
### Default
|
|
35
|
+
<Canvas of={Stories.Default} />
|
|
36
|
+
|
|
37
|
+
### With placeholder
|
|
38
|
+
<Canvas of={Stories.WithPlaceholder} />
|
|
39
|
+
|
|
40
|
+
### With error
|
|
41
|
+
<Canvas of={Stories.WithError} />
|
|
42
|
+
|
|
43
|
+
### With hint
|
|
44
|
+
<Canvas of={Stories.WithHint} />
|
|
45
|
+
|
|
46
|
+
### Disabled
|
|
47
|
+
<Canvas of={Stories.Disabled} />
|
|
48
|
+
|
|
49
|
+
### Interactive
|
|
50
|
+
<Canvas of={Stories.Interactive} />
|
|
51
|
+
|
|
52
|
+
## Props
|
|
53
|
+
|
|
54
|
+
<ArgTypes of={Stories} />
|
|
55
|
+
|
|
56
|
+
## Props reference
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Default | Description |
|
|
59
|
+
|------|------|---------|-------------|
|
|
60
|
+
| `label` | ReactNode | — | Visible label text |
|
|
61
|
+
| `name` | string | — | Field name (used for accessibility) |
|
|
62
|
+
| `value` | string | — | Controlled value |
|
|
63
|
+
| `defaultValue` | string | — | Uncontrolled default value |
|
|
64
|
+
| `placeholder` | string | — | Placeholder text |
|
|
65
|
+
| `numberOfLines` | number | `4` | Number of visible lines |
|
|
66
|
+
| `error` | string | — | Error message displayed below the input |
|
|
67
|
+
| `hint` | string | — | Hint text displayed below the input |
|
|
68
|
+
| `required` | boolean | `false` | Marks the field as required |
|
|
69
|
+
| `disabled` | boolean | `false` | Prevents interaction, reduces opacity |
|
|
70
|
+
| `onChange` | `(value: string) => void` | — | Called with the new text value |
|
|
71
|
+
|
|
72
|
+
## Accessibility
|
|
73
|
+
|
|
74
|
+
- The label is announced by screen readers via `accessibilityLabel`
|
|
75
|
+
- Hint text is announced via `accessibilityHint`
|
|
76
|
+
- `disabled` communicates the disabled state via `accessibilityState`
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
3
|
+
import { Textarea } from './Textarea.native';
|
|
4
|
+
|
|
5
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('Textarea (native)', () => {
|
|
10
|
+
it('renders the label', () => {
|
|
11
|
+
renderWithTheme(<Textarea label="Message" name="message" />);
|
|
12
|
+
expect(screen.getByText('Message')).toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders with placeholder text', () => {
|
|
16
|
+
renderWithTheme(
|
|
17
|
+
<Textarea label="Bio" name="bio" placeholder="Tell us about yourself..." />,
|
|
18
|
+
);
|
|
19
|
+
expect(screen.getByPlaceholderText('Tell us about yourself...')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('renders with a value', () => {
|
|
23
|
+
renderWithTheme(
|
|
24
|
+
<Textarea label="Notes" name="notes" value="Some content" />,
|
|
25
|
+
);
|
|
26
|
+
expect(screen.getByDisplayValue('Some content')).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('shows error message', () => {
|
|
30
|
+
renderWithTheme(
|
|
31
|
+
<Textarea label="Message" name="message" error="Message is required." />,
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Message is required.');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('shows hint message', () => {
|
|
37
|
+
renderWithTheme(
|
|
38
|
+
<Textarea label="Message" name="message" hint="Minimum 20 characters." />,
|
|
39
|
+
);
|
|
40
|
+
expect(screen.getByText('Minimum 20 characters.')).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('applies disabled opacity', () => {
|
|
44
|
+
const { container } = renderWithTheme(
|
|
45
|
+
<Textarea label="Notes" name="notes" disabled />,
|
|
46
|
+
);
|
|
47
|
+
const inputs = container.querySelectorAll('textarea, [style*="opacity"]');
|
|
48
|
+
expect(inputs.length).toBeGreaterThan(0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
4
|
+
import { Textarea } from './Textarea.native';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
tags: ['!stable', 'alpha'],
|
|
8
|
+
title: 'React Native/Form/Textarea',
|
|
9
|
+
component: Textarea,
|
|
10
|
+
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
|
+
parameters: { layout: 'padded' },
|
|
12
|
+
} satisfies Meta<typeof Textarea>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
args: { label: 'Message', name: 'message' },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const WithPlaceholder: Story = {
|
|
22
|
+
args: { label: 'Bio', name: 'bio', placeholder: 'Tell us about yourself...' },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const WithError: Story = {
|
|
26
|
+
args: { label: 'Message', name: 'message', error: 'Message is required.' },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const WithHint: Story = {
|
|
30
|
+
args: { label: 'Message', name: 'message', hint: 'Minimum 20 characters.' },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const Disabled: Story = {
|
|
34
|
+
args: { label: 'Notes', name: 'notes', disabled: true, value: 'Read-only content.' },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Interactive: Story = {
|
|
38
|
+
render: () => {
|
|
39
|
+
const [value, setValue] = useState('');
|
|
40
|
+
return (
|
|
41
|
+
<Textarea
|
|
42
|
+
label="Feedback"
|
|
43
|
+
name="feedback"
|
|
44
|
+
placeholder="Share your thoughts..."
|
|
45
|
+
value={value}
|
|
46
|
+
onChange={setValue}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ReactNode, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
View,
|
|
5
|
+
TextInput,
|
|
6
|
+
StyleProp,
|
|
7
|
+
ViewStyle,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import { spacing, font, border, lineHeight } from '../../../../native/tokens';
|
|
10
|
+
import { useTheme } from '../../../../native/ThemeContext';
|
|
11
|
+
import { Label } from '../atoms/Label.native';
|
|
12
|
+
import { Message } from '../atoms/Message.native';
|
|
13
|
+
|
|
14
|
+
interface TextareaProps {
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
name: string;
|
|
17
|
+
value?: string;
|
|
18
|
+
defaultValue?: string;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
hint?: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
numberOfLines?: number;
|
|
24
|
+
onChange?: (value: string) => void;
|
|
25
|
+
onBlur?: () => void;
|
|
26
|
+
onFocus?: () => void;
|
|
27
|
+
style?: StyleProp<ViewStyle>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const styles = StyleSheet.create({
|
|
31
|
+
container: {
|
|
32
|
+
gap: spacing.xxs,
|
|
33
|
+
},
|
|
34
|
+
textarea: {
|
|
35
|
+
fontFamily: font.family.body,
|
|
36
|
+
fontSize: font.size.body,
|
|
37
|
+
lineHeight: lineHeight.body,
|
|
38
|
+
paddingHorizontal: spacing.s,
|
|
39
|
+
paddingVertical: spacing.xs,
|
|
40
|
+
borderWidth: 1,
|
|
41
|
+
borderRadius: border.radius.xs,
|
|
42
|
+
minHeight: 100,
|
|
43
|
+
textAlignVertical: 'top',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export function Textarea({
|
|
48
|
+
label,
|
|
49
|
+
name,
|
|
50
|
+
value,
|
|
51
|
+
defaultValue,
|
|
52
|
+
placeholder,
|
|
53
|
+
error,
|
|
54
|
+
hint,
|
|
55
|
+
disabled = false,
|
|
56
|
+
numberOfLines = 4,
|
|
57
|
+
onChange,
|
|
58
|
+
onBlur,
|
|
59
|
+
onFocus,
|
|
60
|
+
style,
|
|
61
|
+
}: Readonly<TextareaProps>) {
|
|
62
|
+
const { colors } = useTheme();
|
|
63
|
+
const [focused, setFocused] = useState(false);
|
|
64
|
+
|
|
65
|
+
const borderColor = error
|
|
66
|
+
? colors.colorError
|
|
67
|
+
: focused
|
|
68
|
+
? colors.colorInteractive
|
|
69
|
+
: colors.colorOnBg;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<View style={[styles.container, style]}>
|
|
73
|
+
<Label label={label} />
|
|
74
|
+
<TextInput
|
|
75
|
+
accessibilityLabel={typeof label === 'string' ? label : name}
|
|
76
|
+
accessibilityState={{ disabled }}
|
|
77
|
+
accessibilityHint={hint}
|
|
78
|
+
multiline
|
|
79
|
+
numberOfLines={numberOfLines}
|
|
80
|
+
value={value}
|
|
81
|
+
defaultValue={defaultValue}
|
|
82
|
+
placeholder={placeholder}
|
|
83
|
+
placeholderTextColor={colors.colorOnBgSubtle}
|
|
84
|
+
editable={!disabled}
|
|
85
|
+
onChangeText={onChange}
|
|
86
|
+
onFocus={() => {
|
|
87
|
+
setFocused(true);
|
|
88
|
+
onFocus?.();
|
|
89
|
+
}}
|
|
90
|
+
onBlur={() => {
|
|
91
|
+
setFocused(false);
|
|
92
|
+
onBlur?.();
|
|
93
|
+
}}
|
|
94
|
+
style={[
|
|
95
|
+
styles.textarea,
|
|
96
|
+
{
|
|
97
|
+
borderColor,
|
|
98
|
+
color: colors.colorOnBg,
|
|
99
|
+
backgroundColor: colors.colorBg,
|
|
100
|
+
},
|
|
101
|
+
disabled && { opacity: 0.4 },
|
|
102
|
+
]}
|
|
103
|
+
/>
|
|
104
|
+
<Message type="error" message={error} />
|
|
105
|
+
<Message type="hint" message={hint} />
|
|
106
|
+
</View>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
3
|
+
import { Label } from './Label.native';
|
|
4
|
+
|
|
5
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('Label (native)', () => {
|
|
10
|
+
it('renders the label text', () => {
|
|
11
|
+
renderWithTheme(<Label label="Email address" />);
|
|
12
|
+
expect(screen.getByText('Email address')).toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders ReactNode content as label', () => {
|
|
16
|
+
renderWithTheme(
|
|
17
|
+
<Label
|
|
18
|
+
label={
|
|
19
|
+
<span data-testid="custom-label">
|
|
20
|
+
Custom <strong>label</strong>
|
|
21
|
+
</span>
|
|
22
|
+
}
|
|
23
|
+
/>,
|
|
24
|
+
);
|
|
25
|
+
expect(screen.getByTestId('custom-label')).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('applies semibold font weight', () => {
|
|
29
|
+
renderWithTheme(<Label label="Name" />);
|
|
30
|
+
const label = screen.getByText('Name');
|
|
31
|
+
expect(label).toHaveStyle({ fontWeight: expect.stringMatching(/600|bold|semibold/) });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('applies custom style when provided', () => {
|
|
35
|
+
renderWithTheme(<Label label="Name" style={{ marginBottom: 8 }} />);
|
|
36
|
+
const label = screen.getByText('Name');
|
|
37
|
+
expect(label).toHaveStyle({ marginBottom: 8 });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -7,7 +7,7 @@ const meta = {
|
|
|
7
7
|
tags: ['!stable', 'alpha'],
|
|
8
8
|
title: 'React Native/Form/Label',
|
|
9
9
|
component: Label,
|
|
10
|
-
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
10
|
+
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
11
|
parameters: { layout: 'centered' },
|
|
12
12
|
} satisfies Meta<typeof Label>;
|
|
13
13
|
|
|
@@ -7,7 +7,7 @@ const meta = {
|
|
|
7
7
|
tags: ['!stable', 'alpha'],
|
|
8
8
|
title: 'React Native/Form/Message',
|
|
9
9
|
component: Message,
|
|
10
|
-
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
10
|
+
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
11
11
|
parameters: { layout: 'padded' },
|
|
12
12
|
argTypes: {
|
|
13
13
|
type: { control: 'select', options: ['hint', 'error'] },
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
3
|
+
import { ButtonGroup } from './ButtonGroup.native';
|
|
4
|
+
|
|
5
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('ButtonGroup (native)', () => {
|
|
10
|
+
it('renders children', () => {
|
|
11
|
+
renderWithTheme(
|
|
12
|
+
<ButtonGroup>
|
|
13
|
+
<span data-testid="child-a">A</span>
|
|
14
|
+
<span data-testid="child-b">B</span>
|
|
15
|
+
</ButtonGroup>,
|
|
16
|
+
);
|
|
17
|
+
expect(screen.getByTestId('child-a')).toBeInTheDocument();
|
|
18
|
+
expect(screen.getByTestId('child-b')).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('applies accessibilityLabel to the container', () => {
|
|
22
|
+
const { container } = renderWithTheme(
|
|
23
|
+
<ButtonGroup accessibilityLabel="Actions">
|
|
24
|
+
<span>OK</span>
|
|
25
|
+
</ButtonGroup>,
|
|
26
|
+
);
|
|
27
|
+
expect(container.firstChild).toHaveAttribute('aria-label', 'Actions');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders without accessibilityLabel when not provided', () => {
|
|
31
|
+
const { container } = renderWithTheme(
|
|
32
|
+
<ButtonGroup>
|
|
33
|
+
<span>OK</span>
|
|
34
|
+
</ButtonGroup>,
|
|
35
|
+
);
|
|
36
|
+
expect(container.firstChild).not.toHaveAttribute('aria-label');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders in row direction by default', () => {
|
|
40
|
+
const { container } = renderWithTheme(
|
|
41
|
+
<ButtonGroup>
|
|
42
|
+
<span>A</span>
|
|
43
|
+
</ButtonGroup>,
|
|
44
|
+
);
|
|
45
|
+
expect(container.firstChild).toHaveStyle({ flexDirection: 'row' });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('renders in row direction for flow variant', () => {
|
|
49
|
+
const { container } = renderWithTheme(
|
|
50
|
+
<ButtonGroup variant="flow">
|
|
51
|
+
<span>A</span>
|
|
52
|
+
</ButtonGroup>,
|
|
53
|
+
);
|
|
54
|
+
expect(container.firstChild).toHaveStyle({ flexDirection: 'row' });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders in row direction for content variant', () => {
|
|
58
|
+
const { container } = renderWithTheme(
|
|
59
|
+
<ButtonGroup variant="content">
|
|
60
|
+
<span>A</span>
|
|
61
|
+
</ButtonGroup>,
|
|
62
|
+
);
|
|
63
|
+
expect(container.firstChild).toHaveStyle({ flexDirection: 'row' });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('renders in column direction for card variant', () => {
|
|
67
|
+
const { container } = renderWithTheme(
|
|
68
|
+
<ButtonGroup variant="card">
|
|
69
|
+
<span>A</span>
|
|
70
|
+
</ButtonGroup>,
|
|
71
|
+
);
|
|
72
|
+
expect(container.firstChild).toHaveStyle({ flexDirection: 'column' });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('renders in column direction for modal variant', () => {
|
|
76
|
+
const { container } = renderWithTheme(
|
|
77
|
+
<ButtonGroup variant="modal">
|
|
78
|
+
<span>A</span>
|
|
79
|
+
</ButtonGroup>,
|
|
80
|
+
);
|
|
81
|
+
expect(container.firstChild).toHaveStyle({ flexDirection: 'column' });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -8,7 +8,7 @@ const meta = {
|
|
|
8
8
|
tags: ['!stable', 'alpha'],
|
|
9
9
|
title: 'React Native/Layout/ButtonGroup',
|
|
10
10
|
component: ButtonGroup,
|
|
11
|
-
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
11
|
+
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
12
12
|
parameters: { layout: 'padded' },
|
|
13
13
|
argTypes: {
|
|
14
14
|
variant: { control: 'select', options: ['flow', 'card', 'modal', 'content'] },
|
|
@@ -20,31 +20,25 @@ type Story = StoryObj<typeof meta>;
|
|
|
20
20
|
|
|
21
21
|
export const Flow: Story = {
|
|
22
22
|
render: (args) => (
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</ButtonGroup>
|
|
28
|
-
</ThemeProvider>
|
|
23
|
+
<ButtonGroup {...args} variant="flow">
|
|
24
|
+
<Button>Primary</Button>
|
|
25
|
+
<Button variant="ghost">Secondary</Button>
|
|
26
|
+
</ButtonGroup>
|
|
29
27
|
),
|
|
30
28
|
};
|
|
31
29
|
export const Card: Story = {
|
|
32
30
|
render: (args) => (
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</ButtonGroup>
|
|
38
|
-
</ThemeProvider>
|
|
31
|
+
<ButtonGroup {...args} variant="card">
|
|
32
|
+
<Button>Confirm</Button>
|
|
33
|
+
<Button variant="ghost">Cancel</Button>
|
|
34
|
+
</ButtonGroup>
|
|
39
35
|
),
|
|
40
36
|
};
|
|
41
37
|
export const Modal: Story = {
|
|
42
38
|
render: (args) => (
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
</ButtonGroup>
|
|
48
|
-
</ThemeProvider>
|
|
39
|
+
<ButtonGroup {...args} variant="modal">
|
|
40
|
+
<Button>Save</Button>
|
|
41
|
+
<Button variant="ghost">Discard</Button>
|
|
42
|
+
</ButtonGroup>
|
|
49
43
|
),
|
|
50
44
|
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
+
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
3
|
+
import { Card } from './Card.native';
|
|
4
|
+
|
|
5
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('Card (native)', () => {
|
|
10
|
+
it('renders children', () => {
|
|
11
|
+
renderWithTheme(
|
|
12
|
+
<Card>
|
|
13
|
+
<span data-testid="content">Hello</span>
|
|
14
|
+
</Card>,
|
|
15
|
+
);
|
|
16
|
+
expect(screen.getByTestId('content')).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('renders as a View when onPress is not provided', () => {
|
|
20
|
+
const { container } = renderWithTheme(
|
|
21
|
+
<Card>
|
|
22
|
+
<span>Content</span>
|
|
23
|
+
</Card>,
|
|
24
|
+
);
|
|
25
|
+
expect(container.firstChild).not.toHaveAttribute('role');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders as a Pressable with button role when onPress is provided', () => {
|
|
29
|
+
const handlePress = vi.fn();
|
|
30
|
+
renderWithTheme(
|
|
31
|
+
<Card onPress={handlePress} accessibilityLabel="Tap me">
|
|
32
|
+
<span>Content</span>
|
|
33
|
+
</Card>,
|
|
34
|
+
);
|
|
35
|
+
expect(screen.getByRole('button', { name: 'Tap me' })).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('calls onPress when pressed', () => {
|
|
39
|
+
const handlePress = vi.fn();
|
|
40
|
+
renderWithTheme(
|
|
41
|
+
<Card onPress={handlePress} accessibilityLabel="Tap me">
|
|
42
|
+
<span>Content</span>
|
|
43
|
+
</Card>,
|
|
44
|
+
);
|
|
45
|
+
fireEvent.click(screen.getByRole('button'));
|
|
46
|
+
expect(handlePress).toHaveBeenCalledOnce();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('applies accessibilityLabel to the pressable card', () => {
|
|
50
|
+
renderWithTheme(
|
|
51
|
+
<Card onPress={() => {}} accessibilityLabel="Product card">
|
|
52
|
+
<span>Content</span>
|
|
53
|
+
</Card>,
|
|
54
|
+
);
|
|
55
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Product card');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('applies border for outlined variant', () => {
|
|
59
|
+
const { container } = renderWithTheme(
|
|
60
|
+
<Card variant="outlined">
|
|
61
|
+
<span>Content</span>
|
|
62
|
+
</Card>,
|
|
63
|
+
);
|
|
64
|
+
expect(container.firstChild).toHaveStyle({ borderWidth: 1 });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders elevated variant without throwing', () => {
|
|
68
|
+
renderWithTheme(
|
|
69
|
+
<Card variant="elevated">
|
|
70
|
+
<span data-testid="elevated-content">Content</span>
|
|
71
|
+
</Card>,
|
|
72
|
+
);
|
|
73
|
+
expect(screen.getByTestId('elevated-content')).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('renders with no padding when padding is none', () => {
|
|
77
|
+
const { container } = renderWithTheme(
|
|
78
|
+
<Card padding="none">
|
|
79
|
+
<span>Content</span>
|
|
80
|
+
</Card>,
|
|
81
|
+
);
|
|
82
|
+
expect(container.firstChild).toHaveStyle({ padding: 0 });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('centers content when textAlign is center', () => {
|
|
86
|
+
const { container } = renderWithTheme(
|
|
87
|
+
<Card textAlign="center">
|
|
88
|
+
<span>Content</span>
|
|
89
|
+
</Card>,
|
|
90
|
+
);
|
|
91
|
+
expect(container.firstChild).toHaveStyle({ alignItems: 'center' });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('aligns content to flex-end when textAlign is end', () => {
|
|
95
|
+
const { container } = renderWithTheme(
|
|
96
|
+
<Card textAlign="end">
|
|
97
|
+
<span>Content</span>
|
|
98
|
+
</Card>,
|
|
99
|
+
);
|
|
100
|
+
expect(container.firstChild).toHaveStyle({ alignItems: 'flex-end' });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -8,7 +8,7 @@ const meta = {
|
|
|
8
8
|
tags: ['!stable', 'alpha'],
|
|
9
9
|
title: 'React Native/Layout/Card',
|
|
10
10
|
component: Card,
|
|
11
|
-
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
11
|
+
decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
|
|
12
12
|
parameters: { layout: 'padded' },
|
|
13
13
|
argTypes: {
|
|
14
14
|
variant: { control: 'select', options: ['default', 'elevated', 'outlined'] },
|
|
@@ -39,15 +39,13 @@ export const Pressable: Story = {
|
|
|
39
39
|
};
|
|
40
40
|
export const AllVariants: Story = {
|
|
41
41
|
render: () => (
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</div>
|
|
51
|
-
</ThemeProvider>
|
|
42
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px', maxWidth: 320 }}>
|
|
43
|
+
{(['default', 'elevated', 'outlined'] as const).map(v => (
|
|
44
|
+
<Card key={v} variant={v} padding="medium">
|
|
45
|
+
<Typography variant="h3">{v.charAt(0).toUpperCase() + v.slice(1)}</Typography>
|
|
46
|
+
<Typography>Card with {v} variant.</Typography>
|
|
47
|
+
</Card>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
52
50
|
),
|
|
53
51
|
};
|