@buildcanada/components 0.3.3 → 0.3.5
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/dist/content/Card/Card.d.ts +62 -0
- package/dist/content/Card/Card.d.ts.map +1 -0
- package/dist/content/Card/Card.js +45 -0
- package/dist/content/Card/Card.scss +281 -0
- package/dist/content/Card/index.d.ts +3 -0
- package/dist/content/Card/index.d.ts.map +1 -0
- package/dist/content/Card/index.js +2 -0
- package/dist/content/Hero/Hero.d.ts +28 -0
- package/dist/content/Hero/Hero.d.ts.map +1 -0
- package/dist/content/Hero/Hero.js +16 -0
- package/dist/content/Hero/Hero.scss +150 -0
- package/dist/content/Hero/index.d.ts +3 -0
- package/dist/content/Hero/index.d.ts.map +1 -0
- package/dist/content/Hero/index.js +2 -0
- package/dist/content/StatBlock/StatBlock.d.ts +15 -0
- package/dist/content/StatBlock/StatBlock.d.ts.map +1 -0
- package/dist/content/StatBlock/StatBlock.js +10 -0
- package/dist/content/StatBlock/StatBlock.scss +83 -0
- package/dist/content/StatBlock/index.d.ts +3 -0
- package/dist/content/StatBlock/index.d.ts.map +1 -0
- package/dist/content/StatBlock/index.js +2 -0
- package/dist/feedback/Dialog/Dialog.d.ts +18 -0
- package/dist/feedback/Dialog/Dialog.d.ts.map +1 -0
- package/dist/feedback/Dialog/Dialog.js +33 -0
- package/dist/feedback/Dialog/Dialog.scss +158 -0
- package/dist/feedback/Dialog/index.d.ts +2 -0
- package/dist/feedback/Dialog/index.d.ts.map +1 -0
- package/dist/feedback/Dialog/index.js +1 -0
- package/dist/feedback/PopupForm/PopupForm.d.ts +24 -0
- package/dist/feedback/PopupForm/PopupForm.d.ts.map +1 -0
- package/dist/feedback/PopupForm/PopupForm.js +13 -0
- package/dist/feedback/PopupForm/PopupForm.scss +34 -0
- package/dist/feedback/PopupForm/index.d.ts +2 -0
- package/dist/feedback/PopupForm/index.d.ts.map +1 -0
- package/dist/feedback/PopupForm/index.js +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/layout/Container/Container.d.ts +11 -0
- package/dist/layout/Container/Container.d.ts.map +1 -0
- package/dist/layout/Container/Container.js +7 -0
- package/dist/layout/Container/Container.scss +40 -0
- package/dist/layout/Container/index.d.ts +3 -0
- package/dist/layout/Container/index.d.ts.map +1 -0
- package/dist/layout/Container/index.js +2 -0
- package/dist/layout/Divider/Divider.d.ts +12 -0
- package/dist/layout/Divider/Divider.d.ts.map +1 -0
- package/dist/layout/Divider/Divider.js +7 -0
- package/dist/layout/Divider/Divider.scss +117 -0
- package/dist/layout/Divider/index.d.ts +3 -0
- package/dist/layout/Divider/index.d.ts.map +1 -0
- package/dist/layout/Divider/index.js +2 -0
- package/dist/layout/Grid/Grid.d.ts +24 -0
- package/dist/layout/Grid/Grid.d.ts.map +1 -0
- package/dist/layout/Grid/Grid.js +11 -0
- package/dist/layout/Grid/Grid.scss +81 -0
- package/dist/layout/Grid/index.d.ts +3 -0
- package/dist/layout/Grid/index.d.ts.map +1 -0
- package/dist/layout/Grid/index.js +2 -0
- package/dist/layout/Section/Section.d.ts +13 -0
- package/dist/layout/Section/Section.d.ts.map +1 -0
- package/dist/layout/Section/Section.js +7 -0
- package/dist/layout/Section/Section.scss +74 -0
- package/dist/layout/Section/index.d.ts +3 -0
- package/dist/layout/Section/index.d.ts.map +1 -0
- package/dist/layout/Section/index.js +2 -0
- package/dist/layout/Stack/Stack.d.ts +18 -0
- package/dist/layout/Stack/Stack.d.ts.map +1 -0
- package/dist/layout/Stack/Stack.js +7 -0
- package/dist/layout/Stack/Stack.scss +61 -0
- package/dist/layout/Stack/index.d.ts +3 -0
- package/dist/layout/Stack/index.d.ts.map +1 -0
- package/dist/layout/Stack/index.js +2 -0
- package/dist/navigation/Footer/Footer.d.ts +31 -0
- package/dist/navigation/Footer/Footer.d.ts.map +1 -0
- package/dist/navigation/Footer/Footer.js +21 -0
- package/dist/navigation/Footer/Footer.scss +233 -0
- package/dist/navigation/Footer/index.d.ts +3 -0
- package/dist/navigation/Footer/index.d.ts.map +1 -0
- package/dist/navigation/Footer/index.js +2 -0
- package/dist/navigation/Header/Header.d.ts +23 -0
- package/dist/navigation/Header/Header.d.ts.map +1 -0
- package/dist/navigation/Header/Header.js +16 -0
- package/dist/navigation/Header/Header.scss +325 -0
- package/dist/navigation/Header/index.d.ts +3 -0
- package/dist/navigation/Header/index.d.ts.map +1 -0
- package/dist/navigation/Header/index.js +2 -0
- package/dist/primitives/Button/Button.d.ts +31 -0
- package/dist/primitives/Button/Button.d.ts.map +1 -0
- package/dist/primitives/Button/Button.js +36 -0
- package/dist/primitives/Button/Button.scss +218 -0
- package/dist/primitives/Button/index.d.ts +3 -0
- package/dist/primitives/Button/index.d.ts.map +1 -0
- package/dist/primitives/Button/index.js +2 -0
- package/dist/primitives/Checkbox/Checkbox.d.ts +13 -0
- package/dist/primitives/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/primitives/Checkbox/Checkbox.js +10 -0
- package/dist/primitives/Checkbox/Checkbox.scss +114 -0
- package/dist/primitives/Checkbox/index.d.ts +3 -0
- package/dist/primitives/Checkbox/index.d.ts.map +1 -0
- package/dist/primitives/Checkbox/index.js +2 -0
- package/dist/primitives/TextField/TextField.d.ts +22 -0
- package/dist/primitives/TextField/TextField.d.ts.map +1 -0
- package/dist/primitives/TextField/TextField.js +14 -0
- package/dist/primitives/TextField/TextField.scss +93 -0
- package/dist/primitives/TextField/index.d.ts +3 -0
- package/dist/primitives/TextField/index.d.ts.map +1 -0
- package/dist/primitives/TextField/index.js +2 -0
- package/dist/styles/fonts.scss +27 -0
- package/dist/styles/main.scss +36 -0
- package/dist/styles/tokens.scss +301 -0
- package/dist/styles/typography.scss +232 -0
- package/package.json +12 -11
- package/src/content/Card/Card.stories.tsx +389 -0
- package/src/content/Card/index.ts +2 -2
- package/src/content/Hero/Hero.stories.tsx +299 -0
- package/src/content/Hero/index.ts +2 -2
- package/src/content/StatBlock/StatBlock.stories.tsx +331 -0
- package/src/content/StatBlock/index.ts +2 -2
- package/src/feedback/Dialog/Dialog.stories.tsx +286 -0
- package/src/feedback/Dialog/index.ts +1 -1
- package/src/feedback/PopupForm/PopupForm.stories.tsx +341 -0
- package/src/feedback/PopupForm/PopupForm.tsx +2 -2
- package/src/feedback/PopupForm/index.ts +1 -1
- package/src/index.ts +15 -15
- package/src/layout/Container/Container.stories.tsx +153 -0
- package/src/layout/Container/index.ts +2 -2
- package/src/layout/Divider/Divider.stories.tsx +204 -0
- package/src/layout/Divider/index.ts +2 -2
- package/src/layout/Grid/Grid.stories.tsx +263 -0
- package/src/layout/Grid/index.ts +2 -2
- package/src/layout/Section/Section.stories.tsx +173 -0
- package/src/layout/Section/index.ts +2 -2
- package/src/layout/Stack/Stack.stories.tsx +342 -0
- package/src/layout/Stack/index.ts +2 -2
- package/src/navigation/Footer/Footer.stories.tsx +351 -0
- package/src/navigation/Footer/index.ts +2 -2
- package/src/navigation/Header/Header.stories.tsx +346 -0
- package/src/navigation/Header/index.ts +2 -2
- package/src/primitives/Button/Button.stories.tsx +300 -0
- package/src/primitives/Button/index.ts +2 -2
- package/src/primitives/Checkbox/Checkbox.stories.tsx +204 -0
- package/src/primitives/Checkbox/index.ts +2 -2
- package/src/primitives/TextField/TextField.stories.tsx +265 -0
- package/src/primitives/TextField/index.ts +2 -2
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import { within, userEvent, expect, fn } from "@storybook/test"
|
|
3
|
+
import { useState } from "react"
|
|
4
|
+
|
|
5
|
+
import { Checkbox } from "./Checkbox"
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Checkbox> = {
|
|
8
|
+
title: "Components/Primitives/Checkbox",
|
|
9
|
+
component: Checkbox,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: `
|
|
14
|
+
A styled checkbox component with label support.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
\`\`\`tsx
|
|
19
|
+
import { Checkbox } from "@buildcanada/components"
|
|
20
|
+
|
|
21
|
+
<Checkbox label="I agree to the terms" />
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
## Controlled vs Uncontrolled
|
|
25
|
+
|
|
26
|
+
Use \`defaultChecked\` for uncontrolled or \`checked\` with \`onChange\` for controlled:
|
|
27
|
+
|
|
28
|
+
\`\`\`tsx
|
|
29
|
+
// Uncontrolled
|
|
30
|
+
<Checkbox label="Option" defaultChecked />
|
|
31
|
+
|
|
32
|
+
// Controlled
|
|
33
|
+
<Checkbox
|
|
34
|
+
label="Option"
|
|
35
|
+
checked={isChecked}
|
|
36
|
+
onChange={(e) => setIsChecked(e.target.checked)}
|
|
37
|
+
/>
|
|
38
|
+
\`\`\`
|
|
39
|
+
`,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
argTypes: {
|
|
44
|
+
label: { description: "The label text displayed next to the checkbox" },
|
|
45
|
+
disabled: { description: "Whether the checkbox is disabled" },
|
|
46
|
+
defaultChecked: { description: "Initial checked state (uncontrolled)" },
|
|
47
|
+
checked: { description: "Controlled checked state" },
|
|
48
|
+
onChange: { description: "Callback when checkbox state changes" },
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default meta
|
|
53
|
+
type Story = StoryObj<typeof Checkbox>
|
|
54
|
+
|
|
55
|
+
export const Default: Story = {
|
|
56
|
+
args: {
|
|
57
|
+
label: "I agree to the terms and conditions",
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const Checked: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
label: "Subscribe to newsletter",
|
|
64
|
+
defaultChecked: true,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const Unchecked: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
label: "Remember my preferences",
|
|
71
|
+
defaultChecked: false,
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const Disabled: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
label: "This option is disabled",
|
|
78
|
+
disabled: true,
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const DisabledChecked: Story = {
|
|
83
|
+
args: {
|
|
84
|
+
label: "This option is checked and disabled",
|
|
85
|
+
defaultChecked: true,
|
|
86
|
+
disabled: true,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const LongLabel: Story = {
|
|
91
|
+
args: {
|
|
92
|
+
label: "By checking this box, I confirm that I have read and understood the privacy policy, terms of service, and agree to receive communications from Build Canada.",
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Interactive test: Click to toggle
|
|
97
|
+
export const ClickTest: Story = {
|
|
98
|
+
args: {
|
|
99
|
+
label: "Click to toggle",
|
|
100
|
+
onChange: fn(),
|
|
101
|
+
},
|
|
102
|
+
play: async ({ canvasElement, args }) => {
|
|
103
|
+
const canvas = within(canvasElement)
|
|
104
|
+
const checkbox = canvas.getByRole("checkbox", { name: /click to toggle/i })
|
|
105
|
+
|
|
106
|
+
await expect(checkbox).not.toBeChecked()
|
|
107
|
+
await userEvent.click(checkbox)
|
|
108
|
+
await expect(checkbox).toBeChecked()
|
|
109
|
+
await expect(args.onChange).toHaveBeenCalled()
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Interactive test: Double click
|
|
114
|
+
export const DoubleClickTest: Story = {
|
|
115
|
+
args: {
|
|
116
|
+
label: "Double click test",
|
|
117
|
+
onChange: fn(),
|
|
118
|
+
},
|
|
119
|
+
play: async ({ canvasElement }) => {
|
|
120
|
+
const canvas = within(canvasElement)
|
|
121
|
+
const checkbox = canvas.getByRole("checkbox", { name: /double click/i })
|
|
122
|
+
|
|
123
|
+
await expect(checkbox).not.toBeChecked()
|
|
124
|
+
await userEvent.click(checkbox)
|
|
125
|
+
await expect(checkbox).toBeChecked()
|
|
126
|
+
await userEvent.click(checkbox)
|
|
127
|
+
await expect(checkbox).not.toBeChecked()
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Interactive test: Disabled should not toggle
|
|
132
|
+
export const DisabledClickTest: Story = {
|
|
133
|
+
args: {
|
|
134
|
+
label: "Disabled checkbox",
|
|
135
|
+
disabled: true,
|
|
136
|
+
onChange: fn(),
|
|
137
|
+
},
|
|
138
|
+
play: async ({ canvasElement, args }) => {
|
|
139
|
+
const canvas = within(canvasElement)
|
|
140
|
+
const checkbox = canvas.getByRole("checkbox", { name: /disabled checkbox/i })
|
|
141
|
+
|
|
142
|
+
await expect(checkbox).toBeDisabled()
|
|
143
|
+
await userEvent.click(checkbox)
|
|
144
|
+
await expect(args.onChange).not.toHaveBeenCalled()
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Interactive test: Keyboard interaction
|
|
149
|
+
export const KeyboardTest: Story = {
|
|
150
|
+
args: {
|
|
151
|
+
label: "Keyboard toggle",
|
|
152
|
+
onChange: fn(),
|
|
153
|
+
},
|
|
154
|
+
play: async ({ canvasElement }) => {
|
|
155
|
+
const canvas = within(canvasElement)
|
|
156
|
+
const checkbox = canvas.getByRole("checkbox", { name: /keyboard toggle/i })
|
|
157
|
+
|
|
158
|
+
checkbox.focus()
|
|
159
|
+
await expect(checkbox).toHaveFocus()
|
|
160
|
+
await userEvent.keyboard(" ")
|
|
161
|
+
await expect(checkbox).toBeChecked()
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const Controlled: Story = {
|
|
166
|
+
render: function ControlledCheckbox() {
|
|
167
|
+
const [checked, setChecked] = useState(false)
|
|
168
|
+
return (
|
|
169
|
+
<div>
|
|
170
|
+
<Checkbox
|
|
171
|
+
label={`Controlled checkbox (${checked ? "checked" : "unchecked"})`}
|
|
172
|
+
checked={checked}
|
|
173
|
+
onChange={(e) => setChecked(e.target.checked)}
|
|
174
|
+
/>
|
|
175
|
+
<p style={{ marginTop: "16px", fontFamily: "sans-serif", fontSize: "14px" }}>
|
|
176
|
+
The checkbox is currently: <strong>{checked ? "checked" : "unchecked"}</strong>
|
|
177
|
+
</p>
|
|
178
|
+
</div>
|
|
179
|
+
)
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const MultipleCheckboxes: Story = {
|
|
184
|
+
render: () => (
|
|
185
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
|
186
|
+
<Checkbox label="Option A" defaultChecked />
|
|
187
|
+
<Checkbox label="Option B" />
|
|
188
|
+
<Checkbox label="Option C" />
|
|
189
|
+
<Checkbox label="Option D (disabled)" disabled />
|
|
190
|
+
</div>
|
|
191
|
+
),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const FormExample: Story = {
|
|
195
|
+
render: () => (
|
|
196
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px", maxWidth: "500px" }}>
|
|
197
|
+
<h3 style={{ margin: 0, fontFamily: "sans-serif" }}>Communication Preferences</h3>
|
|
198
|
+
<Checkbox label="Email updates about new features" defaultChecked />
|
|
199
|
+
<Checkbox label="Weekly newsletter" defaultChecked />
|
|
200
|
+
<Checkbox label="Partner offers and promotions" />
|
|
201
|
+
<Checkbox label="Account activity alerts" defaultChecked />
|
|
202
|
+
</div>
|
|
203
|
+
),
|
|
204
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Checkbox, type CheckboxProps } from "./Checkbox"
|
|
2
|
-
export { default } from "./Checkbox"
|
|
1
|
+
export { Checkbox, type CheckboxProps } from "./Checkbox.js"
|
|
2
|
+
export { default } from "./Checkbox.js"
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import { within, userEvent, expect, fn } from "@storybook/test"
|
|
3
|
+
|
|
4
|
+
import { TextField } from "./TextField"
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof TextField> = {
|
|
7
|
+
title: "Components/Primitives/TextField",
|
|
8
|
+
component: TextField,
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
A form text input component with label, hint, and error states.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
\`\`\`tsx
|
|
18
|
+
import { TextField } from "@buildcanada/components"
|
|
19
|
+
|
|
20
|
+
<TextField
|
|
21
|
+
label="Email Address"
|
|
22
|
+
type="email"
|
|
23
|
+
placeholder="you@example.com"
|
|
24
|
+
/>
|
|
25
|
+
\`\`\`
|
|
26
|
+
|
|
27
|
+
## Input Types
|
|
28
|
+
|
|
29
|
+
Supports standard HTML input types: text, email, password, number, tel, url.
|
|
30
|
+
|
|
31
|
+
## Validation States
|
|
32
|
+
|
|
33
|
+
\`\`\`tsx
|
|
34
|
+
// With hint text
|
|
35
|
+
<TextField label="Password" hint="Must be 8+ characters" />
|
|
36
|
+
|
|
37
|
+
// With error
|
|
38
|
+
<TextField label="Email" error="Invalid email format" />
|
|
39
|
+
|
|
40
|
+
// Required field
|
|
41
|
+
<TextField label="Name" required />
|
|
42
|
+
\`\`\`
|
|
43
|
+
`,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
argTypes: {
|
|
48
|
+
type: {
|
|
49
|
+
control: "select",
|
|
50
|
+
options: ["text", "email", "password", "number", "tel", "url"],
|
|
51
|
+
description: "HTML input type",
|
|
52
|
+
},
|
|
53
|
+
label: { description: "Label text above the input" },
|
|
54
|
+
placeholder: { description: "Placeholder text" },
|
|
55
|
+
hint: { description: "Helper text below the input" },
|
|
56
|
+
error: { description: "Error message (shows error state when set)" },
|
|
57
|
+
required: { description: "Whether the field is required" },
|
|
58
|
+
disabled: { description: "Whether the input is disabled" },
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default meta
|
|
63
|
+
type Story = StoryObj<typeof TextField>
|
|
64
|
+
|
|
65
|
+
export const Default: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
label: "Full Name",
|
|
68
|
+
placeholder: "Enter your full name",
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const WithValue: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
label: "Email Address",
|
|
75
|
+
type: "email",
|
|
76
|
+
value: "contact@buildcanada.com",
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const WithPlaceholder: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
label: "Company",
|
|
83
|
+
placeholder: "Your company name",
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const WithHint: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
label: "Password",
|
|
90
|
+
type: "password",
|
|
91
|
+
placeholder: "Enter your password",
|
|
92
|
+
hint: "Must be at least 8 characters long",
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const WithError: Story = {
|
|
97
|
+
args: {
|
|
98
|
+
label: "Email Address",
|
|
99
|
+
type: "email",
|
|
100
|
+
value: "invalid-email",
|
|
101
|
+
error: "Please enter a valid email address",
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const Required: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
label: "Email Address",
|
|
108
|
+
type: "email",
|
|
109
|
+
placeholder: "you@example.com",
|
|
110
|
+
required: true,
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const Disabled: Story = {
|
|
115
|
+
args: {
|
|
116
|
+
label: "Organization",
|
|
117
|
+
value: "Build Canada",
|
|
118
|
+
disabled: true,
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const EmailType: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
label: "Email",
|
|
125
|
+
type: "email",
|
|
126
|
+
placeholder: "you@example.com",
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const PasswordType: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
label: "Password",
|
|
133
|
+
type: "password",
|
|
134
|
+
placeholder: "Enter your password",
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const NumberType: Story = {
|
|
139
|
+
args: {
|
|
140
|
+
label: "Amount (CAD)",
|
|
141
|
+
type: "number",
|
|
142
|
+
placeholder: "0.00",
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const TelType: Story = {
|
|
147
|
+
args: {
|
|
148
|
+
label: "Phone Number",
|
|
149
|
+
type: "tel",
|
|
150
|
+
placeholder: "+1 (555) 123-4567",
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const NoLabel: Story = {
|
|
155
|
+
args: {
|
|
156
|
+
placeholder: "Search...",
|
|
157
|
+
type: "text",
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Interactive test: Type in the field
|
|
162
|
+
export const TypeTest: Story = {
|
|
163
|
+
args: {
|
|
164
|
+
label: "Username",
|
|
165
|
+
placeholder: "Enter username",
|
|
166
|
+
onChange: fn(),
|
|
167
|
+
},
|
|
168
|
+
play: async ({ canvasElement, args }) => {
|
|
169
|
+
const canvas = within(canvasElement)
|
|
170
|
+
const input = canvas.getByRole("textbox", { name: /username/i })
|
|
171
|
+
|
|
172
|
+
await expect(input).toBeInTheDocument()
|
|
173
|
+
await userEvent.type(input, "testuser")
|
|
174
|
+
await expect(input).toHaveValue("testuser")
|
|
175
|
+
await expect(args.onChange).toHaveBeenCalled()
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Interactive test: Focus and blur
|
|
180
|
+
export const FocusBlurTest: Story = {
|
|
181
|
+
args: {
|
|
182
|
+
label: "Email",
|
|
183
|
+
placeholder: "Enter email",
|
|
184
|
+
onFocus: fn(),
|
|
185
|
+
onBlur: fn(),
|
|
186
|
+
},
|
|
187
|
+
play: async ({ canvasElement, args }) => {
|
|
188
|
+
const canvas = within(canvasElement)
|
|
189
|
+
const input = canvas.getByRole("textbox", { name: /email/i })
|
|
190
|
+
|
|
191
|
+
await userEvent.click(input)
|
|
192
|
+
await expect(args.onFocus).toHaveBeenCalled()
|
|
193
|
+
|
|
194
|
+
await userEvent.tab()
|
|
195
|
+
await expect(args.onBlur).toHaveBeenCalled()
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Interactive test: Disabled field
|
|
200
|
+
export const DisabledTest: Story = {
|
|
201
|
+
args: {
|
|
202
|
+
label: "Locked Field",
|
|
203
|
+
value: "Cannot edit this",
|
|
204
|
+
disabled: true,
|
|
205
|
+
},
|
|
206
|
+
play: async ({ canvasElement }) => {
|
|
207
|
+
const canvas = within(canvasElement)
|
|
208
|
+
const input = canvas.getByRole("textbox", { name: /locked field/i })
|
|
209
|
+
|
|
210
|
+
await expect(input).toBeDisabled()
|
|
211
|
+
await expect(input).toHaveValue("Cannot edit this")
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Interactive test: Error state
|
|
216
|
+
export const ErrorStateTest: Story = {
|
|
217
|
+
args: {
|
|
218
|
+
label: "Email",
|
|
219
|
+
value: "invalid",
|
|
220
|
+
error: "Invalid email format",
|
|
221
|
+
},
|
|
222
|
+
play: async ({ canvasElement }) => {
|
|
223
|
+
const canvas = within(canvasElement)
|
|
224
|
+
const input = canvas.getByRole("textbox", { name: /email/i })
|
|
225
|
+
const errorMessage = canvas.getByText("Invalid email format")
|
|
226
|
+
|
|
227
|
+
await expect(input).toHaveAttribute("aria-invalid", "true")
|
|
228
|
+
await expect(errorMessage).toBeInTheDocument()
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const AllStates: Story = {
|
|
233
|
+
render: () => (
|
|
234
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "24px", maxWidth: "400px" }}>
|
|
235
|
+
<TextField
|
|
236
|
+
label="Default"
|
|
237
|
+
placeholder="Enter text..."
|
|
238
|
+
/>
|
|
239
|
+
<TextField
|
|
240
|
+
label="With Value"
|
|
241
|
+
value="Some content"
|
|
242
|
+
/>
|
|
243
|
+
<TextField
|
|
244
|
+
label="With Hint"
|
|
245
|
+
placeholder="Enter text..."
|
|
246
|
+
hint="This is a helpful hint"
|
|
247
|
+
/>
|
|
248
|
+
<TextField
|
|
249
|
+
label="With Error"
|
|
250
|
+
value="Invalid input"
|
|
251
|
+
error="This field has an error"
|
|
252
|
+
/>
|
|
253
|
+
<TextField
|
|
254
|
+
label="Required Field"
|
|
255
|
+
placeholder="This field is required"
|
|
256
|
+
required
|
|
257
|
+
/>
|
|
258
|
+
<TextField
|
|
259
|
+
label="Disabled"
|
|
260
|
+
value="Cannot edit"
|
|
261
|
+
disabled
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
),
|
|
265
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { TextField, type TextFieldProps, type TextFieldType } from "./TextField"
|
|
2
|
-
export { default } from "./TextField"
|
|
1
|
+
export { TextField, type TextFieldProps, type TextFieldType } from "./TextField.js"
|
|
2
|
+
export { default } from "./TextField.js"
|