@nationaldesignstudio/react 0.0.15 → 0.0.16
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/package.json +3 -2
- 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 +219 -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 +102 -0
- package/src/components/atoms/button/button.test.tsx +135 -0
- package/src/components/atoms/button/button.tsx +139 -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 +120 -0
- package/src/components/atoms/button/index.ts +6 -0
- package/src/components/atoms/ndstudio-footer/index.ts +1 -0
- package/src/components/atoms/ndstudio-footer/ndstudio-footer.tsx +55 -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 +130 -0
- package/src/components/atoms/pager-control/pager-control.tsx +329 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +82 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +196 -0
- package/src/components/dev-tools/dev-toolbar/index.ts +1 -0
- package/src/components/dev-tools/grid-overlay/grid-overlay.tsx +41 -0
- package/src/components/dev-tools/grid-overlay/index.ts +1 -0
- package/src/components/dev-tools/index.ts +2 -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 +225 -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 +323 -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 +116 -0
- package/src/components/sections/card-grid/index.ts +1 -0
- package/src/components/sections/faq-section/faq-section.stories.tsx +453 -0
- package/src/components/sections/faq-section/faq-section.tsx +84 -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 +274 -0
- package/src/components/sections/hero/hero.test.tsx +135 -0
- package/src/components/sections/hero/hero.tsx +453 -0
- package/src/components/sections/hero/hero.visual.test.tsx +140 -0
- package/src/components/sections/hero/index.ts +10 -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 +173 -0
- package/src/components/sections/tout/index.ts +1 -0
- package/src/components/sections/tout/tout.stories.tsx +171 -0
- package/src/components/sections/tout/tout.test.tsx +242 -0
- package/src/components/sections/tout/tout.tsx +270 -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 +162 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-event-listener.ts +73 -0
- package/src/index.ts +155 -0
- package/src/lib/theme.ts +1000 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +13 -0
- package/src/stories/GridSystem.stories.tsx +84 -0
- package/src/stories/Introduction.mdx +114 -0
- package/src/stories/ThemeProvider.stories.tsx +357 -0
- package/src/stories/TokenShowcase.stories.tsx +92 -0
- package/src/stories/TokenShowcase.tsx +1429 -0
- package/src/styles.css +11 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/hooks.ts +40 -0
- package/src/theme/index.ts +43 -0
- package/src/theme/utils.ts +104 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
export interface NdstudioFooterProps
|
|
5
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/**
|
|
7
|
+
* The URL to link to
|
|
8
|
+
* @default "https://ndstudio.gov"
|
|
9
|
+
*/
|
|
10
|
+
href?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* NdstudioFooter component displays a "Designed and Engineered in DC by National Design Studio" footer link.
|
|
15
|
+
*
|
|
16
|
+
* This component is designed to be used as a footer within other components like Tout,
|
|
17
|
+
* but can also be used standalone.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* // Used within a Tout component via footer slot
|
|
22
|
+
* <Tout
|
|
23
|
+
* headline="Feature Headline"
|
|
24
|
+
* primaryAction={<Button>Primary</Button>}
|
|
25
|
+
* backgroundMedia={<img src="/bg.jpg" alt="" />}
|
|
26
|
+
* footer={<NdstudioFooter />}
|
|
27
|
+
* />
|
|
28
|
+
*
|
|
29
|
+
* // Standalone usage
|
|
30
|
+
* <NdstudioFooter className="my-custom-class" />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
const NdstudioFooter = React.forwardRef<HTMLDivElement, NdstudioFooterProps>(
|
|
34
|
+
({ className, href = "https://ndstudio.gov", ...props }, ref) => {
|
|
35
|
+
return (
|
|
36
|
+
<div ref={ref} className={cn("text-center", className)} {...props}>
|
|
37
|
+
<p className="typography-label-large uppercase text-ivory-alpha-75">
|
|
38
|
+
Designed and Engineered in DC by{" "}
|
|
39
|
+
<a
|
|
40
|
+
href={href}
|
|
41
|
+
target="_blank"
|
|
42
|
+
rel="noopener noreferrer"
|
|
43
|
+
className="hover:underline"
|
|
44
|
+
>
|
|
45
|
+
National Design Studio
|
|
46
|
+
<span className="sr-only"> (opens in new tab)</span>
|
|
47
|
+
</a>
|
|
48
|
+
</p>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
NdstudioFooter.displayName = "NdstudioFooter";
|
|
54
|
+
|
|
55
|
+
export { NdstudioFooter };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { PagerControl } from ".";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof PagerControl> = {
|
|
6
|
+
title: "Atoms/PagerControl",
|
|
7
|
+
} as Meta<typeof PagerControl>;
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
type Story = StoryObj<typeof PagerControl>;
|
|
11
|
+
|
|
12
|
+
export const Playground: Story = {
|
|
13
|
+
render: (args) => <PagerControl {...args} />,
|
|
14
|
+
};
|
|
15
|
+
Playground.argTypes = {
|
|
16
|
+
count: {
|
|
17
|
+
control: {
|
|
18
|
+
type: "number",
|
|
19
|
+
min: 1,
|
|
20
|
+
max: 10,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
activeIndex: {
|
|
24
|
+
control: {
|
|
25
|
+
type: "number",
|
|
26
|
+
min: 0,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
duration: {
|
|
30
|
+
control: {
|
|
31
|
+
type: "number",
|
|
32
|
+
min: 0,
|
|
33
|
+
max: 10000,
|
|
34
|
+
step: 500,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
autoPlay: {
|
|
38
|
+
control: {
|
|
39
|
+
type: "boolean",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
pauseOnHover: {
|
|
43
|
+
control: {
|
|
44
|
+
type: "boolean",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
loop: {
|
|
48
|
+
control: {
|
|
49
|
+
type: "boolean",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
size: {
|
|
53
|
+
control: {
|
|
54
|
+
type: "radio",
|
|
55
|
+
},
|
|
56
|
+
options: ["sm", "default", "lg"],
|
|
57
|
+
},
|
|
58
|
+
variant: {
|
|
59
|
+
control: {
|
|
60
|
+
type: "radio",
|
|
61
|
+
},
|
|
62
|
+
options: ["charcoal", "ivory"],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
Playground.args = {
|
|
66
|
+
count: 4,
|
|
67
|
+
duration: 5000,
|
|
68
|
+
autoPlay: true,
|
|
69
|
+
pauseOnHover: true,
|
|
70
|
+
loop: true,
|
|
71
|
+
size: "default",
|
|
72
|
+
variant: "charcoal",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Variants
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
export const Charcoal = () => <PagerControl count={4} variant="charcoal" />;
|
|
80
|
+
|
|
81
|
+
export const Ivory = () => <PagerControl count={4} variant="ivory" />;
|
|
82
|
+
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// Sizes
|
|
85
|
+
// =============================================================================
|
|
86
|
+
|
|
87
|
+
export const Small = () => <PagerControl count={4} size="sm" />;
|
|
88
|
+
|
|
89
|
+
export const Default = () => <PagerControl count={4} size="default" />;
|
|
90
|
+
|
|
91
|
+
export const Large = () => <PagerControl count={4} size="lg" />;
|
|
92
|
+
|
|
93
|
+
// =============================================================================
|
|
94
|
+
// Counts
|
|
95
|
+
// =============================================================================
|
|
96
|
+
|
|
97
|
+
export const TwoPages = () => <PagerControl count={2} />;
|
|
98
|
+
|
|
99
|
+
export const FivePages = () => <PagerControl count={5} />;
|
|
100
|
+
|
|
101
|
+
export const EightPages = () => <PagerControl count={8} />;
|
|
102
|
+
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Durations
|
|
105
|
+
// =============================================================================
|
|
106
|
+
|
|
107
|
+
export const FastDuration = () => <PagerControl count={4} duration={2000} />;
|
|
108
|
+
|
|
109
|
+
export const SlowDuration = () => <PagerControl count={4} duration={8000} />;
|
|
110
|
+
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// Controls
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
export const NoAutoPlay = () => <PagerControl count={4} autoPlay={false} />;
|
|
116
|
+
|
|
117
|
+
export const NoLoop = () => (
|
|
118
|
+
<PagerControl count={4} loop={false} duration={3000} />
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
export const NoPauseOnHover = () => (
|
|
122
|
+
<PagerControl count={4} pauseOnHover={false} />
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Controlled
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
export const Controlled = () => {
|
|
130
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div className="flex flex-col items-center gap-spacing-24">
|
|
134
|
+
<PagerControl
|
|
135
|
+
count={4}
|
|
136
|
+
activeIndex={activeIndex}
|
|
137
|
+
onChange={setActiveIndex}
|
|
138
|
+
autoPlay={false}
|
|
139
|
+
/>
|
|
140
|
+
<div className="flex gap-spacing-10">
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
onClick={() => setActiveIndex((prev) => Math.max(0, prev - 1))}
|
|
144
|
+
className="rounded bg-gray-200 px-spacing-12 py-spacing-6"
|
|
145
|
+
>
|
|
146
|
+
Previous
|
|
147
|
+
</button>
|
|
148
|
+
<span className="px-spacing-12 py-spacing-6">
|
|
149
|
+
Page {activeIndex + 1} of 4
|
|
150
|
+
</span>
|
|
151
|
+
<button
|
|
152
|
+
type="button"
|
|
153
|
+
onClick={() => setActiveIndex((prev) => Math.min(3, prev + 1))}
|
|
154
|
+
className="rounded bg-gray-200 px-spacing-12 py-spacing-6"
|
|
155
|
+
>
|
|
156
|
+
Next
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// =============================================================================
|
|
164
|
+
// Integration Example
|
|
165
|
+
// =============================================================================
|
|
166
|
+
|
|
167
|
+
export const WithCarousel = () => {
|
|
168
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
169
|
+
const slides = [
|
|
170
|
+
{ id: 1, color: "bg-blue-100", title: "Slide 1" },
|
|
171
|
+
{ id: 2, color: "bg-green-100", title: "Slide 2" },
|
|
172
|
+
{ id: 3, color: "bg-yellow-100", title: "Slide 3" },
|
|
173
|
+
{ id: 4, color: "bg-purple-100", title: "Slide 4" },
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div className="flex w-[400px] flex-col gap-spacing-16">
|
|
178
|
+
<div className="relative h-[200px] overflow-hidden rounded-radius-12">
|
|
179
|
+
{slides.map((slide, index) => (
|
|
180
|
+
<div
|
|
181
|
+
key={slide.id}
|
|
182
|
+
className={cn(
|
|
183
|
+
"absolute inset-0 flex items-center justify-center transition-transform duration-500",
|
|
184
|
+
slide.color,
|
|
185
|
+
)}
|
|
186
|
+
style={{
|
|
187
|
+
transform: `translateX(${(index - activeIndex) * 100}%)`,
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
<span className="text-xl font-semibold">{slide.title}</span>
|
|
191
|
+
</div>
|
|
192
|
+
))}
|
|
193
|
+
</div>
|
|
194
|
+
<div className="flex justify-center">
|
|
195
|
+
<PagerControl
|
|
196
|
+
count={slides.length}
|
|
197
|
+
activeIndex={activeIndex}
|
|
198
|
+
onChange={setActiveIndex}
|
|
199
|
+
duration={5000}
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Helper for carousel story
|
|
207
|
+
function cn(...classes: (string | boolean | undefined)[]) {
|
|
208
|
+
return classes.filter(Boolean).join(" ");
|
|
209
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { page, userEvent } from "vitest/browser";
|
|
3
|
+
import { render } from "vitest-browser-react";
|
|
4
|
+
import { PagerControl } from "./pager-control";
|
|
5
|
+
|
|
6
|
+
describe("PagerControl", () => {
|
|
7
|
+
describe("Accessibility", () => {
|
|
8
|
+
test("has correct tablist role", async () => {
|
|
9
|
+
render(<PagerControl count={4} autoPlay={false} />);
|
|
10
|
+
await expect
|
|
11
|
+
.element(page.getByRole("tablist", { name: "Page indicators" }))
|
|
12
|
+
.toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("renders correct number of tab buttons", async () => {
|
|
16
|
+
render(<PagerControl count={4} autoPlay={false} />);
|
|
17
|
+
const tabs = page.getByRole("tab").all();
|
|
18
|
+
expect(await tabs).toHaveLength(4);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("active tab has aria-selected true", async () => {
|
|
22
|
+
render(<PagerControl count={4} activeIndex={1} autoPlay={false} />);
|
|
23
|
+
const tabs = page.getByRole("tab").all();
|
|
24
|
+
const tabElements = await tabs;
|
|
25
|
+
await expect
|
|
26
|
+
.element(tabElements[0])
|
|
27
|
+
.toHaveAttribute("aria-selected", "false");
|
|
28
|
+
await expect
|
|
29
|
+
.element(tabElements[1])
|
|
30
|
+
.toHaveAttribute("aria-selected", "true");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("tabs have accessible labels", async () => {
|
|
34
|
+
render(<PagerControl count={3} activeIndex={0} autoPlay={false} />);
|
|
35
|
+
await expect
|
|
36
|
+
.element(page.getByRole("tab", { name: "Page 1 of 3, current" }))
|
|
37
|
+
.toBeInTheDocument();
|
|
38
|
+
await expect
|
|
39
|
+
.element(page.getByRole("tab", { name: "Go to page 2 of 3" }))
|
|
40
|
+
.toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("is focusable via keyboard", async () => {
|
|
44
|
+
render(<PagerControl count={4} autoPlay={false} />);
|
|
45
|
+
await userEvent.keyboard("{Tab}");
|
|
46
|
+
const firstTab = page.getByRole("tab").all();
|
|
47
|
+
await expect.element((await firstTab)[0]).toHaveFocus();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("Interactions", () => {
|
|
52
|
+
test("calls onChange when dot is clicked", async () => {
|
|
53
|
+
const handleChange = vi.fn();
|
|
54
|
+
render(
|
|
55
|
+
<PagerControl
|
|
56
|
+
count={4}
|
|
57
|
+
activeIndex={0}
|
|
58
|
+
onChange={handleChange}
|
|
59
|
+
autoPlay={false}
|
|
60
|
+
/>,
|
|
61
|
+
);
|
|
62
|
+
await page.getByRole("tab", { name: "Go to page 3 of 4" }).click();
|
|
63
|
+
expect(handleChange).toHaveBeenCalledWith(2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("clicking active dot calls onChange with same index", async () => {
|
|
67
|
+
const handleChange = vi.fn();
|
|
68
|
+
render(
|
|
69
|
+
<PagerControl
|
|
70
|
+
count={4}
|
|
71
|
+
activeIndex={1}
|
|
72
|
+
onChange={handleChange}
|
|
73
|
+
autoPlay={false}
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
await page.getByRole("tab", { name: "Page 2 of 4, current" }).click();
|
|
77
|
+
expect(handleChange).toHaveBeenCalledWith(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("updates internal state when uncontrolled", async () => {
|
|
81
|
+
render(<PagerControl count={4} autoPlay={false} />);
|
|
82
|
+
// Initially first is active
|
|
83
|
+
await expect
|
|
84
|
+
.element(page.getByRole("tab", { name: "Page 1 of 4, current" }))
|
|
85
|
+
.toBeInTheDocument();
|
|
86
|
+
|
|
87
|
+
// Click third dot
|
|
88
|
+
await page.getByRole("tab", { name: "Go to page 3 of 4" }).click();
|
|
89
|
+
|
|
90
|
+
// Now third should be active
|
|
91
|
+
await expect
|
|
92
|
+
.element(page.getByRole("tab", { name: "Page 3 of 4, current" }))
|
|
93
|
+
.toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("Variants", () => {
|
|
98
|
+
test("applies charcoal variant classes by default", async () => {
|
|
99
|
+
render(<PagerControl count={4} autoPlay={false} />);
|
|
100
|
+
const tabs = await page.getByRole("tab").all();
|
|
101
|
+
// Active dot track should have bg-alpha-black-30
|
|
102
|
+
await expect.element(tabs[0]).toHaveClass(/bg-alpha-black-30/);
|
|
103
|
+
// Inactive dots should have bg-alpha-black-30
|
|
104
|
+
await expect.element(tabs[1]).toHaveClass(/bg-alpha-black-30/);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("applies ivory variant classes", async () => {
|
|
108
|
+
render(<PagerControl count={4} variant="ivory" autoPlay={false} />);
|
|
109
|
+
const tabs = await page.getByRole("tab").all();
|
|
110
|
+
// Active dot track should have bg-alpha-white-30
|
|
111
|
+
await expect.element(tabs[0]).toHaveClass(/bg-alpha-white-30/);
|
|
112
|
+
// Inactive dots should have bg-alpha-white-30
|
|
113
|
+
await expect.element(tabs[1]).toHaveClass(/bg-alpha-white-30/);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("Count", () => {
|
|
118
|
+
test("renders correct number of dots for count=2", async () => {
|
|
119
|
+
render(<PagerControl count={2} autoPlay={false} />);
|
|
120
|
+
const tabs = await page.getByRole("tab").all();
|
|
121
|
+
expect(tabs).toHaveLength(2);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("renders correct number of dots for count=8", async () => {
|
|
125
|
+
render(<PagerControl count={8} autoPlay={false} />);
|
|
126
|
+
const tabs = await page.getByRole("tab").all();
|
|
127
|
+
expect(tabs).toHaveLength(8);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|