@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,841 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import { Select } from "./select";
3
- import { Text, Stack, Box } from "@/components";
4
- import type { Key } from "react-aria";
5
- import { useState } from "react";
6
- import { type SelectRootProps } from "./select.types";
7
- import { userEvent, within, expect, fn } from "@storybook/test";
8
-
9
- import { useAsyncList } from "react-stately";
10
-
11
- /**
12
- * Storybook metadata configuration
13
- */
14
- const meta: Meta<typeof Select.Root> = {
15
- title: "components/Select",
16
- component: Select.Root,
17
- };
18
-
19
- export default meta;
20
-
21
- /**
22
- * Story type for TypeScript support
23
- * StoryObj provides type checking for our story configurations
24
- */
25
- type Story = StoryObj<typeof Select.Root>;
26
-
27
- /**
28
- * Test data
29
- */
30
-
31
- const selectSizes: SelectRootProps["size"][] = ["sm", "md"];
32
- const selectVariants: SelectRootProps["variant"][] = ["outline", "ghost"];
33
-
34
- const optionGroupOptions = [
35
- {
36
- id: "a",
37
- name: "Fruit",
38
- children: [
39
- { id: 1, name: "Apple" },
40
- { id: 2, name: "Banana" },
41
- { id: 3, name: "Orange" },
42
- { id: 4, name: "Honeydew" },
43
- { id: 5, name: "Grapes" },
44
- { id: 6, name: "Watermelon" },
45
- { id: 7, name: "Cantaloupe" },
46
- { id: 8, name: "Pear" },
47
- ],
48
- },
49
- {
50
- id: "b",
51
- name: "Vegetable",
52
- children: [
53
- { id: 9, name: "Cabbage" },
54
- { id: 10, name: "Broccoli" },
55
- { id: 11, name: "Carrots" },
56
- { id: 12, name: "Lettuce" },
57
- { id: 13, name: "Spinach" },
58
- { id: 14, name: "Bok Choy" },
59
- { id: 15, name: "Cauliflower" },
60
- { id: 16, name: "Potatoes" },
61
- ],
62
- },
63
- ];
64
-
65
- /**
66
- * Base story
67
- * Demonstrates the most basic implementation, an uncontrolled
68
- * select with a few options.
69
- */
70
- export const Base: Story = {
71
- render: () => {
72
- return (
73
- <Select.Root aria-label="Select a fruit" data-testid="select">
74
- <Select.Options>
75
- <Select.Option>Apples</Select.Option>
76
- <Select.Option>Bananas</Select.Option>
77
- <Select.Option>Oranges</Select.Option>
78
- <Select.Option>Cherries</Select.Option>
79
- </Select.Options>
80
- </Select.Root>
81
- );
82
- },
83
- play: async ({ canvasElement, step }) => {
84
- const canvas = within(canvasElement);
85
- const select = canvas.getByTestId("select");
86
- const button = select.querySelector("button");
87
-
88
- await step("Select is rendered with button", async () => {
89
- await expect(button).toBeInTheDocument();
90
- });
91
-
92
- await step("Select button can be focused with keyboard", async () => {
93
- await userEvent.tab();
94
- await expect(button).toHaveFocus();
95
- });
96
-
97
- await step("Select opens on click", async () => {
98
- await userEvent.click(button!);
99
- // the popover is rendered via a react portal outside of the select root
100
- // and can only be found by querying the document directly
101
- const listbox = document.querySelector('[role="listbox"]');
102
- await expect(listbox).toBeInTheDocument();
103
- });
104
-
105
- await step("Options are displayed when open", async () => {
106
- const options = document.querySelectorAll('[role="option"]');
107
- await expect(options.length).toBe(4);
108
- await expect(options[0]).toHaveTextContent("Apples");
109
- });
110
-
111
- await step("Can select an option with click", async () => {
112
- const options = document.querySelectorAll('[role="option"]');
113
- await userEvent.click(options[1]);
114
- await expect(button).toHaveTextContent("Bananas");
115
- });
116
-
117
- await step("Value can be cleared with keyboard", async () => {
118
- const clearButton = select.querySelectorAll("button")[1];
119
- await userEvent.click(clearButton);
120
- await expect(button).toHaveTextContent("Select an item");
121
- });
122
- },
123
- };
124
-
125
- /**
126
- * Controlled State
127
- * The state of the select is controlled from the oustide.
128
- * @see https://react-spectrum.adobe.com/react-aria/Select.html#selection
129
- */
130
- const mockFn = fn();
131
- export const ControlledState: Story = {
132
- render: () => {
133
- const options = [
134
- { id: 1, name: "Koala" },
135
- { id: 2, name: "Kangaroo" },
136
- { id: 3, name: "Platypus" },
137
- { id: 4, name: "Bald Eagle" },
138
- { id: 5, name: "Bison" },
139
- { id: 6, name: "Skunk" },
140
- ];
141
- const [animal, setAnimal] = useState<Key>("Bison");
142
-
143
- const onChangeRequest = (key: Key) => {
144
- setAnimal(key);
145
- mockFn();
146
- };
147
-
148
- return (
149
- <Box>
150
- <Box bg="blueAlpha.2" p="400" my="400" data-testid="value-display">
151
- I'm a Box and not related to Select, but I know it's current value,
152
- it's <mark>{animal ?? "not set"}</mark>.
153
- </Box>
154
- <Select.Root
155
- defaultSelectedKey={animal}
156
- selectedKey={animal}
157
- onSelectionChange={onChangeRequest}
158
- aria-label="Select your new pet"
159
- data-testid="select"
160
- >
161
- <Select.Options items={options}>
162
- {(item: { name: string }) => (
163
- <Select.Option id={item.name}>{item.name}</Select.Option>
164
- )}
165
- </Select.Options>
166
- </Select.Root>
167
- </Box>
168
- );
169
- },
170
- play: async ({ canvasElement, step }) => {
171
- const canvas = within(canvasElement);
172
- const select = canvas.getByTestId("select");
173
- const valueDisplay = canvas.getByTestId("value-display");
174
- const button = select.querySelector("button");
175
-
176
- await step("Select displays the default value", async () => {
177
- await expect(button).toHaveTextContent("Bison");
178
- await expect(valueDisplay).toHaveTextContent("Bison");
179
- });
180
-
181
- await step(
182
- "Selecting a different option updates external state",
183
- async () => {
184
- await userEvent.click(button!);
185
- const listbox = document.querySelector('[role="listbox"]');
186
- await expect(listbox).toBeInTheDocument();
187
-
188
- const options = document.querySelectorAll('[role="option"]');
189
- await userEvent.click(options[0]); // Select Koala
190
-
191
- await expect(button).toHaveTextContent("Koala");
192
- await expect(valueDisplay).toHaveTextContent("Koala");
193
-
194
- await expect(mockFn).toHaveBeenCalled();
195
- }
196
- );
197
- },
198
- };
199
-
200
- /**
201
- * Async Loading
202
- * @see https://react-spectrum.adobe.com/react-aria/Select.html#asynchronous-loading
203
- */
204
- export const AsyncLoading: Story = {
205
- render: () => {
206
- const list = useAsyncList<{ id: number; name: string }>({
207
- load: async () => {
208
- // Simulate a network request
209
- await new Promise((resolve) => setTimeout(resolve, 1000));
210
-
211
- return {
212
- items: [
213
- { id: 1, name: "Koala" },
214
- { id: 2, name: "Kangaroo" },
215
- { id: 3, name: "Platypus" },
216
- { id: 4, name: "Bald Eagle" },
217
- { id: 5, name: "Bison" },
218
- { id: 6, name: "Skunk" },
219
- ],
220
- };
221
- },
222
- });
223
-
224
- const [animal, setAnimal] = useState<Key>();
225
-
226
- const isLoading = list.loadingState === "loading";
227
-
228
- return (
229
- <Box>
230
- <Box bg="blueAlpha.2" p="400" my="400">
231
- Here, an async list is being loaded. And while it is loading, Select
232
- should go into an <mark>isLoading</mark> state (disabled and with a
233
- spinner).
234
- </Box>
235
- <Select.Root
236
- isLoading={isLoading}
237
- selectedKey={animal}
238
- onSelectionChange={setAnimal}
239
- aria-label="Select your new pet"
240
- data-testid="select"
241
- >
242
- <Select.Options items={list.items}>
243
- {(item) => (
244
- <Select.Option key={item.id} id={item.name}>
245
- {item.name}
246
- </Select.Option>
247
- )}
248
- </Select.Options>
249
- </Select.Root>
250
- </Box>
251
- );
252
- },
253
- play: async ({ canvasElement, step }) => {
254
- const canvas = within(canvasElement);
255
- const select = canvas.getByTestId("select");
256
- const button = select.querySelector("button");
257
-
258
- await step("Select is rendered with button", async () => {
259
- await expect(button).toBeInTheDocument();
260
- });
261
-
262
- await step("Select button can not be focused while loading", async () => {
263
- await userEvent.tab();
264
- await expect(button).not.toHaveFocus();
265
- });
266
-
267
- await step("Select can be focused when data is loaded", async () => {
268
- await new Promise((resolve) => setTimeout(resolve, 1000));
269
- await userEvent.tab();
270
- await expect(button).toHaveFocus();
271
- });
272
-
273
- await step("Select opens on click", async () => {
274
- await userEvent.click(button!);
275
- // the popover is rendered via a react portal outside of the select root
276
- // and can only be found by querying the document directly
277
- const listbox = document.querySelector('[role="listbox"]');
278
- await expect(listbox).toBeInTheDocument();
279
- });
280
-
281
- await step("Options are displayed when open", async () => {
282
- const options = document.querySelectorAll('[role="option"]');
283
- await expect(options.length).toBe(6);
284
- await expect(options[0]).toHaveTextContent("Koala");
285
- });
286
- },
287
- };
288
-
289
- /**
290
- * Disabled
291
- * @see https://react-spectrum.adobe.com/react-aria/Select.html#disabled
292
- */
293
- export const Disabled: Story = {
294
- render: () => {
295
- return (
296
- <Select.Root
297
- isDisabled
298
- aria-label="Select some fruit(s)"
299
- data-testid="select"
300
- >
301
- <Select.Options>
302
- <Select.Option>Apples</Select.Option>
303
- <Select.Option>Bananas</Select.Option>
304
- <Select.Option>Oranges</Select.Option>
305
- </Select.Options>
306
- </Select.Root>
307
- );
308
- },
309
- play: async ({ canvasElement, step }) => {
310
- const canvas = within(canvasElement);
311
- const select = canvas.getByTestId("select");
312
- const button = select.querySelector("button");
313
-
314
- await step("Disabled select has disabled attribute", async () => {
315
- await expect(button).toHaveAttribute("disabled");
316
- });
317
-
318
- await step("Disabled select cannot be focused with keyboard", async () => {
319
- await userEvent.tab();
320
- await expect(button).not.toHaveFocus();
321
- });
322
-
323
- await step("Disabled select cannot be clicked to open", async () => {
324
- await expect(button).toHaveStyle({ pointerEvents: "none" });
325
- });
326
- },
327
- };
328
-
329
- export const DisabledOptions: Story = {
330
- render: () => {
331
- return (
332
- <Select.Root
333
- disabledKeys={["2"]}
334
- aria-label="Select some fruit(s)"
335
- data-testid="select"
336
- >
337
- <Select.Options>
338
- <Select.Option id="1">Apples</Select.Option>
339
- <Select.Option id="2">Bananas</Select.Option>
340
- <Select.Option id="3">Oranges</Select.Option>
341
- </Select.Options>
342
- </Select.Root>
343
- );
344
- },
345
- play: async ({ canvasElement, step }) => {
346
- const canvas = within(canvasElement);
347
- const select = canvas.getByTestId("select");
348
- const button = select.querySelector("button");
349
-
350
- await step("Select can be opened", async () => {
351
- await userEvent.click(button!);
352
- const listbox = document.querySelector('[role="listbox"]');
353
- await expect(listbox).toBeInTheDocument();
354
- });
355
-
356
- await step("Disabled option has aria-disabled attribute", async () => {
357
- const options = document.querySelectorAll('[role="option"]');
358
- await expect(options[1]).toHaveAttribute("aria-disabled", "true");
359
- });
360
-
361
- await step("Enabled options can be selected", async () => {
362
- const options = document.querySelectorAll('[role="option"]');
363
- await userEvent.click(options[0]); // Select Apples
364
- await expect(button).toHaveTextContent("Apples");
365
- });
366
-
367
- await step("Disabled option cannot be selected", async () => {
368
- await userEvent.click(button!); // Open select again
369
- const options = document.querySelectorAll('[role="option"]');
370
- await userEvent.click(options[1]); // Try to select Bananas (disabled)
371
- // Select should remain on previously selected option
372
- await expect(button).toHaveTextContent("Apples");
373
- });
374
- },
375
- };
376
-
377
- /**
378
- * Invalid State
379
- * @see https://react-spectrum.adobe.com/react-aria/Select.html#validation
380
- */
381
- export const Invalid: Story = {
382
- render: () => {
383
- return (
384
- <Select.Root
385
- isInvalid
386
- aria-label="Select some fruit(s)"
387
- data-testid="select"
388
- >
389
- <Select.Options>
390
- <Select.Option id="1">Apples</Select.Option>
391
- <Select.Option id="2">Bananas</Select.Option>
392
- <Select.Option id="3">Oranges</Select.Option>
393
- </Select.Options>
394
- </Select.Root>
395
- );
396
- },
397
- play: async ({ canvasElement, step }) => {
398
- const canvas = within(canvasElement);
399
- const select = canvas.getByTestId("select");
400
- const button = select.querySelector("button");
401
-
402
- await step("Invalid select has data-invalid attribute", async () => {
403
- await expect(select).toHaveAttribute("data-invalid", "true");
404
- });
405
-
406
- await step("Invalid select can still be opened and used", async () => {
407
- await userEvent.click(button!);
408
- const listbox = document.querySelector('[role="listbox"]');
409
- await expect(listbox).toBeInTheDocument();
410
-
411
- const options = document.querySelectorAll('[role="option"]');
412
- await userEvent.click(options[0]);
413
- await expect(button).toHaveTextContent("Apples");
414
- });
415
- },
416
- };
417
-
418
- /**
419
- * Option Groups (Simple)
420
- */
421
- export const OptionGroups: Story = {
422
- render: () => {
423
- return (
424
- <Box>
425
- <Select.Root aria-label="Select some fruit(s)" data-testid="select">
426
- <Select.Options>
427
- <Select.OptionGroup label="Fruits">
428
- <Select.Option>Apples</Select.Option>
429
- <Select.Option>Oranges</Select.Option>
430
- <Select.Option>Bananas</Select.Option>
431
- </Select.OptionGroup>
432
- <Select.OptionGroup label="Vegetables">
433
- <Select.Option>Carrots</Select.Option>
434
- <Select.Option>Broccoli</Select.Option>
435
- <Select.Option>Spinach</Select.Option>
436
- </Select.OptionGroup>
437
- </Select.Options>
438
- </Select.Root>
439
- </Box>
440
- );
441
- },
442
- play: async ({ canvasElement, step }) => {
443
- const canvas = within(canvasElement);
444
- const select = canvas.getByTestId("select");
445
- const button = select.querySelector("button");
446
-
447
- await step("Select can be opened", async () => {
448
- await userEvent.click(button!);
449
- const listbox = document.querySelector('[role="listbox"]');
450
- await expect(listbox).toBeInTheDocument();
451
- });
452
-
453
- await step("Option groups are rendered with proper roles", async () => {
454
- const groups = document.querySelectorAll('[role="group"]');
455
- await expect(groups.length).toBe(2);
456
-
457
- const groupLabels = document.querySelectorAll('[role="presentation"]');
458
- await expect(groupLabels[0]).toHaveTextContent("Fruits");
459
- await expect(groupLabels[1]).toHaveTextContent("Vegetables");
460
- });
461
-
462
- await step("Options within groups can be selected", async () => {
463
- const options = document.querySelectorAll('[role="option"]');
464
- await userEvent.click(options[1]); // Select Oranges from first group
465
- await expect(button).toHaveTextContent("Oranges");
466
- });
467
- },
468
- };
469
-
470
- /**
471
- * Option Groups (Dynamic)
472
- */
473
- export const OptionGroupsDynamic: Story = {
474
- render: () => {
475
- return (
476
- <Box>
477
- <Select.Root aria-label="Select something to eat">
478
- <Select.Options items={optionGroupOptions}>
479
- {(groupItem) => (
480
- <Select.OptionGroup
481
- key={groupItem.id}
482
- label={groupItem.name}
483
- items={groupItem.children}
484
- >
485
- {(optionItem) => (
486
- <Select.Option key={optionItem.id}>
487
- {optionItem.name}
488
- </Select.Option>
489
- )}
490
- </Select.OptionGroup>
491
- )}
492
- </Select.Options>
493
- </Select.Root>
494
- </Box>
495
- );
496
- },
497
- };
498
-
499
- /**
500
- * Label + additional descriptions
501
- * demonstrates the use of additional option descriptions
502
- * @see https://react-spectrum.adobe.com/react-aria/Select.html#text-slotss
503
- *
504
- * - test for textValue existance, otherwise typeahead won't work
505
- */
506
- export const WithDescriptions: Story = {
507
- render: () => {
508
- return (
509
- <Select.Root aria-label="Select some fruit(s)" data-testid="select">
510
- <Select.Options>
511
- {/** Variant A - plain html-tags with slot property */}
512
- <Select.Option textValue="Apple">
513
- <p slot="label">Apple</p>
514
- <p slot="description">A classic and versatile fruit.</p>
515
- </Select.Option>
516
- {/** Variant B - text component with slot property */}
517
- <Select.Option textValue="Banana">
518
- <Text slot="label">Banana</Text>
519
- <Text slot="description">A good source of potassium.</Text>
520
- </Select.Option>
521
- <Select.Option textValue="Oranges">
522
- <Text slot="label">Oranges</Text>
523
- <Text slot="description">Rich in vitamin C.</Text>
524
- </Select.Option>
525
- <Select.Option textValue="Strawberries">
526
- <Text slot="label">Strawberries</Text>
527
- <Text slot="description">Sweet and full of antioxidants.</Text>
528
- </Select.Option>
529
- <Select.Option textValue="Grapes">
530
- <Text slot="label">Grapes</Text>
531
- <Text slot="description">
532
- Available in various colors and flavors.
533
- </Text>
534
- </Select.Option>
535
- </Select.Options>
536
- </Select.Root>
537
- );
538
- },
539
- play: async ({ canvasElement, step }) => {
540
- const canvas = within(canvasElement);
541
- const select = canvas.getByTestId("select");
542
- const button = select.querySelector("button");
543
-
544
- await step("Select opens on click", async () => {
545
- await userEvent.click(button!);
546
- const listbox = document.querySelector('[role="listbox"]');
547
- await expect(listbox).toBeInTheDocument();
548
- });
549
-
550
- await step("Options display labels and descriptions", async () => {
551
- const options = document.querySelectorAll('[role="option"]');
552
-
553
- // Check first option has label and description
554
- await expect(
555
- options[0].querySelector('[slot="label"]')
556
- ).toHaveTextContent("Apple");
557
- await expect(
558
- options[0].querySelector('[slot="description"]')
559
- ).toHaveTextContent("A classic and versatile fruit.");
560
- });
561
-
562
- await step("Typeahead works with textValue", async () => {
563
- // Type "b" to navigate to Banana
564
- await userEvent.keyboard("ba");
565
- const options = document.querySelectorAll('[role="option"]');
566
-
567
- // Check that Banana option is focused (has aria-selected)
568
- await expect(options[1]).toHaveAttribute("data-focused", "true");
569
- });
570
- },
571
- };
572
-
573
- /**
574
- * Custom Widths
575
- * custom widths for select-trigger button and popover
576
- */
577
- export const CustomWidths: Story = {
578
- render: () => {
579
- return (
580
- // width for the trigger can be specified on <Select.Root/>,
581
- // width for popover can be specified on <Select.Options/>
582
- <Select.Root width="196px" aria-label="Select something to eat">
583
- <Select.Options width="512px">
584
- <Select.Option>
585
- Extraordinary long Menu Label that noone can read Extraordinary long
586
- Menu Label that noone can read
587
- </Select.Option>
588
- <Select.OptionGroup label="Fruits">
589
- <Select.Option>Apples</Select.Option>
590
- <Select.Option>Oranges</Select.Option>
591
- <Select.Option>Bananas</Select.Option>
592
- </Select.OptionGroup>
593
- <Select.OptionGroup label="Vegetables">
594
- <Select.Option>Carrots</Select.Option>
595
- <Select.Option>Broccoli</Select.Option>
596
- <Select.Option>Spinach</Select.Option>
597
- </Select.OptionGroup>
598
- </Select.Options>
599
- </Select.Root>
600
- );
601
- },
602
- };
603
-
604
- /**
605
- * Super long and complex example
606
- * Demonstrates a complex, long list of options in the middle
607
- * of the screen. Where will the flyout go? We don't know.
608
- */
609
- export const SuperLongAndComplex: Story = {
610
- render: () => {
611
- return (
612
- <Box
613
- display="flex"
614
- w="100vw"
615
- h="100vh"
616
- alignItems="center"
617
- justifyContent="center"
618
- overflow="auto"
619
- >
620
- <Select.Root aria-label="Select something">
621
- <Select.Options>
622
- <Select.OptionGroup label="Fruits">
623
- <Select.Option>
624
- <Text slot="label">Apples</Text>
625
- <Text slot="description">A crisp and juicy classic fruit.</Text>
626
- </Select.Option>
627
- <Select.Option>
628
- <Text slot="label">Oranges</Text>
629
- <Text slot="description">A sweet and tangy citrus fruit.</Text>
630
- </Select.Option>
631
- <Select.Option>
632
- <Text slot="label">Bananas</Text>
633
- <Text slot="description">
634
- A soft and creamy tropical fruit.
635
- </Text>
636
- </Select.Option>
637
- </Select.OptionGroup>
638
- <Select.OptionGroup label="Vegetables">
639
- <Select.Option>
640
- <Text slot="label">Carrots</Text>
641
- <Text slot="description">
642
- A crunchy and nutritious root vegetable.
643
- </Text>
644
- </Select.Option>
645
- <Select.Option>
646
- <Text slot="label">Broccoli</Text>
647
- <Text slot="description">
648
- A green vegetable rich in vitamins.
649
- </Text>
650
- </Select.Option>
651
- <Select.Option>
652
- <Text slot="label">Spinach</Text>
653
- <Text slot="description">
654
- A leafy vegetable packed with iron.
655
- </Text>
656
- </Select.Option>
657
- </Select.OptionGroup>
658
- <Select.OptionGroup label="Grains">
659
- <Select.Option>
660
- <Text slot="label">Rice</Text>
661
- <Text slot="description">
662
- A staple grain consumed worldwide.
663
- </Text>
664
- </Select.Option>
665
- <Select.Option>
666
- <Text slot="label">Wheat</Text>
667
- <Text slot="description">
668
- A common grain used in bread and pasta.
669
- </Text>
670
- </Select.Option>
671
- <Select.Option>
672
- <Text slot="label">Oats</Text>
673
- <Text slot="description">
674
- A healthy grain often eaten for breakfast.
675
- </Text>
676
- </Select.Option>
677
- </Select.OptionGroup>
678
- <Select.OptionGroup label="Proteins">
679
- <Select.Option>
680
- <Text slot="label">Chicken</Text>
681
- <Text slot="description">
682
- A versatile and lean poultry protein.
683
- </Text>
684
- </Select.Option>
685
- <Select.Option>
686
- <Text slot="label">Beef</Text>
687
- <Text slot="description">
688
- A rich and flavorful red meat protein.
689
- </Text>
690
- </Select.Option>
691
- <Select.Option>
692
- <Text slot="label">Pork</Text>
693
- <Text slot="description">
694
- Another popular and versatile meat protein.
695
- </Text>
696
- </Select.Option>
697
- </Select.OptionGroup>
698
- </Select.Options>
699
- </Select.Root>
700
- </Box>
701
- );
702
- },
703
- };
704
-
705
- /**
706
- * Variants and Sizes combined
707
- */
708
- export const VariantsAndSizes: Story = {
709
- render: () => {
710
- const [isInvalid, setInvalid] = useState(false);
711
- return (
712
- <Stack>
713
- {[{}, { isDisabled: true }, { isInvalid: true }].map((props) => (
714
- <Stack
715
- key={JSON.stringify({ props })}
716
- bg="neutral.2"
717
- onClick={() => setInvalid(!isInvalid)}
718
- >
719
- {selectVariants.map((variant) => (
720
- <Stack alignItems="start" key={JSON.stringify({ variant })}>
721
- <Text my="400" fontWeight="600">
722
- {JSON.stringify({ variant, ...props })}
723
- </Text>
724
- {selectSizes.map((size) => (
725
- <Select.Root
726
- size={size}
727
- variant={variant}
728
- key={JSON.stringify({ size })}
729
- {...props}
730
- aria-label="Select something"
731
- >
732
- <Select.Options>
733
- <Select.Option>
734
- Extraordinary long Menu Label that noone can read
735
- Extraordinary long Menu Label that noone can read
736
- </Select.Option>
737
- <Select.Option>Groupless Option No 1.</Select.Option>
738
- <Select.Option>Groupless Option No 2.</Select.Option>
739
- <Select.Option>
740
- <Text slot="label">Groupless Option No 3.</Text>
741
- <Text slot="description">
742
- At least this one has a description.
743
- </Text>
744
- </Select.Option>
745
- <Select.Option>
746
- <Text slot="label">Super freaking long</Text>
747
- <Text slot="description">
748
- At least this one has a description.
749
- </Text>
750
- </Select.Option>
751
- <Select.OptionGroup label="Fruits">
752
- <Select.Option>
753
- <Text slot="label">Apples</Text>
754
- <Text slot="description">
755
- A crisp and juicy classic fruit.
756
- </Text>
757
- </Select.Option>
758
- <Select.Option>
759
- <Text slot="label">Oranges</Text>
760
- <Text slot="description">
761
- A sweet and tangy citrus fruit.
762
- </Text>
763
- </Select.Option>
764
- <Select.Option>
765
- <Text slot="label">Bananas</Text>
766
- <Text slot="description">
767
- A soft and creamy tropical fruit.
768
- </Text>
769
- </Select.Option>
770
- </Select.OptionGroup>
771
- <Select.OptionGroup label="Vegetables">
772
- <Select.Option>
773
- <Text slot="label">Carrots</Text>
774
- <Text slot="description">
775
- A crunchy and nutritious root vegetable.
776
- </Text>
777
- </Select.Option>
778
- <Select.Option>
779
- <Text slot="label">Broccoli</Text>
780
- <Text slot="description">
781
- A green vegetable rich in vitamins.
782
- </Text>
783
- </Select.Option>
784
- <Select.Option>
785
- <Text slot="label">Spinach</Text>
786
- <Text slot="description">
787
- A leafy vegetable packed with iron.
788
- </Text>
789
- </Select.Option>
790
- </Select.OptionGroup>
791
- <Select.OptionGroup label="Grains">
792
- <Select.Option>
793
- <Text slot="label">Rice</Text>
794
- <Text slot="description">
795
- A staple grain consumed worldwide.
796
- </Text>
797
- </Select.Option>
798
- <Select.Option>
799
- <Text slot="label">Wheat</Text>
800
- <Text slot="description">
801
- A common grain used in bread and pasta.
802
- </Text>
803
- </Select.Option>
804
- <Select.Option>
805
- <Text slot="label">Oats</Text>
806
- <Text slot="description">
807
- A healthy grain often eaten for breakfast.
808
- </Text>
809
- </Select.Option>
810
- </Select.OptionGroup>
811
- <Select.OptionGroup label="Proteins">
812
- <Select.Option>
813
- <Text slot="label">Chicken</Text>
814
- <Text slot="description">
815
- A versatile and lean poultry protein.
816
- </Text>
817
- </Select.Option>
818
- <Select.Option>
819
- <Text slot="label">Beef</Text>
820
- <Text slot="description">
821
- A rich and flavorful red meat protein.
822
- </Text>
823
- </Select.Option>
824
- <Select.Option>
825
- <Text slot="label">Pork</Text>
826
- <Text slot="description">
827
- Another popular and versatile meat protein.
828
- </Text>
829
- </Select.Option>
830
- </Select.OptionGroup>
831
- </Select.Options>
832
- </Select.Root>
833
- ))}
834
- </Stack>
835
- ))}
836
- </Stack>
837
- ))}
838
- </Stack>
839
- );
840
- },
841
- };