@nationaldesignstudio/react 0.0.7 → 0.0.9
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/README.md +0 -4
- package/dist/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
- package/dist/assets/react.svg +1 -0
- package/dist/components/atoms/accordion/accordion.d.ts +50 -0
- package/dist/components/{button → atoms/button}/button.d.ts +5 -4
- package/dist/components/{button → atoms/button}/icon-button.d.ts +20 -0
- package/dist/components/atoms/pager-control/pager-control.d.ts +62 -0
- package/dist/components/{card → organisms/card}/card.d.ts +6 -2
- package/dist/components/{navbar → organisms/navbar}/navbar.d.ts +28 -1
- package/dist/components/sections/banner/banner.d.ts +64 -0
- package/dist/components/sections/card-grid/card-grid.d.ts +53 -0
- package/dist/components/sections/faq-section/faq-section.d.ts +44 -0
- package/dist/components/sections/hero/hero.d.ts +73 -0
- package/dist/components/sections/river/river.d.ts +63 -0
- package/dist/components/sections/tout/tout.d.ts +73 -0
- package/dist/components/sections/two-column-section/two-column-section.d.ts +58 -0
- package/dist/index.d.ts +28 -12
- package/dist/index.js +6108 -953
- package/dist/index.js.map +1 -1
- package/dist/tailwind.css +23 -0
- package/dist/tokens.css +2009 -103
- package/package.json +23 -5
- package/src/App.css +0 -0
- package/src/App.tsx +7 -0
- package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
- package/src/assets/react.svg +1 -0
- package/src/components/atoms/accordion/accordion.stories.tsx +228 -0
- package/src/components/atoms/accordion/accordion.tsx +137 -0
- package/src/components/atoms/accordion/index.ts +6 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
- package/src/components/atoms/button/button.stories.tsx +84 -0
- package/src/components/atoms/button/button.test.tsx +141 -0
- package/src/components/atoms/button/button.tsx +95 -0
- package/src/components/atoms/button/button.visual.test.tsx +102 -0
- package/src/components/atoms/button/icon-button.stories.tsx +166 -0
- package/src/components/atoms/button/icon-button.tsx +125 -0
- package/src/components/atoms/button/index.ts +6 -0
- package/src/components/atoms/pager-control/index.ts +5 -0
- package/src/components/atoms/pager-control/pager-control.stories.tsx +209 -0
- package/src/components/atoms/pager-control/pager-control.test.tsx +149 -0
- package/src/components/atoms/pager-control/pager-control.tsx +328 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
- package/src/components/organisms/card/card.stories.tsx +293 -0
- package/src/components/organisms/card/card.test.tsx +245 -0
- package/src/components/organisms/card/card.tsx +227 -0
- package/src/components/organisms/card/card.visual.test.tsx +197 -0
- package/src/components/organisms/card/index.ts +19 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/index.ts +18 -0
- package/src/components/organisms/navbar/navbar.stories.tsx +313 -0
- package/src/components/organisms/navbar/navbar.test.tsx +190 -0
- package/src/components/organisms/navbar/navbar.tsx +317 -0
- package/src/components/organisms/navbar/navbar.visual.test.tsx +85 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/index.ts +1 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +35 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +107 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +73 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +46 -0
- package/src/components/sections/banner/banner.stories.tsx +150 -0
- package/src/components/sections/banner/banner.test.tsx +185 -0
- package/src/components/sections/banner/banner.tsx +130 -0
- package/src/components/sections/banner/index.ts +2 -0
- package/src/components/sections/card-grid/card-grid.stories.tsx +351 -0
- package/src/components/sections/card-grid/card-grid.tsx +118 -0
- package/src/components/sections/card-grid/index.ts +1 -0
- package/src/components/sections/faq-section/faq-section.tsx +77 -0
- package/src/components/sections/faq-section/index.ts +2 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
- package/src/components/sections/hero/hero.stories.tsx +145 -0
- package/src/components/sections/hero/hero.test.tsx +135 -0
- package/src/components/sections/hero/hero.tsx +191 -0
- package/src/components/sections/hero/hero.visual.test.tsx +140 -0
- package/src/components/sections/hero/index.ts +1 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
- package/src/components/sections/prose/index.ts +6 -0
- package/src/components/sections/prose/prose.stories.tsx +144 -0
- package/src/components/sections/prose/prose.test.tsx +178 -0
- package/src/components/sections/prose/prose.tsx +88 -0
- package/src/components/sections/prose/prose.visual.test.tsx +105 -0
- package/src/components/sections/river/index.ts +1 -0
- package/src/components/sections/river/river.stories.tsx +237 -0
- package/src/components/sections/river/river.test.tsx +268 -0
- package/src/components/sections/river/river.tsx +175 -0
- package/src/components/sections/tout/index.ts +1 -0
- package/src/components/sections/tout/tout.stories.tsx +154 -0
- package/src/components/sections/tout/tout.test.tsx +242 -0
- package/src/components/sections/tout/tout.tsx +206 -0
- package/src/components/sections/two-column-section/index.ts +5 -0
- package/src/components/sections/two-column-section/two-column-section.stories.tsx +285 -0
- package/src/components/sections/two-column-section/two-column-section.tsx +152 -0
- package/src/index.ts +98 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +13 -0
- package/src/stories/Introduction.mdx +114 -0
- package/src/stories/TokenShowcase.stories.tsx +92 -0
- package/src/stories/TokenShowcase.tsx +1352 -0
- package/src/styles.css +11 -0
- package/dist/components/hero/hero.d.ts +0 -17
- /package/dist/components/{us-gov-banner → organisms/us-gov-banner}/us-gov-banner.d.ts +0 -0
- /package/dist/components/{prose → sections/prose}/prose.d.ts +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Hero } from ".";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Hero> = {
|
|
5
|
+
title: "Sections/Hero",
|
|
6
|
+
component: Hero,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: "fullscreen",
|
|
9
|
+
},
|
|
10
|
+
argTypes: {
|
|
11
|
+
variant: {
|
|
12
|
+
control: "select",
|
|
13
|
+
options: ["A1", "A2", "A3"],
|
|
14
|
+
description: "Hero variant controlling content alignment",
|
|
15
|
+
},
|
|
16
|
+
title: {
|
|
17
|
+
control: "text",
|
|
18
|
+
description: "The title text displayed in the hero",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
} as Meta<typeof Hero>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof Hero>;
|
|
25
|
+
|
|
26
|
+
export const Playground: Story = {
|
|
27
|
+
render: (args) => <Hero {...args} />,
|
|
28
|
+
};
|
|
29
|
+
Playground.args = {
|
|
30
|
+
title: "Hero Title",
|
|
31
|
+
variant: "A1",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// Variants
|
|
36
|
+
// =============================================================================
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A1 variant: Content aligned at the bottom of the hero.
|
|
40
|
+
* This is the default variant.
|
|
41
|
+
*/
|
|
42
|
+
export const A1: Story = {
|
|
43
|
+
render: () => <Hero variant="A1" title="Hero A1" />,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A2 variant: Content aligned at the top of the hero.
|
|
48
|
+
*/
|
|
49
|
+
export const A2: Story = {
|
|
50
|
+
render: () => <Hero variant="A2" title="Hero A2" />,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A3 variant: Content centered vertically in the hero.
|
|
55
|
+
*/
|
|
56
|
+
export const A3: Story = {
|
|
57
|
+
render: () => <Hero variant="A3" title="Hero A3" />,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Responsive Variants - A1 (Content at bottom)
|
|
62
|
+
// =============================================================================
|
|
63
|
+
|
|
64
|
+
export const A1Desktop: Story = {
|
|
65
|
+
render: () => <Hero variant="A1" title="Hero A1" />,
|
|
66
|
+
globals: {
|
|
67
|
+
viewport: { value: "lg", isRotated: false },
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const A1Tablet: Story = {
|
|
72
|
+
render: () => <Hero variant="A1" title="Hero A1" />,
|
|
73
|
+
globals: {
|
|
74
|
+
viewport: { value: "md", isRotated: false },
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const A1Mobile: Story = {
|
|
79
|
+
render: () => <Hero variant="A1" title="Hero A1" />,
|
|
80
|
+
globals: {
|
|
81
|
+
viewport: { value: "sm", isRotated: false },
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// Responsive Variants - A2 (Content at top)
|
|
87
|
+
// =============================================================================
|
|
88
|
+
|
|
89
|
+
export const A2Desktop: Story = {
|
|
90
|
+
render: () => <Hero variant="A2" title="Hero A2" />,
|
|
91
|
+
globals: {
|
|
92
|
+
viewport: { value: "lg", isRotated: false },
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const A2Tablet: Story = {
|
|
97
|
+
render: () => <Hero variant="A2" title="Hero A2" />,
|
|
98
|
+
globals: {
|
|
99
|
+
viewport: { value: "md", isRotated: false },
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const A2Mobile: Story = {
|
|
104
|
+
render: () => <Hero variant="A2" title="Hero A2" />,
|
|
105
|
+
globals: {
|
|
106
|
+
viewport: { value: "sm", isRotated: false },
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// =============================================================================
|
|
111
|
+
// Responsive Variants - A3 (Content centered)
|
|
112
|
+
// =============================================================================
|
|
113
|
+
|
|
114
|
+
export const A3Desktop: Story = {
|
|
115
|
+
render: () => <Hero variant="A3" title="Hero A3" />,
|
|
116
|
+
globals: {
|
|
117
|
+
viewport: { value: "lg", isRotated: false },
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const A3Tablet: Story = {
|
|
122
|
+
render: () => <Hero variant="A3" title="Hero A3" />,
|
|
123
|
+
globals: {
|
|
124
|
+
viewport: { value: "md", isRotated: false },
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const A3Mobile: Story = {
|
|
129
|
+
render: () => <Hero variant="A3" title="Hero A3" />,
|
|
130
|
+
globals: {
|
|
131
|
+
viewport: { value: "sm", isRotated: false },
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// =============================================================================
|
|
136
|
+
// Examples
|
|
137
|
+
// =============================================================================
|
|
138
|
+
|
|
139
|
+
export const WithLongTitle: Story = {
|
|
140
|
+
render: () => <Hero title="A Much Longer Hero Title That Wraps" />,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const WithCustomClassName: Story = {
|
|
144
|
+
render: () => <Hero title="Custom Background" className="bg-blue-600" />,
|
|
145
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { page } from "vitest/browser";
|
|
3
|
+
import { render } from "vitest-browser-react";
|
|
4
|
+
import { Hero } from "./hero";
|
|
5
|
+
|
|
6
|
+
describe("Hero", () => {
|
|
7
|
+
describe("Accessibility", () => {
|
|
8
|
+
test("renders as section landmark", async () => {
|
|
9
|
+
render(<Hero title="Test Hero" data-testid="hero" />);
|
|
10
|
+
|
|
11
|
+
// Section is a region landmark when it has an accessible name
|
|
12
|
+
const hero = page.getByTestId("hero");
|
|
13
|
+
await expect.element(hero).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("title renders as h1 heading", async () => {
|
|
17
|
+
render(<Hero title="Hero Title" />);
|
|
18
|
+
|
|
19
|
+
await expect
|
|
20
|
+
.element(page.getByRole("heading", { level: 1, name: "Hero Title" }))
|
|
21
|
+
.toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("title is accessible to screen readers", async () => {
|
|
25
|
+
render(<Hero title="Accessible Title" />);
|
|
26
|
+
|
|
27
|
+
await expect
|
|
28
|
+
.element(page.getByText("Accessible Title"))
|
|
29
|
+
.toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("Props", () => {
|
|
34
|
+
test("renders with required title prop", async () => {
|
|
35
|
+
render(<Hero title="Required Title" />);
|
|
36
|
+
|
|
37
|
+
await expect
|
|
38
|
+
.element(page.getByText("Required Title"))
|
|
39
|
+
.toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("supports custom className", async () => {
|
|
43
|
+
render(
|
|
44
|
+
<Hero title="Custom" className="custom-class" data-testid="hero" />,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const hero = page.getByTestId("hero");
|
|
48
|
+
await expect.element(hero).toHaveClass(/custom-class/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("spreads additional props to section element", async () => {
|
|
52
|
+
render(
|
|
53
|
+
<Hero
|
|
54
|
+
title="Props Test"
|
|
55
|
+
data-testid="hero"
|
|
56
|
+
aria-label="Hero section"
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const hero = page.getByTestId("hero");
|
|
61
|
+
await expect.element(hero).toHaveAttribute("aria-label", "Hero section");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("Styling", () => {
|
|
66
|
+
test("applies default background color", async () => {
|
|
67
|
+
render(<Hero title="Default" data-testid="hero" />);
|
|
68
|
+
|
|
69
|
+
const hero = page.getByTestId("hero");
|
|
70
|
+
await expect.element(hero).toHaveClass(/bg-gray-1000/);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("applies responsive height classes", async () => {
|
|
74
|
+
render(<Hero title="Responsive" data-testid="hero" />);
|
|
75
|
+
|
|
76
|
+
const hero = page.getByTestId("hero");
|
|
77
|
+
// Mobile height
|
|
78
|
+
await expect.element(hero).toHaveClass(/h-\[500px\]/);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("Variants", () => {
|
|
83
|
+
test("A1 variant applies items-end alignment (default)", async () => {
|
|
84
|
+
render(<Hero title="A1 Hero" variant="A1" data-testid="hero" />);
|
|
85
|
+
|
|
86
|
+
const hero = page.getByTestId("hero");
|
|
87
|
+
await expect.element(hero).toHaveClass(/items-end/);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("A1 variant is default when no variant specified", async () => {
|
|
91
|
+
render(<Hero title="Default Hero" data-testid="hero" />);
|
|
92
|
+
|
|
93
|
+
const hero = page.getByTestId("hero");
|
|
94
|
+
await expect.element(hero).toHaveClass(/items-end/);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("A2 variant applies items-start alignment", async () => {
|
|
98
|
+
render(<Hero title="A2 Hero" variant="A2" data-testid="hero" />);
|
|
99
|
+
|
|
100
|
+
const hero = page.getByTestId("hero");
|
|
101
|
+
await expect.element(hero).toHaveClass(/items-start/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("A3 variant applies items-center alignment", async () => {
|
|
105
|
+
render(<Hero title="A3 Hero" variant="A3" data-testid="hero" />);
|
|
106
|
+
|
|
107
|
+
const hero = page.getByTestId("hero");
|
|
108
|
+
await expect.element(hero).toHaveClass(/items-center/);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("A1 variant maintains common styles", async () => {
|
|
112
|
+
render(<Hero title="A1 Hero" variant="A1" data-testid="hero" />);
|
|
113
|
+
|
|
114
|
+
const hero = page.getByTestId("hero");
|
|
115
|
+
await expect.element(hero).toHaveClass(/bg-gray-1000/);
|
|
116
|
+
await expect.element(hero).toHaveClass(/h-\[500px\]/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("A2 variant maintains common styles", async () => {
|
|
120
|
+
render(<Hero title="A2 Hero" variant="A2" data-testid="hero" />);
|
|
121
|
+
|
|
122
|
+
const hero = page.getByTestId("hero");
|
|
123
|
+
await expect.element(hero).toHaveClass(/bg-gray-1000/);
|
|
124
|
+
await expect.element(hero).toHaveClass(/h-\[500px\]/);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("A3 variant maintains common styles", async () => {
|
|
128
|
+
render(<Hero title="A3 Hero" variant="A3" data-testid="hero" />);
|
|
129
|
+
|
|
130
|
+
const hero = page.getByTestId("hero");
|
|
131
|
+
await expect.element(hero).toHaveClass(/bg-gray-1000/);
|
|
132
|
+
await expect.element(hero).toHaveClass(/h-\[500px\]/);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hero variants based on Figma BaseKit / Heros
|
|
7
|
+
*
|
|
8
|
+
* Variants:
|
|
9
|
+
* - A1: Content aligned at bottom (default)
|
|
10
|
+
* - A2: Content aligned at top
|
|
11
|
+
* - A3: Content aligned at center
|
|
12
|
+
*
|
|
13
|
+
* Each variant is responsive across breakpoints:
|
|
14
|
+
* - sm (Mobile): 500px height, 20px padding
|
|
15
|
+
* - md (Tablet): 650px height, 56px padding
|
|
16
|
+
* - lg (Desktop): 700-850px height, 64-72px padding
|
|
17
|
+
*/
|
|
18
|
+
const heroVariants = cva(
|
|
19
|
+
[
|
|
20
|
+
"flex w-full bg-gray-1000",
|
|
21
|
+
// Mobile (sm)
|
|
22
|
+
"h-[500px] p-spacing-20",
|
|
23
|
+
// Tablet (md)
|
|
24
|
+
"md:h-[650px] md:p-spacing-56",
|
|
25
|
+
],
|
|
26
|
+
{
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
// A1: Content at bottom
|
|
30
|
+
A1: [
|
|
31
|
+
"items-end",
|
|
32
|
+
// Desktop (lg) - 800px height, 72px padding
|
|
33
|
+
"xl:h-[800px] xl:p-spacing-72",
|
|
34
|
+
],
|
|
35
|
+
// A2: Content at top
|
|
36
|
+
A2: [
|
|
37
|
+
"items-start",
|
|
38
|
+
// Desktop (lg) - 700px height, 64px padding
|
|
39
|
+
"xl:h-[700px] xl:p-spacing-64",
|
|
40
|
+
],
|
|
41
|
+
// A3: Content centered
|
|
42
|
+
A3: [
|
|
43
|
+
"items-center",
|
|
44
|
+
// Desktop (lg) - 800px height, 64px padding
|
|
45
|
+
"xl:h-[800px] xl:p-spacing-64",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
variant: "A1",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export interface HeroProps
|
|
56
|
+
extends React.HTMLAttributes<HTMLElement>,
|
|
57
|
+
VariantProps<typeof heroVariants> {
|
|
58
|
+
/**
|
|
59
|
+
* The title text displayed in the hero
|
|
60
|
+
*/
|
|
61
|
+
title: string;
|
|
62
|
+
/**
|
|
63
|
+
* URL for the background image
|
|
64
|
+
*/
|
|
65
|
+
backgroundImage?: string;
|
|
66
|
+
/**
|
|
67
|
+
* URL for a background video (takes precedence over backgroundImage)
|
|
68
|
+
*/
|
|
69
|
+
backgroundVideo?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Opacity of the dark overlay (0-1, default: 0)
|
|
72
|
+
*/
|
|
73
|
+
overlayOpacity?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Color of the overlay (default: "black")
|
|
76
|
+
*/
|
|
77
|
+
overlayColor?: string;
|
|
78
|
+
/**
|
|
79
|
+
* CSS background-position value (default: "center")
|
|
80
|
+
*/
|
|
81
|
+
backgroundPosition?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Hero component for page headers with large display typography.
|
|
86
|
+
*
|
|
87
|
+
* Features responsive sizing across three variants:
|
|
88
|
+
* - A1: Content at bottom (default)
|
|
89
|
+
* - A2: Content at top
|
|
90
|
+
* - A3: Content centered
|
|
91
|
+
*
|
|
92
|
+
* Each variant responds to breakpoints:
|
|
93
|
+
* - Mobile: 500px height, 20px padding, 64px typography
|
|
94
|
+
* - Tablet (768px+): 650px height, 56px padding, 128-148px typography
|
|
95
|
+
* - Desktop (1440px+): 700-800px height, 64-72px padding, 148-192px typography
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```tsx
|
|
99
|
+
* // Simple hero
|
|
100
|
+
* <Hero title="Welcome" variant="A1" />
|
|
101
|
+
*
|
|
102
|
+
* // With background image and overlay
|
|
103
|
+
* <Hero
|
|
104
|
+
* title="Welcome"
|
|
105
|
+
* variant="A1"
|
|
106
|
+
* backgroundImage="/hero.jpg"
|
|
107
|
+
* overlayOpacity={0.4}
|
|
108
|
+
* />
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
const Hero = React.forwardRef<HTMLElement, HeroProps>(
|
|
112
|
+
(
|
|
113
|
+
{
|
|
114
|
+
className,
|
|
115
|
+
title,
|
|
116
|
+
variant,
|
|
117
|
+
backgroundImage,
|
|
118
|
+
backgroundVideo,
|
|
119
|
+
overlayOpacity = 0,
|
|
120
|
+
overlayColor = "black",
|
|
121
|
+
backgroundPosition = "center",
|
|
122
|
+
...props
|
|
123
|
+
},
|
|
124
|
+
ref,
|
|
125
|
+
) => {
|
|
126
|
+
const hasBackground = backgroundImage || backgroundVideo;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<section
|
|
130
|
+
ref={ref}
|
|
131
|
+
className={cn(
|
|
132
|
+
heroVariants({ variant }),
|
|
133
|
+
hasBackground && "relative overflow-hidden",
|
|
134
|
+
className,
|
|
135
|
+
)}
|
|
136
|
+
{...props}
|
|
137
|
+
>
|
|
138
|
+
{/* Background image */}
|
|
139
|
+
{backgroundImage && !backgroundVideo && (
|
|
140
|
+
<div
|
|
141
|
+
aria-hidden="true"
|
|
142
|
+
className="absolute inset-0 bg-cover"
|
|
143
|
+
style={{
|
|
144
|
+
backgroundImage: `url(${backgroundImage})`,
|
|
145
|
+
backgroundPosition,
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
{/* Background video */}
|
|
151
|
+
{backgroundVideo && (
|
|
152
|
+
<video
|
|
153
|
+
autoPlay
|
|
154
|
+
loop
|
|
155
|
+
muted
|
|
156
|
+
playsInline
|
|
157
|
+
aria-hidden="true"
|
|
158
|
+
className="absolute inset-0 h-full w-full object-cover"
|
|
159
|
+
>
|
|
160
|
+
<source src={backgroundVideo} />
|
|
161
|
+
</video>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
{/* Overlay */}
|
|
165
|
+
{hasBackground && overlayOpacity > 0 && (
|
|
166
|
+
<div
|
|
167
|
+
aria-hidden="true"
|
|
168
|
+
className="absolute inset-0"
|
|
169
|
+
style={{
|
|
170
|
+
backgroundColor: overlayColor,
|
|
171
|
+
opacity: overlayOpacity,
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
{/* Content */}
|
|
177
|
+
<h1
|
|
178
|
+
className={cn(
|
|
179
|
+
"typography-display-large text-gray-50",
|
|
180
|
+
hasBackground && "relative z-10",
|
|
181
|
+
)}
|
|
182
|
+
>
|
|
183
|
+
{title}
|
|
184
|
+
</h1>
|
|
185
|
+
</section>
|
|
186
|
+
);
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
Hero.displayName = "Hero";
|
|
190
|
+
|
|
191
|
+
export { Hero, heroVariants };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { page } from "vitest/browser";
|
|
3
|
+
import { render } from "vitest-browser-react";
|
|
4
|
+
import { Hero } from "./hero";
|
|
5
|
+
|
|
6
|
+
describe("Hero Visual Regression", () => {
|
|
7
|
+
// =========================================================================
|
|
8
|
+
// Variant A1 (Content at bottom) - Default
|
|
9
|
+
// =========================================================================
|
|
10
|
+
describe("Variant A1", () => {
|
|
11
|
+
test("A1 desktop renders correctly", async () => {
|
|
12
|
+
await page.viewport(1440, 900);
|
|
13
|
+
|
|
14
|
+
render(<Hero data-testid="hero" variant="A1" title="Hero A1" />);
|
|
15
|
+
|
|
16
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
17
|
+
"hero-a1-desktop",
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("A1 tablet renders correctly", async () => {
|
|
22
|
+
await page.viewport(834, 700);
|
|
23
|
+
|
|
24
|
+
render(<Hero data-testid="hero" variant="A1" title="Hero A1" />);
|
|
25
|
+
|
|
26
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
27
|
+
"hero-a1-tablet",
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("A1 mobile renders correctly", async () => {
|
|
32
|
+
await page.viewport(393, 600);
|
|
33
|
+
|
|
34
|
+
render(<Hero data-testid="hero" variant="A1" title="Hero A1" />);
|
|
35
|
+
|
|
36
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
37
|
+
"hero-a1-mobile",
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// =========================================================================
|
|
43
|
+
// Variant A2 (Content at top)
|
|
44
|
+
// =========================================================================
|
|
45
|
+
describe("Variant A2", () => {
|
|
46
|
+
test("A2 desktop renders correctly", async () => {
|
|
47
|
+
await page.viewport(1440, 900);
|
|
48
|
+
|
|
49
|
+
render(<Hero data-testid="hero" variant="A2" title="Hero A2" />);
|
|
50
|
+
|
|
51
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
52
|
+
"hero-a2-desktop",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("A2 tablet renders correctly", async () => {
|
|
57
|
+
await page.viewport(834, 700);
|
|
58
|
+
|
|
59
|
+
render(<Hero data-testid="hero" variant="A2" title="Hero A2" />);
|
|
60
|
+
|
|
61
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
62
|
+
"hero-a2-tablet",
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("A2 mobile renders correctly", async () => {
|
|
67
|
+
await page.viewport(393, 600);
|
|
68
|
+
|
|
69
|
+
render(<Hero data-testid="hero" variant="A2" title="Hero A2" />);
|
|
70
|
+
|
|
71
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
72
|
+
"hero-a2-mobile",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// =========================================================================
|
|
78
|
+
// Variant A3 (Content centered)
|
|
79
|
+
// =========================================================================
|
|
80
|
+
describe("Variant A3", () => {
|
|
81
|
+
test("A3 desktop renders correctly", async () => {
|
|
82
|
+
await page.viewport(1440, 900);
|
|
83
|
+
|
|
84
|
+
render(<Hero data-testid="hero" variant="A3" title="Hero A3" />);
|
|
85
|
+
|
|
86
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
87
|
+
"hero-a3-desktop",
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("A3 tablet renders correctly", async () => {
|
|
92
|
+
await page.viewport(834, 700);
|
|
93
|
+
|
|
94
|
+
render(<Hero data-testid="hero" variant="A3" title="Hero A3" />);
|
|
95
|
+
|
|
96
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
97
|
+
"hero-a3-tablet",
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("A3 mobile renders correctly", async () => {
|
|
102
|
+
await page.viewport(393, 600);
|
|
103
|
+
|
|
104
|
+
render(<Hero data-testid="hero" variant="A3" title="Hero A3" />);
|
|
105
|
+
|
|
106
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
107
|
+
"hero-a3-mobile",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// =========================================================================
|
|
113
|
+
// Additional tests
|
|
114
|
+
// =========================================================================
|
|
115
|
+
test("hero with long title renders correctly", async () => {
|
|
116
|
+
await page.viewport(1280, 800);
|
|
117
|
+
|
|
118
|
+
render(
|
|
119
|
+
<Hero data-testid="hero" title="A Much Longer Hero Title That Wraps" />,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot("hero-long-title");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("hero with custom className renders correctly", async () => {
|
|
126
|
+
await page.viewport(1280, 800);
|
|
127
|
+
|
|
128
|
+
render(
|
|
129
|
+
<Hero
|
|
130
|
+
data-testid="hero"
|
|
131
|
+
title="Custom Background"
|
|
132
|
+
className="bg-blue-600"
|
|
133
|
+
/>,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await expect(page.getByTestId("hero")).toMatchScreenshot(
|
|
137
|
+
"hero-custom-class",
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Hero, type HeroProps, heroVariants } from "./hero";
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|