@nationaldesignstudio/react 0.0.17 → 0.1.0
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/component-registry.md +181 -29
- package/dist/components/atoms/accordion/accordion.d.ts +2 -2
- package/dist/components/atoms/background/background.d.ts +158 -0
- package/dist/components/atoms/button/button.d.ts +64 -82
- package/dist/components/atoms/button/icon-button.d.ts +128 -66
- package/dist/components/organisms/card/card.d.ts +130 -4
- package/dist/components/organisms/us-gov-banner/us-gov-banner.d.ts +120 -2
- package/dist/components/sections/hero/hero.d.ts +166 -150
- package/dist/components/sections/quote-block/quote-block.d.ts +152 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4068 -6052
- package/dist/index.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -2
- package/dist/tokens.css +207 -16
- package/package.json +2 -4
- package/src/components/atoms/accordion/accordion.test.tsx +233 -0
- package/src/components/atoms/accordion/accordion.tsx +8 -8
- package/src/components/atoms/background/background.test.tsx +213 -0
- package/src/components/atoms/background/background.tsx +435 -0
- package/src/components/atoms/background/index.ts +22 -0
- package/src/components/atoms/button/button.stories.tsx +81 -32
- package/src/components/atoms/button/button.tsx +101 -49
- package/src/components/atoms/button/icon-button.stories.tsx +179 -28
- package/src/components/atoms/button/icon-button.test.tsx +254 -0
- package/src/components/atoms/button/icon-button.tsx +178 -59
- package/src/components/atoms/pager-control/pager-control.tsx +32 -3
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +2 -0
- package/src/components/organisms/card/card.tsx +82 -24
- package/src/components/organisms/card/index.ts +7 -0
- package/src/components/organisms/navbar/navbar.tsx +2 -0
- package/src/components/organisms/us-gov-banner/index.ts +5 -1
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +72 -16
- package/src/components/sections/hero/hero.stories.tsx +124 -1
- package/src/components/sections/hero/hero.test.tsx +21 -18
- package/src/components/sections/hero/hero.tsx +188 -301
- package/src/components/sections/hero/index.ts +13 -0
- package/src/components/sections/quote-block/index.ts +5 -0
- package/src/components/sections/quote-block/quote-block.tsx +216 -0
- package/src/index.ts +40 -0
- package/src/lib/utils.ts +1 -6
- package/src/stories/ThemeProvider.stories.tsx +11 -5
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
|
-
import { cn } from "@/lib/utils";
|
|
4
3
|
|
|
5
4
|
const cardVariants = tv({
|
|
6
|
-
base: "flex overflow-hidden rounded-surface-card bg-card-background stroke-surface-card border-border-subtle border-solid",
|
|
5
|
+
base: "relative flex overflow-hidden rounded-surface-card bg-card-background stroke-surface-card border-border-subtle border-solid",
|
|
7
6
|
variants: {
|
|
8
7
|
layout: {
|
|
9
8
|
vertical: "w-full flex-col",
|
|
10
9
|
horizontal: "w-full flex-row",
|
|
10
|
+
/**
|
|
11
|
+
* Overlay layout - content sits on top of full-bleed background.
|
|
12
|
+
* Use with Background components for images/gradients.
|
|
13
|
+
*/
|
|
14
|
+
overlay: "w-full flex-col",
|
|
11
15
|
},
|
|
12
16
|
},
|
|
13
17
|
defaultVariants: {
|
|
@@ -25,8 +29,10 @@ export interface CardProps
|
|
|
25
29
|
* Layouts:
|
|
26
30
|
* - vertical: Image on top, content below (default)
|
|
27
31
|
* - horizontal: Image on left, content on right
|
|
32
|
+
* - overlay: Full-bleed background with content on top
|
|
28
33
|
*
|
|
29
34
|
* Use with CardImage, CardContent, CardEyebrow, CardTitle, CardDescription, and CardActions.
|
|
35
|
+
* For overlay layout, use Background components for full-bleed backgrounds.
|
|
30
36
|
*/
|
|
31
37
|
const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|
32
38
|
({ className, layout, ...props }, ref) => {
|
|
@@ -41,6 +47,16 @@ const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|
|
41
47
|
);
|
|
42
48
|
Card.displayName = "Card";
|
|
43
49
|
|
|
50
|
+
const cardImageVariants = tv({
|
|
51
|
+
base: [
|
|
52
|
+
"relative shrink-0 bg-bg-muted",
|
|
53
|
+
// Vertical: full width with aspect ratio
|
|
54
|
+
"aspect-video w-full",
|
|
55
|
+
// When in horizontal card (parent has flex-row), override
|
|
56
|
+
"[.flex-row>&]:aspect-auto [.flex-row>&]:w-2/5 [.flex-row>&]:self-stretch",
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
|
|
44
60
|
export interface CardImageProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
45
61
|
/**
|
|
46
62
|
* The image source URL
|
|
@@ -61,14 +77,7 @@ const CardImage = React.forwardRef<HTMLDivElement, CardImageProps>(
|
|
|
61
77
|
return (
|
|
62
78
|
<div
|
|
63
79
|
ref={ref}
|
|
64
|
-
className={
|
|
65
|
-
"relative shrink-0 bg-gray-500",
|
|
66
|
-
// Vertical: full width with aspect ratio
|
|
67
|
-
"aspect-video w-full",
|
|
68
|
-
// When in horizontal card (parent has flex-row), override
|
|
69
|
-
"[.flex-row>&]:aspect-auto [.flex-row>&]:w-2/5 [.flex-row>&]:self-stretch",
|
|
70
|
-
className,
|
|
71
|
-
)}
|
|
80
|
+
className={cardImageVariants({ class: className })}
|
|
72
81
|
{...props}
|
|
73
82
|
>
|
|
74
83
|
{src && (
|
|
@@ -84,22 +93,47 @@ const CardImage = React.forwardRef<HTMLDivElement, CardImageProps>(
|
|
|
84
93
|
);
|
|
85
94
|
CardImage.displayName = "CardImage";
|
|
86
95
|
|
|
96
|
+
const cardContentVariants = tv({
|
|
97
|
+
base: "flex w-full flex-1 flex-col gap-spatial-card-large-gap p-spatial-card-large-padding",
|
|
98
|
+
variants: {
|
|
99
|
+
/**
|
|
100
|
+
* Vertical alignment of content within the card.
|
|
101
|
+
* Useful for overlay layouts to position content at top/center/bottom.
|
|
102
|
+
*/
|
|
103
|
+
justify: {
|
|
104
|
+
start: "justify-start",
|
|
105
|
+
center: "justify-center",
|
|
106
|
+
end: "justify-end",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
87
111
|
export interface CardContentProps
|
|
88
|
-
extends React.HTMLAttributes<HTMLDivElement
|
|
112
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
113
|
+
VariantProps<typeof cardContentVariants> {}
|
|
89
114
|
|
|
90
115
|
/**
|
|
91
116
|
* Card content container with proper padding and spacing.
|
|
92
117
|
* Uses spatial card tokens for consistent sizing.
|
|
118
|
+
* For overlay layout, add `relative z-10` to ensure content sits above background.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```tsx
|
|
122
|
+
* // Content at bottom of overlay card
|
|
123
|
+
* <Card layout="overlay">
|
|
124
|
+
* <Background.Image src="/hero.jpg" />
|
|
125
|
+
* <CardContent justify="end" className="relative z-10">
|
|
126
|
+
* <CardTitle>Title</CardTitle>
|
|
127
|
+
* </CardContent>
|
|
128
|
+
* </Card>
|
|
129
|
+
* ```
|
|
93
130
|
*/
|
|
94
131
|
const CardContent = React.forwardRef<HTMLDivElement, CardContentProps>(
|
|
95
|
-
({ className, ...props }, ref) => {
|
|
132
|
+
({ className, justify, ...props }, ref) => {
|
|
96
133
|
return (
|
|
97
134
|
<div
|
|
98
135
|
ref={ref}
|
|
99
|
-
className={
|
|
100
|
-
"flex w-full flex-1 flex-col gap-spatial-card-large-gap p-spatial-card-large-padding",
|
|
101
|
-
className,
|
|
102
|
-
)}
|
|
136
|
+
className={cardContentVariants({ justify, class: className })}
|
|
103
137
|
{...props}
|
|
104
138
|
/>
|
|
105
139
|
);
|
|
@@ -107,6 +141,10 @@ const CardContent = React.forwardRef<HTMLDivElement, CardContentProps>(
|
|
|
107
141
|
);
|
|
108
142
|
CardContent.displayName = "CardContent";
|
|
109
143
|
|
|
144
|
+
const cardEyebrowVariants = tv({
|
|
145
|
+
base: "typography-caption-large text-text-muted",
|
|
146
|
+
});
|
|
147
|
+
|
|
110
148
|
export interface CardEyebrowProps
|
|
111
149
|
extends React.HTMLAttributes<HTMLParagraphElement> {}
|
|
112
150
|
|
|
@@ -118,7 +156,7 @@ const CardEyebrow = React.forwardRef<HTMLParagraphElement, CardEyebrowProps>(
|
|
|
118
156
|
return (
|
|
119
157
|
<p
|
|
120
158
|
ref={ref}
|
|
121
|
-
className={
|
|
159
|
+
className={cardEyebrowVariants({ class: className })}
|
|
122
160
|
{...props}
|
|
123
161
|
/>
|
|
124
162
|
);
|
|
@@ -126,6 +164,10 @@ const CardEyebrow = React.forwardRef<HTMLParagraphElement, CardEyebrowProps>(
|
|
|
126
164
|
);
|
|
127
165
|
CardEyebrow.displayName = "CardEyebrow";
|
|
128
166
|
|
|
167
|
+
const cardTitleVariants = tv({
|
|
168
|
+
base: "typography-subheading-small text-text-primary",
|
|
169
|
+
});
|
|
170
|
+
|
|
129
171
|
export interface CardTitleProps
|
|
130
172
|
extends React.HTMLAttributes<HTMLHeadingElement> {
|
|
131
173
|
/**
|
|
@@ -142,7 +184,7 @@ const CardTitle = React.forwardRef<HTMLHeadingElement, CardTitleProps>(
|
|
|
142
184
|
return (
|
|
143
185
|
<Component
|
|
144
186
|
ref={ref}
|
|
145
|
-
className={
|
|
187
|
+
className={cardTitleVariants({ class: className })}
|
|
146
188
|
{...props}
|
|
147
189
|
/>
|
|
148
190
|
);
|
|
@@ -150,6 +192,10 @@ const CardTitle = React.forwardRef<HTMLHeadingElement, CardTitleProps>(
|
|
|
150
192
|
);
|
|
151
193
|
CardTitle.displayName = "CardTitle";
|
|
152
194
|
|
|
195
|
+
const cardDescriptionVariants = tv({
|
|
196
|
+
base: "typography-body-small text-text-secondary",
|
|
197
|
+
});
|
|
198
|
+
|
|
153
199
|
export interface CardDescriptionProps
|
|
154
200
|
extends React.HTMLAttributes<HTMLParagraphElement> {}
|
|
155
201
|
|
|
@@ -163,13 +209,17 @@ const CardDescription = React.forwardRef<
|
|
|
163
209
|
return (
|
|
164
210
|
<p
|
|
165
211
|
ref={ref}
|
|
166
|
-
className={
|
|
212
|
+
className={cardDescriptionVariants({ class: className })}
|
|
167
213
|
{...props}
|
|
168
214
|
/>
|
|
169
215
|
);
|
|
170
216
|
});
|
|
171
217
|
CardDescription.displayName = "CardDescription";
|
|
172
218
|
|
|
219
|
+
const cardBodyVariants = tv({
|
|
220
|
+
base: "flex w-full flex-col gap-spatial-card-small-gap",
|
|
221
|
+
});
|
|
222
|
+
|
|
173
223
|
export interface CardBodyProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
174
224
|
|
|
175
225
|
/**
|
|
@@ -181,10 +231,7 @@ const CardBody = React.forwardRef<HTMLDivElement, CardBodyProps>(
|
|
|
181
231
|
return (
|
|
182
232
|
<div
|
|
183
233
|
ref={ref}
|
|
184
|
-
className={
|
|
185
|
-
"flex w-full flex-col gap-spatial-card-small-gap",
|
|
186
|
-
className,
|
|
187
|
-
)}
|
|
234
|
+
className={cardBodyVariants({ class: className })}
|
|
188
235
|
{...props}
|
|
189
236
|
/>
|
|
190
237
|
);
|
|
@@ -192,6 +239,10 @@ const CardBody = React.forwardRef<HTMLDivElement, CardBodyProps>(
|
|
|
192
239
|
);
|
|
193
240
|
CardBody.displayName = "CardBody";
|
|
194
241
|
|
|
242
|
+
const cardActionsVariants = tv({
|
|
243
|
+
base: "flex gap-spacing-12",
|
|
244
|
+
});
|
|
245
|
+
|
|
195
246
|
export interface CardActionsProps
|
|
196
247
|
extends React.HTMLAttributes<HTMLDivElement> {}
|
|
197
248
|
|
|
@@ -204,7 +255,7 @@ const CardActions = React.forwardRef<HTMLDivElement, CardActionsProps>(
|
|
|
204
255
|
return (
|
|
205
256
|
<div
|
|
206
257
|
ref={ref}
|
|
207
|
-
className={
|
|
258
|
+
className={cardActionsVariants({ class: className })}
|
|
208
259
|
{...props}
|
|
209
260
|
/>
|
|
210
261
|
);
|
|
@@ -216,10 +267,17 @@ export {
|
|
|
216
267
|
Card,
|
|
217
268
|
cardVariants,
|
|
218
269
|
CardImage,
|
|
270
|
+
cardImageVariants,
|
|
219
271
|
CardContent,
|
|
272
|
+
cardContentVariants,
|
|
220
273
|
CardEyebrow,
|
|
274
|
+
cardEyebrowVariants,
|
|
221
275
|
CardTitle,
|
|
276
|
+
cardTitleVariants,
|
|
222
277
|
CardDescription,
|
|
278
|
+
cardDescriptionVariants,
|
|
223
279
|
CardBody,
|
|
280
|
+
cardBodyVariants,
|
|
224
281
|
CardActions,
|
|
282
|
+
cardActionsVariants,
|
|
225
283
|
};
|
|
@@ -15,5 +15,12 @@ export {
|
|
|
15
15
|
type CardProps,
|
|
16
16
|
CardTitle,
|
|
17
17
|
type CardTitleProps,
|
|
18
|
+
cardActionsVariants,
|
|
19
|
+
cardBodyVariants,
|
|
20
|
+
cardContentVariants,
|
|
21
|
+
cardDescriptionVariants,
|
|
22
|
+
cardEyebrowVariants,
|
|
23
|
+
cardImageVariants,
|
|
24
|
+
cardTitleVariants,
|
|
18
25
|
cardVariants,
|
|
19
26
|
} from "./card";
|
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const usGovBannerVariants = tv({
|
|
5
|
+
slots: {
|
|
6
|
+
root: "flex w-full items-center justify-center py-spacing-12",
|
|
7
|
+
content: "flex items-center gap-spacing-8",
|
|
8
|
+
text: "text-[11px] leading-[13px] tracking-[0.17px]",
|
|
9
|
+
},
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: {
|
|
13
|
+
root: "bg-gray-50",
|
|
14
|
+
content: "opacity-70",
|
|
15
|
+
text: "text-gray-900",
|
|
16
|
+
},
|
|
17
|
+
inverted: {
|
|
18
|
+
root: "bg-transparent",
|
|
19
|
+
content: "opacity-70",
|
|
20
|
+
text: "text-text-inverted",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export interface USGovBannerProps
|
|
30
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
31
|
+
VariantProps<typeof usGovBannerVariants> {
|
|
5
32
|
/**
|
|
6
33
|
* Custom flag icon element. Defaults to a US flag SVG.
|
|
7
34
|
*/
|
|
@@ -17,31 +44,39 @@ export interface USGovBannerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
17
44
|
* US Government official website banner.
|
|
18
45
|
* Displays the official government website notice with flag icon.
|
|
19
46
|
* Commonly placed at the very top of government websites.
|
|
47
|
+
*
|
|
48
|
+
* Variants:
|
|
49
|
+
* - default: Light background with dark text (for light pages)
|
|
50
|
+
* - inverted: Transparent background with white text/flag (for dark backgrounds/heroes)
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* // Default (light)
|
|
55
|
+
* <USGovBanner />
|
|
56
|
+
*
|
|
57
|
+
* // Inverted (for dark backgrounds)
|
|
58
|
+
* <USGovBanner variant="inverted" />
|
|
59
|
+
* ```
|
|
20
60
|
*/
|
|
21
61
|
const USGovBanner = React.forwardRef<HTMLDivElement, USGovBannerProps>(
|
|
22
62
|
(
|
|
23
63
|
{
|
|
24
64
|
className,
|
|
65
|
+
variant,
|
|
25
66
|
flagIcon,
|
|
26
67
|
text = "An official website of the United States government",
|
|
27
68
|
...props
|
|
28
69
|
},
|
|
29
70
|
ref,
|
|
30
71
|
) => {
|
|
72
|
+
const styles = usGovBannerVariants({ variant });
|
|
73
|
+
const isInverted = variant === "inverted";
|
|
74
|
+
|
|
31
75
|
return (
|
|
32
|
-
<div
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
className,
|
|
37
|
-
)}
|
|
38
|
-
{...props}
|
|
39
|
-
>
|
|
40
|
-
<div className="flex items-center gap-spacing-8 opacity-70">
|
|
41
|
-
{flagIcon ?? <DefaultUSFlag />}
|
|
42
|
-
<p className="text-[11px] leading-[13px] tracking-[0.17px] text-gray-900">
|
|
43
|
-
{text}
|
|
44
|
-
</p>
|
|
76
|
+
<div ref={ref} className={styles.root({ class: className })} {...props}>
|
|
77
|
+
<div className={styles.content()}>
|
|
78
|
+
{flagIcon ?? (isInverted ? <WhiteUSFlag /> : <DefaultUSFlag />)}
|
|
79
|
+
<p className={styles.text()}>{text}</p>
|
|
45
80
|
</div>
|
|
46
81
|
</div>
|
|
47
82
|
);
|
|
@@ -70,4 +105,25 @@ function DefaultUSFlag() {
|
|
|
70
105
|
);
|
|
71
106
|
}
|
|
72
107
|
|
|
73
|
-
|
|
108
|
+
function WhiteUSFlag() {
|
|
109
|
+
return (
|
|
110
|
+
<svg
|
|
111
|
+
width="16"
|
|
112
|
+
height="12"
|
|
113
|
+
viewBox="0 0 16 12"
|
|
114
|
+
fill="none"
|
|
115
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
116
|
+
aria-hidden="true"
|
|
117
|
+
>
|
|
118
|
+
<path d="M0 0H16V12H0V0Z" fill="white" fillOpacity="0.1" />
|
|
119
|
+
<path
|
|
120
|
+
d="M0 0H16V0.923077H0V0ZM0 1.84615H16V2.76923H0V1.84615ZM0 3.69231H16V4.61538H0V3.69231ZM0 5.53846H16V6.46154H0V5.53846ZM0 7.38462H16V8.30769H0V7.38462ZM0 9.23077H16V10.1538H0V9.23077ZM0 11.0769H16V12H0V11.0769Z"
|
|
121
|
+
fill="white"
|
|
122
|
+
fillOpacity="0.8"
|
|
123
|
+
/>
|
|
124
|
+
<path d="M0 0H8V6.46154H0V0Z" fill="white" fillOpacity="0.6" />
|
|
125
|
+
</svg>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { USGovBanner, usGovBannerVariants };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { Hero, HeroBackground } from ".";
|
|
2
|
+
import { Hero, HeroBackground, HeroContent, HeroHeader } from ".";
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof Hero> = {
|
|
5
5
|
title: "Sections/Hero",
|
|
@@ -30,6 +30,11 @@ const meta: Meta<typeof Hero> = {
|
|
|
30
30
|
control: "color",
|
|
31
31
|
description: "Color of the overlay",
|
|
32
32
|
},
|
|
33
|
+
borderRadius: {
|
|
34
|
+
control: "text",
|
|
35
|
+
description:
|
|
36
|
+
"Border radius for the hero container (e.g., '0 0 20px 20px')",
|
|
37
|
+
},
|
|
33
38
|
},
|
|
34
39
|
} as Meta<typeof Hero>;
|
|
35
40
|
|
|
@@ -146,6 +151,124 @@ export const WithCloudflareStream: Story = {
|
|
|
146
151
|
),
|
|
147
152
|
};
|
|
148
153
|
|
|
154
|
+
// =============================================================================
|
|
155
|
+
// Composition - Top Slot & Border Radius
|
|
156
|
+
// =============================================================================
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Mock banner component for story demonstration
|
|
160
|
+
*/
|
|
161
|
+
const MockBanner = () => (
|
|
162
|
+
<div className="bg-gray-1200 px-spacing-16 py-spacing-8 text-center text-14 text-text-inverted">
|
|
163
|
+
An official website of the United States government
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Mock navigation component for story demonstration
|
|
169
|
+
*/
|
|
170
|
+
const MockNavigation = () => (
|
|
171
|
+
<nav className="flex items-center justify-between px-spacing-56 py-spacing-16">
|
|
172
|
+
<div className="flex items-center gap-spacing-24">
|
|
173
|
+
<div className="size-48 rounded-full bg-gray-50" />
|
|
174
|
+
<div className="flex gap-spacing-24 text-14 text-text-inverted">
|
|
175
|
+
<span className="cursor-pointer hover:opacity-80">Link 1</span>
|
|
176
|
+
<span className="cursor-pointer hover:opacity-80">Link 2</span>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
<div className="text-14 text-text-inverted">Actions</div>
|
|
180
|
+
</nav>
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Hero with top slot containing a banner
|
|
185
|
+
*/
|
|
186
|
+
export const WithTopSlot: Story = {
|
|
187
|
+
render: () => (
|
|
188
|
+
<Hero
|
|
189
|
+
variant="A1"
|
|
190
|
+
title="Hero with Top Slot"
|
|
191
|
+
background="#1a1a1a"
|
|
192
|
+
top={<MockBanner />}
|
|
193
|
+
/>
|
|
194
|
+
),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Hero with rounded bottom corners
|
|
199
|
+
*/
|
|
200
|
+
export const WithRoundedCorners: Story = {
|
|
201
|
+
render: () => (
|
|
202
|
+
<div className="bg-bg-page p-spacing-32">
|
|
203
|
+
<Hero
|
|
204
|
+
variant="A1"
|
|
205
|
+
title="Rounded Corners"
|
|
206
|
+
background="#1a1a1a"
|
|
207
|
+
borderRadius="0 0 20px 20px"
|
|
208
|
+
/>
|
|
209
|
+
</div>
|
|
210
|
+
),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Hero with both top slot and rounded corners
|
|
215
|
+
*/
|
|
216
|
+
export const WithTopAndRoundedCorners: Story = {
|
|
217
|
+
render: () => (
|
|
218
|
+
<div className="bg-bg-page">
|
|
219
|
+
<Hero
|
|
220
|
+
variant="A1"
|
|
221
|
+
title="Full Composition"
|
|
222
|
+
background={
|
|
223
|
+
<HeroBackground.Image
|
|
224
|
+
src="https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1920&q=80"
|
|
225
|
+
position="center"
|
|
226
|
+
/>
|
|
227
|
+
}
|
|
228
|
+
overlayOpacity={0.5}
|
|
229
|
+
borderRadius="0 0 20px 20px"
|
|
230
|
+
top={
|
|
231
|
+
<>
|
|
232
|
+
<MockBanner />
|
|
233
|
+
<MockNavigation />
|
|
234
|
+
</>
|
|
235
|
+
}
|
|
236
|
+
>
|
|
237
|
+
<p className="mt-spacing-16 max-w-[560px] text-20 text-text-inverted">
|
|
238
|
+
A subtitle or description can be added as children
|
|
239
|
+
</p>
|
|
240
|
+
</Hero>
|
|
241
|
+
</div>
|
|
242
|
+
),
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// =============================================================================
|
|
246
|
+
// Sub-components
|
|
247
|
+
// =============================================================================
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Using Hero.Header and Hero.Content sub-components directly
|
|
251
|
+
* for more control over the layout
|
|
252
|
+
*/
|
|
253
|
+
export const UsingSubComponents: Story = {
|
|
254
|
+
render: () => (
|
|
255
|
+
<section className="relative flex min-h-[80vh] flex-col bg-gray-1200">
|
|
256
|
+
<HeroHeader>
|
|
257
|
+
<MockBanner />
|
|
258
|
+
<MockNavigation />
|
|
259
|
+
</HeroHeader>
|
|
260
|
+
<HeroContent className="flex-1 justify-end">
|
|
261
|
+
<h1 className="text-64 font-medium text-text-inverted lg:text-128">
|
|
262
|
+
Custom Layout
|
|
263
|
+
</h1>
|
|
264
|
+
<p className="mt-spacing-16 text-20 text-text-inverted">
|
|
265
|
+
Using HeroHeader and HeroContent sub-components
|
|
266
|
+
</p>
|
|
267
|
+
</HeroContent>
|
|
268
|
+
</section>
|
|
269
|
+
),
|
|
270
|
+
};
|
|
271
|
+
|
|
149
272
|
// =============================================================================
|
|
150
273
|
// Responsive Variants - A1 (Content at bottom)
|
|
151
274
|
// =============================================================================
|
|
@@ -63,73 +63,76 @@ describe("Hero", () => {
|
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
describe("Styling", () => {
|
|
66
|
-
test("applies default background color", async () => {
|
|
66
|
+
test("applies default background color when no background provided", async () => {
|
|
67
67
|
render(<Hero title="Default" data-testid="hero" />);
|
|
68
68
|
|
|
69
69
|
const hero = page.getByTestId("hero");
|
|
70
|
-
|
|
70
|
+
// Uses semantic token bg-bg-overlay for default background
|
|
71
|
+
await expect.element(hero).toHaveClass(/bg-bg-overlay/);
|
|
71
72
|
});
|
|
72
73
|
|
|
73
|
-
test("applies
|
|
74
|
+
test("applies minimum height for viewport coverage", async () => {
|
|
74
75
|
render(<Hero title="Responsive" data-testid="hero" />);
|
|
75
76
|
|
|
76
77
|
const hero = page.getByTestId("hero");
|
|
77
|
-
//
|
|
78
|
-
await expect.element(hero).toHaveClass(/h-\[
|
|
78
|
+
// Uses min-h-[80vh] for all variants
|
|
79
|
+
await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
|
|
79
80
|
});
|
|
80
81
|
});
|
|
81
82
|
|
|
82
83
|
describe("Variants", () => {
|
|
83
|
-
test("A1 variant applies
|
|
84
|
+
test("A1 variant applies flex column layout", async () => {
|
|
84
85
|
render(<Hero title="A1 Hero" variant="A1" data-testid="hero" />);
|
|
85
86
|
|
|
86
87
|
const hero = page.getByTestId("hero");
|
|
87
|
-
await expect.element(hero).toHaveClass(/
|
|
88
|
+
await expect.element(hero).toHaveClass(/flex-col/);
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
test("A1 variant is default when no variant specified", async () => {
|
|
91
92
|
render(<Hero title="Default Hero" data-testid="hero" />);
|
|
92
93
|
|
|
93
94
|
const hero = page.getByTestId("hero");
|
|
94
|
-
|
|
95
|
+
// Default variant still applies min-height and flex layout
|
|
96
|
+
await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
|
|
97
|
+
await expect.element(hero).toHaveClass(/flex-col/);
|
|
95
98
|
});
|
|
96
99
|
|
|
97
|
-
test("A2 variant applies
|
|
100
|
+
test("A2 variant applies flex column layout", async () => {
|
|
98
101
|
render(<Hero title="A2 Hero" variant="A2" data-testid="hero" />);
|
|
99
102
|
|
|
100
103
|
const hero = page.getByTestId("hero");
|
|
101
|
-
await expect.element(hero).toHaveClass(/
|
|
104
|
+
await expect.element(hero).toHaveClass(/flex-col/);
|
|
102
105
|
});
|
|
103
106
|
|
|
104
|
-
test("A3 variant applies
|
|
107
|
+
test("A3 variant applies flex column layout", async () => {
|
|
105
108
|
render(<Hero title="A3 Hero" variant="A3" data-testid="hero" />);
|
|
106
109
|
|
|
107
110
|
const hero = page.getByTestId("hero");
|
|
108
|
-
await expect.element(hero).toHaveClass(/
|
|
111
|
+
await expect.element(hero).toHaveClass(/flex-col/);
|
|
109
112
|
});
|
|
110
113
|
|
|
111
114
|
test("A1 variant maintains common styles", async () => {
|
|
112
115
|
render(<Hero title="A1 Hero" variant="A1" data-testid="hero" />);
|
|
113
116
|
|
|
114
117
|
const hero = page.getByTestId("hero");
|
|
115
|
-
await expect.element(hero).toHaveClass(/bg-
|
|
116
|
-
await expect.element(hero).toHaveClass(/h-\[
|
|
118
|
+
await expect.element(hero).toHaveClass(/bg-bg-overlay/);
|
|
119
|
+
await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
|
|
117
120
|
});
|
|
118
121
|
|
|
119
122
|
test("A2 variant maintains common styles", async () => {
|
|
120
123
|
render(<Hero title="A2 Hero" variant="A2" data-testid="hero" />);
|
|
121
124
|
|
|
122
125
|
const hero = page.getByTestId("hero");
|
|
123
|
-
await expect.element(hero).toHaveClass(/bg-
|
|
124
|
-
await expect.element(hero).toHaveClass(/h-\[
|
|
126
|
+
await expect.element(hero).toHaveClass(/bg-bg-overlay/);
|
|
127
|
+
await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
|
|
125
128
|
});
|
|
126
129
|
|
|
127
130
|
test("A3 variant maintains common styles", async () => {
|
|
128
131
|
render(<Hero title="A3 Hero" variant="A3" data-testid="hero" />);
|
|
129
132
|
|
|
130
133
|
const hero = page.getByTestId("hero");
|
|
131
|
-
await expect.element(hero).toHaveClass(/bg-
|
|
132
|
-
await expect.element(hero).toHaveClass(/h-\[
|
|
134
|
+
await expect.element(hero).toHaveClass(/bg-bg-overlay/);
|
|
135
|
+
await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
|
|
133
136
|
});
|
|
134
137
|
});
|
|
135
138
|
});
|