Skip to content

Commit 1fe68d7

Browse files
committed
feat: add initial storybook structure and multi select stories
1 parent 8325e20 commit 1fe68d7

File tree

8 files changed

+3971
-1379
lines changed

8 files changed

+3971
-1379
lines changed

frontend/.eslintrc.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ module.exports = {
44
'es6': true,
55
'node': true,
66
},
7-
'extends': [
8-
'eslint:recommended',
9-
'plugin:react/recommended',
10-
'plugin:@typescript-eslint/recommended',
11-
'plugin:prettier/recommended',
12-
'plugin:@dword-design/import-alias/recommended',
13-
],
7+
'extends': ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'plugin:@dword-design/import-alias/recommended', 'plugin:storybook/recommended'],
148
'globals': {
159
'$': true,
1610
'$crisp': true,

frontend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ common/project.js
2929

3030
# Sentry Config File
3131
.env.sentry-build-plugin
32+
33+
*storybook.log
34+
storybook-static

frontend/.storybook/main.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import path from 'path'
2+
import { fileURLToPath } from 'url'
3+
4+
const __filename = fileURLToPath(import.meta.url)
5+
const __dirname = path.dirname(__filename)
6+
7+
/** @type { import('@storybook/react-webpack5').StorybookConfig } */
8+
const config = {
9+
"stories": [
10+
"../stories/**/*.mdx",
11+
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
12+
"../documentation/**/*.stories.@(js|jsx|mjs|ts|tsx)"
13+
],
14+
"addons": [
15+
"@storybook/addon-webpack5-compiler-swc",
16+
"@storybook/addon-docs"
17+
],
18+
"framework": {
19+
"name": "@storybook/react-webpack5",
20+
"options": {}
21+
},
22+
webpackFinal: async (config) => {
23+
// Add path aliases
24+
config.resolve.alias = {
25+
...config.resolve.alias,
26+
'common': path.resolve(__dirname, '../common'),
27+
'components': path.resolve(__dirname, '../web/components'),
28+
'project': path.resolve(__dirname, '../web/project'),
29+
}
30+
31+
// Add SCSS support
32+
config.module.rules.push({
33+
test: /\.scss$/,
34+
use: [
35+
'style-loader',
36+
'css-loader',
37+
'sass-loader'
38+
],
39+
include: path.resolve(__dirname, '../'),
40+
})
41+
42+
return config
43+
},
44+
};
45+
export default config;

frontend/.storybook/preview.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** @type { import('@storybook/react-webpack5').Preview } */
2+
3+
// Import global styles
4+
import '../web/styles/styles.scss'
5+
6+
const preview = {
7+
parameters: {
8+
controls: {
9+
matchers: {
10+
color: /(background|color)$/i,
11+
date: /Date$/i,
12+
},
13+
},
14+
},
15+
};
16+
17+
export default preview;

frontend/documentation/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Component Documentation
2+
3+
This directory contains Storybook stories and documentation for our components.
4+
5+
## Structure
6+
7+
```
8+
documentation/
9+
└── components/ # Component stories organized by component
10+
└── multi-select/
11+
└── MultiSelect.stories.tsx
12+
```
13+
14+
## Adding New Component Stories
15+
16+
To add stories for a new component:
17+
18+
1. Create a new directory under `components/` with the component name
19+
2. Add a `.stories.tsx` file (e.g., `ComponentName.stories.tsx`)
20+
3. The stories will automatically be picked up by Storybook
21+
22+
## Running Storybook
23+
24+
```bash
25+
npm run storybook
26+
```
27+
28+
This will start the Storybook dev server at `http://localhost:6006/`
29+
30+
## Building Storybook
31+
32+
```bash
33+
npm run build-storybook
34+
```
35+
36+
This will create a static build of the Storybook in the `storybook-static` directory.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import type { Meta, StoryObj } from '@storybook/react'
2+
import { MultiSelect } from '../../../web/components/base/select/multi-select/MultiSelect'
3+
import React, { useState } from 'react'
4+
5+
const meta: Meta<typeof MultiSelect> = {
6+
title: 'Components/MultiSelect',
7+
component: MultiSelect,
8+
parameters: {
9+
layout: 'centered',
10+
},
11+
tags: ['autodocs'],
12+
}
13+
14+
export default meta
15+
type Story = StoryObj<typeof MultiSelect>
16+
17+
// Wrapper component to handle state
18+
const MultiSelectWrapper = (args: any) => {
19+
const [selectedValues, setSelectedValues] = useState<string[]>(
20+
args.selectedValues || []
21+
)
22+
23+
return (
24+
<div style={{ width: '400px' }}>
25+
<MultiSelect
26+
{...args}
27+
selectedValues={selectedValues}
28+
onSelectionChange={setSelectedValues}
29+
/>
30+
</div>
31+
)
32+
}
33+
34+
// Basic example with simple options
35+
export const Default: Story = {
36+
render: (args) => <MultiSelectWrapper {...args} />,
37+
args: {
38+
options: [
39+
{ label: 'Option 1', value: 'option1' },
40+
{ label: 'Option 2', value: 'option2' },
41+
{ label: 'Option 3', value: 'option3' },
42+
{ label: 'Option 4', value: 'option4' },
43+
{ label: 'Option 5', value: 'option5' },
44+
],
45+
placeholder: 'Select options...',
46+
selectedValues: [],
47+
hideSelectedOptions: true,
48+
},
49+
}
50+
51+
// With label
52+
export const WithLabel: Story = {
53+
render: (args) => <MultiSelectWrapper {...args} />,
54+
args: {
55+
...Default.args,
56+
label: 'Choose your options',
57+
},
58+
}
59+
60+
// With colors
61+
export const WithColors: Story = {
62+
render: (args) => <MultiSelectWrapper {...args} />,
63+
args: {
64+
options: [
65+
{ label: 'Red', value: 'red' },
66+
{ label: 'Green', value: 'green' },
67+
{ label: 'Blue', value: 'blue' },
68+
{ label: 'Yellow', value: 'yellow' },
69+
{ label: 'Purple', value: 'purple' },
70+
],
71+
colorMap: new Map([
72+
['red', '#EF4444'],
73+
['green', '#10B981'],
74+
['blue', '#3B82F6'],
75+
['yellow', '#F59E0B'],
76+
['purple', '#8B5CF6'],
77+
]),
78+
placeholder: 'Select colors...',
79+
selectedValues: [],
80+
hideSelectedOptions: true,
81+
},
82+
}
83+
84+
// Pre-selected values
85+
export const WithPreselectedValues: Story = {
86+
render: (args) => <MultiSelectWrapper {...args} />,
87+
args: {
88+
...WithColors.args,
89+
selectedValues: ['red', 'blue'],
90+
},
91+
}
92+
93+
// Disabled state
94+
export const Disabled: Story = {
95+
render: (args) => <MultiSelectWrapper {...args} />,
96+
args: {
97+
...Default.args,
98+
disabled: true,
99+
selectedValues: ['option1', 'option2'],
100+
},
101+
}
102+
103+
// Small size
104+
export const SmallSize: Story = {
105+
render: (args) => <MultiSelectWrapper {...args} />,
106+
args: {
107+
...Default.args,
108+
size: 'small',
109+
label: 'Small size select',
110+
},
111+
}
112+
113+
// Large size
114+
export const LargeSize: Story = {
115+
render: (args) => <MultiSelectWrapper {...args} />,
116+
args: {
117+
...Default.args,
118+
size: 'large',
119+
label: 'Large size select',
120+
},
121+
}
122+
123+
// Show selected options in dropdown
124+
export const ShowSelectedOptions: Story = {
125+
render: (args) => <MultiSelectWrapper {...args} />,
126+
args: {
127+
...Default.args,
128+
hideSelectedOptions: false,
129+
selectedValues: ['option1', 'option2'],
130+
},
131+
}
132+
133+
// Inline display
134+
export const InlineDisplay: Story = {
135+
render: (args) => <MultiSelectWrapper {...args} />,
136+
args: {
137+
...Default.args,
138+
inline: true,
139+
selectedValues: ['option1', 'option2', 'option3'],
140+
},
141+
}
142+
143+
// Many options
144+
export const ManyOptions: Story = {
145+
render: (args) => <MultiSelectWrapper {...args} />,
146+
args: {
147+
options: Array.from({ length: 50 }, (_, i) => ({
148+
label: `Option ${i + 1}`,
149+
value: `option${i + 1}`,
150+
})),
151+
placeholder: 'Search and select...',
152+
selectedValues: [],
153+
label: 'Select from many options',
154+
},
155+
}
156+
157+
// Long option labels
158+
export const LongLabels: Story = {
159+
render: (args) => <MultiSelectWrapper {...args} />,
160+
args: {
161+
options: [
162+
{
163+
label: 'This is a very long option label that might overflow',
164+
value: 'long1',
165+
},
166+
{
167+
label: 'Another extremely long label to test text overflow behavior',
168+
value: 'long2',
169+
},
170+
{
171+
label: 'Short label',
172+
value: 'short',
173+
},
174+
],
175+
placeholder: 'Select options...',
176+
selectedValues: [],
177+
},
178+
}

0 commit comments

Comments
 (0)