@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.
Files changed (69) hide show
  1. package/package.json +3 -2
  2. package/src/assets/fonts/financier-text-regular.woff2 +0 -0
  3. package/src/assets/fonts/founders-grotesk-mono-regular.woff2 +0 -0
  4. package/src/assets/fonts/soehne-kraftig.woff2 +0 -0
  5. package/src/content/Card/Card.scss +281 -0
  6. package/src/content/Card/Card.stories.tsx +389 -0
  7. package/src/content/Card/Card.tsx +170 -0
  8. package/src/content/Card/index.ts +22 -0
  9. package/src/content/Hero/Hero.scss +150 -0
  10. package/src/content/Hero/Hero.stories.tsx +299 -0
  11. package/src/content/Hero/Hero.tsx +63 -0
  12. package/src/content/Hero/index.ts +13 -0
  13. package/src/content/StatBlock/StatBlock.scss +83 -0
  14. package/src/content/StatBlock/StatBlock.stories.tsx +331 -0
  15. package/src/content/StatBlock/StatBlock.tsx +52 -0
  16. package/src/content/StatBlock/index.ts +2 -0
  17. package/src/feedback/Dialog/Dialog.scss +158 -0
  18. package/src/feedback/Dialog/Dialog.stories.tsx +286 -0
  19. package/src/feedback/Dialog/Dialog.tsx +120 -0
  20. package/src/feedback/Dialog/index.ts +1 -0
  21. package/src/feedback/PopupForm/PopupForm.scss +34 -0
  22. package/src/feedback/PopupForm/PopupForm.stories.tsx +341 -0
  23. package/src/feedback/PopupForm/PopupForm.tsx +90 -0
  24. package/src/feedback/PopupForm/index.ts +1 -0
  25. package/src/index.ts +61 -0
  26. package/src/layout/Container/Container.scss +40 -0
  27. package/src/layout/Container/Container.stories.tsx +153 -0
  28. package/src/layout/Container/Container.tsx +29 -0
  29. package/src/layout/Container/index.ts +2 -0
  30. package/src/layout/Divider/Divider.scss +117 -0
  31. package/src/layout/Divider/Divider.stories.tsx +204 -0
  32. package/src/layout/Divider/Divider.tsx +32 -0
  33. package/src/layout/Divider/index.ts +2 -0
  34. package/src/layout/Grid/Grid.scss +81 -0
  35. package/src/layout/Grid/Grid.stories.tsx +263 -0
  36. package/src/layout/Grid/Grid.tsx +75 -0
  37. package/src/layout/Grid/index.ts +2 -0
  38. package/src/layout/Section/Section.scss +74 -0
  39. package/src/layout/Section/Section.stories.tsx +173 -0
  40. package/src/layout/Section/Section.tsx +37 -0
  41. package/src/layout/Section/index.ts +2 -0
  42. package/src/layout/Stack/Stack.scss +61 -0
  43. package/src/layout/Stack/Stack.stories.tsx +342 -0
  44. package/src/layout/Stack/Stack.tsx +48 -0
  45. package/src/layout/Stack/index.ts +9 -0
  46. package/src/navigation/Footer/Footer.scss +233 -0
  47. package/src/navigation/Footer/Footer.stories.tsx +351 -0
  48. package/src/navigation/Footer/Footer.tsx +174 -0
  49. package/src/navigation/Footer/index.ts +2 -0
  50. package/src/navigation/Header/Header.scss +325 -0
  51. package/src/navigation/Header/Header.stories.tsx +346 -0
  52. package/src/navigation/Header/Header.tsx +185 -0
  53. package/src/navigation/Header/index.ts +2 -0
  54. package/src/primitives/Button/Button.scss +218 -0
  55. package/src/primitives/Button/Button.stories.tsx +300 -0
  56. package/src/primitives/Button/Button.tsx +120 -0
  57. package/src/primitives/Button/index.ts +2 -0
  58. package/src/primitives/Checkbox/Checkbox.scss +114 -0
  59. package/src/primitives/Checkbox/Checkbox.stories.tsx +204 -0
  60. package/src/primitives/Checkbox/Checkbox.tsx +75 -0
  61. package/src/primitives/Checkbox/index.ts +2 -0
  62. package/src/primitives/TextField/TextField.scss +93 -0
  63. package/src/primitives/TextField/TextField.stories.tsx +265 -0
  64. package/src/primitives/TextField/TextField.tsx +105 -0
  65. package/src/primitives/TextField/index.ts +2 -0
  66. package/src/styles/fonts.scss +27 -0
  67. package/src/styles/main.scss +36 -0
  68. package/src/styles/tokens.scss +301 -0
  69. package/src/styles/typography.scss +232 -0
@@ -0,0 +1,325 @@
1
+ @use "../../styles/tokens" as *;
2
+ @use "../../styles/typography" as *;
3
+
4
+ /*******************************************************************************
5
+ * Header Component
6
+ ******************************************************************************/
7
+
8
+ .bc-header {
9
+ position: sticky;
10
+ top: 0;
11
+ z-index: $z-sticky;
12
+ background-color: $white;
13
+ border-bottom: 1px solid $border-muted;
14
+
15
+ /***************************************************************************
16
+ * Announcement Banner
17
+ ***************************************************************************/
18
+
19
+ &__announcement {
20
+ background-color: $charcoal;
21
+ color: $white;
22
+ text-align: center;
23
+ padding: calc($space-1 / 2) $space-2;
24
+ @include body-3;
25
+
26
+ a {
27
+ color: inherit;
28
+ text-decoration: underline;
29
+
30
+ &:hover {
31
+ text-decoration: none;
32
+ }
33
+ }
34
+
35
+ &--auburn {
36
+ background-color: $auburn;
37
+ }
38
+ }
39
+
40
+ /***************************************************************************
41
+ * Main Header Row
42
+ ***************************************************************************/
43
+
44
+ &__main {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: space-between;
48
+ padding: $space-2 $space-3;
49
+ max-width: 1280px;
50
+ margin: 0 auto;
51
+
52
+ @include md-up {
53
+ padding: $space-2 $space-5;
54
+ }
55
+ }
56
+
57
+ /***************************************************************************
58
+ * Logo
59
+ ***************************************************************************/
60
+
61
+ &__logo {
62
+ flex-shrink: 0;
63
+
64
+ a {
65
+ display: block;
66
+ text-decoration: none;
67
+ }
68
+
69
+ img {
70
+ height: 32px;
71
+ width: auto;
72
+
73
+ @include md-up {
74
+ height: 40px;
75
+ }
76
+ }
77
+ }
78
+
79
+ /***************************************************************************
80
+ * Desktop Navigation
81
+ ***************************************************************************/
82
+
83
+ &__nav {
84
+ display: none;
85
+
86
+ @include md-up {
87
+ display: block;
88
+ }
89
+ }
90
+
91
+ &__nav-list {
92
+ display: flex;
93
+ align-items: center;
94
+ gap: $space-1;
95
+ list-style: none;
96
+ margin: 0;
97
+ padding: 0;
98
+ }
99
+
100
+ &__nav-item {
101
+ position: relative;
102
+
103
+ &--has-dropdown {
104
+ // Keep dropdown open when hovering over it
105
+ &:hover .bc-header__dropdown {
106
+ display: block;
107
+ }
108
+ }
109
+ }
110
+
111
+ &__nav-link {
112
+ @include label;
113
+ display: inline-flex;
114
+ align-items: center;
115
+ gap: calc($space-1 / 2);
116
+ padding: $space-1 $space-2;
117
+ color: $text-primary;
118
+ text-decoration: none;
119
+ background: none;
120
+ border: none;
121
+ cursor: pointer;
122
+ transition: color $transition-fast;
123
+
124
+ &:hover {
125
+ color: $auburn;
126
+ }
127
+
128
+ &--dropdown {
129
+ // Visual indicator for dropdown
130
+ }
131
+ }
132
+
133
+ &__dropdown-icon {
134
+ width: 16px;
135
+ height: 16px;
136
+ transition: transform $transition-fast;
137
+
138
+ .bc-header__nav-item:hover & {
139
+ transform: rotate(180deg);
140
+ }
141
+ }
142
+
143
+ /***************************************************************************
144
+ * Dropdown Menu
145
+ ***************************************************************************/
146
+
147
+ &__dropdown {
148
+ display: none;
149
+ position: absolute;
150
+ top: 100%;
151
+ left: 0;
152
+ min-width: 200px;
153
+ background-color: $white;
154
+ border: 1px solid $border-muted;
155
+ box-shadow: $shadow-md;
156
+ list-style: none;
157
+ margin: 0;
158
+ padding: $space-1 0;
159
+ z-index: $z-dropdown;
160
+ }
161
+
162
+ &__dropdown-link {
163
+ @include body-2;
164
+ display: block;
165
+ padding: $space-1 $space-2;
166
+ color: $text-primary;
167
+ text-decoration: none;
168
+ transition: background-color $transition-fast;
169
+
170
+ &:hover {
171
+ background-color: $linen;
172
+ color: $auburn;
173
+ }
174
+ }
175
+
176
+ /***************************************************************************
177
+ * CTA Button
178
+ ***************************************************************************/
179
+
180
+ &__cta {
181
+ @include button-text-sm;
182
+ display: none;
183
+ padding: $space-1 $space-2;
184
+ background-color: $auburn;
185
+ color: $white;
186
+ text-decoration: none;
187
+ border-radius: $radius-none;
188
+ transition: background-color $transition-fast;
189
+
190
+ &:hover {
191
+ background-color: $auburn-600;
192
+ }
193
+
194
+ @include md-up {
195
+ display: inline-block;
196
+ }
197
+ }
198
+
199
+ /***************************************************************************
200
+ * Mobile Toggle
201
+ ***************************************************************************/
202
+
203
+ &__mobile-toggle {
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ width: 44px;
208
+ height: 44px;
209
+ padding: 0;
210
+ background: none;
211
+ border: none;
212
+ cursor: pointer;
213
+
214
+ @include md-up {
215
+ display: none;
216
+ }
217
+ }
218
+
219
+ &__hamburger {
220
+ position: relative;
221
+ width: 24px;
222
+ height: 2px;
223
+ background-color: $charcoal;
224
+ transition: background-color $transition-fast;
225
+
226
+ &::before,
227
+ &::after {
228
+ content: "";
229
+ position: absolute;
230
+ left: 0;
231
+ width: 100%;
232
+ height: 2px;
233
+ background-color: $charcoal;
234
+ transition: transform $transition-fast;
235
+ }
236
+
237
+ &::before {
238
+ top: -8px;
239
+ }
240
+
241
+ &::after {
242
+ bottom: -8px;
243
+ }
244
+ }
245
+
246
+ &__mobile-toggle[aria-expanded="true"] &__hamburger {
247
+ background-color: transparent;
248
+
249
+ &::before {
250
+ transform: translateY(8px) rotate(45deg);
251
+ }
252
+
253
+ &::after {
254
+ transform: translateY(-8px) rotate(-45deg);
255
+ }
256
+ }
257
+
258
+ /***************************************************************************
259
+ * Mobile Menu
260
+ ***************************************************************************/
261
+
262
+ &__mobile-menu {
263
+ display: none;
264
+ position: absolute;
265
+ top: 100%;
266
+ left: 0;
267
+ right: 0;
268
+ background-color: $white;
269
+ border-bottom: 1px solid $border-muted;
270
+ padding: $space-3;
271
+ box-shadow: $shadow-lg;
272
+
273
+ &--open {
274
+ display: block;
275
+ }
276
+
277
+ @include md-up {
278
+ display: none !important;
279
+ }
280
+ }
281
+
282
+ &__mobile-nav {
283
+ list-style: none;
284
+ margin: 0;
285
+ padding: 0;
286
+ }
287
+
288
+ &__mobile-link {
289
+ @include label;
290
+ display: block;
291
+ padding: $space-2 0;
292
+ color: $text-primary;
293
+ text-decoration: none;
294
+ border-bottom: 1px solid $border-muted;
295
+
296
+ &:hover {
297
+ color: $auburn;
298
+ }
299
+
300
+ &--sub {
301
+ @include body-2;
302
+ padding-left: $space-3;
303
+ font-weight: 400;
304
+ }
305
+ }
306
+
307
+ &__mobile-subnav {
308
+ list-style: none;
309
+ margin: 0;
310
+ padding: 0;
311
+ }
312
+
313
+ &__mobile-cta {
314
+ @include button-text-md;
315
+ display: block;
316
+ width: 100%;
317
+ margin-top: $space-3;
318
+ padding: $space-2;
319
+ background-color: $auburn;
320
+ color: $white;
321
+ text-align: center;
322
+ text-decoration: none;
323
+ border-radius: $radius-none;
324
+ }
325
+ }
@@ -0,0 +1,346 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { within, userEvent, expect } from "@storybook/test"
3
+
4
+ import { Header } from "./Header"
5
+
6
+ const meta: Meta<typeof Header> = {
7
+ title: "Components/Navigation/Header",
8
+ component: Header,
9
+ parameters: {
10
+ layout: "fullscreen",
11
+ docs: {
12
+ description: {
13
+ component: `
14
+ A responsive site header with navigation, logo, CTA button, and optional announcement banner.
15
+
16
+ ## Usage
17
+
18
+ \`\`\`tsx
19
+ import { Header } from "@buildcanada/components"
20
+
21
+ <Header
22
+ logo={<Logo />}
23
+ navItems={[
24
+ { label: "Home", href: "/" },
25
+ { label: "About", href: "/about" },
26
+ { label: "Projects", href: "/projects" },
27
+ ]}
28
+ cta={{ label: "Get Started", href: "/start" }}
29
+ />
30
+ \`\`\`
31
+
32
+ ## With Dropdowns
33
+
34
+ \`\`\`tsx
35
+ <Header
36
+ logo={<Logo />}
37
+ navItems={[
38
+ { label: "Home", href: "/" },
39
+ {
40
+ label: "Projects",
41
+ href: "/projects",
42
+ dropdown: [
43
+ { label: "Project A", href: "/projects/a" },
44
+ { label: "Project B", href: "/projects/b" },
45
+ ],
46
+ },
47
+ ]}
48
+ />
49
+ \`\`\`
50
+
51
+ ## Announcement Banner
52
+
53
+ \`\`\`tsx
54
+ <Header
55
+ logo={<Logo />}
56
+ navItems={navItems}
57
+ announcement={{
58
+ text: "New feature available!",
59
+ href: "/new-feature",
60
+ variant: "auburn", // or "default"
61
+ }}
62
+ />
63
+ \`\`\`
64
+
65
+ ## Responsive
66
+
67
+ On mobile, navigation collapses into a hamburger menu.
68
+ `,
69
+ },
70
+ },
71
+ },
72
+ }
73
+
74
+ export default meta
75
+ type Story = StoryObj<typeof Header>
76
+
77
+ const Logo = () => (
78
+ <span style={{
79
+ fontFamily: "'Soehne Kraftig', 'Helvetica Neue', Helvetica, Arial, sans-serif",
80
+ fontSize: "18px",
81
+ fontWeight: 500,
82
+ color: "#272727",
83
+ letterSpacing: "-0.01em",
84
+ }}>
85
+ Build Canada
86
+ </span>
87
+ )
88
+
89
+ const basicNavItems = [
90
+ { label: "Home", href: "/" },
91
+ { label: "About", href: "/about" },
92
+ { label: "Projects", href: "/projects" },
93
+ { label: "Contact", href: "/contact" },
94
+ ]
95
+
96
+ const navWithDropdowns = [
97
+ { label: "Home", href: "/" },
98
+ {
99
+ label: "Projects",
100
+ href: "/projects",
101
+ dropdown: [
102
+ { label: "Canada Spends", href: "/projects/canada-spends" },
103
+ { label: "Budget Tracker", href: "/projects/budget-tracker" },
104
+ { label: "Data Explorer", href: "/projects/data-explorer" },
105
+ ],
106
+ },
107
+ {
108
+ label: "Research",
109
+ href: "/research",
110
+ dropdown: [
111
+ { label: "Reports", href: "/research/reports" },
112
+ { label: "Analysis", href: "/research/analysis" },
113
+ { label: "Publications", href: "/research/publications" },
114
+ ],
115
+ },
116
+ { label: "About", href: "/about" },
117
+ ]
118
+
119
+ export const Default: Story = {
120
+ args: {
121
+ logo: <Logo />,
122
+ navItems: basicNavItems,
123
+ },
124
+ }
125
+
126
+ export const WithCTA: Story = {
127
+ args: {
128
+ logo: <Logo />,
129
+ navItems: basicNavItems,
130
+ cta: {
131
+ label: "Get Started",
132
+ href: "/get-started",
133
+ },
134
+ },
135
+ }
136
+
137
+ export const WithAnnouncement: Story = {
138
+ args: {
139
+ logo: <Logo />,
140
+ navItems: basicNavItems,
141
+ cta: {
142
+ label: "Get Started",
143
+ href: "/get-started",
144
+ },
145
+ announcement: {
146
+ text: "New: Canada Spends 2024 data now available!",
147
+ href: "/projects/canada-spends",
148
+ variant: "default",
149
+ },
150
+ },
151
+ }
152
+
153
+ export const WithAuburnAnnouncement: Story = {
154
+ args: {
155
+ logo: <Logo />,
156
+ navItems: basicNavItems,
157
+ cta: {
158
+ label: "Get Started",
159
+ href: "/get-started",
160
+ },
161
+ announcement: {
162
+ text: "Breaking: Federal Budget 2024 Analysis",
163
+ href: "/research/budget-2024",
164
+ variant: "auburn",
165
+ },
166
+ },
167
+ }
168
+
169
+ export const WithDropdowns: Story = {
170
+ args: {
171
+ logo: <Logo />,
172
+ navItems: navWithDropdowns,
173
+ cta: {
174
+ label: "Explore Data",
175
+ href: "/explore",
176
+ },
177
+ },
178
+ }
179
+
180
+ export const MinimalHeader: Story = {
181
+ args: {
182
+ logo: <Logo />,
183
+ },
184
+ }
185
+
186
+ export const FullFeatured: Story = {
187
+ args: {
188
+ logo: <Logo />,
189
+ navItems: navWithDropdowns,
190
+ cta: {
191
+ label: "Get Started",
192
+ href: "/get-started",
193
+ },
194
+ announcement: {
195
+ text: "New: 2024 Federal Budget Analysis now available",
196
+ href: "/research/budget-2024",
197
+ variant: "auburn",
198
+ },
199
+ },
200
+ }
201
+
202
+ export const ManyNavItems: Story = {
203
+ args: {
204
+ logo: <Logo />,
205
+ navItems: [
206
+ { label: "Home", href: "/" },
207
+ { label: "About", href: "/about" },
208
+ { label: "Projects", href: "/projects" },
209
+ { label: "Research", href: "/research" },
210
+ { label: "Data", href: "/data" },
211
+ { label: "Blog", href: "/blog" },
212
+ { label: "Contact", href: "/contact" },
213
+ ],
214
+ },
215
+ }
216
+
217
+ export const WithTextLogo: Story = {
218
+ args: {
219
+ logo: (
220
+ <span style={{
221
+ fontFamily: "'Soehne Kraftig', 'Helvetica Neue', Helvetica, Arial, sans-serif",
222
+ fontSize: "20px",
223
+ fontWeight: 500,
224
+ color: "#932F2F",
225
+ letterSpacing: "-0.01em",
226
+ }}>
227
+ Company Name
228
+ </span>
229
+ ),
230
+ navItems: basicNavItems,
231
+ cta: {
232
+ label: "Sign Up",
233
+ href: "/signup",
234
+ },
235
+ },
236
+ }
237
+
238
+ export const CanadaSpendsHeader: Story = {
239
+ args: {
240
+ logo: (
241
+ <svg width="160" height="24" viewBox="0 0 160 24" fill="none" xmlns="http://www.w3.org/2000/svg">
242
+ <text x="0" y="18" fontFamily="sans-serif" fontSize="16" fontWeight="600" fill="#272727">
243
+ Canada Spends
244
+ </text>
245
+ </svg>
246
+ ),
247
+ navItems: [
248
+ { label: "Charts", href: "/charts" },
249
+ { label: "Data", href: "/data" },
250
+ {
251
+ label: "Topics",
252
+ href: "/topics",
253
+ dropdown: [
254
+ { label: "Healthcare", href: "/topics/healthcare" },
255
+ { label: "Education", href: "/topics/education" },
256
+ { label: "Defense", href: "/topics/defense" },
257
+ { label: "Infrastructure", href: "/topics/infrastructure" },
258
+ ],
259
+ },
260
+ { label: "About", href: "/about" },
261
+ ],
262
+ cta: {
263
+ label: "Explore",
264
+ href: "/explore",
265
+ },
266
+ },
267
+ }
268
+
269
+ export const ResponsiveDemo: Story = {
270
+ args: {
271
+ logo: <Logo />,
272
+ navItems: navWithDropdowns,
273
+ cta: {
274
+ label: "Get Started",
275
+ href: "/get-started",
276
+ },
277
+ },
278
+ parameters: {
279
+ docs: {
280
+ description: {
281
+ story: "Resize the viewport to see the responsive behavior. On smaller screens, the navigation collapses into a hamburger menu.",
282
+ },
283
+ },
284
+ },
285
+ }
286
+
287
+ // Interactive test: Navigation links
288
+ export const NavigationTest: Story = {
289
+ args: {
290
+ logo: <Logo />,
291
+ navItems: basicNavItems,
292
+ cta: {
293
+ label: "Get Started",
294
+ href: "/get-started",
295
+ },
296
+ },
297
+ play: async ({ canvasElement }) => {
298
+ const canvas = within(canvasElement)
299
+
300
+ // Check that navigation links are present
301
+ const homeLink = canvas.getByRole("link", { name: /home/i })
302
+ const aboutLink = canvas.getByRole("link", { name: /about/i })
303
+ const ctaButton = canvas.getByRole("link", { name: /get started/i })
304
+
305
+ await expect(homeLink).toBeInTheDocument()
306
+ await expect(aboutLink).toBeInTheDocument()
307
+ await expect(ctaButton).toBeInTheDocument()
308
+
309
+ await expect(homeLink).toHaveAttribute("href", "/")
310
+ await expect(aboutLink).toHaveAttribute("href", "/about")
311
+ },
312
+ }
313
+
314
+ // Interactive test: Logo presence
315
+ export const LogoTest: Story = {
316
+ args: {
317
+ logo: <Logo />,
318
+ navItems: basicNavItems,
319
+ },
320
+ play: async ({ canvasElement }) => {
321
+ const canvas = within(canvasElement)
322
+
323
+ // Check that the logo/brand text is present
324
+ const logoText = canvas.getByText(/build canada/i)
325
+ await expect(logoText).toBeInTheDocument()
326
+ },
327
+ }
328
+
329
+ // Interactive test: Announcement banner
330
+ export const AnnouncementTest: Story = {
331
+ args: {
332
+ logo: <Logo />,
333
+ navItems: basicNavItems,
334
+ announcement: {
335
+ text: "Test announcement message",
336
+ href: "/test-link",
337
+ variant: "auburn",
338
+ },
339
+ },
340
+ play: async ({ canvasElement }) => {
341
+ const canvas = within(canvasElement)
342
+
343
+ const announcement = canvas.getByText(/test announcement message/i)
344
+ await expect(announcement).toBeInTheDocument()
345
+ },
346
+ }