@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.
- package/dist/component-registry.md +2405 -0
- package/dist/components/atoms/accordion/accordion.d.ts +44 -3
- package/dist/components/atoms/button/button.d.ts +155 -11
- package/dist/components/atoms/button/icon-button.d.ts +114 -5
- package/dist/components/atoms/ndstudio-footer/ndstudio-footer.d.ts +30 -0
- package/dist/components/atoms/pager-control/pager-control.d.ts +116 -9
- package/dist/components/dev-tools/dev-toolbar/dev-toolbar.d.ts +4 -0
- package/dist/components/dev-tools/grid-overlay/grid-overlay.d.ts +6 -0
- package/dist/components/organisms/card/card.d.ts +40 -4
- package/dist/components/sections/banner/banner.d.ts +39 -6
- package/dist/components/sections/card-grid/card-grid.d.ts +37 -4
- package/dist/components/sections/faq-section/faq-section.d.ts +2 -2
- package/dist/components/sections/hero/hero.d.ts +167 -16
- package/dist/components/sections/river/river.d.ts +37 -4
- package/dist/components/sections/tout/tout.d.ts +86 -6
- package/dist/components/sections/two-column-section/two-column-section.d.ts +80 -6
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/use-event-listener.d.ts +24 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +12034 -5934
- package/dist/index.js.map +1 -1
- package/dist/lib/theme.d.ts +330 -0
- package/dist/tokens.css +13650 -6129
- package/package.json +11 -21
- package/src/App.css +0 -0
- package/src/App.tsx +0 -7
- package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
- package/src/assets/react.svg +0 -1
- package/src/components/atoms/accordion/accordion.stories.tsx +0 -228
- package/src/components/atoms/accordion/accordion.tsx +0 -137
- package/src/components/atoms/accordion/index.ts +0 -6
- 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 +0 -84
- package/src/components/atoms/button/button.test.tsx +0 -141
- package/src/components/atoms/button/button.tsx +0 -95
- package/src/components/atoms/button/button.visual.test.tsx +0 -102
- package/src/components/atoms/button/icon-button.stories.tsx +0 -166
- package/src/components/atoms/button/icon-button.tsx +0 -125
- package/src/components/atoms/button/index.ts +0 -6
- package/src/components/atoms/pager-control/index.ts +0 -5
- package/src/components/atoms/pager-control/pager-control.stories.tsx +0 -209
- package/src/components/atoms/pager-control/pager-control.test.tsx +0 -149
- package/src/components/atoms/pager-control/pager-control.tsx +0 -328
- 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 +0 -293
- package/src/components/organisms/card/card.test.tsx +0 -245
- package/src/components/organisms/card/card.tsx +0 -227
- package/src/components/organisms/card/card.visual.test.tsx +0 -197
- package/src/components/organisms/card/index.ts +0 -19
- 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 +0 -18
- package/src/components/organisms/navbar/navbar.stories.tsx +0 -313
- package/src/components/organisms/navbar/navbar.test.tsx +0 -190
- package/src/components/organisms/navbar/navbar.tsx +0 -317
- package/src/components/organisms/navbar/navbar.visual.test.tsx +0 -85
- 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 +0 -1
- package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +0 -35
- package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +0 -107
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +0 -73
- package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +0 -46
- package/src/components/sections/banner/banner.stories.tsx +0 -150
- package/src/components/sections/banner/banner.test.tsx +0 -185
- package/src/components/sections/banner/banner.tsx +0 -130
- package/src/components/sections/banner/index.ts +0 -2
- package/src/components/sections/card-grid/card-grid.stories.tsx +0 -351
- package/src/components/sections/card-grid/card-grid.tsx +0 -118
- package/src/components/sections/card-grid/index.ts +0 -1
- package/src/components/sections/faq-section/faq-section.tsx +0 -77
- package/src/components/sections/faq-section/index.ts +0 -2
- 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 +0 -145
- package/src/components/sections/hero/hero.test.tsx +0 -135
- package/src/components/sections/hero/hero.tsx +0 -191
- package/src/components/sections/hero/hero.visual.test.tsx +0 -140
- package/src/components/sections/hero/index.ts +0 -1
- 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 +0 -6
- package/src/components/sections/prose/prose.stories.tsx +0 -144
- package/src/components/sections/prose/prose.test.tsx +0 -178
- package/src/components/sections/prose/prose.tsx +0 -88
- package/src/components/sections/prose/prose.visual.test.tsx +0 -105
- package/src/components/sections/river/index.ts +0 -1
- package/src/components/sections/river/river.stories.tsx +0 -237
- package/src/components/sections/river/river.test.tsx +0 -268
- package/src/components/sections/river/river.tsx +0 -175
- package/src/components/sections/tout/index.ts +0 -1
- package/src/components/sections/tout/tout.stories.tsx +0 -154
- package/src/components/sections/tout/tout.test.tsx +0 -242
- package/src/components/sections/tout/tout.tsx +0 -206
- package/src/components/sections/two-column-section/index.ts +0 -5
- package/src/components/sections/two-column-section/two-column-section.stories.tsx +0 -285
- package/src/components/sections/two-column-section/two-column-section.tsx +0 -152
- package/src/index.ts +0 -98
- package/src/lib/utils.ts +0 -6
- package/src/main.tsx +0 -13
- package/src/stories/Introduction.mdx +0 -114
- package/src/stories/TokenShowcase.stories.tsx +0 -92
- package/src/stories/TokenShowcase.tsx +0 -1352
- 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";
|