@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,154 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Button } from "../../atoms/button";
|
|
3
|
+
import { Tout } from ".";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Tout> = {
|
|
6
|
+
title: "Sections/Tout",
|
|
7
|
+
component: Tout,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "fullscreen",
|
|
10
|
+
},
|
|
11
|
+
argTypes: {
|
|
12
|
+
headline: {
|
|
13
|
+
control: "text",
|
|
14
|
+
description: "The headline text",
|
|
15
|
+
},
|
|
16
|
+
body: {
|
|
17
|
+
control: "text",
|
|
18
|
+
description: "The body text",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
} as Meta<typeof Tout>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof Tout>;
|
|
25
|
+
|
|
26
|
+
const PlaceholderBackground = () => (
|
|
27
|
+
<div className="absolute inset-0 bg-gradient-to-br from-gray-300 to-gray-400" />
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const ImageBackground = () => (
|
|
31
|
+
<img
|
|
32
|
+
src="https://images.unsplash.com/photo-1557804506-669a67965ba0?w=1600&h=900&fit=crop"
|
|
33
|
+
alt=""
|
|
34
|
+
className="absolute inset-0 w-full h-full object-cover"
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const Playground: Story = {
|
|
39
|
+
render: (args) => <Tout {...args} />,
|
|
40
|
+
};
|
|
41
|
+
Playground.args = {
|
|
42
|
+
headline: "Brand-Large/Headline/Small",
|
|
43
|
+
body: "A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action.",
|
|
44
|
+
primaryAction: <Button>Primary</Button>,
|
|
45
|
+
secondaryAction: <Button variant="charcoalOutline">Secondary</Button>,
|
|
46
|
+
backgroundMedia: <PlaceholderBackground />,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// Responsive Variants
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
export const Desktop: Story = {
|
|
54
|
+
render: () => (
|
|
55
|
+
<Tout
|
|
56
|
+
headline="Brand-Large/Headline/Small"
|
|
57
|
+
body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
|
|
58
|
+
primaryAction={<Button>Primary</Button>}
|
|
59
|
+
secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
|
|
60
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
61
|
+
/>
|
|
62
|
+
),
|
|
63
|
+
globals: {
|
|
64
|
+
viewport: { value: "lg", isRotated: false },
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Tablet: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<Tout
|
|
71
|
+
headline="Brand-Large/Headline/Small"
|
|
72
|
+
body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
|
|
73
|
+
primaryAction={<Button>Primary</Button>}
|
|
74
|
+
secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
|
|
75
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
76
|
+
/>
|
|
77
|
+
),
|
|
78
|
+
globals: {
|
|
79
|
+
viewport: { value: "md", isRotated: false },
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const Mobile: Story = {
|
|
84
|
+
render: () => (
|
|
85
|
+
<Tout
|
|
86
|
+
headline="Brand-Large/Headline/Small"
|
|
87
|
+
body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
|
|
88
|
+
primaryAction={<Button size="sm">Primary</Button>}
|
|
89
|
+
secondaryAction={
|
|
90
|
+
<Button size="sm" variant="charcoalOutline">
|
|
91
|
+
Secondary
|
|
92
|
+
</Button>
|
|
93
|
+
}
|
|
94
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
95
|
+
/>
|
|
96
|
+
),
|
|
97
|
+
globals: {
|
|
98
|
+
viewport: { value: "sm", isRotated: false },
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// =============================================================================
|
|
103
|
+
// Examples
|
|
104
|
+
// =============================================================================
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* With actual background image
|
|
108
|
+
*/
|
|
109
|
+
export const WithImage: Story = {
|
|
110
|
+
render: () => (
|
|
111
|
+
<Tout
|
|
112
|
+
headline="Work with Purpose"
|
|
113
|
+
body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
|
|
114
|
+
primaryAction={<Button>View Careers</Button>}
|
|
115
|
+
secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
|
|
116
|
+
backgroundMedia={<ImageBackground />}
|
|
117
|
+
/>
|
|
118
|
+
),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Without secondary action
|
|
123
|
+
*/
|
|
124
|
+
export const SingleAction: Story = {
|
|
125
|
+
render: () => (
|
|
126
|
+
<Tout
|
|
127
|
+
headline="Get Started Today"
|
|
128
|
+
body="Begin your journey with our comprehensive onboarding process designed to help you succeed from day one."
|
|
129
|
+
primaryAction={<Button>Start Now</Button>}
|
|
130
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
131
|
+
/>
|
|
132
|
+
),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* With video background (placeholder)
|
|
137
|
+
*/
|
|
138
|
+
export const WithVideoPlaceholder: Story = {
|
|
139
|
+
render: () => (
|
|
140
|
+
<Tout
|
|
141
|
+
headline="Experience Innovation"
|
|
142
|
+
body="See how modern technology is transforming the way government serves its citizens."
|
|
143
|
+
primaryAction={<Button>Watch Video</Button>}
|
|
144
|
+
secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
|
|
145
|
+
backgroundMedia={
|
|
146
|
+
<div className="absolute inset-0 bg-gray-800 flex items-center justify-center">
|
|
147
|
+
<span className="text-gray-400 typography-body-small">
|
|
148
|
+
Video Background
|
|
149
|
+
</span>
|
|
150
|
+
</div>
|
|
151
|
+
}
|
|
152
|
+
/>
|
|
153
|
+
),
|
|
154
|
+
};
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { page } from "vitest/browser";
|
|
3
|
+
import { render } from "vitest-browser-react";
|
|
4
|
+
import { Tout } from "./tout";
|
|
5
|
+
|
|
6
|
+
const PlaceholderBackground = () => (
|
|
7
|
+
<div data-testid="background" className="absolute inset-0 bg-gray-300" />
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
describe("Tout", () => {
|
|
11
|
+
describe("Accessibility", () => {
|
|
12
|
+
test("renders as section landmark", async () => {
|
|
13
|
+
render(
|
|
14
|
+
<Tout
|
|
15
|
+
headline="Test Headline"
|
|
16
|
+
body="Test body"
|
|
17
|
+
primaryAction={<button type="button">Action</button>}
|
|
18
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
19
|
+
data-testid="tout"
|
|
20
|
+
/>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const tout = page.getByTestId("tout");
|
|
24
|
+
await expect.element(tout).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("headline renders as h2", async () => {
|
|
28
|
+
render(
|
|
29
|
+
<Tout
|
|
30
|
+
headline="Tout Headline"
|
|
31
|
+
body="Test body"
|
|
32
|
+
primaryAction={<button type="button">Action</button>}
|
|
33
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
34
|
+
/>,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
await expect
|
|
38
|
+
.element(page.getByRole("heading", { level: 2, name: "Tout Headline" }))
|
|
39
|
+
.toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("body text is accessible", async () => {
|
|
43
|
+
render(
|
|
44
|
+
<Tout
|
|
45
|
+
headline="Test"
|
|
46
|
+
body="Accessible body text"
|
|
47
|
+
primaryAction={<button type="button">Action</button>}
|
|
48
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await expect
|
|
53
|
+
.element(page.getByText("Accessible body text"))
|
|
54
|
+
.toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("background is hidden from screen readers", async () => {
|
|
58
|
+
render(
|
|
59
|
+
<Tout
|
|
60
|
+
headline="Test"
|
|
61
|
+
body="Test"
|
|
62
|
+
primaryAction={<button type="button">Action</button>}
|
|
63
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
64
|
+
data-testid="tout"
|
|
65
|
+
/>,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const tout = page.getByTestId("tout");
|
|
69
|
+
await expect.element(tout).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("Props", () => {
|
|
74
|
+
test("renders with required props", async () => {
|
|
75
|
+
render(
|
|
76
|
+
<Tout
|
|
77
|
+
headline="Required Headline"
|
|
78
|
+
body="Required body"
|
|
79
|
+
primaryAction={<button type="button">Primary</button>}
|
|
80
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
81
|
+
/>,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
await expect
|
|
85
|
+
.element(page.getByText("Required Headline"))
|
|
86
|
+
.toBeInTheDocument();
|
|
87
|
+
await expect.element(page.getByText("Required body")).toBeInTheDocument();
|
|
88
|
+
await expect
|
|
89
|
+
.element(page.getByRole("button", { name: "Primary" }))
|
|
90
|
+
.toBeInTheDocument();
|
|
91
|
+
await expect.element(page.getByTestId("background")).toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("renders secondary action when provided", async () => {
|
|
95
|
+
render(
|
|
96
|
+
<Tout
|
|
97
|
+
headline="Test"
|
|
98
|
+
body="Test"
|
|
99
|
+
primaryAction={<button type="button">Primary</button>}
|
|
100
|
+
secondaryAction={<button type="button">Secondary</button>}
|
|
101
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await expect
|
|
106
|
+
.element(page.getByRole("button", { name: "Primary" }))
|
|
107
|
+
.toBeInTheDocument();
|
|
108
|
+
await expect
|
|
109
|
+
.element(page.getByRole("button", { name: "Secondary" }))
|
|
110
|
+
.toBeInTheDocument();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("does not render secondary action when not provided", async () => {
|
|
114
|
+
render(
|
|
115
|
+
<Tout
|
|
116
|
+
headline="Test"
|
|
117
|
+
body="Test"
|
|
118
|
+
primaryAction={<button type="button">Primary</button>}
|
|
119
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
await expect
|
|
124
|
+
.element(page.getByRole("button", { name: "Primary" }))
|
|
125
|
+
.toBeInTheDocument();
|
|
126
|
+
// Should only have one button
|
|
127
|
+
const buttons = page.getByRole("button");
|
|
128
|
+
await expect.element(buttons).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("supports custom className", async () => {
|
|
132
|
+
render(
|
|
133
|
+
<Tout
|
|
134
|
+
headline="Test"
|
|
135
|
+
body="Test"
|
|
136
|
+
primaryAction={<button type="button">Action</button>}
|
|
137
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
138
|
+
className="custom-class"
|
|
139
|
+
data-testid="tout"
|
|
140
|
+
/>,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const tout = page.getByTestId("tout");
|
|
144
|
+
await expect.element(tout).toHaveClass(/custom-class/);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("spreads additional props to section element", async () => {
|
|
148
|
+
render(
|
|
149
|
+
<Tout
|
|
150
|
+
headline="Test"
|
|
151
|
+
body="Test"
|
|
152
|
+
primaryAction={<button type="button">Action</button>}
|
|
153
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
154
|
+
data-testid="tout"
|
|
155
|
+
aria-label="Tout section"
|
|
156
|
+
/>,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const tout = page.getByTestId("tout");
|
|
160
|
+
await expect.element(tout).toHaveAttribute("aria-label", "Tout section");
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("Styling", () => {
|
|
165
|
+
test("applies full width class", async () => {
|
|
166
|
+
render(
|
|
167
|
+
<Tout
|
|
168
|
+
headline="Test"
|
|
169
|
+
body="Test"
|
|
170
|
+
primaryAction={<button type="button">Action</button>}
|
|
171
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
172
|
+
data-testid="tout"
|
|
173
|
+
/>,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const tout = page.getByTestId("tout");
|
|
177
|
+
await expect.element(tout).toHaveClass(/w-full/);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("applies responsive height classes", async () => {
|
|
181
|
+
render(
|
|
182
|
+
<Tout
|
|
183
|
+
headline="Test"
|
|
184
|
+
body="Test"
|
|
185
|
+
primaryAction={<button type="button">Action</button>}
|
|
186
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
187
|
+
data-testid="tout"
|
|
188
|
+
/>,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const tout = page.getByTestId("tout");
|
|
192
|
+
await expect.element(tout).toHaveClass(/h-\[600px\]/);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("applies relative positioning for z-index context", async () => {
|
|
196
|
+
render(
|
|
197
|
+
<Tout
|
|
198
|
+
headline="Test"
|
|
199
|
+
body="Test"
|
|
200
|
+
primaryAction={<button type="button">Action</button>}
|
|
201
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
202
|
+
data-testid="tout"
|
|
203
|
+
/>,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const tout = page.getByTestId("tout");
|
|
207
|
+
await expect.element(tout).toHaveClass(/relative/);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("Content", () => {
|
|
212
|
+
test("headline has correct styling", async () => {
|
|
213
|
+
render(
|
|
214
|
+
<Tout
|
|
215
|
+
headline="Styled Headline"
|
|
216
|
+
body="Test"
|
|
217
|
+
primaryAction={<button type="button">Action</button>}
|
|
218
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
219
|
+
/>,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const headline = page.getByRole("heading", { level: 2 });
|
|
223
|
+
await expect.element(headline).toHaveClass(/typography-headline-small/);
|
|
224
|
+
await expect.element(headline).toHaveClass(/text-gray-900/);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("body has correct styling", async () => {
|
|
228
|
+
render(
|
|
229
|
+
<Tout
|
|
230
|
+
headline="Test"
|
|
231
|
+
body="Styled body text"
|
|
232
|
+
primaryAction={<button type="button">Action</button>}
|
|
233
|
+
backgroundMedia={<PlaceholderBackground />}
|
|
234
|
+
/>,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const body = page.getByText("Styled body text");
|
|
238
|
+
await expect.element(body).toHaveClass(/typography-body-small/);
|
|
239
|
+
await expect.element(body).toHaveClass(/text-gray-800/);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tout component based on Figma BaseKit / Touts
|
|
6
|
+
*
|
|
7
|
+
* A full-bleed section with a background image and overlaid content.
|
|
8
|
+
* Content can be positioned on the left side or centered.
|
|
9
|
+
*
|
|
10
|
+
* Responsive behavior:
|
|
11
|
+
* - Mobile (sm): 600px height, 4 columns with gap-20, content spans all 4 cols
|
|
12
|
+
* - Tablet (md): 750px height, 12 columns with gap-20, content spans 9 cols (left) or centered
|
|
13
|
+
* - Desktop (lg): 900px height, 24 columns with gap-20, content spans 9 cols (left) or centered
|
|
14
|
+
*
|
|
15
|
+
* This component is self-contained - do NOT wrap in a grid-container.
|
|
16
|
+
*/
|
|
17
|
+
export interface ToutProps extends React.HTMLAttributes<HTMLElement> {
|
|
18
|
+
/**
|
|
19
|
+
* The headline displayed in the tout
|
|
20
|
+
*/
|
|
21
|
+
headline: React.ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* The body text displayed below the headline (optional)
|
|
24
|
+
*/
|
|
25
|
+
body?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Primary action button (required)
|
|
28
|
+
*/
|
|
29
|
+
primaryAction: React.ReactNode;
|
|
30
|
+
/**
|
|
31
|
+
* Secondary action button (optional)
|
|
32
|
+
*/
|
|
33
|
+
secondaryAction?: React.ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* Background media (image or video element)
|
|
36
|
+
* Should be a full-bleed element that covers the entire section
|
|
37
|
+
*/
|
|
38
|
+
backgroundMedia: React.ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Content alignment: left (default) or center
|
|
41
|
+
*/
|
|
42
|
+
align?: "left" | "center";
|
|
43
|
+
/**
|
|
44
|
+
* Show National Design Studio footer link at the bottom of the section
|
|
45
|
+
*/
|
|
46
|
+
ndstudioFooter?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tout component for hero-like sections with background media and overlaid content.
|
|
51
|
+
*
|
|
52
|
+
* This component is self-contained with its own grid.
|
|
53
|
+
* Grid setup:
|
|
54
|
+
* - Desktop (lg): 24 columns, gap-spacing-20, content spans 9 cols
|
|
55
|
+
* - Tablet (md): 12 columns, gap-spacing-20, content spans 9 cols
|
|
56
|
+
* - Mobile: 4 columns, gap-spacing-20, content spans all 4 cols
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* <Tout
|
|
61
|
+
* headline="Feature Headline"
|
|
62
|
+
* body="Description of the feature..."
|
|
63
|
+
* primaryAction={<Button>Primary</Button>}
|
|
64
|
+
* secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
|
|
65
|
+
* backgroundMedia={
|
|
66
|
+
* <img
|
|
67
|
+
* src="/background.jpg"
|
|
68
|
+
* alt=""
|
|
69
|
+
* className="absolute inset-0 w-full h-full object-cover"
|
|
70
|
+
* />
|
|
71
|
+
* }
|
|
72
|
+
* />
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
76
|
+
(
|
|
77
|
+
{
|
|
78
|
+
className,
|
|
79
|
+
headline,
|
|
80
|
+
body,
|
|
81
|
+
primaryAction,
|
|
82
|
+
secondaryAction,
|
|
83
|
+
backgroundMedia,
|
|
84
|
+
align = "left",
|
|
85
|
+
ndstudioFooter,
|
|
86
|
+
...props
|
|
87
|
+
},
|
|
88
|
+
ref,
|
|
89
|
+
) => {
|
|
90
|
+
const isCentered = align === "center";
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<section
|
|
94
|
+
ref={ref}
|
|
95
|
+
className={cn(
|
|
96
|
+
// Full width
|
|
97
|
+
"w-full",
|
|
98
|
+
// Positioning context for background
|
|
99
|
+
"relative overflow-hidden",
|
|
100
|
+
// Responsive height: 600px mobile, 750px tablet, 900px desktop
|
|
101
|
+
"h-[600px] md:h-[750px] lg:h-[900px]",
|
|
102
|
+
className,
|
|
103
|
+
)}
|
|
104
|
+
{...props}
|
|
105
|
+
>
|
|
106
|
+
{/* Background layer - full bleed */}
|
|
107
|
+
<div
|
|
108
|
+
aria-hidden="true"
|
|
109
|
+
className="absolute inset-0 pointer-events-none"
|
|
110
|
+
>
|
|
111
|
+
{/* Fallback background color */}
|
|
112
|
+
<div className="absolute inset-0 bg-gray-500" />
|
|
113
|
+
{/* Background media */}
|
|
114
|
+
{backgroundMedia}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
{/* Inner grid for content alignment */}
|
|
118
|
+
<div
|
|
119
|
+
className={cn(
|
|
120
|
+
// Position above background
|
|
121
|
+
"relative z-10",
|
|
122
|
+
// Grid setup with responsive columns
|
|
123
|
+
"grid w-full h-full",
|
|
124
|
+
// Mobile: 4 columns with gap-20
|
|
125
|
+
"grid-cols-4 gap-spacing-20",
|
|
126
|
+
// Tablet (md): 12 columns
|
|
127
|
+
"md:grid-cols-12",
|
|
128
|
+
// Desktop (lg): 24 columns
|
|
129
|
+
"lg:grid-cols-24",
|
|
130
|
+
// Max width and centering like grid-container
|
|
131
|
+
"max-w-[var(--breakpoint-lg)] mx-auto",
|
|
132
|
+
// Responsive margins matching grid-container
|
|
133
|
+
"px-spacing-20 md:px-spacing-56 lg:px-spacing-72",
|
|
134
|
+
// Vertical padding to position content at bottom
|
|
135
|
+
"py-spacing-36 md:py-spacing-56 lg:py-spacing-72",
|
|
136
|
+
)}
|
|
137
|
+
>
|
|
138
|
+
{/* Content column - aligned to grid */}
|
|
139
|
+
<div
|
|
140
|
+
className={cn(
|
|
141
|
+
// Flex container for content
|
|
142
|
+
"flex flex-col",
|
|
143
|
+
isCentered ? "justify-start items-center" : "justify-end",
|
|
144
|
+
// Responsive gap between text and buttons
|
|
145
|
+
"gap-spacing-28 md:gap-spacing-36",
|
|
146
|
+
// Mobile: all 4 cols
|
|
147
|
+
"col-span-4",
|
|
148
|
+
// Tablet & Desktop: 9 cols left-aligned, full width centered
|
|
149
|
+
isCentered ? "md:col-span-12 lg:col-span-24" : "md:col-span-9",
|
|
150
|
+
)}
|
|
151
|
+
>
|
|
152
|
+
{/* Text content stack */}
|
|
153
|
+
<div
|
|
154
|
+
className={cn(
|
|
155
|
+
"flex flex-col gap-spacing-16",
|
|
156
|
+
isCentered && "items-center text-center",
|
|
157
|
+
)}
|
|
158
|
+
>
|
|
159
|
+
<h2 className="typography-headline-small text-gray-900">
|
|
160
|
+
{headline}
|
|
161
|
+
</h2>
|
|
162
|
+
{body && (
|
|
163
|
+
<p className="typography-body-small text-gray-800">{body}</p>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
{/* CTA buttons */}
|
|
168
|
+
<div
|
|
169
|
+
className={cn(
|
|
170
|
+
"flex flex-row",
|
|
171
|
+
isCentered ? "justify-center" : "items-start",
|
|
172
|
+
// Responsive gap between buttons
|
|
173
|
+
"gap-spacing-8 md:gap-spacing-12",
|
|
174
|
+
"[&>*]:flex-shrink-0",
|
|
175
|
+
)}
|
|
176
|
+
>
|
|
177
|
+
{primaryAction}
|
|
178
|
+
{secondaryAction}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
{/* National Design Studio footer */}
|
|
184
|
+
{ndstudioFooter && (
|
|
185
|
+
<div className="absolute bottom-6 md:bottom-8 left-0 right-0 z-10 text-center">
|
|
186
|
+
<p className="typography-label-large uppercase text-ivory-alpha-75">
|
|
187
|
+
Designed and Engineered in DC by{" "}
|
|
188
|
+
<a
|
|
189
|
+
href="https://ndstudio.gov"
|
|
190
|
+
target="_blank"
|
|
191
|
+
rel="noopener noreferrer"
|
|
192
|
+
className="hover:underline"
|
|
193
|
+
>
|
|
194
|
+
National Design Studio
|
|
195
|
+
<span className="sr-only"> (opens in new tab)</span>
|
|
196
|
+
</a>
|
|
197
|
+
</p>
|
|
198
|
+
</div>
|
|
199
|
+
)}
|
|
200
|
+
</section>
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
Tout.displayName = "Tout";
|
|
205
|
+
|
|
206
|
+
export { Tout };
|