@nationaldesignstudio/react 0.5.2 → 0.5.4

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 (161) hide show
  1. package/package.json +10 -2
  2. package/src/components/organisms/navbar/navbar.tsx +8 -8
  3. package/src/App.css +0 -0
  4. package/src/App.tsx +0 -7
  5. package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
  6. package/src/assets/react.svg +0 -1
  7. package/src/components/atoms/accordion/accordion.stories.tsx +0 -228
  8. package/src/components/atoms/accordion/accordion.test.tsx +0 -231
  9. package/src/components/atoms/accordion/index.ts +0 -6
  10. package/src/components/atoms/background/background.test.tsx +0 -213
  11. package/src/components/atoms/background/index.ts +0 -22
  12. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
  13. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
  14. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
  15. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
  16. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
  17. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
  18. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
  19. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
  20. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
  21. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
  22. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
  23. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
  24. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
  25. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
  26. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
  27. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
  28. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
  29. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
  30. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
  31. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
  32. package/src/components/atoms/button/button.stories.tsx +0 -289
  33. package/src/components/atoms/button/button.test.tsx +0 -419
  34. package/src/components/atoms/button/button.visual.test.tsx +0 -98
  35. package/src/components/atoms/button/icon-button.stories.tsx +0 -260
  36. package/src/components/atoms/button/icon-button.test.tsx +0 -186
  37. package/src/components/atoms/button/index.ts +0 -6
  38. package/src/components/atoms/input/index.ts +0 -17
  39. package/src/components/atoms/input/input-group.stories.tsx +0 -646
  40. package/src/components/atoms/input/input-group.test.tsx +0 -362
  41. package/src/components/atoms/input/input.stories.tsx +0 -228
  42. package/src/components/atoms/input/input.test.tsx +0 -167
  43. package/src/components/atoms/ndstudio-footer/index.ts +0 -1
  44. package/src/components/atoms/pager-control/index.ts +0 -5
  45. package/src/components/atoms/pager-control/pager-control.stories.tsx +0 -207
  46. package/src/components/atoms/pager-control/pager-control.test.tsx +0 -130
  47. package/src/components/atoms/popover/index.ts +0 -30
  48. package/src/components/atoms/popover/popover.stories.tsx +0 -531
  49. package/src/components/atoms/popover/popover.test.tsx +0 -486
  50. package/src/components/atoms/select/index.ts +0 -18
  51. package/src/components/atoms/select/select.stories.tsx +0 -455
  52. package/src/components/atoms/tooltip/index.ts +0 -24
  53. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -348
  54. package/src/components/atoms/tooltip/tooltip.test.tsx +0 -363
  55. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +0 -73
  56. package/src/components/dev-tools/dev-toolbar/index.ts +0 -1
  57. package/src/components/dev-tools/grid-overlay/index.ts +0 -1
  58. package/src/components/dev-tools/index.ts +0 -2
  59. package/src/components/foundation/typography/typography.stories.tsx +0 -401
  60. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
  61. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
  62. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
  63. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
  64. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
  65. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
  66. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
  67. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
  68. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
  69. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
  70. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
  71. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
  72. package/src/components/organisms/card/card.stories.tsx +0 -293
  73. package/src/components/organisms/card/card.test.tsx +0 -247
  74. package/src/components/organisms/card/card.visual.test.tsx +0 -197
  75. package/src/components/organisms/card/index.ts +0 -26
  76. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
  77. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
  78. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
  79. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
  80. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
  81. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
  82. package/src/components/organisms/navbar/index.ts +0 -18
  83. package/src/components/organisms/navbar/navbar.stories.tsx +0 -313
  84. package/src/components/organisms/navbar/navbar.test.tsx +0 -190
  85. package/src/components/organisms/navbar/navbar.visual.test.tsx +0 -85
  86. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-darwin.png +0 -0
  87. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
  88. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
  89. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
  90. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
  91. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
  92. package/src/components/organisms/us-gov-banner/index.ts +0 -5
  93. package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +0 -35
  94. package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +0 -107
  95. package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +0 -46
  96. package/src/components/sections/banner/banner.stories.tsx +0 -150
  97. package/src/components/sections/banner/banner.test.tsx +0 -185
  98. package/src/components/sections/banner/index.ts +0 -2
  99. package/src/components/sections/card-grid/card-grid.stories.tsx +0 -351
  100. package/src/components/sections/card-grid/index.ts +0 -1
  101. package/src/components/sections/faq-section/faq-section.stories.tsx +0 -453
  102. package/src/components/sections/faq-section/index.ts +0 -2
  103. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
  104. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
  105. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
  106. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
  107. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
  108. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
  109. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
  110. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
  111. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
  112. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
  113. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
  114. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
  115. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
  116. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
  117. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
  118. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
  119. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
  120. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
  121. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
  122. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
  123. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
  124. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
  125. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
  126. package/src/components/sections/hero/hero.stories.tsx +0 -397
  127. package/src/components/sections/hero/hero.test.tsx +0 -138
  128. package/src/components/sections/hero/hero.visual.test.tsx +0 -140
  129. package/src/components/sections/hero/index.ts +0 -23
  130. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
  131. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
  132. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
  133. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
  134. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
  135. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
  136. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
  137. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
  138. package/src/components/sections/prose/index.ts +0 -6
  139. package/src/components/sections/prose/prose.stories.tsx +0 -144
  140. package/src/components/sections/prose/prose.test.tsx +0 -178
  141. package/src/components/sections/prose/prose.visual.test.tsx +0 -105
  142. package/src/components/sections/quote-block/index.ts +0 -5
  143. package/src/components/sections/river/index.ts +0 -1
  144. package/src/components/sections/river/river.stories.tsx +0 -237
  145. package/src/components/sections/river/river.test.tsx +0 -268
  146. package/src/components/sections/tout/index.ts +0 -1
  147. package/src/components/sections/tout/tout.stories.tsx +0 -171
  148. package/src/components/sections/tout/tout.test.tsx +0 -242
  149. package/src/components/sections/two-column-section/index.ts +0 -5
  150. package/src/components/sections/two-column-section/two-column-section.stories.tsx +0 -285
  151. package/src/components/shared/index.ts +0 -5
  152. package/src/index.ts +0 -293
  153. package/src/main.tsx +0 -13
  154. package/src/stories/grid-system.stories.tsx +0 -309
  155. package/src/stories/introduction.mdx +0 -128
  156. package/src/stories/theme-provider.stories.tsx +0 -349
  157. package/src/stories/token-showcase.stories.tsx +0 -73
  158. package/src/stories/token-showcase.tsx +0 -777
  159. package/src/styles.css +0 -14
  160. package/src/tests/token-resolution.test.tsx +0 -298
  161. package/src/theme/theme-provider.test.tsx +0 -270
@@ -1,167 +0,0 @@
1
- import { describe, expect, test, vi } from "vitest";
2
- import { page, userEvent } from "vitest/browser";
3
- import { render } from "vitest-browser-react";
4
- import { Input } from "./input";
5
-
6
- describe("Input", () => {
7
- describe("Accessibility", () => {
8
- test("has correct textbox role", async () => {
9
- render(<Input placeholder="Enter text" />);
10
- await expect.element(page.getByRole("textbox")).toBeInTheDocument();
11
- });
12
-
13
- test("is focusable via keyboard", async () => {
14
- render(<Input placeholder="Focusable" />);
15
- await userEvent.keyboard("{Tab}");
16
- await expect.element(page.getByRole("textbox")).toHaveFocus();
17
- });
18
-
19
- test("disabled input has disabled attribute", async () => {
20
- render(<Input disabled placeholder="Disabled" />);
21
- await expect.element(page.getByRole("textbox")).toBeDisabled();
22
- });
23
-
24
- test("disabled input is not focusable", async () => {
25
- render(
26
- <>
27
- <Input disabled placeholder="Disabled" />
28
- <Input placeholder="After" />
29
- </>,
30
- );
31
- await userEvent.keyboard("{Tab}");
32
- // Focus should skip the disabled input and go to the next one
33
- await expect.element(page.getByPlaceholder("After")).toHaveFocus();
34
- });
35
-
36
- test("input with aria-label has accessible name", async () => {
37
- render(<Input aria-label="Search query" />);
38
- await expect
39
- .element(page.getByRole("textbox", { name: "Search query" }))
40
- .toBeInTheDocument();
41
- });
42
-
43
- test("error state sets aria-invalid", async () => {
44
- render(<Input error placeholder="Error input" />);
45
- await expect
46
- .element(page.getByRole("textbox"))
47
- .toHaveAttribute("aria-invalid", "true");
48
- });
49
-
50
- test("non-error state does not have aria-invalid", async () => {
51
- render(<Input placeholder="Normal input" />);
52
- const input = page.getByRole("textbox");
53
- await expect.element(input).not.toHaveAttribute("aria-invalid");
54
- });
55
- });
56
-
57
- describe("Interactions", () => {
58
- test("accepts text input", async () => {
59
- render(<Input placeholder="Type here" />);
60
- const input = page.getByRole("textbox");
61
- await input.fill("Hello World");
62
- await expect.element(input).toHaveValue("Hello World");
63
- });
64
-
65
- test("calls onChange when text is entered", async () => {
66
- const handleChange = vi.fn();
67
- render(<Input onChange={handleChange} placeholder="Type here" />);
68
- await page.getByRole("textbox").fill("Test");
69
- expect(handleChange).toHaveBeenCalled();
70
- });
71
-
72
- test("calls onFocus when focused", async () => {
73
- const handleFocus = vi.fn();
74
- render(<Input onFocus={handleFocus} placeholder="Focus me" />);
75
- await page.getByRole("textbox").click();
76
- expect(handleFocus).toHaveBeenCalledOnce();
77
- });
78
-
79
- test("calls onBlur when focus is lost", async () => {
80
- const handleBlur = vi.fn();
81
- render(
82
- <>
83
- <Input onBlur={handleBlur} placeholder="Blur me" />
84
- <button type="button">Other</button>
85
- </>,
86
- );
87
- await page.getByRole("textbox").click();
88
- await page.getByRole("button").click();
89
- expect(handleBlur).toHaveBeenCalledOnce();
90
- });
91
-
92
- test("respects maxLength attribute", async () => {
93
- render(<Input maxLength={5} placeholder="Max 5 chars" />);
94
- const input = page.getByRole("textbox");
95
- await input.fill("1234567890");
96
- await expect.element(input).toHaveValue("12345");
97
- });
98
-
99
- test("respects readonly attribute", async () => {
100
- render(<Input readOnly defaultValue="Read only" />);
101
- const input = page.getByRole("textbox");
102
- await expect.element(input).toHaveAttribute("readonly", "");
103
- await expect.element(input).toHaveValue("Read only");
104
- });
105
- });
106
-
107
- describe("Variants", () => {
108
- test("applies default size", async () => {
109
- render(<Input placeholder="Default" />);
110
- const input = page.getByRole("textbox");
111
- await expect.element(input).toHaveAttribute("data-size", "default");
112
- });
113
-
114
- test("applies small size", async () => {
115
- render(<Input size="sm" placeholder="Small" />);
116
- const input = page.getByRole("textbox");
117
- await expect.element(input).toHaveAttribute("data-size", "sm");
118
- });
119
-
120
- test("applies large size", async () => {
121
- render(<Input size="lg" placeholder="Large" />);
122
- const input = page.getByRole("textbox");
123
- await expect.element(input).toHaveAttribute("data-size", "lg");
124
- });
125
-
126
- test("applies error state", async () => {
127
- render(<Input error placeholder="Error" />);
128
- const input = page.getByRole("textbox");
129
- await expect.element(input).toHaveAttribute("data-error", "true");
130
- });
131
- });
132
-
133
- describe("Input Types", () => {
134
- test("renders email type", async () => {
135
- render(<Input type="email" placeholder="Email" />);
136
- await expect
137
- .element(page.getByRole("textbox"))
138
- .toHaveAttribute("type", "email");
139
- });
140
-
141
- test("renders password type", async () => {
142
- render(<Input type="password" placeholder="Password" />);
143
- // Password inputs don't have textbox role
144
- const input = page.getByPlaceholder("Password");
145
- await expect.element(input).toHaveAttribute("type", "password");
146
- });
147
-
148
- test("renders number type", async () => {
149
- render(<Input type="number" placeholder="Number" />);
150
- const input = page.getByRole("spinbutton");
151
- await expect.element(input).toHaveAttribute("type", "number");
152
- });
153
-
154
- test("renders search type", async () => {
155
- render(<Input type="search" placeholder="Search" />);
156
- const input = page.getByRole("searchbox");
157
- await expect.element(input).toHaveAttribute("type", "search");
158
- });
159
-
160
- test("defaults to text type", async () => {
161
- render(<Input placeholder="Default type" />);
162
- await expect
163
- .element(page.getByRole("textbox"))
164
- .toHaveAttribute("type", "text");
165
- });
166
- });
167
- });
@@ -1 +0,0 @@
1
- export { NdstudioFooter, type NdstudioFooterProps } from "./ndstudio-footer";
@@ -1,5 +0,0 @@
1
- export {
2
- PagerControl,
3
- type PagerControlProps,
4
- pagerControlVariants,
5
- } from "./pager-control";
@@ -1,207 +0,0 @@
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-24">
134
- <PagerControl
135
- count={4}
136
- activeIndex={activeIndex}
137
- onChange={setActiveIndex}
138
- autoPlay={false}
139
- />
140
- <div className="flex gap-10">
141
- <button
142
- type="button"
143
- onClick={() => setActiveIndex((prev) => Math.max(0, prev - 1))}
144
- className="rounded bg-gray-200 px-12 py-6"
145
- >
146
- Previous
147
- </button>
148
- <span className="px-12 py-6">Page {activeIndex + 1} of 4</span>
149
- <button
150
- type="button"
151
- onClick={() => setActiveIndex((prev) => Math.min(3, prev + 1))}
152
- className="rounded bg-gray-200 px-12 py-6"
153
- >
154
- Next
155
- </button>
156
- </div>
157
- </div>
158
- );
159
- };
160
-
161
- // =============================================================================
162
- // Integration Example
163
- // =============================================================================
164
-
165
- export const WithCarousel = () => {
166
- const [activeIndex, setActiveIndex] = React.useState(0);
167
- const slides = [
168
- { id: 1, color: "bg-blue-100", title: "Slide 1" },
169
- { id: 2, color: "bg-green-100", title: "Slide 2" },
170
- { id: 3, color: "bg-yellow-100", title: "Slide 3" },
171
- { id: 4, color: "bg-purple-100", title: "Slide 4" },
172
- ];
173
-
174
- return (
175
- <div className="flex w-[400px] flex-col gap-16">
176
- <div className="relative h-[200px] overflow-hidden rounded-radius-12">
177
- {slides.map((slide, index) => (
178
- <div
179
- key={slide.id}
180
- className={cn(
181
- "absolute inset-0 flex items-center justify-center transition-transform duration-500",
182
- slide.color,
183
- )}
184
- style={{
185
- transform: `translateX(${(index - activeIndex) * 100}%)`,
186
- }}
187
- >
188
- <span className="text-xl font-semibold">{slide.title}</span>
189
- </div>
190
- ))}
191
- </div>
192
- <div className="flex justify-center">
193
- <PagerControl
194
- count={slides.length}
195
- activeIndex={activeIndex}
196
- onChange={setActiveIndex}
197
- duration={5000}
198
- />
199
- </div>
200
- </div>
201
- );
202
- };
203
-
204
- // Helper for carousel story
205
- function cn(...classes: (string | boolean | undefined)[]) {
206
- return classes.filter(Boolean).join(" ");
207
- }
@@ -1,130 +0,0 @@
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
- });
@@ -1,30 +0,0 @@
1
- export type {
2
- PopoverArrowProps,
3
- PopoverBackdropProps,
4
- PopoverCloseProps,
5
- PopoverDescriptionProps,
6
- PopoverPopupProps,
7
- PopoverPortalProps,
8
- PopoverPositionerProps,
9
- PopoverProps,
10
- PopoverRootProps,
11
- PopoverTitleProps,
12
- PopoverTriggerProps,
13
- } from "./popover";
14
-
15
- export {
16
- Popover,
17
- PopoverArrow,
18
- PopoverBackdrop,
19
- PopoverClose,
20
- PopoverDescription,
21
- PopoverParts,
22
- PopoverPopup,
23
- PopoverPortal,
24
- PopoverPositioner,
25
- PopoverRoot,
26
- PopoverTitle,
27
- PopoverTrigger,
28
- popoverArrowVariants,
29
- popoverPopupVariants,
30
- } from "./popover";