@liguelead/design-system 0.0.28 → 0.0.30
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/components/Alert/Alert.stories.tsx +60 -0
- package/components/Button/Button.appearance.ts +2 -2
- package/components/Button/Button.stories.tsx +75 -0
- package/components/Checkbox/Checkbox.stories.tsx +42 -0
- package/components/Combobox/Combobox.stories.tsx +112 -0
- package/components/Combobox/Combobox.styles.ts +7 -3
- package/components/Combobox/Combobox.tsx +3 -10
- package/components/DatePicker/DatePicker.stories.tsx +108 -0
- package/components/DatePicker/DatePicker.styles.ts +394 -50
- package/components/DatePicker/DatePicker.tsx +353 -118
- package/components/DatePicker/DatePicker.types.ts +40 -24
- package/components/Dialog/Dialog.stories.tsx +63 -0
- package/components/IconButton/IconButton.stories.tsx +49 -0
- package/components/InputOpt/InputOpt.stories.tsx +61 -0
- package/components/LinkButton/LinkButton.stories.tsx +88 -0
- package/components/PageWrapper/PageWrapper.stories.tsx +153 -0
- package/components/RadioButton/RadioButton.stories.tsx +70 -0
- package/components/RequiredAsterisk/RequiredAsterisk.stories.tsx +44 -0
- package/components/SegmentedButton/SegmentedButton.stories.tsx +182 -0
- package/components/Select/Select.stories.tsx +63 -0
- package/components/Table/Table.stories.tsx +53 -0
- package/components/Table/Table.styles.ts +76 -0
- package/components/Table/Table.tsx +157 -0
- package/components/Table/Table.types.ts +16 -0
- package/components/Table/index.ts +1 -0
- package/components/Table/utils/getPageNumbers.ts +35 -0
- package/components/Table/utils/index.ts +1 -0
- package/components/Table/utils/types.ts +4 -0
- package/components/Text/Text.stories.tsx +61 -0
- package/components/Text/Text.types.ts +1 -0
- package/components/TextField/TextField.stories.tsx +84 -0
- package/components/Toaster/Toaster.stories.tsx +129 -0
- package/components/Toaster/Toaster.ts +19 -0
- package/components/Toaster/ToasterProvider.tsx +9 -1
- package/components/Wizard/Wizard.stories.tsx +186 -0
- package/components/index.ts +1 -0
- package/package.json +16 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import InputOpt from './InputOpt'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof InputOpt> = {
|
|
5
|
+
title: 'Form/InputOpt',
|
|
6
|
+
component: InputOpt,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: {
|
|
12
|
+
length: {
|
|
13
|
+
control: 'number',
|
|
14
|
+
description: 'Number of input fields'
|
|
15
|
+
},
|
|
16
|
+
label: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'Input label'
|
|
19
|
+
},
|
|
20
|
+
helperText: {
|
|
21
|
+
control: 'text',
|
|
22
|
+
description: 'Helper text below input'
|
|
23
|
+
},
|
|
24
|
+
placeholderChar: {
|
|
25
|
+
control: 'text',
|
|
26
|
+
description: 'Placeholder character for empty fields'
|
|
27
|
+
},
|
|
28
|
+
inputMode: {
|
|
29
|
+
control: 'select',
|
|
30
|
+
options: ['numeric', 'text'],
|
|
31
|
+
description: 'Input mode for mobile keyboards'
|
|
32
|
+
},
|
|
33
|
+
autoFocus: {
|
|
34
|
+
control: 'boolean',
|
|
35
|
+
description: 'Auto focus first input on mount'
|
|
36
|
+
},
|
|
37
|
+
disabled: {
|
|
38
|
+
control: 'boolean',
|
|
39
|
+
description: 'Whether inputs are disabled'
|
|
40
|
+
},
|
|
41
|
+
requiredSymbol: {
|
|
42
|
+
control: 'boolean',
|
|
43
|
+
description: 'Show required asterisk'
|
|
44
|
+
},
|
|
45
|
+
error: {
|
|
46
|
+
control: 'boolean',
|
|
47
|
+
description: 'Whether inputs have error state'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default meta
|
|
53
|
+
type Story = StoryObj<typeof meta>
|
|
54
|
+
|
|
55
|
+
export const Default: Story = {
|
|
56
|
+
args: {
|
|
57
|
+
label: 'Verification Code',
|
|
58
|
+
length: 6,
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import LinkButton from './LinkButton'
|
|
3
|
+
import { themes } from '@liguelead/foundation'
|
|
4
|
+
import { iconMap, iconOptions } from '../../../stories/utils/icons'
|
|
5
|
+
|
|
6
|
+
type ColorTypes = NonNullable<React.ComponentProps<typeof LinkButton>['color']>
|
|
7
|
+
|
|
8
|
+
const themeColors = Object.keys(themes.spa.colors) as ColorTypes[]
|
|
9
|
+
|
|
10
|
+
const meta: Meta<typeof LinkButton> = {
|
|
11
|
+
title: 'Navigation/LinkButton',
|
|
12
|
+
component: LinkButton,
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: 'centered',
|
|
15
|
+
},
|
|
16
|
+
tags: ['autodocs'],
|
|
17
|
+
argTypes: {
|
|
18
|
+
href: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'Link URL'
|
|
21
|
+
},
|
|
22
|
+
children: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'Link content'
|
|
25
|
+
},
|
|
26
|
+
size: {
|
|
27
|
+
control: 'select',
|
|
28
|
+
options: ['sm', 'md', 'lg'],
|
|
29
|
+
description: 'Size of the link button'
|
|
30
|
+
},
|
|
31
|
+
color: {
|
|
32
|
+
control: 'select',
|
|
33
|
+
options: themeColors,
|
|
34
|
+
description: 'Color theme of the link button'
|
|
35
|
+
},
|
|
36
|
+
disabled: {
|
|
37
|
+
control: 'boolean',
|
|
38
|
+
description: 'Whether the link button is disabled'
|
|
39
|
+
},
|
|
40
|
+
fullWidth: {
|
|
41
|
+
control: 'boolean',
|
|
42
|
+
description: 'Whether the link button takes full width'
|
|
43
|
+
},
|
|
44
|
+
leftIcon: {
|
|
45
|
+
control: 'select',
|
|
46
|
+
options: iconOptions,
|
|
47
|
+
description: 'Left icon',
|
|
48
|
+
},
|
|
49
|
+
rightIcon: {
|
|
50
|
+
control: 'select',
|
|
51
|
+
options: iconOptions,
|
|
52
|
+
description: 'Right icon',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default meta
|
|
58
|
+
type Story = StoryObj<typeof meta>
|
|
59
|
+
export const Default: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
href: '#',
|
|
62
|
+
children: 'Link Button',
|
|
63
|
+
size: 'md',
|
|
64
|
+
color: themeColors[0],
|
|
65
|
+
disabled: false,
|
|
66
|
+
fullWidth: false,
|
|
67
|
+
leftIcon: 'none',
|
|
68
|
+
rightIcon: 'none',
|
|
69
|
+
},
|
|
70
|
+
render: (args) => {
|
|
71
|
+
const leftIcon = args.leftIcon === 'none' ? undefined : iconMap[args.leftIcon as keyof typeof iconMap]
|
|
72
|
+
const rightIcon = args.rightIcon === 'none' ? undefined : iconMap[args.rightIcon as keyof typeof iconMap]
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<LinkButton
|
|
76
|
+
href={args.href}
|
|
77
|
+
leftIcon={leftIcon}
|
|
78
|
+
rightIcon={rightIcon}
|
|
79
|
+
size={args.size}
|
|
80
|
+
color={args.color}
|
|
81
|
+
disabled={args.disabled}
|
|
82
|
+
fullWidth={args.fullWidth}
|
|
83
|
+
>
|
|
84
|
+
{args.children}
|
|
85
|
+
</LinkButton>
|
|
86
|
+
)
|
|
87
|
+
},
|
|
88
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import PageWrapper from './PageWrapper'
|
|
3
|
+
import Text from '../Text/Text'
|
|
4
|
+
import Button from '../Button/Button'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof PageWrapper> = {
|
|
7
|
+
title: 'Layout/PageWrapper',
|
|
8
|
+
component: PageWrapper,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'fullscreen',
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
width: {
|
|
15
|
+
control: 'text',
|
|
16
|
+
description: 'Maximum width of the wrapper (in pixels)'
|
|
17
|
+
},
|
|
18
|
+
bgColor: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: ['primary', 'secondary', 'success', 'warning', 'error', 'white', 'neutral100', 'neutral200'],
|
|
21
|
+
description: 'Background color'
|
|
22
|
+
},
|
|
23
|
+
padding: {
|
|
24
|
+
control: 'select',
|
|
25
|
+
options: ['spacing8', 'spacing12', 'spacing16', 'spacing20', 'spacing24', 'spacing32'],
|
|
26
|
+
description: 'Internal padding'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default meta
|
|
32
|
+
type Story = StoryObj<typeof meta>
|
|
33
|
+
|
|
34
|
+
export const Default: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
children: (
|
|
37
|
+
<div>
|
|
38
|
+
<Text size="heading02">Page Content</Text>
|
|
39
|
+
<Text style={{ marginTop: '16px' }}>
|
|
40
|
+
This is the content inside the page wrapper. It provides consistent spacing and layout.
|
|
41
|
+
</Text>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const WithMaxWidth: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
width: '800',
|
|
50
|
+
children: (
|
|
51
|
+
<div>
|
|
52
|
+
<Text size="heading02">Constrained Width</Text>
|
|
53
|
+
<Text style={{ marginTop: '16px' }}>
|
|
54
|
+
This wrapper has a maximum width of 800px and will be centered on larger screens.
|
|
55
|
+
</Text>
|
|
56
|
+
</div>
|
|
57
|
+
),
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const DifferentBackgrounds: Story = {
|
|
62
|
+
render: () => (
|
|
63
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }}>
|
|
64
|
+
<PageWrapper bgColor="white">
|
|
65
|
+
<Text size="heading03">White Background</Text>
|
|
66
|
+
<Text>Default white background wrapper.</Text>
|
|
67
|
+
</PageWrapper>
|
|
68
|
+
|
|
69
|
+
<PageWrapper bgColor="neutral100">
|
|
70
|
+
<Text size="heading03">Neutral Background</Text>
|
|
71
|
+
<Text>Light neutral background wrapper.</Text>
|
|
72
|
+
</PageWrapper>
|
|
73
|
+
|
|
74
|
+
<PageWrapper bgColor="primary" style={{ color: 'white' }}>
|
|
75
|
+
<Text size="heading03" style={{ color: 'white' }}>Primary Background</Text>
|
|
76
|
+
<Text style={{ color: 'white' }}>Primary colored background wrapper.</Text>
|
|
77
|
+
</PageWrapper>
|
|
78
|
+
</div>
|
|
79
|
+
),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const DifferentPadding: Story = {
|
|
83
|
+
render: () => (
|
|
84
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }}>
|
|
85
|
+
<PageWrapper padding="spacing8">
|
|
86
|
+
<Text size="heading03">Small Padding</Text>
|
|
87
|
+
<Text>This wrapper has small padding (8px).</Text>
|
|
88
|
+
</PageWrapper>
|
|
89
|
+
|
|
90
|
+
<PageWrapper padding="spacing16">
|
|
91
|
+
<Text size="heading03">Medium Padding</Text>
|
|
92
|
+
<Text>This wrapper has medium padding (16px).</Text>
|
|
93
|
+
</PageWrapper>
|
|
94
|
+
|
|
95
|
+
<PageWrapper padding="spacing32">
|
|
96
|
+
<Text size="heading03">Large Padding</Text>
|
|
97
|
+
<Text>This wrapper has large padding (32px).</Text>
|
|
98
|
+
</PageWrapper>
|
|
99
|
+
</div>
|
|
100
|
+
),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const FormLayout: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
width: '600',
|
|
106
|
+
padding: 'spacing24',
|
|
107
|
+
children: (
|
|
108
|
+
<div>
|
|
109
|
+
<Text size="heading02" style={{ marginBottom: '24px' }}>Contact Form</Text>
|
|
110
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
111
|
+
<div>
|
|
112
|
+
<Text tag="label">Name</Text>
|
|
113
|
+
<input style={{ width: '100%', padding: '8px', marginTop: '4px' }} />
|
|
114
|
+
</div>
|
|
115
|
+
<div>
|
|
116
|
+
<Text tag="label">Email</Text>
|
|
117
|
+
<input type="email" style={{ width: '100%', padding: '8px', marginTop: '4px' }} />
|
|
118
|
+
</div>
|
|
119
|
+
<div>
|
|
120
|
+
<Text tag="label">Message</Text>
|
|
121
|
+
<textarea style={{ width: '100%', padding: '8px', marginTop: '4px', minHeight: '100px' }} />
|
|
122
|
+
</div>
|
|
123
|
+
<Button style={{ marginTop: '16px' }}>Submit</Button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
),
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const DashboardLayout: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
width: '1200',
|
|
133
|
+
children: (
|
|
134
|
+
<div>
|
|
135
|
+
<Text size="heading01" style={{ marginBottom: '24px' }}>Dashboard</Text>
|
|
136
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '16px' }}>
|
|
137
|
+
<div style={{ padding: '16px', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
|
|
138
|
+
<Text size="heading03">Widget 1</Text>
|
|
139
|
+
<Text>Some dashboard content here.</Text>
|
|
140
|
+
</div>
|
|
141
|
+
<div style={{ padding: '16px', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
|
|
142
|
+
<Text size="heading03">Widget 2</Text>
|
|
143
|
+
<Text>More dashboard content here.</Text>
|
|
144
|
+
</div>
|
|
145
|
+
<div style={{ padding: '16px', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
|
|
146
|
+
<Text size="heading03">Widget 3</Text>
|
|
147
|
+
<Text>Additional dashboard content.</Text>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
),
|
|
152
|
+
},
|
|
153
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import RadioButton from './RadioButton'
|
|
3
|
+
import { iconMap, iconOptions } from '../../../stories/utils/icons'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof RadioButton> = {
|
|
6
|
+
title: 'Form/RadioButton',
|
|
7
|
+
component: RadioButton,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
argTypes: {
|
|
13
|
+
label: {
|
|
14
|
+
control: 'text',
|
|
15
|
+
description: 'Radio button label'
|
|
16
|
+
},
|
|
17
|
+
description: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'Radio button description'
|
|
20
|
+
},
|
|
21
|
+
value: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'Radio button value'
|
|
24
|
+
},
|
|
25
|
+
variant: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['default', 'box'],
|
|
28
|
+
description: 'Radio button variant'
|
|
29
|
+
},
|
|
30
|
+
checked: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Whether radio is checked'
|
|
33
|
+
},
|
|
34
|
+
disabled: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Whether radio is disabled'
|
|
37
|
+
},
|
|
38
|
+
rightIcon: {
|
|
39
|
+
control: 'select',
|
|
40
|
+
options: iconOptions,
|
|
41
|
+
description: 'Right icon',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default meta
|
|
47
|
+
type Story = StoryObj<typeof meta>
|
|
48
|
+
|
|
49
|
+
export const Default: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
label: 'Radio Option',
|
|
52
|
+
value: 'option1',
|
|
53
|
+
rightIcon: 'none',
|
|
54
|
+
},
|
|
55
|
+
render: (args) => {
|
|
56
|
+
const rightIcon = args.rightIcon === 'none' ? undefined : iconMap[args.rightIcon as keyof typeof iconMap]
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<RadioButton
|
|
60
|
+
label={args.label}
|
|
61
|
+
value={args.value}
|
|
62
|
+
description={args.description}
|
|
63
|
+
variant={args.variant}
|
|
64
|
+
checked={args.checked}
|
|
65
|
+
disabled={args.disabled}
|
|
66
|
+
rightIcon={rightIcon}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
},
|
|
70
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import RequiredAsterisk from './RequiredAsterisk'
|
|
3
|
+
import Text from '../Text/Text'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof RequiredAsterisk> = {
|
|
6
|
+
title: 'Data Display/RequiredAsterisk',
|
|
7
|
+
component: RequiredAsterisk,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default meta
|
|
15
|
+
type Story = StoryObj<typeof meta>
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {}
|
|
18
|
+
|
|
19
|
+
export const WithLabel: Story = {
|
|
20
|
+
render: () => (
|
|
21
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
|
22
|
+
<Text tag="label">Field Label</Text>
|
|
23
|
+
<RequiredAsterisk />
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const InForm: Story = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
31
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
|
32
|
+
<Text tag="label">Required Field</Text>
|
|
33
|
+
<RequiredAsterisk />
|
|
34
|
+
</div>
|
|
35
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
|
36
|
+
<Text tag="label">Another Required Field</Text>
|
|
37
|
+
<RequiredAsterisk />
|
|
38
|
+
</div>
|
|
39
|
+
<div>
|
|
40
|
+
<Text tag="label">Optional Field</Text>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import SegmentedButton from './SegmentedButton'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof SegmentedButton> = {
|
|
6
|
+
title: 'Form/SegmentedButton',
|
|
7
|
+
component: SegmentedButton,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
argTypes: {
|
|
13
|
+
size: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['sm', 'md', 'lg'],
|
|
16
|
+
description: 'Size of the segmented button'
|
|
17
|
+
},
|
|
18
|
+
color: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: ['primary', 'secondary', 'success', 'warning', 'error'],
|
|
21
|
+
description: 'Color theme of the segmented button'
|
|
22
|
+
},
|
|
23
|
+
disabled: {
|
|
24
|
+
control: 'boolean',
|
|
25
|
+
description: 'Whether the segmented button is disabled'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default meta
|
|
31
|
+
type Story = StoryObj<typeof meta>
|
|
32
|
+
|
|
33
|
+
export const Default: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
options: {
|
|
36
|
+
leftButton: { label: 'Left' },
|
|
37
|
+
rightButton: { label: 'Right' }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const Interactive: Story = {
|
|
43
|
+
render: (args) => {
|
|
44
|
+
const [selected, setSelected] = useState<'left' | 'right'>('left')
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div>
|
|
48
|
+
<SegmentedButton
|
|
49
|
+
{...args}
|
|
50
|
+
options={{
|
|
51
|
+
leftButton: {
|
|
52
|
+
label: 'Option A',
|
|
53
|
+
action: () => setSelected('left')
|
|
54
|
+
},
|
|
55
|
+
rightButton: {
|
|
56
|
+
label: 'Option B',
|
|
57
|
+
action: () => setSelected('right')
|
|
58
|
+
}
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
<p style={{ marginTop: '16px', fontSize: '14px' }}>
|
|
62
|
+
Selected: {selected === 'left' ? 'Option A' : 'Option B'}
|
|
63
|
+
</p>
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const Sizes: Story = {
|
|
70
|
+
render: () => (
|
|
71
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
|
|
72
|
+
<SegmentedButton
|
|
73
|
+
size="sm"
|
|
74
|
+
options={{
|
|
75
|
+
leftButton: { label: 'Small' },
|
|
76
|
+
rightButton: { label: 'Small' }
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
<SegmentedButton
|
|
80
|
+
size="md"
|
|
81
|
+
options={{
|
|
82
|
+
leftButton: { label: 'Medium' },
|
|
83
|
+
rightButton: { label: 'Medium' }
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
<SegmentedButton
|
|
87
|
+
size="lg"
|
|
88
|
+
options={{
|
|
89
|
+
leftButton: { label: 'Large' },
|
|
90
|
+
rightButton: { label: 'Large' }
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const Colors: Story = {
|
|
98
|
+
render: () => (
|
|
99
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
|
|
100
|
+
<SegmentedButton
|
|
101
|
+
color="primary"
|
|
102
|
+
options={{
|
|
103
|
+
leftButton: { label: 'Primary' },
|
|
104
|
+
rightButton: { label: 'Primary' }
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
107
|
+
<SegmentedButton
|
|
108
|
+
color="secondary"
|
|
109
|
+
options={{
|
|
110
|
+
leftButton: { label: 'Secondary' },
|
|
111
|
+
rightButton: { label: 'Secondary' }
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
<SegmentedButton
|
|
115
|
+
color="success50"
|
|
116
|
+
options={{
|
|
117
|
+
leftButton: { label: 'Success' },
|
|
118
|
+
rightButton: { label: 'Success' }
|
|
119
|
+
}}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const ViewToggle: Story = {
|
|
126
|
+
render: () => {
|
|
127
|
+
const [, setView] = useState<'list' | 'grid'>('list')
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<SegmentedButton
|
|
131
|
+
options={{
|
|
132
|
+
leftButton: {
|
|
133
|
+
label: 'List View',
|
|
134
|
+
action: () => setView('list')
|
|
135
|
+
},
|
|
136
|
+
rightButton: {
|
|
137
|
+
label: 'Grid View',
|
|
138
|
+
action: () => setView('grid')
|
|
139
|
+
}
|
|
140
|
+
}}
|
|
141
|
+
/>
|
|
142
|
+
)
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const Disabled: Story = {
|
|
147
|
+
args: {
|
|
148
|
+
disabled: true,
|
|
149
|
+
options: {
|
|
150
|
+
leftButton: { label: 'Disabled' },
|
|
151
|
+
rightButton: { label: 'Disabled' }
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const YesNo: Story = {
|
|
157
|
+
render: () => {
|
|
158
|
+
const [answer, setAnswer] = useState<'yes' | 'no' | null>(null)
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div>
|
|
162
|
+
<SegmentedButton
|
|
163
|
+
options={{
|
|
164
|
+
leftButton: {
|
|
165
|
+
label: 'Yes',
|
|
166
|
+
action: () => setAnswer('yes')
|
|
167
|
+
},
|
|
168
|
+
rightButton: {
|
|
169
|
+
label: 'No',
|
|
170
|
+
action: () => setAnswer('no')
|
|
171
|
+
}
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
{answer && (
|
|
175
|
+
<p style={{ marginTop: '16px', fontSize: '14px' }}>
|
|
176
|
+
Answer: {answer}
|
|
177
|
+
</p>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
)
|
|
181
|
+
},
|
|
182
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import Select from './Select'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Select> = {
|
|
5
|
+
title: 'Form/Select',
|
|
6
|
+
component: Select,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
args: {
|
|
12
|
+
options: [],
|
|
13
|
+
},
|
|
14
|
+
argTypes: {
|
|
15
|
+
label: {
|
|
16
|
+
control: 'text',
|
|
17
|
+
description: 'Select label',
|
|
18
|
+
},
|
|
19
|
+
placeholder: {
|
|
20
|
+
control: 'text',
|
|
21
|
+
description: 'Select placeholder',
|
|
22
|
+
},
|
|
23
|
+
helperText: {
|
|
24
|
+
control: 'text',
|
|
25
|
+
description: 'Helper text below select',
|
|
26
|
+
},
|
|
27
|
+
size: {
|
|
28
|
+
control: 'select',
|
|
29
|
+
options: ['sm', 'md', 'lg'],
|
|
30
|
+
description: 'Select size',
|
|
31
|
+
},
|
|
32
|
+
disabled: {
|
|
33
|
+
control: 'boolean',
|
|
34
|
+
description: 'Whether select is disabled',
|
|
35
|
+
},
|
|
36
|
+
requiredSymbol: {
|
|
37
|
+
control: 'boolean',
|
|
38
|
+
description: 'Show required asterisk',
|
|
39
|
+
},
|
|
40
|
+
options: {
|
|
41
|
+
control: 'object',
|
|
42
|
+
description: 'Options for the select dropdown',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default meta
|
|
48
|
+
type Story = StoryObj<typeof meta>
|
|
49
|
+
|
|
50
|
+
const defaultOptions = [
|
|
51
|
+
{ label: 'Option 1', value: 'option1' },
|
|
52
|
+
{ label: 'Option 2', value: 'option2' },
|
|
53
|
+
{ label: 'Option 3', value: 'option3' },
|
|
54
|
+
{ label: 'Option 4', value: 'option4' },
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
export const Default: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
label: 'Select an option',
|
|
60
|
+
placeholder: 'Choose...',
|
|
61
|
+
options: defaultOptions,
|
|
62
|
+
},
|
|
63
|
+
}
|