@buildcanada/components 0.3.4 → 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/package.json +3 -2
- package/src/assets/fonts/financier-text-regular.woff2 +0 -0
- package/src/assets/fonts/founders-grotesk-mono-regular.woff2 +0 -0
- package/src/assets/fonts/soehne-kraftig.woff2 +0 -0
- package/src/content/Card/Card.scss +281 -0
- package/src/content/Card/Card.stories.tsx +389 -0
- package/src/content/Card/Card.tsx +170 -0
- package/src/content/Card/index.ts +22 -0
- package/src/content/Hero/Hero.scss +150 -0
- package/src/content/Hero/Hero.stories.tsx +299 -0
- package/src/content/Hero/Hero.tsx +63 -0
- package/src/content/Hero/index.ts +13 -0
- package/src/content/StatBlock/StatBlock.scss +83 -0
- package/src/content/StatBlock/StatBlock.stories.tsx +331 -0
- package/src/content/StatBlock/StatBlock.tsx +52 -0
- package/src/content/StatBlock/index.ts +2 -0
- package/src/feedback/Dialog/Dialog.scss +158 -0
- package/src/feedback/Dialog/Dialog.stories.tsx +286 -0
- package/src/feedback/Dialog/Dialog.tsx +120 -0
- package/src/feedback/Dialog/index.ts +1 -0
- package/src/feedback/PopupForm/PopupForm.scss +34 -0
- package/src/feedback/PopupForm/PopupForm.stories.tsx +341 -0
- package/src/feedback/PopupForm/PopupForm.tsx +90 -0
- package/src/feedback/PopupForm/index.ts +1 -0
- package/src/index.ts +61 -0
- package/src/layout/Container/Container.scss +40 -0
- package/src/layout/Container/Container.stories.tsx +153 -0
- package/src/layout/Container/Container.tsx +29 -0
- package/src/layout/Container/index.ts +2 -0
- package/src/layout/Divider/Divider.scss +117 -0
- package/src/layout/Divider/Divider.stories.tsx +204 -0
- package/src/layout/Divider/Divider.tsx +32 -0
- package/src/layout/Divider/index.ts +2 -0
- package/src/layout/Grid/Grid.scss +81 -0
- package/src/layout/Grid/Grid.stories.tsx +263 -0
- package/src/layout/Grid/Grid.tsx +75 -0
- package/src/layout/Grid/index.ts +2 -0
- package/src/layout/Section/Section.scss +74 -0
- package/src/layout/Section/Section.stories.tsx +173 -0
- package/src/layout/Section/Section.tsx +37 -0
- package/src/layout/Section/index.ts +2 -0
- package/src/layout/Stack/Stack.scss +61 -0
- package/src/layout/Stack/Stack.stories.tsx +342 -0
- package/src/layout/Stack/Stack.tsx +48 -0
- package/src/layout/Stack/index.ts +9 -0
- package/src/navigation/Footer/Footer.scss +233 -0
- package/src/navigation/Footer/Footer.stories.tsx +351 -0
- package/src/navigation/Footer/Footer.tsx +174 -0
- package/src/navigation/Footer/index.ts +2 -0
- package/src/navigation/Header/Header.scss +325 -0
- package/src/navigation/Header/Header.stories.tsx +346 -0
- package/src/navigation/Header/Header.tsx +185 -0
- package/src/navigation/Header/index.ts +2 -0
- package/src/primitives/Button/Button.scss +218 -0
- package/src/primitives/Button/Button.stories.tsx +300 -0
- package/src/primitives/Button/Button.tsx +120 -0
- package/src/primitives/Button/index.ts +2 -0
- package/src/primitives/Checkbox/Checkbox.scss +114 -0
- package/src/primitives/Checkbox/Checkbox.stories.tsx +204 -0
- package/src/primitives/Checkbox/Checkbox.tsx +75 -0
- package/src/primitives/Checkbox/index.ts +2 -0
- package/src/primitives/TextField/TextField.scss +93 -0
- package/src/primitives/TextField/TextField.stories.tsx +265 -0
- package/src/primitives/TextField/TextField.tsx +105 -0
- package/src/primitives/TextField/index.ts +2 -0
- package/src/styles/fonts.scss +27 -0
- package/src/styles/main.scss +36 -0
- package/src/styles/tokens.scss +301 -0
- package/src/styles/typography.scss +232 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import cx from "classnames"
|
|
2
|
+
import { useState } from "react"
|
|
3
|
+
|
|
4
|
+
export interface NavItem {
|
|
5
|
+
label: string
|
|
6
|
+
href: string
|
|
7
|
+
dropdown?: NavItem[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface HeaderProps {
|
|
11
|
+
logo: React.ReactNode
|
|
12
|
+
navItems?: NavItem[]
|
|
13
|
+
cta?: {
|
|
14
|
+
label: string
|
|
15
|
+
href: string
|
|
16
|
+
}
|
|
17
|
+
announcement?: {
|
|
18
|
+
text: string
|
|
19
|
+
href?: string
|
|
20
|
+
variant?: "default" | "auburn"
|
|
21
|
+
}
|
|
22
|
+
className?: string
|
|
23
|
+
style?: React.CSSProperties
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function Header({
|
|
27
|
+
logo,
|
|
28
|
+
navItems = [],
|
|
29
|
+
cta,
|
|
30
|
+
announcement,
|
|
31
|
+
className,
|
|
32
|
+
style,
|
|
33
|
+
}: HeaderProps) {
|
|
34
|
+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
35
|
+
const [openDropdown, setOpenDropdown] = useState<string | null>(null)
|
|
36
|
+
|
|
37
|
+
const classes = cx("bc-header", className)
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<header className={classes} style={style}>
|
|
41
|
+
{announcement && (
|
|
42
|
+
<div
|
|
43
|
+
className={cx("bc-header__announcement", {
|
|
44
|
+
"bc-header__announcement--auburn":
|
|
45
|
+
announcement.variant === "auburn",
|
|
46
|
+
})}
|
|
47
|
+
>
|
|
48
|
+
{announcement.href ? (
|
|
49
|
+
<a href={announcement.href}>{announcement.text}</a>
|
|
50
|
+
) : (
|
|
51
|
+
<span>{announcement.text}</span>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
<div className="bc-header__main">
|
|
56
|
+
<div className="bc-header__logo">{logo}</div>
|
|
57
|
+
|
|
58
|
+
<nav className="bc-header__nav" aria-label="Main navigation">
|
|
59
|
+
<ul className="bc-header__nav-list">
|
|
60
|
+
{navItems.map((item) => (
|
|
61
|
+
<li
|
|
62
|
+
key={item.label}
|
|
63
|
+
className={cx("bc-header__nav-item", {
|
|
64
|
+
"bc-header__nav-item--has-dropdown": item.dropdown,
|
|
65
|
+
})}
|
|
66
|
+
onMouseEnter={() =>
|
|
67
|
+
item.dropdown && setOpenDropdown(item.label)
|
|
68
|
+
}
|
|
69
|
+
onMouseLeave={() =>
|
|
70
|
+
item.dropdown && setOpenDropdown(null)
|
|
71
|
+
}
|
|
72
|
+
>
|
|
73
|
+
{item.dropdown ? (
|
|
74
|
+
<>
|
|
75
|
+
<button
|
|
76
|
+
className="bc-header__nav-link bc-header__nav-link--dropdown"
|
|
77
|
+
aria-expanded={openDropdown === item.label}
|
|
78
|
+
aria-haspopup="true"
|
|
79
|
+
>
|
|
80
|
+
{item.label}
|
|
81
|
+
<svg
|
|
82
|
+
className="bc-header__dropdown-icon"
|
|
83
|
+
viewBox="0 0 20 20"
|
|
84
|
+
fill="currentColor"
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
>
|
|
87
|
+
<path
|
|
88
|
+
fillRule="evenodd"
|
|
89
|
+
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
90
|
+
clipRule="evenodd"
|
|
91
|
+
/>
|
|
92
|
+
</svg>
|
|
93
|
+
</button>
|
|
94
|
+
{openDropdown === item.label && (
|
|
95
|
+
<ul className="bc-header__dropdown">
|
|
96
|
+
{item.dropdown.map((subItem) => (
|
|
97
|
+
<li key={subItem.label}>
|
|
98
|
+
<a
|
|
99
|
+
href={subItem.href}
|
|
100
|
+
className="bc-header__dropdown-link"
|
|
101
|
+
>
|
|
102
|
+
{subItem.label}
|
|
103
|
+
</a>
|
|
104
|
+
</li>
|
|
105
|
+
))}
|
|
106
|
+
</ul>
|
|
107
|
+
)}
|
|
108
|
+
</>
|
|
109
|
+
) : (
|
|
110
|
+
<a href={item.href} className="bc-header__nav-link">
|
|
111
|
+
{item.label}
|
|
112
|
+
</a>
|
|
113
|
+
)}
|
|
114
|
+
</li>
|
|
115
|
+
))}
|
|
116
|
+
</ul>
|
|
117
|
+
</nav>
|
|
118
|
+
|
|
119
|
+
{cta && (
|
|
120
|
+
<a href={cta.href} className="bc-header__cta">
|
|
121
|
+
{cta.label}
|
|
122
|
+
</a>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
<button
|
|
126
|
+
className="bc-header__mobile-toggle"
|
|
127
|
+
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
128
|
+
aria-expanded={mobileMenuOpen}
|
|
129
|
+
aria-label="Toggle navigation menu"
|
|
130
|
+
>
|
|
131
|
+
<span className="bc-header__hamburger" />
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
{/* Mobile Menu */}
|
|
136
|
+
<div
|
|
137
|
+
className={cx("bc-header__mobile-menu", {
|
|
138
|
+
"bc-header__mobile-menu--open": mobileMenuOpen,
|
|
139
|
+
})}
|
|
140
|
+
>
|
|
141
|
+
<nav aria-label="Mobile navigation">
|
|
142
|
+
<ul className="bc-header__mobile-nav">
|
|
143
|
+
{navItems.map((item) => (
|
|
144
|
+
<li key={item.label}>
|
|
145
|
+
<a
|
|
146
|
+
href={item.href}
|
|
147
|
+
className="bc-header__mobile-link"
|
|
148
|
+
onClick={() => setMobileMenuOpen(false)}
|
|
149
|
+
>
|
|
150
|
+
{item.label}
|
|
151
|
+
</a>
|
|
152
|
+
{item.dropdown && (
|
|
153
|
+
<ul className="bc-header__mobile-subnav">
|
|
154
|
+
{item.dropdown.map((subItem) => (
|
|
155
|
+
<li key={subItem.label}>
|
|
156
|
+
<a
|
|
157
|
+
href={subItem.href}
|
|
158
|
+
className="bc-header__mobile-link bc-header__mobile-link--sub"
|
|
159
|
+
onClick={() => setMobileMenuOpen(false)}
|
|
160
|
+
>
|
|
161
|
+
{subItem.label}
|
|
162
|
+
</a>
|
|
163
|
+
</li>
|
|
164
|
+
))}
|
|
165
|
+
</ul>
|
|
166
|
+
)}
|
|
167
|
+
</li>
|
|
168
|
+
))}
|
|
169
|
+
</ul>
|
|
170
|
+
{cta && (
|
|
171
|
+
<a
|
|
172
|
+
href={cta.href}
|
|
173
|
+
className="bc-header__mobile-cta"
|
|
174
|
+
onClick={() => setMobileMenuOpen(false)}
|
|
175
|
+
>
|
|
176
|
+
{cta.label}
|
|
177
|
+
</a>
|
|
178
|
+
)}
|
|
179
|
+
</nav>
|
|
180
|
+
</div>
|
|
181
|
+
</header>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default Header
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
@use "../../styles/tokens" as *;
|
|
2
|
+
@use "../../styles/typography" as *;
|
|
3
|
+
|
|
4
|
+
/*******************************************************************************
|
|
5
|
+
* Button Component
|
|
6
|
+
*
|
|
7
|
+
* Design notes:
|
|
8
|
+
* - Square corners (border-radius: 0) per brand motif "Huge Square Buttons"
|
|
9
|
+
* - Uppercase text with letter-spacing
|
|
10
|
+
* - Three sizes: sm (36px), md (44px), lg (56px)
|
|
11
|
+
******************************************************************************/
|
|
12
|
+
|
|
13
|
+
.bc-btn {
|
|
14
|
+
@include button-text-md;
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
gap: $space-1;
|
|
19
|
+
border: 2px solid transparent;
|
|
20
|
+
border-radius: $radius-none;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
text-decoration: none;
|
|
23
|
+
transition: all $transition-fast;
|
|
24
|
+
white-space: nowrap;
|
|
25
|
+
|
|
26
|
+
&:focus-visible {
|
|
27
|
+
outline: 2px solid $auburn;
|
|
28
|
+
outline-offset: 2px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/***************************************************************************
|
|
32
|
+
* Size Variants
|
|
33
|
+
***************************************************************************/
|
|
34
|
+
|
|
35
|
+
&--sm {
|
|
36
|
+
@include button-text-sm;
|
|
37
|
+
padding: $space-1 $space-2;
|
|
38
|
+
min-height: 36px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&--md {
|
|
42
|
+
@include button-text-md;
|
|
43
|
+
padding: calc($space-1 + 4px) $space-3;
|
|
44
|
+
min-height: 44px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&--lg {
|
|
48
|
+
@include button-text-lg;
|
|
49
|
+
padding: $space-2 $space-4;
|
|
50
|
+
min-height: 56px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/***************************************************************************
|
|
54
|
+
* Solid Variants
|
|
55
|
+
***************************************************************************/
|
|
56
|
+
|
|
57
|
+
&--solid-auburn {
|
|
58
|
+
background-color: $auburn;
|
|
59
|
+
border-color: $auburn;
|
|
60
|
+
color: $white;
|
|
61
|
+
|
|
62
|
+
&:hover:not(:disabled) {
|
|
63
|
+
background-color: $auburn-600;
|
|
64
|
+
border-color: $auburn-600;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&:active:not(:disabled) {
|
|
68
|
+
background-color: $auburn-700;
|
|
69
|
+
border-color: $auburn-700;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
&--solid-charcoal {
|
|
74
|
+
background-color: $charcoal;
|
|
75
|
+
border-color: $charcoal;
|
|
76
|
+
color: $white;
|
|
77
|
+
|
|
78
|
+
&:hover:not(:disabled) {
|
|
79
|
+
background-color: $gray-700;
|
|
80
|
+
border-color: $gray-700;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
&:active:not(:disabled) {
|
|
84
|
+
background-color: $gray-600;
|
|
85
|
+
border-color: $gray-600;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
&--solid-linen {
|
|
90
|
+
background-color: $linen;
|
|
91
|
+
border-color: $linen;
|
|
92
|
+
color: $charcoal;
|
|
93
|
+
|
|
94
|
+
&:hover:not(:disabled) {
|
|
95
|
+
background-color: darken($linen, 5%);
|
|
96
|
+
border-color: darken($linen, 5%);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&:active:not(:disabled) {
|
|
100
|
+
background-color: darken($linen, 8%);
|
|
101
|
+
border-color: darken($linen, 8%);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/***************************************************************************
|
|
106
|
+
* Outline Variants
|
|
107
|
+
***************************************************************************/
|
|
108
|
+
|
|
109
|
+
&--outline-auburn {
|
|
110
|
+
background-color: transparent;
|
|
111
|
+
border-color: $auburn;
|
|
112
|
+
color: $auburn;
|
|
113
|
+
|
|
114
|
+
&:hover:not(:disabled) {
|
|
115
|
+
background-color: $auburn;
|
|
116
|
+
color: $white;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&:active:not(:disabled) {
|
|
120
|
+
background-color: $auburn-600;
|
|
121
|
+
border-color: $auburn-600;
|
|
122
|
+
color: $white;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&--outline-charcoal {
|
|
127
|
+
background-color: transparent;
|
|
128
|
+
border-color: $charcoal;
|
|
129
|
+
color: $charcoal;
|
|
130
|
+
|
|
131
|
+
&:hover:not(:disabled) {
|
|
132
|
+
background-color: $charcoal;
|
|
133
|
+
color: $white;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&:active:not(:disabled) {
|
|
137
|
+
background-color: $gray-700;
|
|
138
|
+
border-color: $gray-700;
|
|
139
|
+
color: $white;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
&--outline-white {
|
|
144
|
+
background-color: transparent;
|
|
145
|
+
border-color: $white;
|
|
146
|
+
color: $white;
|
|
147
|
+
|
|
148
|
+
&:hover:not(:disabled) {
|
|
149
|
+
background-color: $white;
|
|
150
|
+
color: $charcoal;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&:active:not(:disabled) {
|
|
154
|
+
background-color: $gray-100;
|
|
155
|
+
border-color: $gray-100;
|
|
156
|
+
color: $charcoal;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/***************************************************************************
|
|
161
|
+
* State Modifiers
|
|
162
|
+
***************************************************************************/
|
|
163
|
+
|
|
164
|
+
&:disabled {
|
|
165
|
+
opacity: 0.5;
|
|
166
|
+
cursor: not-allowed;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
&--icon-only {
|
|
170
|
+
padding: $space-1;
|
|
171
|
+
|
|
172
|
+
&.bc-btn--sm {
|
|
173
|
+
padding: calc($space-1 - 2px);
|
|
174
|
+
min-width: 36px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
&.bc-btn--md {
|
|
178
|
+
padding: calc($space-1 + 2px);
|
|
179
|
+
min-width: 44px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
&.bc-btn--lg {
|
|
183
|
+
padding: $space-2;
|
|
184
|
+
min-width: 56px;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
&--full-width {
|
|
189
|
+
width: 100%;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/***************************************************************************
|
|
193
|
+
* Icon Styles
|
|
194
|
+
***************************************************************************/
|
|
195
|
+
|
|
196
|
+
&__icon {
|
|
197
|
+
font-size: 0.875em;
|
|
198
|
+
transition: transform $transition-fast;
|
|
199
|
+
|
|
200
|
+
&--left {
|
|
201
|
+
margin-right: $space-1;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
&--right {
|
|
205
|
+
margin-left: $space-1;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Animated arrow on hover (buildcanada.com style)
|
|
210
|
+
&:hover:not(:disabled) &__icon--right {
|
|
211
|
+
transform: translateX(4px);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
&__text {
|
|
215
|
+
// Ensure text is vertically centered
|
|
216
|
+
line-height: 1;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import { within, userEvent, expect, fn } from "@storybook/test"
|
|
3
|
+
import { faDownload, faPlus, faCheck, faEnvelope } from "@fortawesome/free-solid-svg-icons"
|
|
4
|
+
|
|
5
|
+
import { Button } from "./Button"
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Button> = {
|
|
8
|
+
title: "Components/Primitives/Button",
|
|
9
|
+
component: Button,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: `
|
|
14
|
+
A versatile button component that supports multiple variants, sizes, and icon configurations.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
\`\`\`tsx
|
|
19
|
+
import { Button } from "@buildcanada/components"
|
|
20
|
+
|
|
21
|
+
<Button text="Click me" variant="solid-auburn" />
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **6 variants**: solid-auburn, solid-charcoal, solid-linen, outline-auburn, outline-charcoal, outline-white
|
|
27
|
+
- **3 sizes**: sm, md, lg
|
|
28
|
+
- **Icon support**: Left or right positioned icons using FontAwesome
|
|
29
|
+
- **Link mode**: Can render as an anchor tag with \`href\` prop
|
|
30
|
+
- **Full width**: Expand to container width with \`fullWidth\` prop
|
|
31
|
+
`,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
argTypes: {
|
|
36
|
+
variant: {
|
|
37
|
+
control: "select",
|
|
38
|
+
options: [
|
|
39
|
+
"solid-auburn",
|
|
40
|
+
"solid-charcoal",
|
|
41
|
+
"solid-linen",
|
|
42
|
+
"outline-auburn",
|
|
43
|
+
"outline-charcoal",
|
|
44
|
+
"outline-white",
|
|
45
|
+
],
|
|
46
|
+
description: "The visual style variant of the button",
|
|
47
|
+
},
|
|
48
|
+
size: {
|
|
49
|
+
control: "radio",
|
|
50
|
+
options: ["sm", "md", "lg"],
|
|
51
|
+
description: "The size of the button",
|
|
52
|
+
},
|
|
53
|
+
iconPosition: {
|
|
54
|
+
control: "radio",
|
|
55
|
+
options: ["left", "right"],
|
|
56
|
+
description: "Position of the icon relative to the text",
|
|
57
|
+
},
|
|
58
|
+
text: {
|
|
59
|
+
description: "The button label text",
|
|
60
|
+
},
|
|
61
|
+
icon: {
|
|
62
|
+
description: "FontAwesome icon definition. Defaults to arrow-right.",
|
|
63
|
+
},
|
|
64
|
+
disabled: {
|
|
65
|
+
description: "Whether the button is disabled",
|
|
66
|
+
},
|
|
67
|
+
fullWidth: {
|
|
68
|
+
description: "Whether the button should expand to full container width",
|
|
69
|
+
},
|
|
70
|
+
href: {
|
|
71
|
+
description: "If provided, renders as an anchor tag instead of button",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default meta
|
|
77
|
+
type Story = StoryObj<typeof Button>
|
|
78
|
+
|
|
79
|
+
export const Default: Story = {
|
|
80
|
+
args: {
|
|
81
|
+
text: "Get Started",
|
|
82
|
+
variant: "solid-auburn",
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const SolidAuburn: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
text: "Learn More",
|
|
89
|
+
variant: "solid-auburn",
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const SolidCharcoal: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
text: "View Projects",
|
|
96
|
+
variant: "solid-charcoal",
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const SolidLinen: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
text: "Explore",
|
|
103
|
+
variant: "solid-linen",
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const OutlineAuburn: Story = {
|
|
108
|
+
args: {
|
|
109
|
+
text: "Subscribe",
|
|
110
|
+
variant: "outline-auburn",
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const OutlineCharcoal: Story = {
|
|
115
|
+
args: {
|
|
116
|
+
text: "Read Article",
|
|
117
|
+
variant: "outline-charcoal",
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const OutlineWhite: Story = {
|
|
122
|
+
args: {
|
|
123
|
+
text: "Contact Us",
|
|
124
|
+
variant: "outline-white",
|
|
125
|
+
},
|
|
126
|
+
parameters: {
|
|
127
|
+
backgrounds: { default: "charcoal" },
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const Small: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
text: "Small Button",
|
|
134
|
+
variant: "solid-auburn",
|
|
135
|
+
size: "sm",
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const Medium: Story = {
|
|
140
|
+
args: {
|
|
141
|
+
text: "Medium Button",
|
|
142
|
+
variant: "solid-auburn",
|
|
143
|
+
size: "md",
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const Large: Story = {
|
|
148
|
+
args: {
|
|
149
|
+
text: "Large Button",
|
|
150
|
+
variant: "solid-auburn",
|
|
151
|
+
size: "lg",
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const WithIconRight: Story = {
|
|
156
|
+
args: {
|
|
157
|
+
text: "Download Report",
|
|
158
|
+
variant: "solid-auburn",
|
|
159
|
+
icon: faDownload,
|
|
160
|
+
iconPosition: "right",
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const WithIconLeft: Story = {
|
|
165
|
+
args: {
|
|
166
|
+
text: "Add Item",
|
|
167
|
+
variant: "solid-charcoal",
|
|
168
|
+
icon: faPlus,
|
|
169
|
+
iconPosition: "left",
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const IconOnly: Story = {
|
|
174
|
+
args: {
|
|
175
|
+
icon: faEnvelope,
|
|
176
|
+
variant: "outline-auburn",
|
|
177
|
+
ariaLabel: "Send email",
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const NoIcon: Story = {
|
|
182
|
+
args: {
|
|
183
|
+
text: "No Arrow",
|
|
184
|
+
variant: "solid-auburn",
|
|
185
|
+
icon: null,
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export const Disabled: Story = {
|
|
190
|
+
args: {
|
|
191
|
+
text: "Disabled",
|
|
192
|
+
variant: "solid-auburn",
|
|
193
|
+
disabled: true,
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const FullWidth: Story = {
|
|
198
|
+
args: {
|
|
199
|
+
text: "Full Width Button",
|
|
200
|
+
variant: "solid-auburn",
|
|
201
|
+
fullWidth: true,
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const AsLink: Story = {
|
|
206
|
+
args: {
|
|
207
|
+
text: "Visit Website",
|
|
208
|
+
variant: "solid-auburn",
|
|
209
|
+
href: "https://buildcanada.com",
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const SubmitButton: Story = {
|
|
214
|
+
args: {
|
|
215
|
+
text: "Submit Form",
|
|
216
|
+
variant: "solid-auburn",
|
|
217
|
+
type: "submit",
|
|
218
|
+
icon: faCheck,
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Interactive test: Button click
|
|
223
|
+
export const ClickTest: Story = {
|
|
224
|
+
args: {
|
|
225
|
+
text: "Click Me",
|
|
226
|
+
variant: "solid-auburn",
|
|
227
|
+
onClick: fn(),
|
|
228
|
+
},
|
|
229
|
+
play: async ({ canvasElement, args }) => {
|
|
230
|
+
const canvas = within(canvasElement)
|
|
231
|
+
const button = canvas.getByRole("button", { name: /click me/i })
|
|
232
|
+
|
|
233
|
+
await expect(button).toBeInTheDocument()
|
|
234
|
+
await userEvent.click(button)
|
|
235
|
+
await expect(args.onClick).toHaveBeenCalled()
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Interactive test: Disabled button should not be clickable
|
|
240
|
+
export const DisabledClickTest: Story = {
|
|
241
|
+
args: {
|
|
242
|
+
text: "Disabled Button",
|
|
243
|
+
variant: "solid-auburn",
|
|
244
|
+
disabled: true,
|
|
245
|
+
onClick: fn(),
|
|
246
|
+
},
|
|
247
|
+
play: async ({ canvasElement, args }) => {
|
|
248
|
+
const canvas = within(canvasElement)
|
|
249
|
+
const button = canvas.getByRole("button", { name: /disabled button/i })
|
|
250
|
+
|
|
251
|
+
await expect(button).toBeDisabled()
|
|
252
|
+
await userEvent.click(button)
|
|
253
|
+
await expect(args.onClick).not.toHaveBeenCalled()
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Interactive test: Hover state
|
|
258
|
+
export const HoverTest: Story = {
|
|
259
|
+
args: {
|
|
260
|
+
text: "Hover Me",
|
|
261
|
+
variant: "solid-auburn",
|
|
262
|
+
},
|
|
263
|
+
play: async ({ canvasElement }) => {
|
|
264
|
+
const canvas = within(canvasElement)
|
|
265
|
+
const button = canvas.getByRole("button", { name: /hover me/i })
|
|
266
|
+
|
|
267
|
+
await expect(button).toBeInTheDocument()
|
|
268
|
+
await userEvent.hover(button)
|
|
269
|
+
// Visual check - hover state should be visible
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export const AllVariants: Story = {
|
|
274
|
+
render: () => (
|
|
275
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
276
|
+
<div style={{ display: "flex", flexWrap: "wrap", gap: "8px", alignItems: "center" }}>
|
|
277
|
+
<Button text="Solid Auburn" variant="solid-auburn" />
|
|
278
|
+
<Button text="Solid Charcoal" variant="solid-charcoal" />
|
|
279
|
+
<Button text="Solid Linen" variant="solid-linen" />
|
|
280
|
+
</div>
|
|
281
|
+
<div style={{ display: "flex", flexWrap: "wrap", gap: "8px", alignItems: "center" }}>
|
|
282
|
+
<Button text="Outline Auburn" variant="outline-auburn" />
|
|
283
|
+
<Button text="Outline Charcoal" variant="outline-charcoal" />
|
|
284
|
+
</div>
|
|
285
|
+
<div style={{ display: "flex", flexWrap: "wrap", gap: "8px", alignItems: "center", backgroundColor: "#272727", padding: "16px" }}>
|
|
286
|
+
<Button text="Outline White" variant="outline-white" />
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
),
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export const AllSizes: Story = {
|
|
293
|
+
render: () => (
|
|
294
|
+
<div style={{ display: "flex", flexWrap: "wrap", gap: "16px", alignItems: "center" }}>
|
|
295
|
+
<Button text="Small" variant="solid-auburn" size="sm" />
|
|
296
|
+
<Button text="Medium" variant="solid-auburn" size="md" />
|
|
297
|
+
<Button text="Large" variant="solid-auburn" size="lg" />
|
|
298
|
+
</div>
|
|
299
|
+
),
|
|
300
|
+
}
|