@commercetools/nimbus 0.0.2 → 0.0.3

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 (302) hide show
  1. package/dist/index.d.ts +1412 -0
  2. package/dist/index.js +11183 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.umd.cjs +27 -0
  5. package/dist/index.umd.cjs.map +1 -0
  6. package/package.json +63 -39
  7. package/.storybook/apca-check/index.ts +0 -150
  8. package/.storybook/main.ts +0 -64
  9. package/.storybook/preview.tsx +0 -92
  10. package/.storybook/vitest.setup.ts +0 -13
  11. package/docs/architecture-decisions/adr-0001-consumer-component-apis.md +0 -177
  12. package/docs/architecture-decisions/adr-0002-compound-component-extraction.md +0 -82
  13. package/src/components/accordion/accordion-context.tsx +0 -17
  14. package/src/components/accordion/accordion.mdx +0 -172
  15. package/src/components/accordion/accordion.recipe.tsx +0 -98
  16. package/src/components/accordion/accordion.slots.tsx +0 -39
  17. package/src/components/accordion/accordion.stories.tsx +0 -188
  18. package/src/components/accordion/accordion.tsx +0 -16
  19. package/src/components/accordion/accordion.types.tsx +0 -54
  20. package/src/components/accordion/components/accordion-content.tsx +0 -28
  21. package/src/components/accordion/components/accordion-group.tsx +0 -27
  22. package/src/components/accordion/components/accordion-header.tsx +0 -63
  23. package/src/components/accordion/components/accordion-item.tsx +0 -87
  24. package/src/components/accordion/index.ts +0 -2
  25. package/src/components/alert/alert.mdx +0 -92
  26. package/src/components/alert/alert.recipe.tsx +0 -65
  27. package/src/components/alert/alert.slots.tsx +0 -46
  28. package/src/components/alert/alert.stories.tsx +0 -308
  29. package/src/components/alert/alert.tsx +0 -18
  30. package/src/components/alert/alert.types.tsx +0 -70
  31. package/src/components/alert/components/alert.actions.tsx +0 -27
  32. package/src/components/alert/components/alert.description.tsx +0 -27
  33. package/src/components/alert/components/alert.dismiss-button.tsx +0 -41
  34. package/src/components/alert/components/alert.root.tsx +0 -92
  35. package/src/components/alert/components/alert.title.tsx +0 -29
  36. package/src/components/alert/index.ts +0 -2
  37. package/src/components/avatar/avatar.mdx +0 -80
  38. package/src/components/avatar/avatar.recipe.tsx +0 -36
  39. package/src/components/avatar/avatar.slots.tsx +0 -16
  40. package/src/components/avatar/avatar.stories.tsx +0 -136
  41. package/src/components/avatar/avatar.tsx +0 -34
  42. package/src/components/avatar/avatar.types.ts +0 -33
  43. package/src/components/avatar/index.ts +0 -2
  44. package/src/components/badge/badge.mdx +0 -91
  45. package/src/components/badge/badge.recipe.tsx +0 -72
  46. package/src/components/badge/badge.slots.tsx +0 -8
  47. package/src/components/badge/badge.stories.tsx +0 -124
  48. package/src/components/badge/badge.tsx +0 -35
  49. package/src/components/badge/badge.types.tsx +0 -40
  50. package/src/components/badge/index.ts +0 -2
  51. package/src/components/bleed/bleed.tsx +0 -1
  52. package/src/components/bleed/index.ts +0 -1
  53. package/src/components/box/box.mdx +0 -115
  54. package/src/components/box/box.stories.tsx +0 -71
  55. package/src/components/box/box.tsx +0 -11
  56. package/src/components/box/index.ts +0 -1
  57. package/src/components/button/button.mdx +0 -169
  58. package/src/components/button/button.recipe.ts +0 -185
  59. package/src/components/button/button.slots.tsx +0 -45
  60. package/src/components/button/button.stories.tsx +0 -369
  61. package/src/components/button/button.tsx +0 -37
  62. package/src/components/button/button.types.ts +0 -14
  63. package/src/components/button/index.ts +0 -2
  64. package/src/components/card/card.mdx +0 -92
  65. package/src/components/card/card.recipe.tsx +0 -71
  66. package/src/components/card/card.slots.tsx +0 -50
  67. package/src/components/card/card.stories.tsx +0 -175
  68. package/src/components/card/card.tsx +0 -9
  69. package/src/components/card/card.types.ts +0 -22
  70. package/src/components/card/components/card.content.tsx +0 -29
  71. package/src/components/card/components/card.header.tsx +0 -28
  72. package/src/components/card/components/card.root.tsx +0 -62
  73. package/src/components/card/index.ts +0 -2
  74. package/src/components/checkbox/checkbox.mdx +0 -78
  75. package/src/components/checkbox/checkbox.recipe.tsx +0 -116
  76. package/src/components/checkbox/checkbox.slots.tsx +0 -33
  77. package/src/components/checkbox/checkbox.stories.tsx +0 -200
  78. package/src/components/checkbox/checkbox.tsx +0 -77
  79. package/src/components/checkbox/checkbox.types.tsx +0 -22
  80. package/src/components/checkbox/index.ts +0 -2
  81. package/src/components/code/code.mdx +0 -17
  82. package/src/components/code/code.recipe.ts +0 -63
  83. package/src/components/code/code.tsx +0 -1
  84. package/src/components/code/index.ts +0 -1
  85. package/src/components/dialog/dialog.mdx +0 -20
  86. package/src/components/dialog/dialog.recipe.ts +0 -254
  87. package/src/components/dialog/dialog.tsx +0 -61
  88. package/src/components/dialog/index.ts +0 -1
  89. package/src/components/em/em.mdx +0 -17
  90. package/src/components/em/em.tsx +0 -1
  91. package/src/components/em/index.ts +0 -1
  92. package/src/components/flex/flex.mdx +0 -41
  93. package/src/components/flex/flex.tsx +0 -1
  94. package/src/components/flex/index.ts +0 -1
  95. package/src/components/grid/grid.mdx +0 -156
  96. package/src/components/grid/grid.stories.tsx +0 -151
  97. package/src/components/grid/grid.tsx +0 -29
  98. package/src/components/grid/index.ts +0 -1
  99. package/src/components/heading/heading.mdx +0 -23
  100. package/src/components/heading/heading.recipe.ts +0 -49
  101. package/src/components/heading/heading.tsx +0 -1
  102. package/src/components/heading/index.ts +0 -1
  103. package/src/components/highlight/highlight.mdx +0 -18
  104. package/src/components/highlight/highlight.tsx +0 -1
  105. package/src/components/highlight/index.ts +0 -1
  106. package/src/components/icon-button/icon-button.mdx +0 -98
  107. package/src/components/icon-button/icon-button.stories.tsx +0 -188
  108. package/src/components/icon-button/icon-button.tsx +0 -21
  109. package/src/components/icon-button/icon-button.types.tsx +0 -10
  110. package/src/components/icon-button/index.ts +0 -2
  111. package/src/components/index.ts +0 -33
  112. package/src/components/input/index.ts +0 -1
  113. package/src/components/input/input.mdx +0 -20
  114. package/src/components/input/input.recipe.ts +0 -95
  115. package/src/components/input/input.tsx +0 -1
  116. package/src/components/input-group/index.ts +0 -1
  117. package/src/components/input-group/input-group.mdx +0 -20
  118. package/src/components/input-group/input-group.tsx +0 -44
  119. package/src/components/kbd/index.ts +0 -1
  120. package/src/components/kbd/kbd.mdx +0 -18
  121. package/src/components/kbd/kbd.recipe.ts +0 -57
  122. package/src/components/kbd/kbd.tsx +0 -1
  123. package/src/components/link/index.ts +0 -2
  124. package/src/components/link/link.mdx +0 -77
  125. package/src/components/link/link.recipe.ts +0 -52
  126. package/src/components/link/link.slots.tsx +0 -29
  127. package/src/components/link/link.stories.tsx +0 -204
  128. package/src/components/link/link.tsx +0 -38
  129. package/src/components/link/link.types.tsx +0 -26
  130. package/src/components/list/index.ts +0 -1
  131. package/src/components/list/list.mdx +0 -18
  132. package/src/components/list/list.recipe.ts +0 -68
  133. package/src/components/list/list.tsx +0 -9
  134. package/src/components/loading-spinner/index.ts +0 -2
  135. package/src/components/loading-spinner/loading-spinner.mdx +0 -92
  136. package/src/components/loading-spinner/loading-spinner.recipe.tsx +0 -70
  137. package/src/components/loading-spinner/loading-spinner.slots.tsx +0 -38
  138. package/src/components/loading-spinner/loading-spinner.stories.tsx +0 -97
  139. package/src/components/loading-spinner/loading-spinner.tsx +0 -50
  140. package/src/components/loading-spinner/loading-spinner.types.tsx +0 -21
  141. package/src/components/nimbus-provider/color-mode.tsx +0 -32
  142. package/src/components/nimbus-provider/index.ts +0 -2
  143. package/src/components/nimbus-provider/nimbus-provider.mdx +0 -21
  144. package/src/components/nimbus-provider/nimbus-provider.tsx +0 -51
  145. package/src/components/select/components/select.clear-button.tsx +0 -31
  146. package/src/components/select/components/select.option-group.tsx +0 -48
  147. package/src/components/select/components/select.option.tsx +0 -21
  148. package/src/components/select/components/select.options.tsx +0 -23
  149. package/src/components/select/components/select.root.tsx +0 -81
  150. package/src/components/select/index.ts +0 -2
  151. package/src/components/select/select.mdx +0 -170
  152. package/src/components/select/select.recipe.tsx +0 -216
  153. package/src/components/select/select.slots.tsx +0 -58
  154. package/src/components/select/select.stories.tsx +0 -841
  155. package/src/components/select/select.tsx +0 -18
  156. package/src/components/select/select.types.tsx +0 -37
  157. package/src/components/simple-grid/index.ts +0 -1
  158. package/src/components/simple-grid/simple-grid.mdx +0 -133
  159. package/src/components/simple-grid/simple-grid.stories.tsx +0 -143
  160. package/src/components/simple-grid/simple-grid.tsx +0 -31
  161. package/src/components/stack/index.ts +0 -1
  162. package/src/components/stack/stack.mdx +0 -88
  163. package/src/components/stack/stack.stories.tsx +0 -82
  164. package/src/components/stack/stack.tsx +0 -15
  165. package/src/components/table/index.ts +0 -1
  166. package/src/components/table/table.mdx +0 -23
  167. package/src/components/table/table.recipe.ts +0 -170
  168. package/src/components/table/table.tsx +0 -43
  169. package/src/components/text/index.ts +0 -1
  170. package/src/components/text/text.mdx +0 -244
  171. package/src/components/text/text.tsx +0 -23
  172. package/src/components/text-input/index.ts +0 -2
  173. package/src/components/text-input/text-input.mdx +0 -118
  174. package/src/components/text-input/text-input.recipe.tsx +0 -68
  175. package/src/components/text-input/text-input.slots.tsx +0 -24
  176. package/src/components/text-input/text-input.stories.tsx +0 -282
  177. package/src/components/text-input/text-input.tsx +0 -39
  178. package/src/components/text-input/text-input.types.ts +0 -7
  179. package/src/components/toggle-button-group/components/toggle-button-group.button.tsx +0 -14
  180. package/src/components/toggle-button-group/components/toggle-button-group.root.tsx +0 -15
  181. package/src/components/toggle-button-group/index.ts +0 -2
  182. package/src/components/toggle-button-group/toggle-button-group.mdx +0 -94
  183. package/src/components/toggle-button-group/toggle-button-group.recipe.tsx +0 -64
  184. package/src/components/toggle-button-group/toggle-button-group.slots.tsx +0 -26
  185. package/src/components/toggle-button-group/toggle-button-group.stories.tsx +0 -311
  186. package/src/components/toggle-button-group/toggle-button-group.tsx +0 -12
  187. package/src/components/toggle-button-group/toggle-button-group.types.tsx +0 -62
  188. package/src/components/tooltip/index.ts +0 -4
  189. package/src/components/tooltip/make-element-focusable.stories.tsx +0 -56
  190. package/src/components/tooltip/make-element-focusable.tsx +0 -57
  191. package/src/components/tooltip/tooltip-trigger.stories.tsx +0 -157
  192. package/src/components/tooltip/tooltip-trigger.tsx +0 -15
  193. package/src/components/tooltip/tooltip.mdx +0 -48
  194. package/src/components/tooltip/tooltip.recipe.ts +0 -26
  195. package/src/components/tooltip/tooltip.slots.ts +0 -35
  196. package/src/components/tooltip/tooltip.stories.tsx +0 -139
  197. package/src/components/tooltip/tooltip.tsx +0 -31
  198. package/src/components/tooltip/tooltip.types.ts +0 -44
  199. package/src/components/visually-hidden/index.ts +0 -1
  200. package/src/components/visually-hidden/visually-hidden.mdx +0 -61
  201. package/src/components/visually-hidden/visually-hidden.stories.tsx +0 -124
  202. package/src/components/visually-hidden/visually-hidden.tsx +0 -18
  203. package/src/docs/accessibility.mdx +0 -21
  204. package/src/docs/background.mdx +0 -154
  205. package/src/docs/border.mdx +0 -226
  206. package/src/docs/changelog.mdx +0 -17
  207. package/src/docs/components-layout.mdx +0 -22
  208. package/src/docs/components.mdx +0 -17
  209. package/src/docs/data-display.mdx +0 -23
  210. package/src/docs/display.mdx +0 -55
  211. package/src/docs/effects.mdx +0 -73
  212. package/src/docs/feedback.mdx +0 -22
  213. package/src/docs/filters.mdx +0 -268
  214. package/src/docs/flex-and-grid.mdx +0 -445
  215. package/src/docs/forms.mdx +0 -22
  216. package/src/docs/generated/index.mdx +0 -16
  217. package/src/docs/getting-started.mdx +0 -17
  218. package/src/docs/home.mdx +0 -56
  219. package/src/docs/hooks.mdx +0 -16
  220. package/src/docs/inputs.mdx +0 -21
  221. package/src/docs/installation.mdx +0 -60
  222. package/src/docs/interactivity.mdx +0 -278
  223. package/src/docs/known-issues.mdx +0 -19
  224. package/src/docs/layout.mdx +0 -301
  225. package/src/docs/list.mdx +0 -82
  226. package/src/docs/markdown.mdx +0 -234
  227. package/src/docs/media.mdx +0 -22
  228. package/src/docs/naivgation.mdx +0 -22
  229. package/src/docs/playground.mdx +0 -16
  230. package/src/docs/rfcs-component-structure-rfcs.mdx +0 -17
  231. package/src/docs/rfcs-component-structure.mdx +0 -74
  232. package/src/docs/rfcs-hook-structure.mdx +0 -59
  233. package/src/docs/sizing.mdx +0 -210
  234. package/src/docs/spacing.mdx +0 -193
  235. package/src/docs/style-props-typography.mdx +0 -373
  236. package/src/docs/style-props.mdx +0 -15
  237. package/src/docs/svg.mdx +0 -58
  238. package/src/docs/tables.mdx +0 -95
  239. package/src/docs/toc.mdx +0 -39
  240. package/src/docs/tokens/animations.mdx +0 -68
  241. package/src/docs/tokens/aspect-ratios.mdx +0 -21
  242. package/src/docs/tokens/blurs.mdx +0 -20
  243. package/src/docs/tokens/borders.mdx +0 -25
  244. package/src/docs/tokens/breakpoints.mdx +0 -35
  245. package/src/docs/tokens/colors.mdx +0 -86
  246. package/src/docs/tokens/cursors.mdx +0 -21
  247. package/src/docs/tokens/radii.mdx +0 -23
  248. package/src/docs/tokens/shadows.mdx +0 -21
  249. package/src/docs/tokens/sizes.mdx +0 -54
  250. package/src/docs/tokens/spacing.mdx +0 -35
  251. package/src/docs/tokens/typography.mdx +0 -61
  252. package/src/docs/tokens/z-indices.mdx +0 -23
  253. package/src/docs/tokens-other.mdx +0 -17
  254. package/src/docs/tokens.mdx +0 -16
  255. package/src/docs/transforms.mdx +0 -150
  256. package/src/docs/transitions.mdx +0 -164
  257. package/src/docs/typography.mdx +0 -17
  258. package/src/docs/utilities.mdx +0 -17
  259. package/src/hooks/index.ts +0 -2
  260. package/src/hooks/use-copy-to-clipboard/use-copy-to-clipboard.mdx +0 -54
  261. package/src/hooks/use-copy-to-clipboard/use-copy-to-clipboard.ts +0 -1
  262. package/src/hooks/use-hotkeys/use-hotkeys.mdx +0 -48
  263. package/src/hooks/use-hotkeys/use-hotkeys.stories.tsx +0 -69
  264. package/src/hooks/use-hotkeys/use-hotkeys.ts +0 -1
  265. package/src/index.ts +0 -3
  266. package/src/test/utils.tsx +0 -20
  267. package/src/theme/animation-styles.ts +0 -52
  268. package/src/theme/breakpoints.ts +0 -32
  269. package/src/theme/global-css.ts +0 -53
  270. package/src/theme/index.ts +0 -35
  271. package/src/theme/keyframes.ts +0 -192
  272. package/src/theme/layer-styles.ts +0 -12
  273. package/src/theme/recipes/index.ts +0 -21
  274. package/src/theme/semantic-tokens/colors.ts +0 -55
  275. package/src/theme/semantic-tokens/index.ts +0 -9
  276. package/src/theme/semantic-tokens/radii.ts +0 -3
  277. package/src/theme/semantic-tokens/shadows.ts +0 -4
  278. package/src/theme/slot-recipes/index.ts +0 -15
  279. package/src/theme/text-styles.ts +0 -8
  280. package/src/theme/tokens/animations.ts +0 -4
  281. package/src/theme/tokens/aspect-ratios.ts +0 -5
  282. package/src/theme/tokens/blurs.ts +0 -5
  283. package/src/theme/tokens/borders.ts +0 -4
  284. package/src/theme/tokens/colors.ts +0 -8
  285. package/src/theme/tokens/cursor.ts +0 -4
  286. package/src/theme/tokens/durations.ts +0 -4
  287. package/src/theme/tokens/easings.ts +0 -4
  288. package/src/theme/tokens/font-sizes.ts +0 -4
  289. package/src/theme/tokens/font-weights.ts +0 -4
  290. package/src/theme/tokens/fonts.ts +0 -4
  291. package/src/theme/tokens/index.ts +0 -57
  292. package/src/theme/tokens/letter-spacings.ts +0 -24
  293. package/src/theme/tokens/line-heights.ts +0 -4
  294. package/src/theme/tokens/radii.ts +0 -4
  295. package/src/theme/tokens/sizes.ts +0 -120
  296. package/src/theme/tokens/spacing.ts +0 -4
  297. package/src/theme/tokens/z-index.ts +0 -4
  298. package/src/utils/extractStyleProps.ts +0 -26
  299. package/src/utils/fixedForwardRef.ts +0 -17
  300. package/tsconfig.json +0 -38
  301. package/vite.config.ts +0 -54
  302. package/vitest.config.ts +0 -50
@@ -1,311 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import { ToggleButtonGroup } from "./toggle-button-group";
3
- import { Stack } from "../stack";
4
- import { userEvent, within, expect, fn } from "@storybook/test";
5
- import { SentimentSatisfied as DemoIcon } from "@commercetools/nimbus-icons";
6
-
7
- /**
8
- * Storybook metadata configuration
9
- */
10
- const meta: Meta<typeof ToggleButtonGroup.Root> = {
11
- title: "components/ToggleButtonGroup",
12
- component: ToggleButtonGroup.Root,
13
- };
14
-
15
- export default meta;
16
-
17
- /**
18
- * Story type for TypeScript support
19
- */
20
- type Story = StoryObj<typeof ToggleButtonGroup.Root>;
21
-
22
- const defaultChildren = (
23
- <>
24
- <ToggleButtonGroup.Button id="left">Left</ToggleButtonGroup.Button>
25
- <ToggleButtonGroup.Button id="center">Center</ToggleButtonGroup.Button>
26
- <ToggleButtonGroup.Button id="right">Right</ToggleButtonGroup.Button>
27
- </>
28
- );
29
-
30
- type ToggleButtonGroupSize = "md" | "xs"; // Replace with actual derived type if possible
31
- type ToggleButtonGroupTone = "primary" | "critical" | "neutral"; // Replace with actual derived type
32
-
33
- const sizes: ToggleButtonGroupSize[] = ["md", "xs"];
34
- const tones: ToggleButtonGroupTone[] = ["primary", "critical", "neutral"];
35
-
36
- /**
37
- * Base story
38
- *
39
- * Demonstrates the most basic implementation with interaction tests.
40
- */
41
- export const Base: Story = {
42
- args: {
43
- size: "md",
44
- tone: "primary",
45
- children: defaultChildren,
46
- onSelectionChange: fn(),
47
- "aria-label": "Test Button Group",
48
- },
49
- play: async ({ canvasElement, args, step }) => {
50
- const canvas = within(canvasElement);
51
- // Get the group by its accessible name
52
- const group = canvas.getByRole("radiogroup", {
53
- name: /Test Button Group/i,
54
- });
55
- const buttons = within(group).getAllByRole("radio");
56
- const [leftButton, centerButton, rightButton] = buttons;
57
- const onSelectionChange = args.onSelectionChange as ReturnType<typeof fn>;
58
-
59
- await step("Initial Rendering", async () => {
60
- await expect(group).toBeInTheDocument();
61
- await expect(buttons).toHaveLength(3);
62
- // RAC ToggleButton uses aria-checked for selection state
63
- await expect(leftButton).toHaveAttribute("aria-checked", "false");
64
- await expect(centerButton).toHaveAttribute("aria-checked", "false");
65
- await expect(rightButton).toHaveAttribute("aria-checked", "false");
66
- await expect(onSelectionChange).not.toHaveBeenCalled();
67
- });
68
-
69
- await step(
70
- "Keyboard Navigation (Tab into group, Arrows between buttons)",
71
- async () => {
72
- await userEvent.tab(); // Tab into the group (focuses the first button)
73
- await expect(leftButton).toHaveFocus();
74
-
75
- await userEvent.keyboard("{ArrowRight}");
76
- await expect(centerButton).toHaveFocus();
77
-
78
- await userEvent.keyboard("{ArrowRight}");
79
- await expect(rightButton).toHaveFocus();
80
-
81
- await userEvent.keyboard("{ArrowLeft}");
82
- await expect(centerButton).toHaveFocus();
83
- }
84
- );
85
-
86
- await step("Keyboard Selection (Space)", async () => {
87
- // centerButton currently has focus
88
- await userEvent.keyboard("{ }"); // Press Space
89
- await expect(centerButton).toHaveAttribute("aria-checked", "true");
90
- await expect(leftButton).toHaveAttribute("aria-checked", "false"); // Should deselect others in single-select mode
91
- await expect(onSelectionChange).toHaveBeenCalledTimes(1);
92
-
93
- // Select another button
94
- await userEvent.keyboard("{ArrowLeft}"); // Move focus to leftButton
95
- await expect(leftButton).toHaveFocus();
96
- await userEvent.keyboard("{ }"); // Press Space
97
- await expect(leftButton).toHaveAttribute("aria-checked", "true");
98
- await expect(centerButton).toHaveAttribute("aria-checked", "false");
99
- await expect(onSelectionChange).toHaveBeenCalledTimes(2);
100
-
101
- // Deselect by pressing again (common toggle behavior)
102
- await userEvent.keyboard("{ }"); // Press Space
103
- await expect(leftButton).toHaveAttribute("aria-checked", "false");
104
- await expect(onSelectionChange).toHaveBeenCalledTimes(3);
105
- });
106
-
107
- // Reset selection for next step
108
- await userEvent.click(leftButton); // Click to select 'left'
109
- onSelectionChange.mockClear(); // Clear mock history
110
-
111
- await step("Mouse Selection", async () => {
112
- await userEvent.click(rightButton);
113
- await expect(rightButton).toHaveAttribute("aria-checked", "true");
114
- await expect(leftButton).toHaveAttribute("aria-checked", "false");
115
- await expect(centerButton).toHaveAttribute("aria-checked", "false");
116
- await expect(onSelectionChange).toHaveBeenCalledTimes(1);
117
-
118
- // Click again to deselect
119
- await userEvent.click(rightButton);
120
- await expect(rightButton).toHaveAttribute("aria-checked", "false");
121
- await expect(onSelectionChange).toHaveBeenCalledTimes(2);
122
- });
123
- },
124
- };
125
-
126
- /**
127
- * Disabled Group Story
128
- *
129
- * Tests behavior when the entire group is disabled, with one button visually selected.
130
- */
131
- export const DisabledGroup: Story = {
132
- args: {
133
- ...Base.args, // Reuse args from Base
134
- isDisabled: true,
135
- selectedKeys: ["center"], // Set the middle button as initially selected
136
- onSelectionChange: fn(),
137
- "aria-label": "Disabled Test Group",
138
- },
139
- play: async ({ canvasElement, args, step }) => {
140
- const canvas = within(canvasElement);
141
- const group = canvas.getByRole("radiogroup", {
142
- name: /Disabled Test Group/i,
143
- });
144
- const buttons = within(group).getAllByRole("radio");
145
- const [leftButton, centerButton, rightButton] = buttons;
146
- const onSelectionChange = args.onSelectionChange as ReturnType<typeof fn>;
147
-
148
- await step("Initial Render shows default selection visually", async () => {
149
- // Check that the defaultValue is reflected in aria-checked state
150
- await expect(centerButton).toHaveAttribute("aria-checked", "true");
151
- await expect(leftButton).toHaveAttribute("aria-checked", "false");
152
- await expect(rightButton).toHaveAttribute("aria-checked", "false");
153
- });
154
-
155
- await step("Buttons are functionally disabled", async () => {
156
- await expect(leftButton).toBeDisabled();
157
- await expect(centerButton).toBeDisabled();
158
- await expect(rightButton).toBeDisabled();
159
- });
160
-
161
- await step("Cannot focus buttons via keyboard", async () => {
162
- // Try tabbing - focus should skip the disabled group
163
- await userEvent.tab();
164
- await expect(leftButton).not.toHaveFocus();
165
- await expect(centerButton).not.toHaveFocus();
166
- await expect(rightButton).not.toHaveFocus();
167
- });
168
-
169
- await step("Cannot change selection via click", async () => {
170
- // Click the selected button
171
- await userEvent.click(centerButton);
172
- // State should not change
173
- await expect(centerButton).toHaveAttribute("aria-checked", "true");
174
- await expect(onSelectionChange).not.toHaveBeenCalled();
175
-
176
- // Click a non-selected button
177
- await userEvent.click(leftButton);
178
- // State should not change
179
- await expect(leftButton).toHaveAttribute("aria-checked", "false");
180
- await expect(centerButton).toHaveAttribute("aria-checked", "true"); // Still selected
181
- await expect(onSelectionChange).not.toHaveBeenCalled();
182
- });
183
- },
184
- };
185
-
186
- /**
187
- * Default Selection Story
188
- *
189
- * Tests behavior when a value is pre-selected.
190
- */
191
- export const DefaultSelection: Story = {
192
- args: {
193
- ...Base.args, // Reuse args from Base
194
- selectedKeys: ["center"], // Set the middle button as initially selected
195
- onSelectionChange: fn(), // Ensure separate mock for this story
196
- "aria-label": "Default Selection Test Group",
197
- },
198
- play: async ({ canvasElement, args, step }) => {
199
- const canvas = within(canvasElement);
200
- const group = canvas.getByRole("radiogroup", {
201
- name: /Default Selection Test Group/i,
202
- });
203
- const buttons = within(group).getAllByRole("radio");
204
- const [leftButton, centerButton, rightButton] = buttons;
205
- const onSelectionChange = args.onSelectionChange as ReturnType<typeof fn>;
206
-
207
- await step("Initial Render shows default selection", async () => {
208
- await expect(centerButton).toHaveAttribute("aria-checked", "true");
209
- await expect(leftButton).toHaveAttribute("aria-checked", "false");
210
- await expect(rightButton).toHaveAttribute("aria-checked", "false");
211
- });
212
-
213
- await step("Can change selection via click", async () => {
214
- await userEvent.click(leftButton);
215
- // await expect(leftButton).toHaveAttribute("aria-checked", "true");
216
- // await expect(centerButton).toHaveAttribute("aria-checked", "false");
217
- await expect(onSelectionChange).toHaveBeenCalledTimes(1);
218
- await expect(onSelectionChange).toHaveBeenLastCalledWith(
219
- new Set(["left"])
220
- );
221
- });
222
- },
223
- };
224
-
225
- /**
226
- * Showcase Sizes - Minimal test focusing on rendering
227
- */
228
- export const Sizes: Story = {
229
- render: (args) => (
230
- <Stack direction="row" gap="400" alignItems="center">
231
- {sizes.map((size) => (
232
- <ToggleButtonGroup.Root
233
- key={size}
234
- {...args}
235
- size={size}
236
- aria-label={`Size ${size} Group`}
237
- >
238
- {defaultChildren}
239
- </ToggleButtonGroup.Root>
240
- ))}
241
- </Stack>
242
- ),
243
- args: {
244
- onSelectionChange: fn(), // Add mock even if not testing interaction heavily here
245
- },
246
- play: async ({ canvasElement, step }) => {
247
- const canvas = within(canvasElement);
248
- // Simple check: ensure both groups render
249
- await step("Render groups for each size", async () => {
250
- const groups = canvas.getAllByRole("radiogroup");
251
- await expect(groups).toHaveLength(sizes.length);
252
- // Optionally check a button exists in each
253
- await expect(
254
- within(groups[0]).getAllByRole("radio").length
255
- ).toBeGreaterThan(0);
256
- await expect(
257
- within(groups[1]).getAllByRole("radio").length
258
- ).toBeGreaterThan(0);
259
- });
260
- },
261
- };
262
-
263
- /**
264
- * Showcase Tones - Minimal test focusing on rendering
265
- */
266
- export const Tones: Story = {
267
- render: (args) => (
268
- <Stack>
269
- {tones.map((tone) => (
270
- <ToggleButtonGroup.Root
271
- key={tone}
272
- {...args}
273
- tone={tone}
274
- aria-label={`Tone ${tone} Group`}
275
- >
276
- <ToggleButtonGroup.Button id="left">
277
- <DemoIcon />
278
- </ToggleButtonGroup.Button>
279
- <ToggleButtonGroup.Button id="center">
280
- <DemoIcon />
281
- </ToggleButtonGroup.Button>
282
- <ToggleButtonGroup.Button id="right">
283
- <DemoIcon />
284
- </ToggleButtonGroup.Button>
285
- </ToggleButtonGroup.Root>
286
- ))}
287
- </Stack>
288
- ),
289
- args: {
290
- size: "md",
291
- onSelectionChange: fn(),
292
- },
293
- play: async ({ canvasElement, step }) => {
294
- const canvas = within(canvasElement);
295
- // Ensure all groups render
296
- await step("Render groups for each tone", async () => {
297
- const groups = canvas.getAllByRole("radiogroup");
298
- await expect(groups).toHaveLength(tones.length);
299
- // Optionally check a button exists in each
300
- await expect(
301
- within(groups[0]).getAllByRole("radio").length
302
- ).toBeGreaterThan(0);
303
- await expect(
304
- within(groups[1]).getAllByRole("radio").length
305
- ).toBeGreaterThan(0);
306
- await expect(
307
- within(groups[2]).getAllByRole("radio").length
308
- ).toBeGreaterThan(0);
309
- });
310
- },
311
- };
@@ -1,12 +0,0 @@
1
- import { ToggleButtonGroupRoot } from "./components/toggle-button-group.root";
2
- import { ToggleButtonGroupButton } from "./components/toggle-button-group.button";
3
-
4
- /**
5
- * ToggleButtonGroup
6
- * ============================================================
7
- * To group multiple `Button` components together, visually and logically, representing a set of related actions.
8
- */
9
- export const ToggleButtonGroup = {
10
- Root: ToggleButtonGroupRoot,
11
- Button: ToggleButtonGroupButton,
12
- };
@@ -1,62 +0,0 @@
1
- import type {
2
- HTMLChakraProps,
3
- RecipeProps,
4
- RecipeVariantProps,
5
- } from "@chakra-ui/react";
6
- import { buttonGroupRecipe } from "./toggle-button-group.recipe";
7
- import type {
8
- AriaToggleButtonGroupProps,
9
- AriaToggleButtonProps,
10
- } from "react-aria";
11
- import {
12
- ToggleButton as RacToggleButton,
13
- ToggleButtonGroup as RacToggleButtonGroup,
14
- } from "react-aria-components";
15
- import type {
16
- ForwardRefExoticComponent,
17
- PropsWithChildren,
18
- RefAttributes,
19
- } from "react";
20
-
21
- // ============================================================
22
- // Root Component (`<ToggleButtonGroup>`)
23
- // ============================================================
24
-
25
- /** Base Chakra styling props for the root `div` slot. */
26
- type ToggleButtonGroupRootSlotProps = HTMLChakraProps<
27
- "div",
28
- RecipeProps<"div">
29
- >;
30
-
31
- /** Combined props for the root element (Chakra styles + Aria behavior + Recipe variants). */
32
- type ToggleButtonGroupRootProps = ToggleButtonGroupRootSlotProps &
33
- AriaToggleButtonGroupProps &
34
- RecipeVariantProps<typeof buttonGroupRecipe>;
35
-
36
- /** Final external props for the `<ToggleButtonGroup>` component, including `children`. */
37
- export type ToggleButtonGroupProps =
38
- PropsWithChildren<ToggleButtonGroupRootProps>;
39
-
40
- /** Type signature for the main `ToggleButtonGroup` component (using `forwardRef`). */
41
- export type ToggleButtonGroupRootComponent = ForwardRefExoticComponent<
42
- ToggleButtonGroupProps & RefAttributes<typeof RacToggleButtonGroup>
43
- >;
44
-
45
- // ============================================================
46
- // Button Sub-Component (`<ToggleButtonGroup.Button>`)
47
- // ============================================================
48
-
49
- /** Base Chakra styling props for the `button` slot. */
50
- type ToggleButtonGroupButtonSlotProps = HTMLChakraProps<
51
- "button",
52
- RecipeProps<"button">
53
- >;
54
-
55
- /** Combined props for the button element (Chakra styles + Aria behavior). */
56
- export type ToggleButtonGroupButtonProps = ToggleButtonGroupButtonSlotProps &
57
- AriaToggleButtonProps;
58
-
59
- /** Type signature for the `ToggleButtonGroup.Button` sub-component (using `forwardRef`). */
60
- export type ToggleButtonGroupButtonComponent = ForwardRefExoticComponent<
61
- ToggleButtonGroupButtonProps & RefAttributes<typeof RacToggleButton>
62
- >;
@@ -1,4 +0,0 @@
1
- export * from "./tooltip";
2
- export * from "./tooltip-trigger";
3
- export * from "./make-element-focusable";
4
- export * from "./tooltip.types";
@@ -1,56 +0,0 @@
1
- import { createRef } from "react";
2
- import type { Meta, StoryObj } from "@storybook/react";
3
- import { within, expect } from "@storybook/test";
4
- import { Tooltip, TooltipTrigger, MakeElementFocusable } from "@/components";
5
-
6
- const meta: Meta<typeof MakeElementFocusable> = {
7
- title: "components/Tooltip/MakeElementFocusable",
8
- component: MakeElementFocusable,
9
- render: (args) => (
10
- <TooltipTrigger delay={0} closeDelay={0}>
11
- <MakeElementFocusable {...args} />
12
- <Tooltip>Demo Tooltip</Tooltip>
13
- </TooltipTrigger>
14
- ),
15
- };
16
-
17
- export default meta;
18
-
19
- /**
20
- * Story type for TypeScript support
21
- * StoryObj provides type checking for our story configurations
22
- */
23
- //TODO: figure out props table for this component
24
- type Story = StoryObj<typeof MakeElementFocusable>;
25
-
26
- // TODO: how much do we want to test react-aria-components?
27
- const elementRef = createRef<HTMLElement>();
28
- export const Base: Story = {
29
- args: {
30
- children: <button>custom button</button>,
31
- ref: elementRef,
32
- },
33
- play: async ({ canvasElement, step }) => {
34
- // need to get the parent node in order to have the tooltip portal in scope
35
- const canvas = within(
36
- (canvasElement.parentNode as HTMLElement) ?? canvasElement
37
- );
38
-
39
- await step(
40
- "it renders a tooltip with the proper text when child is custom component",
41
- async () => {
42
- const button = canvas.getByRole("button", { name: "custom button" });
43
- button.click();
44
- button.focus();
45
- await canvas.findByRole("tooltip", {
46
- name: "Demo Tooltip",
47
- });
48
- }
49
- );
50
- await step("it forwards a ref to the custom element", async () => {
51
- const button = canvas.getByRole("button", { name: "custom button" });
52
-
53
- await expect(elementRef.current).toBe(button);
54
- });
55
- },
56
- };
@@ -1,57 +0,0 @@
1
- import {
2
- cloneElement,
3
- forwardRef,
4
- useRef,
5
- type PropsWithChildren,
6
- isValidElement,
7
- } from "react";
8
- import {
9
- mergeProps,
10
- useObjectRef,
11
- useFocusable,
12
- type FocusableOptions,
13
- } from "react-aria";
14
-
15
- import { mergeRefs } from "@chakra-ui/react";
16
-
17
- /**
18
- * MakeElementFocusable
19
- * ============================================================
20
- * A helper component that adds props from `react-aria`s `useFocusable` hook
21
- * to its child so that it can be used as a trigger element for a `Tooltip`
22
- *
23
- * Caveats:
24
- * - Using non-interactive elements as tooltip triggers is against ARIA best-practices,
25
- * it is your responsibility to ensure that the underlying trigger element handles
26
- * focus and hover interactions correctly for keyboard-only users
27
- *
28
- * Features:
29
- * - allows forwarding refs to the underlying DOM element
30
- * - accepts all native html 'HTMLElement' attributes (including aria- & data-attributes)
31
- *
32
- * Further Context:
33
- * - [React Aria Components Tooltip Documentation](https://react-spectrum.adobe.com/react-aria/Tooltip.html)
34
- * - [React Aria Components Issue re:Tooltip with custom trigger](https://github.com/adobe/react-spectrum/issues/5733#issuecomment-1918691983)
35
- * - [ARIA Tooltip Pattern](https://www.w3.org/TR/wai-aria-1.2/#tooltip)
36
- */
37
- export const MakeElementFocusable = forwardRef<
38
- HTMLElement,
39
- PropsWithChildren<FocusableOptions<HTMLElement>>
40
- >(function MakeElementFocusable(props, forwardedRef) {
41
- const localRef = useRef<HTMLElement>(null);
42
- const ref = useObjectRef(mergeRefs(localRef, forwardedRef));
43
- const { focusableProps } = useFocusable(props, ref);
44
- if (isValidElement(props.children)) {
45
- return cloneElement(
46
- props.children,
47
- mergeProps(
48
- focusableProps,
49
- props.children.props as PropsWithChildren<
50
- FocusableOptions<HTMLElement>
51
- >,
52
- { ref: ref }
53
- )
54
- );
55
- }
56
- });
57
- MakeElementFocusable.displayName = "MakeElementFocusable";
@@ -1,157 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import { within, expect, fn } from "@storybook/test";
3
- import { Tooltip, TooltipTrigger, Button } from "@/components";
4
-
5
- const meta: Meta<typeof TooltipTrigger> = {
6
- title: "components/tooltip/TooltipTrigger",
7
- component: TooltipTrigger,
8
- render: (args) => (
9
- <TooltipTrigger {...args}>
10
- <Button>hover/focus me</Button>
11
- <Tooltip>Demo Tooltip</Tooltip>
12
- </TooltipTrigger>
13
- ),
14
- };
15
-
16
- export default meta;
17
-
18
- /**
19
- * Story type for TypeScript support
20
- * StoryObj provides type checking for our story configurations
21
- */
22
- //TODO: each component is in its own file so that the props table gets built for it,
23
- // is there a better way for declaring compound components such as this one?
24
- type Story = StoryObj<typeof TooltipTrigger>;
25
-
26
- // TODO: how much do we want to test react-aria-components?
27
- export const Base: Story = {
28
- args: {},
29
- play: async ({ canvasElement, step }) => {
30
- // need to get the parent node in order to have the tooltip portal in scope
31
- const canvas = within(
32
- (canvasElement.parentNode as HTMLElement) ?? canvasElement
33
- );
34
- await step("it renders a tooltip with the proper text", async () => {
35
- const button = canvas.getByRole("button", { name: "hover/focus me" });
36
- button.click();
37
- button.focus();
38
- await canvas.findByRole("tooltip", {
39
- name: "Demo Tooltip",
40
- });
41
- });
42
- },
43
- };
44
-
45
- export const Delay: Story = {
46
- args: {
47
- delay: 0,
48
- },
49
- play: async ({ canvasElement, step }) => {
50
- // need to get the parent node in order to have the tooltip portal in scope
51
- const canvas = within(
52
- (canvasElement.parentNode as HTMLElement) ?? canvasElement
53
- );
54
- await step("it renders a tooltip immediately when delay is 0", async () => {
55
- const button = canvas.getByRole("button", { name: "hover/focus me" });
56
- button.click();
57
- button.focus();
58
- await canvas.findByRole("tooltip", {
59
- name: "Demo Tooltip",
60
- });
61
- });
62
- },
63
- };
64
-
65
- export const isOpen: Story = {
66
- args: {
67
- isOpen: true,
68
- },
69
- play: async ({ canvasElement, step }) => {
70
- // need to get the parent node in order to have the tooltip portal in scope
71
- const canvas = within(
72
- (canvasElement.parentNode as HTMLElement) ?? canvasElement
73
- );
74
- await step("it renders tooltip on mount when isOpen is true", async () => {
75
- await canvas.findByRole("tooltip", {
76
- name: "Demo Tooltip",
77
- });
78
- });
79
- await step(
80
- "it does not hide tooltip on blur when isOpen is true",
81
- async () => {
82
- await canvas.findByRole("tooltip", {
83
- name: "Demo Tooltip",
84
- });
85
- const button = canvas.getByRole("button", { name: "hover/focus me" });
86
- button.click();
87
- button.focus();
88
- button.blur();
89
- await canvas.findByRole("tooltip", {
90
- name: "Demo Tooltip",
91
- });
92
- }
93
- );
94
- },
95
- };
96
-
97
- export const defaultOpen: Story = {
98
- args: {
99
- defaultOpen: true,
100
- },
101
- play: async ({ canvasElement, step }) => {
102
- // need to get the parent node in order to have the tooltip portal in scope
103
- const canvas = within(
104
- (canvasElement.parentNode as HTMLElement) ?? canvasElement
105
- );
106
- await step(
107
- "it renders tooltip on mount when defaultOpen is true",
108
- async () => {
109
- await canvas.findByRole("tooltip", {
110
- name: "Demo Tooltip",
111
- });
112
- }
113
- );
114
- await step(
115
- "it does not hide tooltip on blur when isOpen is true",
116
- async () => {
117
- await canvas.findByRole("tooltip", {
118
- name: "Demo Tooltip",
119
- });
120
- const button = canvas.getByRole("button", { name: "hover/focus me" });
121
- button.click();
122
- button.focus();
123
- button.blur();
124
- await new Promise((resolve) => setTimeout(resolve, 1));
125
- await expect(
126
- canvas.queryByRole("tooltip", {
127
- name: "Demo Tooltip",
128
- })
129
- ).not.toBeInTheDocument();
130
- }
131
- );
132
- },
133
- };
134
-
135
- export const onOpenChange: Story = {
136
- args: {
137
- onOpenChange: fn(),
138
- },
139
- play: async ({ canvasElement, args, step }) => {
140
- // need to get the parent node in order to have the tooltip portal in scope
141
- const canvas = within(
142
- (canvasElement.parentNode as HTMLElement) ?? canvasElement
143
- );
144
-
145
- await step(
146
- "it should call onOpenChange when entering and exiting",
147
- async () => {
148
- const button = canvas.getByRole("button", { name: "hover/focus me" });
149
- button.click();
150
- button.focus();
151
- await expect(args.onOpenChange).toHaveBeenCalledTimes(1);
152
- button.blur();
153
- await expect(args.onOpenChange).toHaveBeenCalledTimes(2);
154
- }
155
- );
156
- },
157
- };
@@ -1,15 +0,0 @@
1
- import { TooltipTrigger as RATooltipTrigger } from "react-aria-components";
2
-
3
- /**
4
- * TooltipTrigger
5
- * ============================================================
6
- * TooltipTrigger wraps around a trigger element and a Tooltip.
7
- * It handles opening and closing the Tooltip when the user hovers over or focuses the trigger,
8
- * and positioning the Tooltip relative to the trigger.
9
- *
10
- * Directly exported from `react-aria-components`
11
- * - [React Aria Components Tooltip Documentation](https://react-spectrum.adobe.com/react-aria/Tooltip.html)
12
- */
13
- export const TooltipTrigger = RATooltipTrigger;
14
- // @ts-expect-error displaynames can be set on component instances, necessary for pretty react-live output
15
- TooltipTrigger.displayName = "TooltipTrigger";