goobs-frontend 0.8.22 → 0.8.24
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/package.json +22 -2
- package/src/app/layout.tsx +18 -0
- package/src/components/Accordion/accordion.stories.tsx +325 -0
- package/src/components/Button/button.stories.tsx +157 -0
- package/src/components/Button/index.tsx +25 -48
- package/src/components/Card/card.stories.tsx +367 -0
- package/src/components/Checkbox/checkbox.stories.tsx +133 -0
- package/src/components/CodeCopy/codecopy.stories.tsx +128 -0
- package/src/components/ComplexTextEditor/MarkdownEditor/index.tsx +5 -3
- package/src/components/ComplexTextEditor/editor.stories.tsx +177 -0
- package/src/components/ComplexTextEditor/utils/useMarkdownEditor.tsx +29 -11
- package/src/components/ComplexTextEditor/utils/useRichtextEditor.tsx +4 -4
- package/src/components/ConfirmationCodeInput/codeinput.stories.tsx +170 -0
- package/src/components/ConfirmationCodeInput/index.tsx +28 -14
- package/src/components/DataGrid/Table/ColumnHeaderRow/index.tsx +26 -17
- package/src/components/DataGrid/Table/Rows/index.tsx +76 -17
- package/src/components/DataGrid/Table/index.tsx +25 -17
- package/src/components/DataGrid/datagrid.stories.tsx +307 -0
- package/src/components/DataGrid/index.tsx +12 -13
- package/src/components/DataGrid/utils/useComputeTableResize.tsx +27 -7
- package/src/components/DataGrid/utils/useToolbarSearchbar.tsx +59 -36
- package/src/components/DateField/datefield.stories.tsx +144 -0
- package/src/components/Dropdown/dropdown.stories.tsx +268 -0
- package/src/components/Dropdown/index.tsx +200 -62
- package/src/components/Grid/index.tsx +72 -94
- package/src/components/IncrementNumberField/incrementnumberfield.stories.tsx +152 -0
- package/src/components/Nav/index.tsx +104 -118
- package/src/components/Nav/nav.stories.tsx +283 -0
- package/src/components/NumberField/numberfield.stories.tsx +137 -0
- package/src/components/PasswordField/passwordfield.stories.tsx +120 -0
- package/src/components/PhoneNumberField/phonenumberfield.stories.tsx +134 -0
- package/src/components/PricingTable/index.tsx +23 -4
- package/src/components/PricingTable/pricingtable.stories.tsx +144 -0
- package/src/components/ProjectBoard/AddTask/client.tsx +20 -49
- package/src/components/ProjectBoard/ManageTask/client.tsx +17 -21
- package/src/components/ProjectBoard/index.tsx +4 -15
- package/src/components/ProjectBoard/projectboard.stories.tsx +384 -0
- package/src/components/QRCode/qrcode.stories.tsx +117 -0
- package/src/components/RadioGroup/index.tsx +1 -1
- package/src/components/RadioGroup/radiogroup.stories.tsx +155 -0
- package/src/components/SearchableDropdown/index.tsx +18 -32
- package/src/components/SearchableDropdown/searchabledropdown.stories.tsx +230 -0
- package/src/components/Searchbar/searchbar.stories.tsx +144 -0
- package/src/components/Stepper/stepper.stories.tsx +255 -0
- package/src/components/Tabs/tabs.stories.tsx +212 -0
- package/src/components/TextField/textfield.stories.tsx +225 -0
- package/src/components/Toolbar/toolbar.stories.tsx +169 -0
- package/src/components/Tooltip/tooltip.stories.tsx +170 -0
- package/src/components/TransferList/index.tsx +1 -1
- package/src/components/TransferList/transferlist.stories.tsx +212 -0
- package/src/components/Typography/typography.stories.tsx +147 -0
- package/src/stories/Button.stories.ts +53 -0
- package/src/stories/Button.tsx +47 -0
- package/src/stories/Configure.mdx +451 -0
- package/src/stories/Header.stories.ts +33 -0
- package/src/stories/Header.tsx +71 -0
- package/src/stories/Page.stories.ts +32 -0
- package/src/stories/Page.tsx +91 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/stories/button.css +30 -0
- package/src/stories/header.css +32 -0
- package/src/stories/page.css +69 -0
- package/src/app/_app.tsx +0 -8
- package/src/components/Grid/defaultconfig.tsx +0 -131
- package/src/components/ProjectBoard/jotai/task.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A comprehensive React-based UI library built on Material-UI, offering a wide range of customizable components including grids, typography, buttons, cards, forms, navigation, pricing tables, steppers, tooltips, accordions, and more. Designed for building responsive and consistent user interfaces with advanced features like form validation, theming, and code syntax highlighting.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
"dev": "next dev",
|
|
17
17
|
"build": "next build",
|
|
18
18
|
"start": "next start",
|
|
19
|
-
"lint": "next lint"
|
|
19
|
+
"lint": "next lint",
|
|
20
|
+
"storybook": "storybook dev -p 6006",
|
|
21
|
+
"chromatic": "chromatic --project-token=chpt_2d34a5f45351b6c",
|
|
22
|
+
"build-storybook": "storybook build"
|
|
20
23
|
},
|
|
21
24
|
"dependencies": {
|
|
22
25
|
"@emotion/cache": "^11.14.0",
|
|
@@ -37,20 +40,32 @@
|
|
|
37
40
|
"slate-dom": "^0.111.0",
|
|
38
41
|
"slate-history": "^0.110.3",
|
|
39
42
|
"slate-react": "^0.112.0",
|
|
43
|
+
"storybook": "^8.4.7",
|
|
40
44
|
"zod": "^3.24.1",
|
|
41
45
|
"zod-formik-adapter": "^1.3.0"
|
|
42
46
|
},
|
|
43
47
|
"devDependencies": {
|
|
48
|
+
"@chromatic-com/storybook": "^3.2.3",
|
|
44
49
|
"@next/eslint-plugin-next": "^15.1.4",
|
|
50
|
+
"@storybook/addon-essentials": "^8.4.7",
|
|
51
|
+
"@storybook/addon-interactions": "^8.4.7",
|
|
52
|
+
"@storybook/addon-onboarding": "8.4.7",
|
|
53
|
+
"@storybook/blocks": "8.4.7",
|
|
54
|
+
"@storybook/nextjs": "^8.4.7",
|
|
55
|
+
"@storybook/react": "^8.4.7",
|
|
56
|
+
"@storybook/test": "^8.4.7",
|
|
57
|
+
"@storybook/testing-library": "^0.2.2",
|
|
45
58
|
"@types/node": "^22.10.5",
|
|
46
59
|
"@types/react": "19.0.5",
|
|
47
60
|
"@types/react-dom": "^19.0.3",
|
|
48
61
|
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
49
62
|
"@typescript-eslint/parser": "^8.19.1",
|
|
63
|
+
"chromatic": "^11.24.0",
|
|
50
64
|
"eslint": "^9.18.0",
|
|
51
65
|
"eslint-config-next": "^15.1.4",
|
|
52
66
|
"eslint-config-prettier": "^9.1.0",
|
|
53
67
|
"eslint-plugin-prettier": "^5.2.1",
|
|
68
|
+
"eslint-plugin-storybook": "^0.11.2",
|
|
54
69
|
"prettier": "^3.4.2",
|
|
55
70
|
"react": "^19.0.0",
|
|
56
71
|
"react-dom": "^19.0.0",
|
|
@@ -121,5 +136,10 @@
|
|
|
121
136
|
"resolutions": {
|
|
122
137
|
"string-width": "^4.2.3",
|
|
123
138
|
"strip-ansi": "^6.0.1"
|
|
139
|
+
},
|
|
140
|
+
"eslintConfig": {
|
|
141
|
+
"extends": [
|
|
142
|
+
"plugin:storybook/recommended"
|
|
143
|
+
]
|
|
124
144
|
}
|
|
125
145
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/app/layout.tsx
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
export const metadata = {
|
|
5
|
+
title: 'Goobs Frontend',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function RootLayout({
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
children: React.ReactNode
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<html lang="en">
|
|
15
|
+
<body>{children}</body>
|
|
16
|
+
</html>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// src/components/Accordion/accordion.stories.tsx
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
4
|
+
import { userEvent, within, expect } from '@storybook/test'
|
|
5
|
+
import { Accordion, AccordionSummary, AccordionDetails } from './index'
|
|
6
|
+
import Typography from '../Typography'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Setup story metadata
|
|
10
|
+
*/
|
|
11
|
+
const meta: Meta<typeof Accordion> = {
|
|
12
|
+
title: 'Components/Accordion',
|
|
13
|
+
component: Accordion,
|
|
14
|
+
parameters: {
|
|
15
|
+
a11y: {
|
|
16
|
+
disable: false,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
// If you want color pickers or controls for specific props, you can define them in argTypes
|
|
20
|
+
argTypes: {},
|
|
21
|
+
}
|
|
22
|
+
export default meta
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<typeof Accordion>
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Reusable child content
|
|
28
|
+
*/
|
|
29
|
+
const sampleContent = (
|
|
30
|
+
<Typography
|
|
31
|
+
fontvariant="merriparagraph"
|
|
32
|
+
text="This is the accordion content."
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 1) Basic single Accordion
|
|
38
|
+
*/
|
|
39
|
+
export const SingleAccordion: Story = {
|
|
40
|
+
name: 'Single Accordion (collapsed by default)',
|
|
41
|
+
render: args => (
|
|
42
|
+
<Accordion {...args}>
|
|
43
|
+
<AccordionSummary>Single Accordion</AccordionSummary>
|
|
44
|
+
<AccordionDetails>{sampleContent}</AccordionDetails>
|
|
45
|
+
</Accordion>
|
|
46
|
+
),
|
|
47
|
+
play: async ({ canvasElement }) => {
|
|
48
|
+
const canvas = within(canvasElement)
|
|
49
|
+
|
|
50
|
+
// 1. Verify that the accordion content is not visible initially
|
|
51
|
+
expect(
|
|
52
|
+
canvas.queryByText('This is the accordion content.')
|
|
53
|
+
).not.toBeInTheDocument()
|
|
54
|
+
|
|
55
|
+
// 2. Click on the summary to expand
|
|
56
|
+
await userEvent.click(canvas.getByText('Single Accordion'))
|
|
57
|
+
|
|
58
|
+
// 3. Verify that the content is now visible
|
|
59
|
+
expect(
|
|
60
|
+
canvas.getByText('This is the accordion content.')
|
|
61
|
+
).toBeInTheDocument()
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 2) Basic single Accordion (defaultExpanded)
|
|
67
|
+
*/
|
|
68
|
+
export const DefaultExpanded: Story = {
|
|
69
|
+
name: 'Single Accordion (defaultExpanded)',
|
|
70
|
+
render: args => (
|
|
71
|
+
<Accordion defaultExpanded {...args}>
|
|
72
|
+
<AccordionSummary>Default Expanded</AccordionSummary>
|
|
73
|
+
<AccordionDetails>{sampleContent}</AccordionDetails>
|
|
74
|
+
</Accordion>
|
|
75
|
+
),
|
|
76
|
+
// Add async and return the assertion
|
|
77
|
+
play: async ({ canvasElement }) => {
|
|
78
|
+
const canvas = within(canvasElement)
|
|
79
|
+
// Return the assertion to properly handle the promise
|
|
80
|
+
return expect(
|
|
81
|
+
canvas.getByText('This is the accordion content.')
|
|
82
|
+
).toBeInTheDocument()
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 3) Multiple Accordions
|
|
88
|
+
*/
|
|
89
|
+
export const MultipleAccordions: Story = {
|
|
90
|
+
name: 'Multiple Accordions',
|
|
91
|
+
render: args => (
|
|
92
|
+
<>
|
|
93
|
+
<Accordion {...args}>
|
|
94
|
+
<AccordionSummary>First Item</AccordionSummary>
|
|
95
|
+
<AccordionDetails>
|
|
96
|
+
<Typography
|
|
97
|
+
fontvariant="merriparagraph"
|
|
98
|
+
text="Details for first item."
|
|
99
|
+
/>
|
|
100
|
+
</AccordionDetails>
|
|
101
|
+
</Accordion>
|
|
102
|
+
<Accordion {...args}>
|
|
103
|
+
<AccordionSummary>Second Item</AccordionSummary>
|
|
104
|
+
<AccordionDetails>
|
|
105
|
+
<Typography
|
|
106
|
+
fontvariant="merriparagraph"
|
|
107
|
+
text="Details for second item."
|
|
108
|
+
/>
|
|
109
|
+
</AccordionDetails>
|
|
110
|
+
</Accordion>
|
|
111
|
+
<Accordion {...args}>
|
|
112
|
+
<AccordionSummary>Third Item</AccordionSummary>
|
|
113
|
+
<AccordionDetails>
|
|
114
|
+
<Typography
|
|
115
|
+
fontvariant="merriparagraph"
|
|
116
|
+
text="Details for third item."
|
|
117
|
+
/>
|
|
118
|
+
</AccordionDetails>
|
|
119
|
+
</Accordion>
|
|
120
|
+
</>
|
|
121
|
+
),
|
|
122
|
+
play: async ({ canvasElement }) => {
|
|
123
|
+
const canvas = within(canvasElement)
|
|
124
|
+
|
|
125
|
+
// Initially, no details are visible
|
|
126
|
+
expect(
|
|
127
|
+
canvas.queryByText('Details for first item.')
|
|
128
|
+
).not.toBeInTheDocument()
|
|
129
|
+
expect(
|
|
130
|
+
canvas.queryByText('Details for second item.')
|
|
131
|
+
).not.toBeInTheDocument()
|
|
132
|
+
expect(
|
|
133
|
+
canvas.queryByText('Details for third item.')
|
|
134
|
+
).not.toBeInTheDocument()
|
|
135
|
+
|
|
136
|
+
// Expand the second item
|
|
137
|
+
await userEvent.click(canvas.getByText('Second Item'))
|
|
138
|
+
expect(canvas.getByText('Details for second item.')).toBeInTheDocument()
|
|
139
|
+
|
|
140
|
+
// Expand the first item
|
|
141
|
+
await userEvent.click(canvas.getByText('First Item'))
|
|
142
|
+
expect(canvas.getByText('Details for first item.')).toBeInTheDocument()
|
|
143
|
+
|
|
144
|
+
// The second item should remain expanded unless the `Accordion` is configured otherwise
|
|
145
|
+
expect(canvas.getByText('Details for second item.')).toBeInTheDocument()
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 4) Nested Accordion
|
|
151
|
+
*/
|
|
152
|
+
export const NestedAccordion: Story = {
|
|
153
|
+
name: 'Nested Accordion',
|
|
154
|
+
render: args => (
|
|
155
|
+
<Accordion {...args}>
|
|
156
|
+
<AccordionSummary>Parent Accordion</AccordionSummary>
|
|
157
|
+
<AccordionDetails>
|
|
158
|
+
<Typography fontvariant="merriparagraph" text="Top-level content" />
|
|
159
|
+
<Accordion {...args}>
|
|
160
|
+
<AccordionSummary>Nested Accordion</AccordionSummary>
|
|
161
|
+
<AccordionDetails>{sampleContent}</AccordionDetails>
|
|
162
|
+
</Accordion>
|
|
163
|
+
</AccordionDetails>
|
|
164
|
+
</Accordion>
|
|
165
|
+
),
|
|
166
|
+
play: async ({ canvasElement }) => {
|
|
167
|
+
const canvas = within(canvasElement)
|
|
168
|
+
|
|
169
|
+
// The nested content is not initially visible
|
|
170
|
+
expect(
|
|
171
|
+
canvas.queryByText('This is the accordion content.')
|
|
172
|
+
).not.toBeInTheDocument()
|
|
173
|
+
|
|
174
|
+
// Expand the parent
|
|
175
|
+
await userEvent.click(canvas.getByText('Parent Accordion'))
|
|
176
|
+
expect(canvas.getByText('Top-level content')).toBeInTheDocument()
|
|
177
|
+
|
|
178
|
+
// Now expand the nested accordion
|
|
179
|
+
await userEvent.click(canvas.getByText('Nested Accordion'))
|
|
180
|
+
expect(
|
|
181
|
+
canvas.getByText('This is the accordion content.')
|
|
182
|
+
).toBeInTheDocument()
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 5) Custom styles
|
|
188
|
+
*/
|
|
189
|
+
export const CustomStyles: Story = {
|
|
190
|
+
name: 'Custom Styled Accordion',
|
|
191
|
+
render: args => (
|
|
192
|
+
<Accordion
|
|
193
|
+
sx={{
|
|
194
|
+
border: '2px solid #4caf50',
|
|
195
|
+
borderRadius: '8px',
|
|
196
|
+
marginTop: '10px',
|
|
197
|
+
}}
|
|
198
|
+
{...args}
|
|
199
|
+
>
|
|
200
|
+
<AccordionSummary>Custom Styles</AccordionSummary>
|
|
201
|
+
<AccordionDetails>
|
|
202
|
+
<Typography
|
|
203
|
+
fontvariant="merriparagraph"
|
|
204
|
+
text="Look at this fancy border!"
|
|
205
|
+
/>
|
|
206
|
+
</AccordionDetails>
|
|
207
|
+
</Accordion>
|
|
208
|
+
),
|
|
209
|
+
play: async ({ canvasElement }) => {
|
|
210
|
+
const canvas = within(canvasElement)
|
|
211
|
+
// Expand and verify
|
|
212
|
+
await userEvent.click(canvas.getByText('Custom Styles'))
|
|
213
|
+
expect(canvas.getByText('Look at this fancy border!')).toBeInTheDocument()
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 6) Accordion with many lines of content
|
|
219
|
+
*/
|
|
220
|
+
export const LargeContent: Story = {
|
|
221
|
+
name: 'Accordion With Large Content',
|
|
222
|
+
render: args => (
|
|
223
|
+
<Accordion {...args}>
|
|
224
|
+
<AccordionSummary>Lots of Text</AccordionSummary>
|
|
225
|
+
<AccordionDetails>
|
|
226
|
+
<div>
|
|
227
|
+
<Typography fontvariant="merriparagraph" text="Line 1" />
|
|
228
|
+
<Typography fontvariant="merriparagraph" text="Line 2" />
|
|
229
|
+
<Typography fontvariant="merriparagraph" text="Line 3" />
|
|
230
|
+
<Typography fontvariant="merriparagraph" text="Line 4" />
|
|
231
|
+
<Typography fontvariant="merriparagraph" text="Line 5" />
|
|
232
|
+
<Typography fontvariant="merriparagraph" text="Line 6" />
|
|
233
|
+
<Typography fontvariant="merriparagraph" text="Line 7" />
|
|
234
|
+
<Typography fontvariant="merriparagraph" text="Line 8" />
|
|
235
|
+
<Typography fontvariant="merriparagraph" text="Line 9" />
|
|
236
|
+
</div>
|
|
237
|
+
</AccordionDetails>
|
|
238
|
+
</Accordion>
|
|
239
|
+
),
|
|
240
|
+
play: async ({ canvasElement }) => {
|
|
241
|
+
const canvas = within(canvasElement)
|
|
242
|
+
// Expand
|
|
243
|
+
await userEvent.click(canvas.getByText('Lots of Text'))
|
|
244
|
+
// Confirm a few lines
|
|
245
|
+
expect(canvas.getByText('Line 1')).toBeInTheDocument()
|
|
246
|
+
expect(canvas.getByText('Line 9')).toBeInTheDocument()
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 7) Disabled Accordion
|
|
252
|
+
*/
|
|
253
|
+
export const DisabledAccordion: Story = {
|
|
254
|
+
name: 'Disabled Accordion',
|
|
255
|
+
render: args => (
|
|
256
|
+
<Accordion disabled {...args}>
|
|
257
|
+
<AccordionSummary>Cannot Expand</AccordionSummary>
|
|
258
|
+
<AccordionDetails>{sampleContent}</AccordionDetails>
|
|
259
|
+
</Accordion>
|
|
260
|
+
),
|
|
261
|
+
play: async ({ canvasElement }) => {
|
|
262
|
+
const canvas = within(canvasElement)
|
|
263
|
+
// Try to expand
|
|
264
|
+
await userEvent.click(canvas.getByText('Cannot Expand'))
|
|
265
|
+
// Content should remain hidden
|
|
266
|
+
expect(
|
|
267
|
+
canvas.queryByText('This is the accordion content.')
|
|
268
|
+
).not.toBeInTheDocument()
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 8) Controlled Accordion (toggle via props)
|
|
274
|
+
* We must put our hook usage in a separate
|
|
275
|
+
* React component whose name starts with a capital letter.
|
|
276
|
+
*/
|
|
277
|
+
const ControlledAccordionExample = () => {
|
|
278
|
+
const [isExpanded, setIsExpanded] = React.useState(false)
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<>
|
|
282
|
+
<button onClick={() => setIsExpanded(!isExpanded)}>
|
|
283
|
+
Toggle Accordion
|
|
284
|
+
</button>
|
|
285
|
+
<Accordion expanded={isExpanded}>
|
|
286
|
+
<AccordionSummary>Controlled Accordion</AccordionSummary>
|
|
287
|
+
<AccordionDetails>
|
|
288
|
+
<Typography
|
|
289
|
+
fontvariant="merriparagraph"
|
|
290
|
+
text="This is controlled externally."
|
|
291
|
+
/>
|
|
292
|
+
</AccordionDetails>
|
|
293
|
+
</Accordion>
|
|
294
|
+
</>
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export const ControlledAccordion: Story = {
|
|
299
|
+
name: 'Controlled Accordion (toggle via props)',
|
|
300
|
+
render: () => <ControlledAccordionExample />,
|
|
301
|
+
play: async ({ canvasElement }) => {
|
|
302
|
+
const canvas = within(canvasElement)
|
|
303
|
+
// Initially hidden
|
|
304
|
+
expect(
|
|
305
|
+
canvas.queryByText('This is controlled externally.')
|
|
306
|
+
).not.toBeInTheDocument()
|
|
307
|
+
|
|
308
|
+
// Click the external toggle button
|
|
309
|
+
await userEvent.click(
|
|
310
|
+
canvas.getByRole('button', { name: 'Toggle Accordion' })
|
|
311
|
+
)
|
|
312
|
+
// Now the content should appear
|
|
313
|
+
expect(
|
|
314
|
+
canvas.getByText('This is controlled externally.')
|
|
315
|
+
).toBeInTheDocument()
|
|
316
|
+
|
|
317
|
+
// Click again to hide
|
|
318
|
+
await userEvent.click(
|
|
319
|
+
canvas.getByRole('button', { name: 'Toggle Accordion' })
|
|
320
|
+
)
|
|
321
|
+
expect(
|
|
322
|
+
canvas.queryByText('This is controlled externally.')
|
|
323
|
+
).not.toBeInTheDocument()
|
|
324
|
+
},
|
|
325
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// src/components/Button/button.stories.tsx
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
5
|
+
import { userEvent, within, expect } from '@storybook/test'
|
|
6
|
+
import CustomButton /* , { CustomButtonProps } */ from './index' // Remove { CustomButtonProps } if unused
|
|
7
|
+
// For an example icon usage, we can import something from Material UI icons:
|
|
8
|
+
import { Send } from '@mui/icons-material'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Storybook metadata
|
|
12
|
+
*/
|
|
13
|
+
const meta: Meta<typeof CustomButton> = {
|
|
14
|
+
title: 'Components/Button',
|
|
15
|
+
component: CustomButton,
|
|
16
|
+
// If you want Storybook controls for certain props (color pickers, etc.), configure them here:
|
|
17
|
+
argTypes: {
|
|
18
|
+
backgroundcolor: { control: 'color' },
|
|
19
|
+
fontcolor: { control: 'color' },
|
|
20
|
+
iconcolor: { control: 'color' },
|
|
21
|
+
iconsize: { control: 'text' },
|
|
22
|
+
},
|
|
23
|
+
parameters: {
|
|
24
|
+
a11y: {
|
|
25
|
+
disable: false,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
export default meta
|
|
30
|
+
|
|
31
|
+
type Story = StoryObj<typeof CustomButton>
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 1) Default Button
|
|
35
|
+
*/
|
|
36
|
+
export const DefaultButton: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
text: 'Click Me',
|
|
39
|
+
},
|
|
40
|
+
// Optional test using Storybook "play" function
|
|
41
|
+
play: async ({ canvasElement }) => {
|
|
42
|
+
const canvas = within(canvasElement)
|
|
43
|
+
|
|
44
|
+
// Grab the button
|
|
45
|
+
const buttonEl = canvas.getByRole('button', { name: /click me/i })
|
|
46
|
+
|
|
47
|
+
// Simulate a user click
|
|
48
|
+
await userEvent.click(buttonEl)
|
|
49
|
+
|
|
50
|
+
// Verify some condition - for now, just ensure it's not disabled
|
|
51
|
+
expect(buttonEl).not.toBeDisabled()
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 2) Disabled Button
|
|
57
|
+
*/
|
|
58
|
+
export const DisabledButton: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
text: 'I am disabled',
|
|
61
|
+
disableButton: 'true',
|
|
62
|
+
},
|
|
63
|
+
// Add an 'await' call or remove `async`
|
|
64
|
+
play: async ({ canvasElement }) => {
|
|
65
|
+
const canvas = within(canvasElement)
|
|
66
|
+
const buttonEl = canvas.getByRole('button', { name: /i am disabled/i })
|
|
67
|
+
|
|
68
|
+
// Attempt a click just to satisfy 'await'
|
|
69
|
+
await userEvent.click(buttonEl)
|
|
70
|
+
|
|
71
|
+
// This button should be disabled
|
|
72
|
+
expect(buttonEl).toBeDisabled()
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 3) Text (Transparent) Button
|
|
78
|
+
* (backgroundcolor="none" => "text" style)
|
|
79
|
+
*/
|
|
80
|
+
export const TextButton: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
text: 'I am text only',
|
|
83
|
+
backgroundcolor: 'none', // Transparent
|
|
84
|
+
fontcolor: '#1976d2', // Typical link-blue color
|
|
85
|
+
},
|
|
86
|
+
// Add an 'await' call or remove `async`
|
|
87
|
+
play: async ({ canvasElement }) => {
|
|
88
|
+
const canvas = within(canvasElement)
|
|
89
|
+
const buttonEl = canvas.getByRole('button', { name: /i am text only/i })
|
|
90
|
+
|
|
91
|
+
// Confirm it is not disabled by clicking
|
|
92
|
+
await userEvent.click(buttonEl)
|
|
93
|
+
expect(buttonEl).not.toBeDisabled()
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 4) Button with Icon on the Left
|
|
99
|
+
*/
|
|
100
|
+
export const WithIconLeft: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
text: 'Send',
|
|
103
|
+
icon: <Send />, // MUI icon
|
|
104
|
+
iconlocation: 'left', // Icon on the left
|
|
105
|
+
},
|
|
106
|
+
play: async ({ canvasElement }) => {
|
|
107
|
+
const canvas = within(canvasElement)
|
|
108
|
+
const buttonEl = canvas.getByRole('button', { name: /send/i })
|
|
109
|
+
await userEvent.click(buttonEl)
|
|
110
|
+
// We can assert it's still present
|
|
111
|
+
expect(buttonEl).toBeInTheDocument()
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 5) Button with Icon on the Right
|
|
117
|
+
*/
|
|
118
|
+
export const WithIconRight: Story = {
|
|
119
|
+
args: {
|
|
120
|
+
text: 'Send',
|
|
121
|
+
icon: <Send />,
|
|
122
|
+
iconlocation: 'right',
|
|
123
|
+
},
|
|
124
|
+
// No "play" test here, but you can add one if you like
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 6) Button with Icon Above the Text
|
|
129
|
+
*/
|
|
130
|
+
export const WithIconAbove: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
text: 'Send',
|
|
133
|
+
icon: <Send />,
|
|
134
|
+
iconlocation: 'above', // Icon stacked on top
|
|
135
|
+
fontlocation: 'center', // Center text
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 7) Custom Colors & Sizing
|
|
141
|
+
*/
|
|
142
|
+
export const CustomColorsAndSize: Story = {
|
|
143
|
+
args: {
|
|
144
|
+
text: 'Custom Colors',
|
|
145
|
+
backgroundcolor: '#4caf50',
|
|
146
|
+
fontcolor: '#ffffff',
|
|
147
|
+
width: '120px',
|
|
148
|
+
height: '50px',
|
|
149
|
+
},
|
|
150
|
+
play: async ({ canvasElement }) => {
|
|
151
|
+
const canvas = within(canvasElement)
|
|
152
|
+
const buttonEl = canvas.getByRole('button', { name: /custom colors/i })
|
|
153
|
+
|
|
154
|
+
await userEvent.click(buttonEl)
|
|
155
|
+
expect(buttonEl).not.toBeDisabled()
|
|
156
|
+
},
|
|
157
|
+
}
|