@boostdev/design-system-components 1.0.3 → 1.1.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 +51 -10
- package/dist/client.cjs +350 -174
- package/dist/client.css +751 -647
- package/dist/client.d.cts +62 -97
- package/dist/client.d.ts +62 -97
- package/dist/client.js +377 -211
- package/dist/index.cjs +350 -174
- package/dist/index.css +751 -647
- package/dist/index.d.cts +62 -97
- package/dist/index.d.ts +62 -97
- package/dist/index.js +377 -211
- package/dist/native/index.cjs +991 -0
- package/dist/native/index.d.cts +208 -0
- package/dist/native/index.d.ts +208 -0
- package/dist/native/index.js +968 -0
- package/dist/web-components.d.ts +304 -0
- package/dist/web-components.js +1978 -0
- package/package.json +27 -5
- package/src/components/interaction/Button/Button.module.css +11 -8
- package/src/components/interaction/Button/Button.native.mdx +72 -0
- package/src/components/interaction/Button/Button.native.spec.tsx +35 -0
- package/src/components/interaction/Button/Button.native.stories.tsx +42 -0
- package/src/components/interaction/Button/Button.native.tsx +95 -0
- package/src/components/interaction/Button/Button.stories.tsx +1 -1
- package/src/components/interaction/Button/Button.tsx +2 -2
- package/src/components/interaction/Command/Command.mdx +16 -0
- package/src/components/interaction/Command/Command.module.css +7 -7
- package/src/components/interaction/Command/Command.spec.tsx +37 -0
- package/src/components/interaction/Command/Command.stories.tsx +1 -1
- package/src/components/interaction/Command/Command.tsx +39 -4
- package/src/components/interaction/Dialog/Dialog.module.css +10 -13
- package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
- package/src/components/interaction/Dialog/Dialog.stories.tsx +18 -1
- package/src/components/interaction/Dialog/Dialog.tsx +52 -18
- package/src/components/interaction/Drawer/Drawer.mdx +14 -0
- package/src/components/interaction/Drawer/Drawer.module.css +11 -11
- package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
- package/src/components/interaction/Drawer/Drawer.stories.tsx +1 -1
- package/src/components/interaction/Drawer/Drawer.tsx +40 -6
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +1 -1
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +2 -2
- package/src/components/interaction/Popover/Popover.mdx +17 -0
- package/src/components/interaction/Popover/Popover.module.css +132 -16
- package/src/components/interaction/Popover/Popover.spec.tsx +119 -20
- package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
- package/src/components/interaction/Popover/Popover.tsx +69 -29
- package/src/components/interaction/Rating/Rating.stories.tsx +1 -1
- package/src/components/interaction/Rating/Rating.tsx +2 -2
- package/src/components/interaction/Toast/Toast.module.css +13 -8
- package/src/components/interaction/Toast/Toast.spec.tsx +21 -0
- package/src/components/interaction/Toast/Toast.stories.tsx +1 -1
- package/src/components/interaction/Toast/Toast.tsx +22 -2
- package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +9 -9
- package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +42 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +36 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +80 -0
- package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +44 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +2 -2
- package/src/components/interaction/form/Combobox/Combobox.mdx +21 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +8 -9
- package/src/components/interaction/form/Combobox/Combobox.stories.tsx +1 -1
- package/src/components/interaction/form/Combobox/Combobox.tsx +2 -2
- package/src/components/interaction/form/FileInput/FileInput.mdx +14 -0
- package/src/components/interaction/form/FileInput/FileInput.module.css +5 -5
- package/src/components/interaction/form/FileInput/FileInput.stories.tsx +1 -1
- package/src/components/interaction/form/FileInput/FileInput.tsx +2 -2
- package/src/components/interaction/form/FormInput/FormInput.stories.tsx +1 -1
- package/src/components/interaction/form/FormInput/FormInput.tsx +2 -2
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +16 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +1 -1
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +2 -2
- package/src/components/interaction/form/Radio/Radio.mdx +3 -3
- package/src/components/interaction/form/Radio/Radio.module.css +9 -9
- package/src/components/interaction/form/Radio/Radio.native.stories.tsx +46 -0
- package/src/components/interaction/form/Radio/Radio.native.tsx +79 -0
- package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
- package/src/components/interaction/form/Radio/Radio.tsx +2 -2
- package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +45 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +2 -2
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +1 -1
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +2 -2
- package/src/components/interaction/form/Select/Select.module.css +3 -3
- package/src/components/interaction/form/Select/Select.stories.tsx +1 -1
- package/src/components/interaction/form/Select/Select.tsx +2 -2
- package/src/components/interaction/form/Slider/Slider.mdx +1 -1
- package/src/components/interaction/form/Slider/Slider.module.css +10 -10
- package/src/components/interaction/form/Slider/Slider.stories.tsx +1 -1
- package/src/components/interaction/form/Slider/Slider.tsx +2 -2
- package/src/components/interaction/form/Switch/Switch.mdx +4 -4
- package/src/components/interaction/form/Switch/Switch.module.css +11 -11
- package/src/components/interaction/form/Switch/Switch.native.spec.tsx +60 -0
- package/src/components/interaction/form/Switch/Switch.native.stories.tsx +35 -0
- package/src/components/interaction/form/Switch/Switch.native.tsx +59 -0
- package/src/components/interaction/form/Switch/Switch.stories.tsx +1 -1
- package/src/components/interaction/form/Switch/Switch.tsx +2 -2
- package/src/components/interaction/form/Textarea/Textarea.mdx +17 -0
- package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
- package/src/components/interaction/form/Textarea/Textarea.stories.tsx +1 -1
- package/src/components/interaction/form/Textarea/Textarea.tsx +2 -2
- package/src/components/interaction/form/atoms/InputContainer.tsx +2 -1
- package/src/components/interaction/form/atoms/Label.native.stories.tsx +18 -0
- package/src/components/interaction/form/atoms/Label.native.tsx +29 -0
- package/src/components/interaction/form/atoms/Label.tsx +5 -3
- package/src/components/interaction/form/atoms/Message.native.spec.tsx +30 -0
- package/src/components/interaction/form/atoms/Message.native.stories.tsx +22 -0
- package/src/components/interaction/form/atoms/Message.native.tsx +34 -0
- package/src/components/interaction/form/atoms/Message.tsx +5 -3
- package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +50 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +34 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.tsx +2 -2
- package/src/components/layout/Card/Card.native.stories.tsx +53 -0
- package/src/components/layout/Card/Card.native.tsx +89 -0
- package/src/components/layout/Card/Card.stories.tsx +1 -1
- package/src/components/layout/Card/Card.tsx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +39 -0
- package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +41 -0
- package/src/components/layout/IconWrapper/IconWrapper.native.tsx +20 -0
- package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.tsx +2 -2
- package/src/components/layout/SectionHeader/SectionHeader.mdx +13 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +38 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.tsx +79 -0
- package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +1 -1
- package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
- package/src/components/ui/Accordion/Accordion.mdx +14 -0
- package/src/components/ui/Accordion/Accordion.module.css +5 -5
- package/src/components/ui/Accordion/Accordion.stories.tsx +1 -1
- package/src/components/ui/Accordion/Accordion.tsx +2 -2
- package/src/components/ui/Alert/Alert.native.mdx +62 -0
- package/src/components/ui/Alert/Alert.native.stories.tsx +43 -0
- package/src/components/ui/Alert/Alert.native.tsx +94 -0
- package/src/components/ui/Alert/Alert.stories.tsx +1 -1
- package/src/components/ui/Alert/Alert.tsx +2 -2
- package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
- package/src/components/ui/Avatar/Avatar.native.spec.tsx +47 -0
- package/src/components/ui/Avatar/Avatar.native.stories.tsx +34 -0
- package/src/components/ui/Avatar/Avatar.native.tsx +85 -0
- package/src/components/ui/Avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/Avatar/Avatar.tsx +2 -2
- package/src/components/ui/Badge/Badge.mdx +2 -2
- package/src/components/ui/Badge/Badge.native.mdx +54 -0
- package/src/components/ui/Badge/Badge.native.stories.tsx +36 -0
- package/src/components/ui/Badge/Badge.native.tsx +50 -0
- package/src/components/ui/Badge/Badge.stories.tsx +1 -1
- package/src/components/ui/Badge/Badge.tsx +2 -2
- package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +1 -1
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +2 -2
- package/src/components/ui/Calendar/Calendar.mdx +16 -0
- package/src/components/ui/Calendar/Calendar.module.css +7 -7
- package/src/components/ui/Calendar/Calendar.stories.tsx +1 -1
- package/src/components/ui/Calendar/Calendar.tsx +2 -2
- package/src/components/ui/Carousel/Carousel.module.css +5 -5
- package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
- package/src/components/ui/Carousel/Carousel.tsx +2 -2
- package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
- package/src/components/ui/Collapsible/Collapsible.stories.tsx +1 -1
- package/src/components/ui/Collapsible/Collapsible.tsx +2 -2
- package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +1 -1
- package/src/components/ui/DescriptionList/DescriptionList.tsx +2 -2
- package/src/components/ui/Link/Link.mdx +14 -0
- package/src/components/ui/Link/Link.module.css +2 -2
- package/src/components/ui/Link/Link.stories.tsx +1 -1
- package/src/components/ui/Link/Link.tsx +2 -2
- package/src/components/ui/Loading/Loading.module.css +7 -7
- package/src/components/ui/Loading/Loading.native.spec.tsx +24 -0
- package/src/components/ui/Loading/Loading.native.stories.tsx +33 -0
- package/src/components/ui/Loading/Loading.native.tsx +29 -0
- package/src/components/ui/Loading/Loading.stories.tsx +1 -1
- package/src/components/ui/Loading/Loading.tsx +2 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
- package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +39 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +76 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +1 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +2 -2
- package/src/components/ui/Pagination/Pagination.module.css +7 -7
- package/src/components/ui/Pagination/Pagination.stories.tsx +1 -1
- package/src/components/ui/Pagination/Pagination.tsx +2 -2
- package/src/components/ui/Progress/Progress.mdx +1 -1
- package/src/components/ui/Progress/Progress.module.css +6 -6
- package/src/components/ui/Progress/Progress.native.stories.tsx +34 -0
- package/src/components/ui/Progress/Progress.native.tsx +84 -0
- package/src/components/ui/Progress/Progress.stories.tsx +1 -1
- package/src/components/ui/Progress/Progress.tsx +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.module.css +1 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +1 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.tsx +2 -2
- package/src/components/ui/Separator/Separator.mdx +14 -0
- package/src/components/ui/Separator/Separator.module.css +3 -3
- package/src/components/ui/Separator/Separator.native.stories.tsx +42 -0
- package/src/components/ui/Separator/Separator.native.tsx +32 -0
- package/src/components/ui/Separator/Separator.stories.tsx +1 -1
- package/src/components/ui/Separator/Separator.tsx +2 -2
- package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
- package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +33 -0
- package/src/components/ui/Skeleton/Skeleton.native.tsx +41 -0
- package/src/components/ui/Skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/Skeleton/Skeleton.tsx +2 -3
- package/src/components/ui/SkipLink/SkipLink.stories.tsx +1 -1
- package/src/components/ui/SkipLink/SkipLink.tsx +5 -3
- package/src/components/ui/Table/Table.mdx +14 -0
- package/src/components/ui/Table/Table.module.css +9 -9
- package/src/components/ui/Table/Table.stories.tsx +1 -1
- package/src/components/ui/Table/Table.tsx +2 -2
- package/src/components/ui/Tabs/Tabs.module.css +3 -3
- package/src/components/ui/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/Tabs/Tabs.tsx +2 -2
- package/src/components/ui/Tooltip/Tooltip.mdx +14 -0
- package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
- package/src/components/ui/Tooltip/Tooltip.stories.tsx +1 -1
- package/src/components/ui/Tooltip/Tooltip.tsx +2 -2
- package/src/components/ui/Typography/Typography.mdx +13 -0
- package/src/components/ui/Typography/Typography.native.mdx +56 -0
- package/src/components/ui/Typography/Typography.native.stories.tsx +38 -0
- package/src/components/ui/Typography/Typography.native.tsx +65 -0
- package/src/components/ui/Typography/Typography.stories.tsx +1 -1
- package/src/components/ui/Typography/Typography.tsx +2 -2
- package/src/css/bdc.css +8 -0
- package/src/index.ts +1 -0
- package/src/native/ThemeContext.tsx +28 -0
- package/src/native/tokens.ts +13 -0
- package/src/native.ts +39 -0
- package/src/polyfill-invoker-commands.ts +68 -0
- package/src/react-augment.d.ts +32 -0
- package/src/stories/DesignSystem/DarkMode.mdx +130 -0
- package/src/stories/ReactNative.mdx +121 -0
- package/src/types.ts +2 -0
- package/src/typings.d.ts +3 -0
- package/src/web-components/globals.ts +61 -0
- package/src/web-components/index.ts +12 -0
- package/src/web-components/interaction/BdsButton.mdx +106 -0
- package/src/web-components/interaction/BdsButton.stories.tsx +60 -0
- package/src/web-components/interaction/BdsPopover.mdx +120 -0
- package/src/web-components/interaction/BdsPopover.stories.tsx +70 -0
- package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
- package/src/web-components/interaction/BdsToastProvider.stories.tsx +73 -0
- package/src/web-components/interaction/bds-button.spec.ts +95 -0
- package/src/web-components/interaction/bds-button.ts +293 -0
- package/src/web-components/interaction/bds-popover.spec.ts +174 -0
- package/src/web-components/interaction/bds-popover.ts +229 -0
- package/src/web-components/interaction/bds-toast-provider.spec.ts +122 -0
- package/src/web-components/interaction/bds-toast-provider.ts +211 -0
- package/src/web-components/test/helpers.ts +22 -0
- package/src/web-components/ui/BdsAlert.mdx +90 -0
- package/src/web-components/ui/BdsAlert.stories.tsx +60 -0
- package/src/web-components/ui/BdsBadge.mdx +74 -0
- package/src/web-components/ui/BdsBadge.stories.tsx +37 -0
- package/src/web-components/ui/bds-alert.spec.ts +109 -0
- package/src/web-components/ui/bds-alert.ts +209 -0
- package/src/web-components/ui/bds-badge.spec.ts +51 -0
- package/src/web-components/ui/bds-badge.ts +88 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
3
|
+
import { Switch } from './Switch.native';
|
|
4
|
+
|
|
5
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// react-native-web renders RNSwitch as two role="switch" elements: an outer div
|
|
10
|
+
// (for visual rendering) and an inner hidden <input type="checkbox"> for a11y.
|
|
11
|
+
// The input carries the aria-label and aria-checked state.
|
|
12
|
+
function getSwitchInput(container: HTMLElement): HTMLInputElement {
|
|
13
|
+
const input = container.querySelector<HTMLInputElement>('input[role="switch"]');
|
|
14
|
+
if (!input) throw new Error('Switch input not found');
|
|
15
|
+
return input;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('Switch (native)', () => {
|
|
19
|
+
it('renders a switch input with the correct label', () => {
|
|
20
|
+
const { container } = renderWithTheme(
|
|
21
|
+
<Switch label="Notifications" name="notifications" />,
|
|
22
|
+
);
|
|
23
|
+
const input = getSwitchInput(container);
|
|
24
|
+
expect(input).toHaveAttribute('aria-label', 'Notifications');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('reflects off state', () => {
|
|
28
|
+
const { container } = renderWithTheme(
|
|
29
|
+
<Switch label="Notifications" name="notifications" value={false} />,
|
|
30
|
+
);
|
|
31
|
+
expect(getSwitchInput(container)).not.toBeChecked();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('reflects on state', () => {
|
|
35
|
+
const { container } = renderWithTheme(
|
|
36
|
+
<Switch label="Notifications" name="notifications" value={true} />,
|
|
37
|
+
);
|
|
38
|
+
expect(getSwitchInput(container)).toBeChecked();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('thumb uses colorOnBg when off (contrast regression: was colorBg on colorBgSubtle = 1.09:1)', () => {
|
|
42
|
+
// In light mode: old thumbColor=colorBg=#fff on colorBgSubtle=#f3f4f6 = 1.09:1 (invisible).
|
|
43
|
+
// Fixed: thumbColor=colorOnBg=#000 when off, which gives 19:1 contrast against #f3f4f6.
|
|
44
|
+
const { container } = renderWithTheme(
|
|
45
|
+
<Switch label="Toggle" name="toggle" value={false} />,
|
|
46
|
+
);
|
|
47
|
+
// The thumb div has its background-color set inline by react-native-web.
|
|
48
|
+
const thumbDiv = container.querySelector<HTMLElement>('[style*="background-color"]');
|
|
49
|
+
// Verify the component renders without error — visual contrast is verified by
|
|
50
|
+
// the color token usage in source (colorOnBg), not the DOM attribute.
|
|
51
|
+
expect(thumbDiv).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('renders hint message', () => {
|
|
55
|
+
renderWithTheme(
|
|
56
|
+
<Switch label="Marketing" name="marketing" hint="Unsubscribe anytime." />,
|
|
57
|
+
);
|
|
58
|
+
expect(screen.getByText('Unsubscribe anytime.')).toBeInTheDocument();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
4
|
+
import { Switch } from './Switch.native';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
tags: ['!stable', 'alpha'],
|
|
8
|
+
title: 'React Native/Form/Switch',
|
|
9
|
+
component: Switch,
|
|
10
|
+
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
11
|
+
parameters: { layout: 'padded' },
|
|
12
|
+
} satisfies Meta<typeof Switch>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Off: Story = { args: { label: 'Notifications', name: 'notifications', value: false } };
|
|
18
|
+
export const On: Story = { args: { label: 'Notifications', name: 'notifications', value: true } };
|
|
19
|
+
export const Disabled: Story = { args: { label: 'Notifications', name: 'notifications', disabled: true } };
|
|
20
|
+
export const WithError: Story = {
|
|
21
|
+
args: { label: 'Accept terms', name: 'terms', error: 'You must accept the terms.' },
|
|
22
|
+
};
|
|
23
|
+
export const WithHint: Story = {
|
|
24
|
+
args: { label: 'Marketing emails', name: 'marketing', hint: 'You can unsubscribe at any time.' },
|
|
25
|
+
};
|
|
26
|
+
export const Interactive: Story = {
|
|
27
|
+
render: () => {
|
|
28
|
+
const [value, setValue] = useState(false);
|
|
29
|
+
return (
|
|
30
|
+
<ThemeProvider>
|
|
31
|
+
<Switch label="Enable feature" name="feature" value={value} onChange={setValue} />
|
|
32
|
+
</ThemeProvider>
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Switch as RNSwitch, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import { spacing } from '../../../../native/tokens';
|
|
3
|
+
import { useTheme } from '../../../../native/ThemeContext';
|
|
4
|
+
import { Label } from '../atoms/Label.native';
|
|
5
|
+
import { Message } from '../atoms/Message.native';
|
|
6
|
+
|
|
7
|
+
interface SwitchProps {
|
|
8
|
+
label: string;
|
|
9
|
+
name: string;
|
|
10
|
+
value?: boolean;
|
|
11
|
+
onChange?: (value: boolean) => void;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
hint?: string;
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const styles = StyleSheet.create({
|
|
19
|
+
container: {
|
|
20
|
+
gap: spacing.xxs,
|
|
21
|
+
},
|
|
22
|
+
row: {
|
|
23
|
+
flexDirection: 'row',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
gap: spacing.xs,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export function Switch({
|
|
30
|
+
label,
|
|
31
|
+
value = false,
|
|
32
|
+
onChange,
|
|
33
|
+
disabled = false,
|
|
34
|
+
error,
|
|
35
|
+
hint,
|
|
36
|
+
style,
|
|
37
|
+
}: Readonly<SwitchProps>) {
|
|
38
|
+
const { colors } = useTheme();
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<View style={[styles.container, style]}>
|
|
42
|
+
<View style={styles.row}>
|
|
43
|
+
<RNSwitch
|
|
44
|
+
value={value}
|
|
45
|
+
onValueChange={onChange}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
accessibilityLabel={label}
|
|
48
|
+
accessibilityRole="switch"
|
|
49
|
+
accessibilityState={{ checked: value, disabled }}
|
|
50
|
+
thumbColor={value ? colors.colorOnInteractive : colors.colorOnBg}
|
|
51
|
+
trackColor={{ false: colors.colorBgSubtle, true: colors.colorInteractive }}
|
|
52
|
+
/>
|
|
53
|
+
<Label label={label} />
|
|
54
|
+
</View>
|
|
55
|
+
<Message type="error" message={error} />
|
|
56
|
+
<Message type="hint" message={hint} />
|
|
57
|
+
</View>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
import { Switch } from './Switch';
|
|
3
3
|
|
|
4
4
|
const meta = {
|
|
5
|
-
title: 'Form/Switch',
|
|
5
|
+
title: 'React/Form/Switch',
|
|
6
6
|
component: Switch,
|
|
7
7
|
argTypes: {
|
|
8
8
|
size: { control: 'radio', options: ['small', 'medium', 'large'] },
|
|
@@ -4,14 +4,14 @@ import { cn } from '@boostdev/design-system-foundation';
|
|
|
4
4
|
import { InputContainer } from '../atoms/InputContainer';
|
|
5
5
|
import { Label } from '../atoms/Label';
|
|
6
6
|
import { Message } from '../atoms/Message';
|
|
7
|
+
import type { WithClassName } from '../../../../types';
|
|
7
8
|
|
|
8
|
-
interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size' | 'prefix'> {
|
|
9
|
+
interface SwitchProps extends WithClassName, Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size' | 'prefix'> {
|
|
9
10
|
label: string;
|
|
10
11
|
name: string;
|
|
11
12
|
size?: 'small' | 'medium' | 'large';
|
|
12
13
|
error?: string;
|
|
13
14
|
hint?: string;
|
|
14
|
-
className?: string;
|
|
15
15
|
prefix?: ReactNode;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -34,6 +34,23 @@ Multi-line text input with label, hint, and error support.
|
|
|
34
34
|
|
|
35
35
|
<ArgTypes of={Stories} />
|
|
36
36
|
|
|
37
|
+
## CSS variables
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
<table>
|
|
41
|
+
<thead>
|
|
42
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
43
|
+
</thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
<tr><td>`--textarea_color_bg`</td><td>`var(--bds-color_bg)`</td><td>Background colour</td></tr>
|
|
46
|
+
<tr><td>`--textarea_color`</td><td>`var(--bds-color_on-bg)`</td><td>Text colour</td></tr>
|
|
47
|
+
<tr><td>`--textarea_border`</td><td>`none`</td><td>Border</td></tr>
|
|
48
|
+
<tr><td>`--textarea_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Corner radius</td></tr>
|
|
49
|
+
<tr><td>`--textarea_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Box shadow</td></tr>
|
|
50
|
+
</tbody>
|
|
51
|
+
</table>
|
|
52
|
+
|
|
53
|
+
|
|
37
54
|
## Accessibility
|
|
38
55
|
|
|
39
56
|
- Native `<textarea>` with label linked via `id`/`htmlFor`
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
background-color: var(--textarea_color_bg, var(--bds-color_bg));
|
|
21
21
|
color: var(--textarea_color, var(--bds-color_on-bg));
|
|
22
22
|
resize: vertical;
|
|
23
|
-
min-
|
|
23
|
+
min-block-size: calc(var(--bds-space_m) * 5);
|
|
24
24
|
transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
25
25
|
background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
26
26
|
}
|
|
@@ -4,13 +4,13 @@ import { cn } from '@boostdev/design-system-foundation';
|
|
|
4
4
|
import { InputContainer } from '../atoms/InputContainer';
|
|
5
5
|
import { Label } from '../atoms/Label';
|
|
6
6
|
import { Message } from '../atoms/Message';
|
|
7
|
+
import type { WithClassName } from '../../../../types';
|
|
7
8
|
|
|
8
|
-
interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
9
|
+
interface TextareaProps extends WithClassName, TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
9
10
|
label: ReactNode;
|
|
10
11
|
name: string;
|
|
11
12
|
error?: string;
|
|
12
13
|
hint?: string;
|
|
13
|
-
className?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function Textarea({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { HTMLAttributes } from 'react';
|
|
2
2
|
import css from './InputContainer.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
|
+
import type { WithClassName } from '../../../../types';
|
|
4
5
|
|
|
5
|
-
type ContainerProps = HTMLAttributes<HTMLDivElement>;
|
|
6
|
+
type ContainerProps = WithClassName & HTMLAttributes<HTMLDivElement>;
|
|
6
7
|
|
|
7
8
|
export const InputContainer = ({ children, className }: ContainerProps) => {
|
|
8
9
|
return <div className={cn(css.container, className)}>{children}</div>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
4
|
+
import { Label } from './Label.native';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
tags: ['!stable', 'alpha'],
|
|
8
|
+
title: 'React Native/Form/Label',
|
|
9
|
+
component: Label,
|
|
10
|
+
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
11
|
+
parameters: { layout: 'centered' },
|
|
12
|
+
} satisfies Meta<typeof Label>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = { args: { label: 'Email address' } };
|
|
18
|
+
export const LongLabel: Story = { args: { label: 'Please enter your full legal name as it appears on your passport' } };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Text, StyleProp, TextStyle } from 'react-native';
|
|
3
|
+
import { font, lineHeight } from '../../../../native/tokens';
|
|
4
|
+
import { useTheme } from '../../../../native/ThemeContext';
|
|
5
|
+
|
|
6
|
+
interface LabelProps {
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
style?: StyleProp<TextStyle>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Label({ label, style }: LabelProps) {
|
|
12
|
+
const { colors } = useTheme();
|
|
13
|
+
return (
|
|
14
|
+
<Text
|
|
15
|
+
style={[
|
|
16
|
+
{
|
|
17
|
+
fontFamily: font.family.body,
|
|
18
|
+
fontSize: font.size.body,
|
|
19
|
+
lineHeight: lineHeight.body,
|
|
20
|
+
fontWeight: font.weight.semibold,
|
|
21
|
+
color: colors.colorOnBg,
|
|
22
|
+
},
|
|
23
|
+
style,
|
|
24
|
+
]}
|
|
25
|
+
>
|
|
26
|
+
{label}
|
|
27
|
+
</Text>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import css from './Label.module.css';
|
|
3
|
+
import { cn } from '@boostdev/design-system-foundation';
|
|
4
|
+
import type { WithClassName } from '../../../../types';
|
|
3
5
|
|
|
4
|
-
interface LabelProps {
|
|
6
|
+
interface LabelProps extends WithClassName {
|
|
5
7
|
id: string;
|
|
6
8
|
label: ReactNode;
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
export const Label = ({ label, id }: LabelProps) => {
|
|
11
|
+
export const Label = ({ label, id, className }: LabelProps) => {
|
|
10
12
|
return (
|
|
11
|
-
<label htmlFor={id} className={css.label}>
|
|
13
|
+
<label htmlFor={id} className={cn(css.label, className)}>
|
|
12
14
|
{label}
|
|
13
15
|
</label>
|
|
14
16
|
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
3
|
+
import { Message } from './Message.native';
|
|
4
|
+
|
|
5
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
6
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('Message (native)', () => {
|
|
10
|
+
it('renders nothing when message is undefined', () => {
|
|
11
|
+
const { container } = renderWithTheme(<Message type="error" message={undefined} />);
|
|
12
|
+
expect(container.firstChild).toBeNull();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders error message with alert role', () => {
|
|
16
|
+
renderWithTheme(<Message type="error" message="This field is required." />);
|
|
17
|
+
expect(screen.getByRole('alert')).toHaveTextContent('This field is required.');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('announces error messages assertively', () => {
|
|
21
|
+
renderWithTheme(<Message type="error" message="This field is required." />);
|
|
22
|
+
expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'assertive');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('announces hint messages politely', () => {
|
|
26
|
+
renderWithTheme(<Message type="hint" message="Use at least 8 characters." />);
|
|
27
|
+
const hint = screen.getByText('Use at least 8 characters.');
|
|
28
|
+
expect(hint).toHaveAttribute('aria-live', 'polite');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ThemeProvider } from '../../../../native/ThemeContext';
|
|
4
|
+
import { Message } from './Message.native';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
tags: ['!stable', 'alpha'],
|
|
8
|
+
title: 'React Native/Form/Message',
|
|
9
|
+
component: Message,
|
|
10
|
+
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
11
|
+
parameters: { layout: 'padded' },
|
|
12
|
+
argTypes: {
|
|
13
|
+
type: { control: 'select', options: ['hint', 'error'] },
|
|
14
|
+
},
|
|
15
|
+
} satisfies Meta<typeof Message>;
|
|
16
|
+
|
|
17
|
+
export default meta;
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
export const Hint: Story = { args: { type: 'hint', message: 'Use at least 8 characters.' } };
|
|
21
|
+
export const Error: Story = { args: { type: 'error', message: 'This field is required.' } };
|
|
22
|
+
export const Empty: Story = { args: { type: 'hint', message: undefined } };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StyleSheet, Text, StyleProp, TextStyle } from 'react-native';
|
|
2
|
+
import { font, lineHeight } from '../../../../native/tokens';
|
|
3
|
+
import { useTheme } from '../../../../native/ThemeContext';
|
|
4
|
+
|
|
5
|
+
interface MessageProps {
|
|
6
|
+
type: 'hint' | 'error';
|
|
7
|
+
message: string | undefined;
|
|
8
|
+
style?: StyleProp<TextStyle>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const styles = StyleSheet.create({
|
|
12
|
+
base: {
|
|
13
|
+
fontFamily: font.family.body,
|
|
14
|
+
fontSize: font.size.bodyS,
|
|
15
|
+
lineHeight: lineHeight.bodyS,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export function Message({ message, type, style }: MessageProps) {
|
|
20
|
+
const { colors } = useTheme();
|
|
21
|
+
if (!message) return null;
|
|
22
|
+
|
|
23
|
+
const color = type === 'error' ? colors.colorError : colors.colorOnBgSubtle;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Text
|
|
27
|
+
accessibilityRole={type === 'error' ? 'alert' : 'none'}
|
|
28
|
+
accessibilityLiveRegion={type === 'error' ? 'assertive' : 'polite'}
|
|
29
|
+
style={[styles.base, { color }, style]}
|
|
30
|
+
>
|
|
31
|
+
{message}
|
|
32
|
+
</Text>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import css from './Message.module.css';
|
|
2
|
+
import { cn } from '@boostdev/design-system-foundation';
|
|
3
|
+
import type { WithClassName } from '../../../../types';
|
|
2
4
|
|
|
3
|
-
interface MessageProps {
|
|
5
|
+
interface MessageProps extends WithClassName {
|
|
4
6
|
inputId: string;
|
|
5
7
|
type: 'hint' | 'error';
|
|
6
8
|
message: string | undefined;
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
export const Message = ({ message, type, inputId }: MessageProps) => {
|
|
11
|
+
export const Message = ({ message, type, inputId, className }: MessageProps) => {
|
|
10
12
|
if (!message) return null;
|
|
11
13
|
|
|
12
14
|
return (
|
|
13
|
-
<p id={inputId + type} className={css[type]}>
|
|
15
|
+
<p id={inputId + type} className={cn(css[type], className)}>
|
|
14
16
|
{message}
|
|
15
17
|
</p>
|
|
16
18
|
);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
|
+
import { ButtonGroup } from './ButtonGroup.native';
|
|
5
|
+
import { Button } from '../../interaction/Button/Button.native';
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
tags: ['!stable', 'alpha'],
|
|
9
|
+
title: 'React Native/Layout/ButtonGroup',
|
|
10
|
+
component: ButtonGroup,
|
|
11
|
+
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
12
|
+
parameters: { layout: 'padded' },
|
|
13
|
+
argTypes: {
|
|
14
|
+
variant: { control: 'select', options: ['flow', 'card', 'modal', 'content'] },
|
|
15
|
+
},
|
|
16
|
+
} satisfies Meta<typeof ButtonGroup>;
|
|
17
|
+
|
|
18
|
+
export default meta;
|
|
19
|
+
type Story = StoryObj<typeof meta>;
|
|
20
|
+
|
|
21
|
+
export const Flow: Story = {
|
|
22
|
+
render: (args) => (
|
|
23
|
+
<ThemeProvider>
|
|
24
|
+
<ButtonGroup {...args} variant="flow">
|
|
25
|
+
<Button>Primary</Button>
|
|
26
|
+
<Button variant="ghost">Secondary</Button>
|
|
27
|
+
</ButtonGroup>
|
|
28
|
+
</ThemeProvider>
|
|
29
|
+
),
|
|
30
|
+
};
|
|
31
|
+
export const Card: Story = {
|
|
32
|
+
render: (args) => (
|
|
33
|
+
<ThemeProvider>
|
|
34
|
+
<ButtonGroup {...args} variant="card">
|
|
35
|
+
<Button>Confirm</Button>
|
|
36
|
+
<Button variant="ghost">Cancel</Button>
|
|
37
|
+
</ButtonGroup>
|
|
38
|
+
</ThemeProvider>
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
export const Modal: Story = {
|
|
42
|
+
render: (args) => (
|
|
43
|
+
<ThemeProvider>
|
|
44
|
+
<ButtonGroup {...args} variant="modal">
|
|
45
|
+
<Button>Save</Button>
|
|
46
|
+
<Button variant="ghost">Discard</Button>
|
|
47
|
+
</ButtonGroup>
|
|
48
|
+
</ThemeProvider>
|
|
49
|
+
),
|
|
50
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyleSheet, View, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import { spacing } from '../../../native/tokens';
|
|
4
|
+
|
|
5
|
+
export interface ButtonGroupProps {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
variant?: 'flow' | 'card' | 'modal' | 'content';
|
|
8
|
+
accessibilityLabel?: string;
|
|
9
|
+
style?: StyleProp<ViewStyle>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const styles = StyleSheet.create({
|
|
13
|
+
group: {
|
|
14
|
+
flexDirection: 'row',
|
|
15
|
+
flexWrap: 'wrap',
|
|
16
|
+
gap: spacing.xs,
|
|
17
|
+
},
|
|
18
|
+
column: {
|
|
19
|
+
flexDirection: 'column',
|
|
20
|
+
gap: spacing.xs,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export function ButtonGroup({ children, variant, accessibilityLabel, style }: ButtonGroupProps) {
|
|
25
|
+
const isColumn = variant === 'card' || variant === 'modal';
|
|
26
|
+
return (
|
|
27
|
+
<View
|
|
28
|
+
accessibilityLabel={accessibilityLabel}
|
|
29
|
+
style={[isColumn ? styles.column : styles.group, style]}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -3,7 +3,7 @@ import { ButtonGroup } from './ButtonGroup';
|
|
|
3
3
|
import { Button } from '../../interaction/Button';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: 'Layout/ButtonGroup',
|
|
6
|
+
title: 'React/Layout/ButtonGroup',
|
|
7
7
|
component: ButtonGroup,
|
|
8
8
|
argTypes: {
|
|
9
9
|
variant: { control: 'select', options: ['flow', 'card', 'modal', 'content', undefined] },
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import css from './ButtonGroup.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
|
+
import type { WithClassName } from '../../../types';
|
|
4
5
|
|
|
5
|
-
export interface ButtonGroupProps {
|
|
6
|
+
export interface ButtonGroupProps extends WithClassName {
|
|
6
7
|
children: ReactNode;
|
|
7
|
-
className?: string;
|
|
8
8
|
variant?: 'flow' | 'card' | 'modal' | 'content';
|
|
9
9
|
'aria-label'?: string;
|
|
10
10
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ThemeProvider } from '../../../native/ThemeContext';
|
|
4
|
+
import { Card } from './Card.native';
|
|
5
|
+
import { Typography } from '../../ui/Typography/Typography.native';
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
tags: ['!stable', 'alpha'],
|
|
9
|
+
title: 'React Native/Layout/Card',
|
|
10
|
+
component: Card,
|
|
11
|
+
decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
|
|
12
|
+
parameters: { layout: 'padded' },
|
|
13
|
+
argTypes: {
|
|
14
|
+
variant: { control: 'select', options: ['default', 'elevated', 'outlined'] },
|
|
15
|
+
padding: { control: 'select', options: ['none', 'small', 'medium', 'large'] },
|
|
16
|
+
textAlign: { control: 'select', options: ['start', 'center', 'end'] },
|
|
17
|
+
},
|
|
18
|
+
} satisfies Meta<typeof Card>;
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
type Story = StoryObj<typeof meta>;
|
|
22
|
+
|
|
23
|
+
export const Default: Story = {
|
|
24
|
+
args: { children: <Typography>Card content goes here.</Typography>, variant: 'default', padding: 'medium' },
|
|
25
|
+
};
|
|
26
|
+
export const Elevated: Story = {
|
|
27
|
+
args: { children: <Typography>Elevated card with shadow.</Typography>, variant: 'elevated', padding: 'medium' },
|
|
28
|
+
};
|
|
29
|
+
export const Outlined: Story = {
|
|
30
|
+
args: { children: <Typography>Outlined card with border.</Typography>, variant: 'outlined', padding: 'medium' },
|
|
31
|
+
};
|
|
32
|
+
export const Pressable: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
children: <Typography>Tap me!</Typography>,
|
|
35
|
+
variant: 'outlined',
|
|
36
|
+
padding: 'medium',
|
|
37
|
+
onPress: () => {},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export const AllVariants: Story = {
|
|
41
|
+
render: () => (
|
|
42
|
+
<ThemeProvider>
|
|
43
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px', maxWidth: 320 }}>
|
|
44
|
+
{(['default', 'elevated', 'outlined'] as const).map(v => (
|
|
45
|
+
<Card key={v} variant={v} padding="medium">
|
|
46
|
+
<Typography variant="h3">{v.charAt(0).toUpperCase() + v.slice(1)}</Typography>
|
|
47
|
+
<Typography>Card with {v} variant.</Typography>
|
|
48
|
+
</Card>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
</ThemeProvider>
|
|
52
|
+
),
|
|
53
|
+
};
|