@nationaldesignstudio/react 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/component-registry.md +2405 -0
  2. package/dist/components/atoms/accordion/accordion.d.ts +44 -3
  3. package/dist/components/atoms/button/button.d.ts +155 -11
  4. package/dist/components/atoms/button/icon-button.d.ts +114 -5
  5. package/dist/components/atoms/ndstudio-footer/ndstudio-footer.d.ts +30 -0
  6. package/dist/components/atoms/pager-control/pager-control.d.ts +116 -9
  7. package/dist/components/dev-tools/dev-toolbar/dev-toolbar.d.ts +4 -0
  8. package/dist/components/dev-tools/grid-overlay/grid-overlay.d.ts +6 -0
  9. package/dist/components/organisms/card/card.d.ts +40 -4
  10. package/dist/components/sections/banner/banner.d.ts +39 -6
  11. package/dist/components/sections/card-grid/card-grid.d.ts +37 -4
  12. package/dist/components/sections/faq-section/faq-section.d.ts +2 -2
  13. package/dist/components/sections/hero/hero.d.ts +167 -16
  14. package/dist/components/sections/river/river.d.ts +37 -4
  15. package/dist/components/sections/tout/tout.d.ts +86 -6
  16. package/dist/components/sections/two-column-section/two-column-section.d.ts +80 -6
  17. package/dist/hooks/index.d.ts +1 -0
  18. package/dist/hooks/use-event-listener.d.ts +24 -0
  19. package/dist/index.d.ts +9 -2
  20. package/dist/index.js +12034 -5934
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/theme.d.ts +330 -0
  23. package/dist/tokens.css +13650 -6129
  24. package/package.json +11 -21
  25. package/src/App.css +0 -0
  26. package/src/App.tsx +0 -7
  27. package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
  28. package/src/assets/react.svg +0 -1
  29. package/src/components/atoms/accordion/accordion.stories.tsx +0 -228
  30. package/src/components/atoms/accordion/accordion.tsx +0 -137
  31. package/src/components/atoms/accordion/index.ts +0 -6
  32. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
  33. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
  34. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
  35. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
  36. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
  37. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
  38. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
  39. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
  40. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
  41. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
  42. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
  43. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
  44. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
  45. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
  46. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
  47. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
  48. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
  49. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
  50. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
  51. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
  52. package/src/components/atoms/button/button.stories.tsx +0 -84
  53. package/src/components/atoms/button/button.test.tsx +0 -141
  54. package/src/components/atoms/button/button.tsx +0 -95
  55. package/src/components/atoms/button/button.visual.test.tsx +0 -102
  56. package/src/components/atoms/button/icon-button.stories.tsx +0 -166
  57. package/src/components/atoms/button/icon-button.tsx +0 -125
  58. package/src/components/atoms/button/index.ts +0 -6
  59. package/src/components/atoms/pager-control/index.ts +0 -5
  60. package/src/components/atoms/pager-control/pager-control.stories.tsx +0 -209
  61. package/src/components/atoms/pager-control/pager-control.test.tsx +0 -149
  62. package/src/components/atoms/pager-control/pager-control.tsx +0 -328
  63. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
  64. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
  65. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
  66. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
  67. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
  68. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
  69. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
  70. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
  71. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
  72. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
  73. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
  74. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
  75. package/src/components/organisms/card/card.stories.tsx +0 -293
  76. package/src/components/organisms/card/card.test.tsx +0 -245
  77. package/src/components/organisms/card/card.tsx +0 -227
  78. package/src/components/organisms/card/card.visual.test.tsx +0 -197
  79. package/src/components/organisms/card/index.ts +0 -19
  80. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
  81. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
  82. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
  83. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
  84. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
  85. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
  86. package/src/components/organisms/navbar/index.ts +0 -18
  87. package/src/components/organisms/navbar/navbar.stories.tsx +0 -313
  88. package/src/components/organisms/navbar/navbar.test.tsx +0 -190
  89. package/src/components/organisms/navbar/navbar.tsx +0 -317
  90. package/src/components/organisms/navbar/navbar.visual.test.tsx +0 -85
  91. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-darwin.png +0 -0
  92. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
  93. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
  94. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
  95. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
  96. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
  97. package/src/components/organisms/us-gov-banner/index.ts +0 -1
  98. package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +0 -35
  99. package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +0 -107
  100. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +0 -73
  101. package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +0 -46
  102. package/src/components/sections/banner/banner.stories.tsx +0 -150
  103. package/src/components/sections/banner/banner.test.tsx +0 -185
  104. package/src/components/sections/banner/banner.tsx +0 -130
  105. package/src/components/sections/banner/index.ts +0 -2
  106. package/src/components/sections/card-grid/card-grid.stories.tsx +0 -351
  107. package/src/components/sections/card-grid/card-grid.tsx +0 -118
  108. package/src/components/sections/card-grid/index.ts +0 -1
  109. package/src/components/sections/faq-section/faq-section.tsx +0 -77
  110. package/src/components/sections/faq-section/index.ts +0 -2
  111. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
  112. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
  113. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
  114. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
  115. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
  116. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
  117. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
  118. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
  119. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
  120. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
  121. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
  122. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
  123. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
  124. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
  125. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
  126. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
  127. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
  128. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
  129. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
  130. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
  131. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
  132. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
  133. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
  134. package/src/components/sections/hero/hero.stories.tsx +0 -145
  135. package/src/components/sections/hero/hero.test.tsx +0 -135
  136. package/src/components/sections/hero/hero.tsx +0 -191
  137. package/src/components/sections/hero/hero.visual.test.tsx +0 -140
  138. package/src/components/sections/hero/index.ts +0 -1
  139. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
  140. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
  141. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
  142. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
  143. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
  144. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
  145. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
  146. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
  147. package/src/components/sections/prose/index.ts +0 -6
  148. package/src/components/sections/prose/prose.stories.tsx +0 -144
  149. package/src/components/sections/prose/prose.test.tsx +0 -178
  150. package/src/components/sections/prose/prose.tsx +0 -88
  151. package/src/components/sections/prose/prose.visual.test.tsx +0 -105
  152. package/src/components/sections/river/index.ts +0 -1
  153. package/src/components/sections/river/river.stories.tsx +0 -237
  154. package/src/components/sections/river/river.test.tsx +0 -268
  155. package/src/components/sections/river/river.tsx +0 -175
  156. package/src/components/sections/tout/index.ts +0 -1
  157. package/src/components/sections/tout/tout.stories.tsx +0 -154
  158. package/src/components/sections/tout/tout.test.tsx +0 -242
  159. package/src/components/sections/tout/tout.tsx +0 -206
  160. package/src/components/sections/two-column-section/index.ts +0 -5
  161. package/src/components/sections/two-column-section/two-column-section.stories.tsx +0 -285
  162. package/src/components/sections/two-column-section/two-column-section.tsx +0 -152
  163. package/src/index.ts +0 -98
  164. package/src/lib/utils.ts +0 -6
  165. package/src/main.tsx +0 -13
  166. package/src/stories/Introduction.mdx +0 -114
  167. package/src/stories/TokenShowcase.stories.tsx +0 -92
  168. package/src/stories/TokenShowcase.tsx +0 -1352
  169. package/src/styles.css +0 -11
@@ -1,237 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { Button } from "../../atoms/button";
3
- import { River } from ".";
4
-
5
- const meta: Meta<typeof River> = {
6
- title: "Sections/River",
7
- component: River,
8
- parameters: {
9
- layout: "fullscreen",
10
- },
11
- argTypes: {
12
- variant: {
13
- control: "select",
14
- options: ["A", "B"],
15
- description: "Layout variant",
16
- },
17
- headline: {
18
- control: "text",
19
- description: "The headline text",
20
- },
21
- body: {
22
- control: "text",
23
- description: "The body text",
24
- },
25
- },
26
- decorators: [
27
- (Story) => (
28
- <div className="grid-container">
29
- <Story />
30
- </div>
31
- ),
32
- ],
33
- } as Meta<typeof River>;
34
-
35
- export default meta;
36
- type Story = StoryObj<typeof River>;
37
-
38
- const PlaceholderImage = () => (
39
- <div className="bg-gray-200 w-full aspect-[4/3] rounded-lg flex items-center justify-center">
40
- <span className="text-gray-500 typography-body-small">
41
- Image Placeholder
42
- </span>
43
- </div>
44
- );
45
-
46
- export const Playground: Story = {
47
- render: (args) => <River {...args} />,
48
- };
49
- Playground.args = {
50
- variant: "A",
51
- headline: "Feature Headline",
52
- body: "Use rivers to present content with supporting media. They work great for feature highlights, product showcases, and storytelling sections.",
53
- primaryAction: <Button>Primary Action</Button>,
54
- secondaryAction: <Button variant="charcoalOutline">Secondary</Button>,
55
- media: <PlaceholderImage />,
56
- };
57
-
58
- // =============================================================================
59
- // Variants
60
- // =============================================================================
61
-
62
- /**
63
- * Variant A: Text on left, media on right (desktop)
64
- */
65
- export const VariantA: Story = {
66
- render: () => (
67
- <River
68
- variant="A"
69
- headline="Text Left, Media Right"
70
- body="Variant A places the text content on the left (9 columns) and media on the right (15 columns) on desktop viewports. On mobile and tablet, they stack vertically."
71
- primaryAction={<Button>Primary Action</Button>}
72
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
73
- media={<PlaceholderImage />}
74
- />
75
- ),
76
- };
77
-
78
- /**
79
- * Variant B: Media on left, text on right (desktop)
80
- */
81
- export const VariantB: Story = {
82
- render: () => (
83
- <River
84
- variant="B"
85
- headline="Media Left, Text Right"
86
- body="Variant B places the media on the left (15 columns) and text content on the right (9 columns) on desktop viewports. On mobile and tablet, they stack vertically with text first."
87
- primaryAction={<Button>Primary Action</Button>}
88
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
89
- media={<PlaceholderImage />}
90
- />
91
- ),
92
- };
93
-
94
- // =============================================================================
95
- // Responsive Variants
96
- // =============================================================================
97
-
98
- export const VariantADesktop: Story = {
99
- render: () => (
100
- <River
101
- variant="A"
102
- headline="Desktop View"
103
- body="On desktop (lg, 1440px), the content spans 9 columns and the media spans 15 columns in a horizontal layout."
104
- primaryAction={<Button>Primary</Button>}
105
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
106
- media={<PlaceholderImage />}
107
- />
108
- ),
109
- globals: {
110
- viewport: { value: "lg", isRotated: false },
111
- },
112
- };
113
-
114
- export const VariantATablet: Story = {
115
- render: () => (
116
- <River
117
- variant="A"
118
- headline="Tablet View"
119
- body="On tablet (md, 768px), the content and media stack vertically with the text above the media."
120
- primaryAction={<Button>Primary</Button>}
121
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
122
- media={<PlaceholderImage />}
123
- />
124
- ),
125
- globals: {
126
- viewport: { value: "md", isRotated: false },
127
- },
128
- };
129
-
130
- export const VariantAMobile: Story = {
131
- render: () => (
132
- <River
133
- variant="A"
134
- headline="Mobile View"
135
- body="On mobile (sm, 320px), content is stacked with smaller button sizing."
136
- primaryAction={<Button size="sm">Primary</Button>}
137
- secondaryAction={
138
- <Button size="sm" variant="charcoalOutline">
139
- Secondary
140
- </Button>
141
- }
142
- media={<PlaceholderImage />}
143
- />
144
- ),
145
- globals: {
146
- viewport: { value: "sm", isRotated: false },
147
- },
148
- };
149
-
150
- export const VariantBDesktop: Story = {
151
- render: () => (
152
- <River
153
- variant="B"
154
- headline="Desktop View - Reversed"
155
- body="Variant B reverses the layout, placing media on the left and content on the right."
156
- primaryAction={<Button>Primary</Button>}
157
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
158
- media={<PlaceholderImage />}
159
- />
160
- ),
161
- globals: {
162
- viewport: { value: "lg", isRotated: false },
163
- },
164
- };
165
-
166
- // =============================================================================
167
- // Examples
168
- // =============================================================================
169
-
170
- /**
171
- * Without secondary action
172
- */
173
- export const SingleAction: Story = {
174
- render: () => (
175
- <River
176
- variant="A"
177
- headline="Single Action Button"
178
- body="Rivers can also be used with just a primary action button when a secondary action isn't needed."
179
- primaryAction={<Button>Learn More</Button>}
180
- media={<PlaceholderImage />}
181
- />
182
- ),
183
- };
184
-
185
- /**
186
- * Alternating rivers for visual rhythm
187
- */
188
- export const AlternatingRivers: Story = {
189
- render: () => (
190
- <>
191
- <River
192
- variant="A"
193
- headline="First Feature"
194
- body="Start with text on the left for the first section."
195
- primaryAction={<Button>Explore</Button>}
196
- media={<PlaceholderImage />}
197
- />
198
- <River
199
- variant="B"
200
- headline="Second Feature"
201
- body="Alternate to media on the left for visual variety."
202
- primaryAction={<Button>Discover</Button>}
203
- media={<PlaceholderImage />}
204
- className="bg-gray-100"
205
- />
206
- <River
207
- variant="A"
208
- headline="Third Feature"
209
- body="Return to the original layout to create rhythm."
210
- primaryAction={<Button>Learn More</Button>}
211
- media={<PlaceholderImage />}
212
- />
213
- </>
214
- ),
215
- };
216
-
217
- /**
218
- * With actual image
219
- */
220
- export const WithImage: Story = {
221
- render: () => (
222
- <River
223
- variant="A"
224
- headline="Real World Example"
225
- body="Rivers work great with actual images, videos, or any media content. The media column is designed to accommodate various aspect ratios."
226
- primaryAction={<Button>Get Started</Button>}
227
- secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
228
- media={
229
- <img
230
- src="https://images.unsplash.com/photo-1551434678-e076c223a692?w=800&h=600&fit=crop"
231
- alt="Team collaboration"
232
- className="rounded-lg object-cover"
233
- />
234
- }
235
- />
236
- ),
237
- };
@@ -1,268 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { page } from "vitest/browser";
3
- import { render } from "vitest-browser-react";
4
- import { River } from "./river";
5
-
6
- const PlaceholderMedia = () => <div data-testid="media">Media</div>;
7
-
8
- describe("River", () => {
9
- describe("Accessibility", () => {
10
- test("renders as section landmark", async () => {
11
- render(
12
- <River
13
- headline="Test Headline"
14
- body="Test body"
15
- primaryAction={<button type="button">Action</button>}
16
- media={<PlaceholderMedia />}
17
- data-testid="river"
18
- />,
19
- );
20
-
21
- const river = page.getByTestId("river");
22
- await expect.element(river).toBeInTheDocument();
23
- });
24
-
25
- test("headline renders as h2", async () => {
26
- render(
27
- <River
28
- headline="River Headline"
29
- body="Test body"
30
- primaryAction={<button type="button">Action</button>}
31
- media={<PlaceholderMedia />}
32
- />,
33
- );
34
-
35
- await expect
36
- .element(
37
- page.getByRole("heading", { level: 2, name: "River Headline" }),
38
- )
39
- .toBeInTheDocument();
40
- });
41
-
42
- test("body text is accessible", async () => {
43
- render(
44
- <River
45
- headline="Test"
46
- body="Accessible body text"
47
- primaryAction={<button type="button">Action</button>}
48
- media={<PlaceholderMedia />}
49
- />,
50
- );
51
-
52
- await expect
53
- .element(page.getByText("Accessible body text"))
54
- .toBeInTheDocument();
55
- });
56
- });
57
-
58
- describe("Props", () => {
59
- test("renders with required props", async () => {
60
- render(
61
- <River
62
- headline="Required Headline"
63
- body="Required body"
64
- primaryAction={<button type="button">Primary</button>}
65
- media={<PlaceholderMedia />}
66
- />,
67
- );
68
-
69
- await expect
70
- .element(page.getByText("Required Headline"))
71
- .toBeInTheDocument();
72
- await expect.element(page.getByText("Required body")).toBeInTheDocument();
73
- await expect
74
- .element(page.getByRole("button", { name: "Primary" }))
75
- .toBeInTheDocument();
76
- await expect.element(page.getByTestId("media")).toBeInTheDocument();
77
- });
78
-
79
- test("renders secondary action when provided", async () => {
80
- render(
81
- <River
82
- headline="Test"
83
- body="Test"
84
- primaryAction={<button type="button">Primary</button>}
85
- secondaryAction={<button type="button">Secondary</button>}
86
- media={<PlaceholderMedia />}
87
- />,
88
- );
89
-
90
- await expect
91
- .element(page.getByRole("button", { name: "Primary" }))
92
- .toBeInTheDocument();
93
- await expect
94
- .element(page.getByRole("button", { name: "Secondary" }))
95
- .toBeInTheDocument();
96
- });
97
-
98
- test("does not render secondary action when not provided", async () => {
99
- render(
100
- <River
101
- headline="Test"
102
- body="Test"
103
- primaryAction={<button type="button">Primary</button>}
104
- media={<PlaceholderMedia />}
105
- />,
106
- );
107
-
108
- await expect
109
- .element(page.getByRole("button", { name: "Primary" }))
110
- .toBeInTheDocument();
111
- // Should only have one button
112
- const buttons = page.getByRole("button");
113
- await expect.element(buttons).toBeInTheDocument();
114
- });
115
-
116
- test("supports custom className", async () => {
117
- render(
118
- <River
119
- headline="Test"
120
- body="Test"
121
- primaryAction={<button type="button">Action</button>}
122
- media={<PlaceholderMedia />}
123
- className="custom-class"
124
- data-testid="river"
125
- />,
126
- );
127
-
128
- const river = page.getByTestId("river");
129
- await expect.element(river).toHaveClass(/custom-class/);
130
- });
131
-
132
- test("spreads additional props to section element", async () => {
133
- render(
134
- <River
135
- headline="Test"
136
- body="Test"
137
- primaryAction={<button type="button">Action</button>}
138
- media={<PlaceholderMedia />}
139
- data-testid="river"
140
- aria-label="River section"
141
- />,
142
- );
143
-
144
- const river = page.getByTestId("river");
145
- await expect
146
- .element(river)
147
- .toHaveAttribute("aria-label", "River section");
148
- });
149
- });
150
-
151
- describe("Styling", () => {
152
- test("applies col-full for grid alignment", async () => {
153
- render(
154
- <River
155
- headline="Test"
156
- body="Test"
157
- primaryAction={<button type="button">Action</button>}
158
- media={<PlaceholderMedia />}
159
- data-testid="river"
160
- />,
161
- );
162
-
163
- const river = page.getByTestId("river");
164
- await expect.element(river).toHaveClass(/col-full/);
165
- });
166
-
167
- test("applies responsive padding classes", async () => {
168
- render(
169
- <River
170
- headline="Test"
171
- body="Test"
172
- primaryAction={<button type="button">Action</button>}
173
- media={<PlaceholderMedia />}
174
- data-testid="river"
175
- />,
176
- );
177
-
178
- const river = page.getByTestId("river");
179
- // Mobile padding
180
- await expect.element(river).toHaveClass(/px-spacing-20/);
181
- await expect.element(river).toHaveClass(/pt-spacing-72/);
182
- await expect.element(river).toHaveClass(/pb-spacing-20/);
183
- });
184
- });
185
-
186
- describe("Variants", () => {
187
- test("variant A is default", async () => {
188
- render(
189
- <River
190
- headline="Test"
191
- body="Test"
192
- primaryAction={<button type="button">Action</button>}
193
- media={<PlaceholderMedia />}
194
- data-testid="river"
195
- />,
196
- );
197
-
198
- const river = page.getByTestId("river");
199
- await expect.element(river).toBeInTheDocument();
200
- });
201
-
202
- test("variant A renders content before media", async () => {
203
- render(
204
- <River
205
- variant="A"
206
- headline="Test Headline"
207
- body="Test"
208
- primaryAction={<button type="button">Action</button>}
209
- media={<PlaceholderMedia />}
210
- data-testid="river"
211
- />,
212
- );
213
-
214
- // Both should be present
215
- await expect.element(page.getByText("Test Headline")).toBeInTheDocument();
216
- await expect.element(page.getByTestId("media")).toBeInTheDocument();
217
- });
218
-
219
- test("variant B renders media before content", async () => {
220
- render(
221
- <River
222
- variant="B"
223
- headline="Test Headline"
224
- body="Test"
225
- primaryAction={<button type="button">Action</button>}
226
- media={<PlaceholderMedia />}
227
- data-testid="river"
228
- />,
229
- );
230
-
231
- // Both should be present
232
- await expect.element(page.getByText("Test Headline")).toBeInTheDocument();
233
- await expect.element(page.getByTestId("media")).toBeInTheDocument();
234
- });
235
- });
236
-
237
- describe("Content", () => {
238
- test("headline has correct styling", async () => {
239
- render(
240
- <River
241
- headline="Styled Headline"
242
- body="Test"
243
- primaryAction={<button type="button">Action</button>}
244
- media={<PlaceholderMedia />}
245
- />,
246
- );
247
-
248
- const headline = page.getByRole("heading", { level: 2 });
249
- await expect.element(headline).toHaveClass(/typography-headline-small/);
250
- await expect.element(headline).toHaveClass(/text-gray-900/);
251
- });
252
-
253
- test("body has correct styling", async () => {
254
- render(
255
- <River
256
- headline="Test"
257
- body="Styled body text"
258
- primaryAction={<button type="button">Action</button>}
259
- media={<PlaceholderMedia />}
260
- />,
261
- );
262
-
263
- const body = page.getByText("Styled body text");
264
- await expect.element(body).toHaveClass(/typography-body-small/);
265
- await expect.element(body).toHaveClass(/text-gray-800/);
266
- });
267
- });
268
- });
@@ -1,175 +0,0 @@
1
- import { cva, type VariantProps } from "class-variance-authority";
2
- import * as React from "react";
3
- import { cn } from "@/lib/utils";
4
-
5
- /**
6
- * River component for content sections with text and media
7
- *
8
- * Variants:
9
- * - A: Text on left (9 cols), media on right (15 cols) on desktop
10
- * - B: Media on left (15 cols), text on right (9 cols) on desktop
11
- *
12
- * Uses the 24-column grid system. Must be placed inside a `grid-container`.
13
- */
14
- const riverVariants = cva(
15
- // Base styles - col-full within parent grid, responsive padding
16
- [
17
- "col-full",
18
- // Small (mobile): 20px x, 72px top, 20px bottom
19
- "px-spacing-20 pt-spacing-72 pb-spacing-20",
20
- // Medium (tablet): 56px x, 96px y
21
- "md:px-spacing-56 md:py-spacing-96",
22
- // Large (desktop): 72px x, 128px y
23
- "lg:px-spacing-72 lg:py-spacing-128",
24
- ],
25
- {
26
- variants: {
27
- variant: {
28
- A: "",
29
- B: "",
30
- },
31
- },
32
- defaultVariants: {
33
- variant: "A",
34
- },
35
- },
36
- );
37
-
38
- export interface RiverProps
39
- extends React.HTMLAttributes<HTMLElement>,
40
- VariantProps<typeof riverVariants> {
41
- /**
42
- * The headline text
43
- */
44
- headline: string;
45
- /**
46
- * The body text
47
- */
48
- body: string;
49
- /**
50
- * Primary action button (required)
51
- */
52
- primaryAction: React.ReactNode;
53
- /**
54
- * Secondary action button (optional)
55
- */
56
- secondaryAction?: React.ReactNode;
57
- /**
58
- * Media content (image, video, etc.)
59
- */
60
- media: React.ReactNode;
61
- }
62
-
63
- /**
64
- * River component for content sections with text and media.
65
- *
66
- * Uses the 24-column grid system - must be placed inside a `grid-container`.
67
- *
68
- * Layout:
69
- * - Mobile/Tablet: Stacked (text above media)
70
- * - Desktop (lg+):
71
- * - Variant A: Text (9 cols) | Media (15 cols)
72
- * - Variant B: Media (15 cols) | Text (9 cols)
73
- *
74
- * @example
75
- * ```tsx
76
- * <div className="grid-container">
77
- * <River
78
- * variant="A"
79
- * headline="Feature Headline"
80
- * body="Description of the feature..."
81
- * primaryAction={<Button>Primary</Button>}
82
- * secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
83
- * media={<img src="..." alt="Feature" />}
84
- * />
85
- * </div>
86
- * ```
87
- */
88
- const River = React.forwardRef<HTMLElement, RiverProps>(
89
- (
90
- {
91
- className,
92
- variant,
93
- headline,
94
- body,
95
- primaryAction,
96
- secondaryAction,
97
- media,
98
- ...props
99
- },
100
- ref,
101
- ) => {
102
- const contentColumn = (
103
- <div
104
- className={cn(
105
- "flex flex-col",
106
- // Full width on mobile/tablet, 9 cols on desktop
107
- "lg:col-span-9",
108
- )}
109
- >
110
- {/* Text content with 16px gap */}
111
- <div className="flex flex-col gap-spacing-16">
112
- <h2 className="typography-headline-small text-gray-900">
113
- {headline}
114
- </h2>
115
- <p className="typography-body-small text-gray-800">{body}</p>
116
- </div>
117
-
118
- {/* Buttons with 36px gap from text, responsive sizes */}
119
- <div
120
- className={cn(
121
- "flex flex-row gap-spacing-16 mt-spacing-36",
122
- "[&>*]:flex-shrink-0",
123
- )}
124
- >
125
- {primaryAction}
126
- {secondaryAction}
127
- </div>
128
- </div>
129
- );
130
-
131
- const mediaColumn = (
132
- <div
133
- className={cn(
134
- // Full width on mobile/tablet, 15 cols on desktop
135
- "lg:col-span-15",
136
- // Ensure media fills the container
137
- "[&>*]:w-full [&>*]:h-auto",
138
- )}
139
- >
140
- {media}
141
- </div>
142
- );
143
-
144
- return (
145
- <section
146
- ref={ref}
147
- className={cn(riverVariants({ variant }), className)}
148
- {...props}
149
- >
150
- {/* Inner grid container for 24-col layout */}
151
- <div
152
- className={cn(
153
- "grid grid-cols-1 gap-spacing-36",
154
- "lg:grid-cols-24 lg:gap-spacing-36",
155
- )}
156
- >
157
- {variant === "B" ? (
158
- <>
159
- {mediaColumn}
160
- {contentColumn}
161
- </>
162
- ) : (
163
- <>
164
- {contentColumn}
165
- {mediaColumn}
166
- </>
167
- )}
168
- </div>
169
- </section>
170
- );
171
- },
172
- );
173
- River.displayName = "River";
174
-
175
- export { River, riverVariants };
@@ -1 +0,0 @@
1
- export { Tout, type ToutProps } from "./tout";