@commercetools/nimbus-mcp 0.1.0 → 2.11.0

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 (453) hide show
  1. package/README.md +63 -14
  2. package/data/docs/route-manifest.json +10998 -0
  3. package/data/docs/routes/components-accessibility-visually-hidden.json +388 -0
  4. package/data/docs/routes/components-accessibility.json +34 -0
  5. package/data/docs/routes/components-buttons-button.json +715 -0
  6. package/data/docs/routes/components-buttons-icon-button.json +852 -0
  7. package/data/docs/routes/components-buttons-icon-toggle-button.json +594 -0
  8. package/data/docs/routes/components-buttons-split-button.json +670 -0
  9. package/data/docs/routes/components-buttons-toggle-button-group.json +722 -0
  10. package/data/docs/routes/components-buttons-toggle-button.json +689 -0
  11. package/data/docs/routes/components-buttons.json +36 -0
  12. package/data/docs/routes/components-data-display-badge.json +555 -0
  13. package/data/docs/routes/components-data-display-card.json +338 -0
  14. package/data/docs/routes/components-data-display-data-table.json +855 -0
  15. package/data/docs/routes/components-data-display-draggable-list.json +596 -0
  16. package/data/docs/routes/components-data-display-table.json +472 -0
  17. package/data/docs/routes/components-data-display-tag-group.json +535 -0
  18. package/data/docs/routes/components-data-display.json +34 -0
  19. package/data/docs/routes/components-feedback-alert.json +696 -0
  20. package/data/docs/routes/components-feedback-dialog.json +682 -0
  21. package/data/docs/routes/components-feedback-drawer.json +600 -0
  22. package/data/docs/routes/components-feedback-loading-spinner.json +415 -0
  23. package/data/docs/routes/components-feedback-progress-bar.json +661 -0
  24. package/data/docs/routes/components-feedback-toast.json +1040 -0
  25. package/data/docs/routes/components-feedback-tooltip.json +510 -0
  26. package/data/docs/routes/components-feedback.json +34 -0
  27. package/data/docs/routes/components-forms-field-errors.json +557 -0
  28. package/data/docs/routes/components-forms-form-field.json +848 -0
  29. package/data/docs/routes/components-forms-group.json +427 -0
  30. package/data/docs/routes/components-forms-localized-field.json +770 -0
  31. package/data/docs/routes/components-forms.json +37 -0
  32. package/data/docs/routes/components-inputs-calendar.json +611 -0
  33. package/data/docs/routes/components-inputs-checkbox.json +774 -0
  34. package/data/docs/routes/components-inputs-combo-box.json +761 -0
  35. package/data/docs/routes/components-inputs-date-input.json +628 -0
  36. package/data/docs/routes/components-inputs-date-picker.json +709 -0
  37. package/data/docs/routes/components-inputs-date-range-picker.json +599 -0
  38. package/data/docs/routes/components-inputs-money-input.json +721 -0
  39. package/data/docs/routes/components-inputs-multiline-text-input.json +611 -0
  40. package/data/docs/routes/components-inputs-number-input.json +647 -0
  41. package/data/docs/routes/components-inputs-password-input.json +576 -0
  42. package/data/docs/routes/components-inputs-radio-input.json +583 -0
  43. package/data/docs/routes/components-inputs-range-calendar.json +607 -0
  44. package/data/docs/routes/components-inputs-rich-text-input.json +599 -0
  45. package/data/docs/routes/components-inputs-scoped-search-input.json +570 -0
  46. package/data/docs/routes/components-inputs-search-input.json +588 -0
  47. package/data/docs/routes/components-inputs-select-input.json +960 -0
  48. package/data/docs/routes/components-inputs-switch.json +720 -0
  49. package/data/docs/routes/components-inputs-text-input.json +566 -0
  50. package/data/docs/routes/components-inputs-time-input.json +775 -0
  51. package/data/docs/routes/components-inputs.json +34 -0
  52. package/data/docs/routes/components-layout-box.json +501 -0
  53. package/data/docs/routes/components-layout-defaultpage.json +748 -0
  54. package/data/docs/routes/components-layout-flex.json +587 -0
  55. package/data/docs/routes/components-layout-grid.json +393 -0
  56. package/data/docs/routes/components-layout-modalpage.json +716 -0
  57. package/data/docs/routes/components-layout-pagecontent.json +673 -0
  58. package/data/docs/routes/components-layout-scrollarea.json +428 -0
  59. package/data/docs/routes/components-layout-separator.json +461 -0
  60. package/data/docs/routes/components-layout-simple-grid.json +519 -0
  61. package/data/docs/routes/components-layout-spacer.json +573 -0
  62. package/data/docs/routes/components-layout-stack.json +481 -0
  63. package/data/docs/routes/components-layout.json +34 -0
  64. package/data/docs/routes/components-media-avatar.json +427 -0
  65. package/data/docs/routes/components-media-icon.json +663 -0
  66. package/data/docs/routes/components-media-image.json +511 -0
  67. package/data/docs/routes/components-media-inline-svg.json +586 -0
  68. package/data/docs/routes/components-media.json +34 -0
  69. package/data/docs/routes/components-navigation-accordion.json +643 -0
  70. package/data/docs/routes/components-navigation-collapsible-motion.json +628 -0
  71. package/data/docs/routes/components-navigation-link.json +554 -0
  72. package/data/docs/routes/components-navigation-menu.json +546 -0
  73. package/data/docs/routes/components-navigation-pagination.json +502 -0
  74. package/data/docs/routes/components-navigation-steps.json +629 -0
  75. package/data/docs/routes/components-navigation-tabnav.json +546 -0
  76. package/data/docs/routes/components-navigation-tabs.json +635 -0
  77. package/data/docs/routes/components-navigation-toolbar.json +549 -0
  78. package/data/docs/routes/components-navigation.json +34 -0
  79. package/data/docs/routes/components-typography-code.json +39 -0
  80. package/data/docs/routes/components-typography-heading.json +402 -0
  81. package/data/docs/routes/components-typography-kbd.json +399 -0
  82. package/data/docs/routes/components-typography-list.json +593 -0
  83. package/data/docs/routes/components-typography-text.json +444 -0
  84. package/data/docs/routes/components-typography.json +34 -0
  85. package/data/docs/routes/components-utilities-nimbus-i18n-provider.json +295 -0
  86. package/data/docs/routes/components-utilities-nimbus-provider.json +663 -0
  87. package/data/docs/routes/components-utilities.json +34 -0
  88. package/data/docs/routes/components.json +33 -0
  89. package/data/docs/routes/home-contribute-adrs-adr0001-consumer-component-apis.json +314 -0
  90. package/data/docs/routes/home-contribute-adrs-adr0002-compound-component-extraction.json +160 -0
  91. package/data/docs/routes/home-contribute-adrs-adr0003-component-lifecycle-states.json +460 -0
  92. package/data/docs/routes/home-contribute-adrs.json +205 -0
  93. package/data/docs/routes/home-contribute-development-setup.json +213 -0
  94. package/data/docs/routes/home-contribute-stats.json +36 -0
  95. package/data/docs/routes/home-contribute.json +36 -0
  96. package/data/docs/routes/home-design-tokens-aspect-ratios.json +36 -0
  97. package/data/docs/routes/home-design-tokens-borders.json +35 -0
  98. package/data/docs/routes/home-design-tokens-colors.json +157 -0
  99. package/data/docs/routes/home-design-tokens-other-animations.json +119 -0
  100. package/data/docs/routes/home-design-tokens-other-blurs.json +36 -0
  101. package/data/docs/routes/home-design-tokens-other-breakpoints.json +61 -0
  102. package/data/docs/routes/home-design-tokens-other-cursors.json +36 -0
  103. package/data/docs/routes/home-design-tokens-other-z-indices.json +39 -0
  104. package/data/docs/routes/home-design-tokens-other.json +35 -0
  105. package/data/docs/routes/home-design-tokens-radii.json +59 -0
  106. package/data/docs/routes/home-design-tokens-shadows.json +57 -0
  107. package/data/docs/routes/home-design-tokens-sizes.json +137 -0
  108. package/data/docs/routes/home-design-tokens-spacing.json +36 -0
  109. package/data/docs/routes/home-design-tokens-typography.json +184 -0
  110. package/data/docs/routes/home-design-tokens.json +34 -0
  111. package/data/docs/routes/home-getting-started-core-concepts.json +301 -0
  112. package/data/docs/routes/home-getting-started-installation.json +621 -0
  113. package/data/docs/routes/home-getting-started-mcp-server-overview.json +139 -0
  114. package/data/docs/routes/home-getting-started-mcp-server-setup.json +316 -0
  115. package/data/docs/routes/home-getting-started-release-process.json +294 -0
  116. package/data/docs/routes/home-getting-started-testing-setup.json +296 -0
  117. package/data/docs/routes/home-playground-markdown.json +638 -0
  118. package/data/docs/routes/home-playground-toc.json +169 -0
  119. package/data/docs/routes/home-playground.json +34 -0
  120. package/data/docs/routes/home-style-props-background.json +236 -0
  121. package/data/docs/routes/home-style-props-border.json +310 -0
  122. package/data/docs/routes/home-style-props-display.json +120 -0
  123. package/data/docs/routes/home-style-props-effects.json +116 -0
  124. package/data/docs/routes/home-style-props-filters.json +396 -0
  125. package/data/docs/routes/home-style-props-flex-and-grid.json +496 -0
  126. package/data/docs/routes/home-style-props-interactivity.json +356 -0
  127. package/data/docs/routes/home-style-props-layout.json +422 -0
  128. package/data/docs/routes/home-style-props-list.json +116 -0
  129. package/data/docs/routes/home-style-props-sizing.json +244 -0
  130. package/data/docs/routes/home-style-props-spacing.json +228 -0
  131. package/data/docs/routes/home-style-props-svg.json +96 -0
  132. package/data/docs/routes/home-style-props-tables.json +116 -0
  133. package/data/docs/routes/home-style-props-transforms.json +216 -0
  134. package/data/docs/routes/home-style-props-transitions.json +216 -0
  135. package/data/docs/routes/home-style-props-typography.json +536 -0
  136. package/data/docs/routes/home-style-props.json +33 -0
  137. package/data/docs/routes/home.json +32 -0
  138. package/data/docs/routes/hooks-usecopytoclipboard.json +76 -0
  139. package/data/docs/routes/hooks-usehotkeys.json +117 -0
  140. package/data/docs/routes/hooks.json +33 -0
  141. package/data/docs/routes/icons.json +32 -0
  142. package/data/docs/routes/patterns-fields-date-range-picker-field.json +393 -0
  143. package/data/docs/routes/patterns-fields-money-input-field.json +415 -0
  144. package/data/docs/routes/patterns-fields-multiline-text-input-field.json +404 -0
  145. package/data/docs/routes/patterns-fields-number-input-field.json +470 -0
  146. package/data/docs/routes/patterns-fields-password-input-field.json +319 -0
  147. package/data/docs/routes/patterns-fields-search-input-field.json +382 -0
  148. package/data/docs/routes/patterns-fields-text-input-field.json +404 -0
  149. package/data/docs/routes/patterns-fields.json +78 -0
  150. package/data/docs/routes/patterns.json +34 -0
  151. package/data/docs/search-index.json +1 -0
  152. package/data/docs/types/Accordion.json +12 -0
  153. package/data/docs/types/AccordionContent.json +286 -0
  154. package/data/docs/types/AccordionHeader.json +891 -0
  155. package/data/docs/types/AccordionHeaderRightContent.json +27 -0
  156. package/data/docs/types/AccordionItem.json +242 -0
  157. package/data/docs/types/AccordionRoot.json +162 -0
  158. package/data/docs/types/Alert.json +12 -0
  159. package/data/docs/types/AlertActions.json +11 -0
  160. package/data/docs/types/AlertDescription.json +118 -0
  161. package/data/docs/types/AlertDismissButton.json +937 -0
  162. package/data/docs/types/AlertRoot.json +42 -0
  163. package/data/docs/types/AlertTitle.json +118 -0
  164. package/data/docs/types/Avatar.json +125 -0
  165. package/data/docs/types/Badge.json +64 -0
  166. package/data/docs/types/Body.json +67 -0
  167. package/data/docs/types/Box.json +85 -0
  168. package/data/docs/types/Button.json +1015 -0
  169. package/data/docs/types/Calendar.json +565 -0
  170. package/data/docs/types/Caption.json +67 -0
  171. package/data/docs/types/Card.json +12 -0
  172. package/data/docs/types/CardContent.json +27 -0
  173. package/data/docs/types/CardHeader.json +27 -0
  174. package/data/docs/types/CardRoot.json +106 -0
  175. package/data/docs/types/Cell.json +227 -0
  176. package/data/docs/types/Checkbox.json +897 -0
  177. package/data/docs/types/Code.json +112 -0
  178. package/data/docs/types/CollapsibleMotionContent.json +35 -0
  179. package/data/docs/types/CollapsibleMotionRoot.json +99 -0
  180. package/data/docs/types/CollapsibleMotionTrigger.json +71 -0
  181. package/data/docs/types/Column.json +101 -0
  182. package/data/docs/types/ColumnGroup.json +101 -0
  183. package/data/docs/types/ColumnHeader.json +193 -0
  184. package/data/docs/types/ComboBoxListBox.json +751 -0
  185. package/data/docs/types/ComboBoxOption.json +672 -0
  186. package/data/docs/types/ComboBoxPopover.json +786 -0
  187. package/data/docs/types/ComboBoxRoot.json +747 -0
  188. package/data/docs/types/ComboBoxSection.json +277 -0
  189. package/data/docs/types/ComboBoxTrigger.json +70 -0
  190. package/data/docs/types/Content.json +33 -0
  191. package/data/docs/types/DataTable.json +596 -0
  192. package/data/docs/types/DataTableBody.json +223 -0
  193. package/data/docs/types/DataTableFooter.json +27 -0
  194. package/data/docs/types/DataTableHeader.json +269 -0
  195. package/data/docs/types/DataTableManager.json +11 -0
  196. package/data/docs/types/DataTableRoot.json +590 -0
  197. package/data/docs/types/DataTableTable.json +271 -0
  198. package/data/docs/types/DateInput.json +792 -0
  199. package/data/docs/types/DatePicker.json +700 -0
  200. package/data/docs/types/DateRangePicker.json +936 -0
  201. package/data/docs/types/DateRangePickerField.json +1047 -0
  202. package/data/docs/types/DefaultPage.json +12 -0
  203. package/data/docs/types/DefaultPageActions.json +27 -0
  204. package/data/docs/types/DefaultPageBackLink.json +213 -0
  205. package/data/docs/types/DefaultPageContent.json +27 -0
  206. package/data/docs/types/DefaultPageFooter.json +27 -0
  207. package/data/docs/types/DefaultPageHeader.json +27 -0
  208. package/data/docs/types/DefaultPageRoot.json +106 -0
  209. package/data/docs/types/DefaultPageSubtitle.json +27 -0
  210. package/data/docs/types/DefaultPageTabNav.json +28 -0
  211. package/data/docs/types/DefaultPageTitle.json +27 -0
  212. package/data/docs/types/DialogBody.json +27 -0
  213. package/data/docs/types/DialogCloseTrigger.json +939 -0
  214. package/data/docs/types/DialogContent.json +27 -0
  215. package/data/docs/types/DialogFooter.json +27 -0
  216. package/data/docs/types/DialogHeader.json +27 -0
  217. package/data/docs/types/DialogRoot.json +138 -0
  218. package/data/docs/types/DialogTitle.json +27 -0
  219. package/data/docs/types/DialogTrigger.json +80 -0
  220. package/data/docs/types/DraggableList.json +12 -0
  221. package/data/docs/types/DraggableListField.json +894 -0
  222. package/data/docs/types/DraggableListItem.json +574 -0
  223. package/data/docs/types/DraggableListRoot.json +745 -0
  224. package/data/docs/types/Drawer.json +12 -0
  225. package/data/docs/types/DrawerBody.json +27 -0
  226. package/data/docs/types/DrawerCloseTrigger.json +939 -0
  227. package/data/docs/types/DrawerContent.json +27 -0
  228. package/data/docs/types/DrawerFooter.json +27 -0
  229. package/data/docs/types/DrawerHeader.json +27 -0
  230. package/data/docs/types/DrawerRoot.json +142 -0
  231. package/data/docs/types/DrawerTitle.json +27 -0
  232. package/data/docs/types/DrawerTrigger.json +80 -0
  233. package/data/docs/types/FieldErrors.getBuiltInMessage.json +11 -0
  234. package/data/docs/types/FieldErrors.getCustomMessage.json +9 -0
  235. package/data/docs/types/FieldErrors.json +109 -0
  236. package/data/docs/types/Flex.json +238 -0
  237. package/data/docs/types/Footer.json +67 -0
  238. package/data/docs/types/FormFieldDescription.json +11 -0
  239. package/data/docs/types/FormFieldError.json +11 -0
  240. package/data/docs/types/FormFieldInfoBox.json +27 -0
  241. package/data/docs/types/FormFieldInput.json +11 -0
  242. package/data/docs/types/FormFieldLabel.json +11 -0
  243. package/data/docs/types/FormFieldRoot.json +148 -0
  244. package/data/docs/types/Grid.json +253 -0
  245. package/data/docs/types/GridProps.json +11 -0
  246. package/data/docs/types/Group.json +143 -0
  247. package/data/docs/types/Header.json +67 -0
  248. package/data/docs/types/Heading.json +109 -0
  249. package/data/docs/types/Icon.json +112 -0
  250. package/data/docs/types/IconButton.json +1019 -0
  251. package/data/docs/types/IconToggleButton.json +787 -0
  252. package/data/docs/types/Image.json +373 -0
  253. package/data/docs/types/Indicator.json +67 -0
  254. package/data/docs/types/InlineSvg.json +98 -0
  255. package/data/docs/types/Item.json +67 -0
  256. package/data/docs/types/Kbd.json +118 -0
  257. package/data/docs/types/Link.json +380 -0
  258. package/data/docs/types/List.json +12 -0
  259. package/data/docs/types/ListIndicator.json +70 -0
  260. package/data/docs/types/ListItem.json +70 -0
  261. package/data/docs/types/ListRoot.json +124 -0
  262. package/data/docs/types/LoadingSpinner.json +87 -0
  263. package/data/docs/types/LocalizedField.json +460 -0
  264. package/data/docs/types/LocalizedStringFormatter.json +9 -0
  265. package/data/docs/types/MakeElementFocusable.json +196 -0
  266. package/data/docs/types/MenuContent.json +111 -0
  267. package/data/docs/types/MenuItem.json +671 -0
  268. package/data/docs/types/MenuRoot.json +670 -0
  269. package/data/docs/types/MenuSection.json +364 -0
  270. package/data/docs/types/MenuSubmenu.json +111 -0
  271. package/data/docs/types/MenuSubmenuTrigger.json +67 -0
  272. package/data/docs/types/MenuTrigger.json +906 -0
  273. package/data/docs/types/ModalPage.json +12 -0
  274. package/data/docs/types/ModalPageActions.json +27 -0
  275. package/data/docs/types/ModalPageContent.json +27 -0
  276. package/data/docs/types/ModalPageFooter.json +27 -0
  277. package/data/docs/types/ModalPageHeader.json +27 -0
  278. package/data/docs/types/ModalPageRoot.json +87 -0
  279. package/data/docs/types/ModalPageSubtitle.json +27 -0
  280. package/data/docs/types/ModalPageTabNav.json +28 -0
  281. package/data/docs/types/ModalPageTitle.json +27 -0
  282. package/data/docs/types/ModalPageTopBar.json +57 -0
  283. package/data/docs/types/MoneyInput.isEmpty.json +40 -0
  284. package/data/docs/types/MoneyInput.json +282 -0
  285. package/data/docs/types/MoneyInputField.json +379 -0
  286. package/data/docs/types/MoneyInputFieldProps.json +9 -0
  287. package/data/docs/types/MultilineTextInput.json +1194 -0
  288. package/data/docs/types/MultilineTextInputField.json +1269 -0
  289. package/data/docs/types/MultilineTextInputFieldProps.json +9 -0
  290. package/data/docs/types/NimbusI18nProvider.json +42 -0
  291. package/data/docs/types/NimbusI18nProviderProps.json +9 -0
  292. package/data/docs/types/NimbusProvider.json +270 -0
  293. package/data/docs/types/NumberInput.json +952 -0
  294. package/data/docs/types/NumberInputField.json +1004 -0
  295. package/data/docs/types/NumberInputFieldProps.json +9 -0
  296. package/data/docs/types/PageContent.json +11 -0
  297. package/data/docs/types/PageContentColumn.json +99 -0
  298. package/data/docs/types/PageContentRoot.json +114 -0
  299. package/data/docs/types/Pagination.json +159 -0
  300. package/data/docs/types/PasswordInput.json +1120 -0
  301. package/data/docs/types/PasswordInputField.json +1216 -0
  302. package/data/docs/types/PasswordInputFieldProps.json +9 -0
  303. package/data/docs/types/ProgressBar.json +280 -0
  304. package/data/docs/types/RadioInputOption.json +550 -0
  305. package/data/docs/types/RadioInputRoot.json +514 -0
  306. package/data/docs/types/RangeCalendar.json +618 -0
  307. package/data/docs/types/RichTextInput.json +134 -0
  308. package/data/docs/types/Root.json +122 -0
  309. package/data/docs/types/Row.json +67 -0
  310. package/data/docs/types/ScopedSearchInput.isEmpty.json +40 -0
  311. package/data/docs/types/ScopedSearchInput.json +253 -0
  312. package/data/docs/types/ScrollArea.json +179 -0
  313. package/data/docs/types/ScrollAreaElementIds.json +9 -0
  314. package/data/docs/types/ScrollAreaProps.json +9 -0
  315. package/data/docs/types/SearchInput.json +1165 -0
  316. package/data/docs/types/SearchInputField.json +1240 -0
  317. package/data/docs/types/Select.json +12 -0
  318. package/data/docs/types/SelectOption.json +572 -0
  319. package/data/docs/types/SelectOptionGroup.json +215 -0
  320. package/data/docs/types/SelectOptions.json +693 -0
  321. package/data/docs/types/SelectRoot.json +926 -0
  322. package/data/docs/types/Separator.json +65 -0
  323. package/data/docs/types/SimpleGrid.json +291 -0
  324. package/data/docs/types/Spacer.json +27 -0
  325. package/data/docs/types/SpacerProps.json +9 -0
  326. package/data/docs/types/SplitButton.json +203 -0
  327. package/data/docs/types/Stack.json +144 -0
  328. package/data/docs/types/Steps.json +12 -0
  329. package/data/docs/types/StepsChangeDetails.json +9 -0
  330. package/data/docs/types/StepsCompletedContent.json +28 -0
  331. package/data/docs/types/StepsCompletedContentProps.json +9 -0
  332. package/data/docs/types/StepsContent.json +43 -0
  333. package/data/docs/types/StepsContentProps.json +9 -0
  334. package/data/docs/types/StepsDescription.json +28 -0
  335. package/data/docs/types/StepsDescriptionProps.json +9 -0
  336. package/data/docs/types/StepsIndicator.json +28 -0
  337. package/data/docs/types/StepsIndicatorProps.json +9 -0
  338. package/data/docs/types/StepsItem.json +43 -0
  339. package/data/docs/types/StepsItemProps.json +9 -0
  340. package/data/docs/types/StepsList.json +28 -0
  341. package/data/docs/types/StepsListProps.json +9 -0
  342. package/data/docs/types/StepsNextTrigger.json +62 -0
  343. package/data/docs/types/StepsNextTriggerProps.json +9 -0
  344. package/data/docs/types/StepsNumber.json +28 -0
  345. package/data/docs/types/StepsNumberProps.json +9 -0
  346. package/data/docs/types/StepsPrevTrigger.json +62 -0
  347. package/data/docs/types/StepsPrevTriggerProps.json +9 -0
  348. package/data/docs/types/StepsRoot.json +183 -0
  349. package/data/docs/types/StepsRootProps.json +11 -0
  350. package/data/docs/types/StepsSeparator.json +28 -0
  351. package/data/docs/types/StepsSeparatorProps.json +9 -0
  352. package/data/docs/types/StepsStatus.json +57 -0
  353. package/data/docs/types/StepsStatusProps.json +9 -0
  354. package/data/docs/types/StepsTitle.json +28 -0
  355. package/data/docs/types/StepsTitleProps.json +9 -0
  356. package/data/docs/types/StepsTrigger.json +47 -0
  357. package/data/docs/types/StepsTriggerProps.json +9 -0
  358. package/data/docs/types/Switch.json +371 -0
  359. package/data/docs/types/TabListProps.json +9 -0
  360. package/data/docs/types/TabNav.json +12 -0
  361. package/data/docs/types/TabNavItem.json +300 -0
  362. package/data/docs/types/TabNavItemProps.json +9 -0
  363. package/data/docs/types/TabNavProps.json +9 -0
  364. package/data/docs/types/TabNavRoot.json +80 -0
  365. package/data/docs/types/TabPanelProps.json +9 -0
  366. package/data/docs/types/TabPanelsProps.json +9 -0
  367. package/data/docs/types/TabProps.json +9 -0
  368. package/data/docs/types/Table.json +9 -0
  369. package/data/docs/types/TableBody.json +67 -0
  370. package/data/docs/types/TableBodyProps.json +9 -0
  371. package/data/docs/types/TableCaption.json +67 -0
  372. package/data/docs/types/TableCaptionProps.json +9 -0
  373. package/data/docs/types/TableCell.json +227 -0
  374. package/data/docs/types/TableCellProps.json +9 -0
  375. package/data/docs/types/TableColumn.json +101 -0
  376. package/data/docs/types/TableColumnGroup.json +101 -0
  377. package/data/docs/types/TableColumnGroupProps.json +9 -0
  378. package/data/docs/types/TableColumnHeader.json +193 -0
  379. package/data/docs/types/TableColumnHeaderProps.json +9 -0
  380. package/data/docs/types/TableColumnProps.json +9 -0
  381. package/data/docs/types/TableFooter.json +67 -0
  382. package/data/docs/types/TableFooterProps.json +9 -0
  383. package/data/docs/types/TableHeader.json +67 -0
  384. package/data/docs/types/TableHeaderProps.json +9 -0
  385. package/data/docs/types/TableRoot.json +365 -0
  386. package/data/docs/types/TableRootProps.json +12 -0
  387. package/data/docs/types/TableRow.json +67 -0
  388. package/data/docs/types/TableRowProps.json +9 -0
  389. package/data/docs/types/TableScrollArea.json +82 -0
  390. package/data/docs/types/TableScrollAreaProps.json +9 -0
  391. package/data/docs/types/Tabs.json +12 -0
  392. package/data/docs/types/TabsList.json +110 -0
  393. package/data/docs/types/TabsPanel.json +112 -0
  394. package/data/docs/types/TabsPanels.json +108 -0
  395. package/data/docs/types/TabsRoot.json +211 -0
  396. package/data/docs/types/TabsTab.json +174 -0
  397. package/data/docs/types/TagGroup.json +12 -0
  398. package/data/docs/types/TagGroupRoot.json +306 -0
  399. package/data/docs/types/TagGroupTag.json +595 -0
  400. package/data/docs/types/TagGroupTagList.json +166 -0
  401. package/data/docs/types/Text.json +119 -0
  402. package/data/docs/types/TextInput.json +1156 -0
  403. package/data/docs/types/TextInputField.json +1263 -0
  404. package/data/docs/types/TimeInput.json +752 -0
  405. package/data/docs/types/ToastAction.json +9 -0
  406. package/data/docs/types/ToastManagerApi.json +9 -0
  407. package/data/docs/types/ToastOptions.json +9 -0
  408. package/data/docs/types/ToastOutlet.json +12 -0
  409. package/data/docs/types/ToastPlacement.json +9 -0
  410. package/data/docs/types/ToastPromiseOptions.json +9 -0
  411. package/data/docs/types/ToastType.json +9 -0
  412. package/data/docs/types/ToastVariant.json +9 -0
  413. package/data/docs/types/ToggleButton.json +789 -0
  414. package/data/docs/types/ToggleButtonGroup.json +9 -0
  415. package/data/docs/types/ToggleButtonGroupButton.json +331 -0
  416. package/data/docs/types/ToggleButtonGroupRoot.json +269 -0
  417. package/data/docs/types/Toolbar.json +176 -0
  418. package/data/docs/types/Tooltip.json +12 -0
  419. package/data/docs/types/TooltipContent.json +372 -0
  420. package/data/docs/types/TooltipRoot.json +179 -0
  421. package/data/docs/types/Trigger.json +69 -0
  422. package/data/docs/types/VisuallyHidden.json +93 -0
  423. package/data/docs/types/__object.json +12 -0
  424. package/data/docs/types/filters.json +11 -0
  425. package/data/docs/types/manifest.json +280 -0
  426. package/data/docs/types/toast.json +234 -0
  427. package/data/docs/types/useColorMode.json +13 -0
  428. package/data/docs/types/useColorModeValue.json +13 -0
  429. package/data/docs/types/useColorScheme.json +12 -0
  430. package/data/docs/types/useLocalizedStringFormatter.json +14 -0
  431. package/data/icons.json +21940 -0
  432. package/data/tokens.json +40061 -0
  433. package/dist/index.js +2516 -17
  434. package/package.json +25 -6
  435. package/dist/data-loader.d.ts +0 -102
  436. package/dist/data-loader.js +0 -104
  437. package/dist/index.d.ts +0 -13
  438. package/dist/server.d.ts +0 -9
  439. package/dist/server.js +0 -22
  440. package/dist/server.spec.d.ts +0 -1
  441. package/dist/server.spec.js +0 -69
  442. package/dist/tools/list-components.d.ts +0 -9
  443. package/dist/tools/list-components.js +0 -42
  444. package/dist/types.d.ts +0 -28
  445. package/dist/types.js +0 -4
  446. package/src/data-loader.ts +0 -226
  447. package/src/index.ts +0 -29
  448. package/src/server.spec.ts +0 -86
  449. package/src/server.ts +0 -28
  450. package/src/tools/list-components.ts +0 -49
  451. package/src/types.ts +0 -31
  452. package/tsconfig.json +0 -14
  453. package/vitest.config.ts +0 -9
@@ -0,0 +1,761 @@
1
+ {
2
+ "meta": {
3
+ "id": "Components-ComboBox",
4
+ "title": "Combo box",
5
+ "exportName": "ComboBox",
6
+ "description": "A combobox is an input that combines a text field with a pop-up list of options. It allows users to select a value from a predefined list or enter their own custom value.",
7
+ "lifecycleState": "Stable",
8
+ "order": 999,
9
+ "repoPath": "packages/nimbus/src/components/combobox/combobox.mdx",
10
+ "menu": [
11
+ "Components",
12
+ "Inputs",
13
+ "Combo box"
14
+ ],
15
+ "route": "components/inputs/combo-box",
16
+ "tags": [
17
+ "component",
18
+ "combobox",
19
+ "autocomplete",
20
+ "dropdown",
21
+ "typeahead"
22
+ ],
23
+ "toc": [
24
+ {
25
+ "value": "Overview",
26
+ "href": "#overview",
27
+ "depth": 2,
28
+ "numbering": [
29
+ 1,
30
+ 1
31
+ ],
32
+ "parent": "root"
33
+ },
34
+ {
35
+ "value": "Key features",
36
+ "href": "#key-features",
37
+ "depth": 3,
38
+ "numbering": [
39
+ 1,
40
+ 1,
41
+ 1
42
+ ],
43
+ "parent": "root"
44
+ },
45
+ {
46
+ "value": "Resources",
47
+ "href": "#resources",
48
+ "depth": 3,
49
+ "numbering": [
50
+ 1,
51
+ 1,
52
+ 2
53
+ ],
54
+ "parent": "root"
55
+ },
56
+ {
57
+ "value": "Variables",
58
+ "href": "#variables",
59
+ "depth": 2,
60
+ "numbering": [
61
+ 1,
62
+ 2
63
+ ],
64
+ "parent": "root"
65
+ },
66
+ {
67
+ "value": "Visual options",
68
+ "href": "#visual-options",
69
+ "depth": 3,
70
+ "numbering": [
71
+ 1,
72
+ 2,
73
+ 1
74
+ ],
75
+ "parent": "root"
76
+ },
77
+ {
78
+ "value": "Size",
79
+ "href": "#size",
80
+ "depth": 4,
81
+ "numbering": [
82
+ 1,
83
+ 2,
84
+ 1,
85
+ 1
86
+ ],
87
+ "parent": "root"
88
+ },
89
+ {
90
+ "value": "Appearances",
91
+ "href": "#appearances",
92
+ "depth": 4,
93
+ "numbering": [
94
+ 1,
95
+ 2,
96
+ 1,
97
+ 2
98
+ ],
99
+ "parent": "root"
100
+ },
101
+ {
102
+ "value": "Selection types",
103
+ "href": "#selection-types",
104
+ "depth": 4,
105
+ "numbering": [
106
+ 1,
107
+ 2,
108
+ 1,
109
+ 3
110
+ ],
111
+ "parent": "root"
112
+ },
113
+ {
114
+ "value": "Async Loading",
115
+ "href": "#async-loading",
116
+ "depth": 3,
117
+ "numbering": [
118
+ 1,
119
+ 2,
120
+ 2
121
+ ],
122
+ "parent": "root"
123
+ }
124
+ ],
125
+ "layout": "app-frame",
126
+ "tabs": [
127
+ {
128
+ "key": "overview",
129
+ "title": "Overview",
130
+ "order": 0
131
+ },
132
+ {
133
+ "key": "guidelines",
134
+ "title": "Guidelines",
135
+ "order": 2
136
+ },
137
+ {
138
+ "key": "dev",
139
+ "title": "Implementation",
140
+ "order": 3
141
+ },
142
+ {
143
+ "key": "a11y",
144
+ "title": "Accessibility",
145
+ "order": 4
146
+ }
147
+ ]
148
+ },
149
+ "mdx": "\n## Overview\n\nThe combobox component provides both selection and text input capabilities. It\ndisplays a list of options that users can choose from, while also allowing them\nto type directly into the text field. This functionality improves user\nexperience by assisting with value selection and, in some cases, prevents\ninvalid entries.\n\n### Key features\n\n- **External State Management:** Full control over async logic using React Stately's `useAsyncList`\n- **Debouncing:** Use `useDebouncedCallback` from `use-debounce` to reduce API calls while typing\n- **Minimum Search Length:** Implement custom validation in the `load` function\n- **Automatic Cancellation:** `useAsyncList` cancels previous requests via AbortSignal\n- **Loading States:** Access `list.loadingState` to display loading indicators\n- **Error Handling:** Manage errors in your `load` function with full control\n\n### Resources\n\nDeep dive into implementation details and access the Nimbus design library.\n\n[Figma library](https://www.figma.com/design/gHbAJGfcrCv7f2bgzUQgHq/NIMBUS-Guidelines?node-id=1852-4333&m=dev)\n\n## Variables\n\nGet familiar with the features.\n\n### Visual options\n\n#### Size\n\nThe medium size of the combobox is default, the small sizes are meant for more\ncompact uses.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"seasonal\", name: \"Seasonal\", description: \"Grower's choice\" },\n { id: \"greens\", name: \"Greens\", description: \"Leafy and nutritious\" },\n { id: \"fruit\", name: \"Fruit\", description: \"Sweet and tart\" },\n { id: \"root-vegetables\", name: \"Root Vegetables\", description: \"Firm and hearty\" }\n ];\n\n return (\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"flex-start\">\n <ComboBox.Root size=\"md\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n\n <ComboBox.Root size=\"sm\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n )\n}\n```\n\n#### Appearances\n\nCombobox supports solid and ghost styles.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"seasonal\", name: \"Seasonal\", description: \"Grower's choice\" },\n { id: \"greens\", name: \"Greens\", description: \"Leafy and nutritious\" },\n { id: \"fruit\", name: \"Fruit\", description: \"Sweet and tart\" },\n { id: \"root-vegetables\", name: \"Root Vegetables\", description: \"Firm and hearty\" }\n ];\n\n return (\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"flex-start\">\n <ComboBox.Root variant=\"solid\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n\n <ComboBox.Root variant=\"ghost\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n )\n}\n```\n\n#### Selection types\n\nSingle and multi-select is supported by comboboxes.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"surprise-me\", name: \"Surprise me\", description: \"A mix of delights\" },\n { id: \"toys-only\", name: \"Toys only\", description: \"Balls, stuffies, and tug toys\" },\n { id: \"treats-only\", name: \"Treats only\", description: \"Chews, bites, and crunchy snacks\" },\n { id: \"heavy-chewers\", name: \"Heavy chewers\", description: \"Only difficult to destroy toys\" }\n ];\n\n return (\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"flex-start\">\n <ComboBox.Root items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n\n <ComboBox.Root\n selectionMode=\"multiple\"\n items={options}\n placeholder=\"Search...\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n )\n}\n```\n\n### Async Loading\n\nComboBox supports async data loading by managing the async state externally\nusing React Stately's `useAsyncList` hook. This pattern gives you full control\nover data fetching and works seamlessly with ComboBox's filtering and selection\nfeatures.\n\n**Use cases:**\n\n- Loading large datasets on demand\n- Providing search suggestions from a server\n- Fetching filtered results based on user input\n\n```jsx live\nconst App = () => {\n const [error, setError] = useState(null);\n const [inputValue, setInputValue] = useState(\"\");\n const [isPending, setIsPending] = useState(false);\n const minSearchLength = 2;\n\n // Manage async data loading with useAsyncList\n const list = useAsyncList({\n async load({ signal, filterText }) {\n setError(null);\n\n // Don't load if below minimum search length\n if (!filterText || filterText.length < minSearchLength) {\n return { items: [] };\n }\n\n try {\n const response = await fetch(\n `https://swapi.py4e.com/api/people/?search=${encodeURIComponent(filterText)}`,\n { signal }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n return { items: data.results || [] };\n } catch (err) {\n if (err.name === \"AbortError\") {\n throw err;\n }\n setError(err.message);\n return { items: [] };\n }\n }\n });\n\n // Debounce only the API call, not the input update\n const debouncedSetFilterText = useDebouncedCallback(\n (value) => {\n setIsPending(false);\n list.setFilterText(value);\n },\n 300\n );\n\n // Handle input changes: update immediately for responsive typing, debounce API calls\n const handleInputChange = (value) => {\n setInputValue(value);\n setIsPending(true);\n debouncedSetFilterText(value);\n };\n\n // Compute combined loading state\n // Check all loading states from useAsyncList: \"loading\", \"loadingMore\", \"filtering\"\n const isLoading = isPending ||\n list.loadingState === \"loading\" ||\n list.loadingState === \"loadingMore\" ||\n list.loadingState === \"filtering\";\n\n return (\n <FormField.Root invalid={!!error}>\n <ComboBox.Root\n label=\"Search Star Wars Characters\"\n placeholder=\"Type to search (min 2 characters)...\"\n items={list.items}\n inputValue={inputValue}\n onInputChange={handleInputChange}\n isLoading={isLoading}\n getKey={(character) => character.name}\n // Disable client-side filtering since server already filters results\n filter={(nodes) => nodes}\n // Keep menu open during async loading to show loading/empty states\n allowsEmptyMenu={true}\n renderEmptyState={({ isLoading }) => {\n if (isLoading) {\n return <Text padding=\"300\">Loading...</Text>;\n }\n if (!inputValue || inputValue.length < minSearchLength) {\n return <Text padding=\"300\">Type at least {minSearchLength} characters to search</Text>;\n }\n return <Text padding=\"300\">No results found</Text>;\n }}\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(character) => (\n <ComboBox.Option textValue={character.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{character.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">\n {character.gender} • Born {character.birth_year}\n </Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n {error && <FormField.ErrorMessage>{error}</FormField.ErrorMessage>}\n </FormField.Root>\n );\n};\n```\n",
150
+ "views": {
151
+ "overview": {
152
+ "mdx": "\n## Overview\n\nThe combobox component provides both selection and text input capabilities. It\ndisplays a list of options that users can choose from, while also allowing them\nto type directly into the text field. This functionality improves user\nexperience by assisting with value selection and, in some cases, prevents\ninvalid entries.\n\n### Key features\n\n- **External State Management:** Full control over async logic using React Stately's `useAsyncList`\n- **Debouncing:** Use `useDebouncedCallback` from `use-debounce` to reduce API calls while typing\n- **Minimum Search Length:** Implement custom validation in the `load` function\n- **Automatic Cancellation:** `useAsyncList` cancels previous requests via AbortSignal\n- **Loading States:** Access `list.loadingState` to display loading indicators\n- **Error Handling:** Manage errors in your `load` function with full control\n\n### Resources\n\nDeep dive into implementation details and access the Nimbus design library.\n\n[Figma library](https://www.figma.com/design/gHbAJGfcrCv7f2bgzUQgHq/NIMBUS-Guidelines?node-id=1852-4333&m=dev)\n\n## Variables\n\nGet familiar with the features.\n\n### Visual options\n\n#### Size\n\nThe medium size of the combobox is default, the small sizes are meant for more\ncompact uses.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"seasonal\", name: \"Seasonal\", description: \"Grower's choice\" },\n { id: \"greens\", name: \"Greens\", description: \"Leafy and nutritious\" },\n { id: \"fruit\", name: \"Fruit\", description: \"Sweet and tart\" },\n { id: \"root-vegetables\", name: \"Root Vegetables\", description: \"Firm and hearty\" }\n ];\n\n return (\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"flex-start\">\n <ComboBox.Root size=\"md\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n\n <ComboBox.Root size=\"sm\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n )\n}\n```\n\n#### Appearances\n\nCombobox supports solid and ghost styles.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"seasonal\", name: \"Seasonal\", description: \"Grower's choice\" },\n { id: \"greens\", name: \"Greens\", description: \"Leafy and nutritious\" },\n { id: \"fruit\", name: \"Fruit\", description: \"Sweet and tart\" },\n { id: \"root-vegetables\", name: \"Root Vegetables\", description: \"Firm and hearty\" }\n ];\n\n return (\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"flex-start\">\n <ComboBox.Root variant=\"solid\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n\n <ComboBox.Root variant=\"ghost\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n )\n}\n```\n\n#### Selection types\n\nSingle and multi-select is supported by comboboxes.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"surprise-me\", name: \"Surprise me\", description: \"A mix of delights\" },\n { id: \"toys-only\", name: \"Toys only\", description: \"Balls, stuffies, and tug toys\" },\n { id: \"treats-only\", name: \"Treats only\", description: \"Chews, bites, and crunchy snacks\" },\n { id: \"heavy-chewers\", name: \"Heavy chewers\", description: \"Only difficult to destroy toys\" }\n ];\n\n return (\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"flex-start\">\n <ComboBox.Root items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n\n <ComboBox.Root\n selectionMode=\"multiple\"\n items={options}\n placeholder=\"Search...\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n )\n}\n```\n\n### Async Loading\n\nComboBox supports async data loading by managing the async state externally\nusing React Stately's `useAsyncList` hook. This pattern gives you full control\nover data fetching and works seamlessly with ComboBox's filtering and selection\nfeatures.\n\n**Use cases:**\n\n- Loading large datasets on demand\n- Providing search suggestions from a server\n- Fetching filtered results based on user input\n\n```jsx live\nconst App = () => {\n const [error, setError] = useState(null);\n const [inputValue, setInputValue] = useState(\"\");\n const [isPending, setIsPending] = useState(false);\n const minSearchLength = 2;\n\n // Manage async data loading with useAsyncList\n const list = useAsyncList({\n async load({ signal, filterText }) {\n setError(null);\n\n // Don't load if below minimum search length\n if (!filterText || filterText.length < minSearchLength) {\n return { items: [] };\n }\n\n try {\n const response = await fetch(\n `https://swapi.py4e.com/api/people/?search=${encodeURIComponent(filterText)}`,\n { signal }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n return { items: data.results || [] };\n } catch (err) {\n if (err.name === \"AbortError\") {\n throw err;\n }\n setError(err.message);\n return { items: [] };\n }\n }\n });\n\n // Debounce only the API call, not the input update\n const debouncedSetFilterText = useDebouncedCallback(\n (value) => {\n setIsPending(false);\n list.setFilterText(value);\n },\n 300\n );\n\n // Handle input changes: update immediately for responsive typing, debounce API calls\n const handleInputChange = (value) => {\n setInputValue(value);\n setIsPending(true);\n debouncedSetFilterText(value);\n };\n\n // Compute combined loading state\n // Check all loading states from useAsyncList: \"loading\", \"loadingMore\", \"filtering\"\n const isLoading = isPending ||\n list.loadingState === \"loading\" ||\n list.loadingState === \"loadingMore\" ||\n list.loadingState === \"filtering\";\n\n return (\n <FormField.Root invalid={!!error}>\n <ComboBox.Root\n label=\"Search Star Wars Characters\"\n placeholder=\"Type to search (min 2 characters)...\"\n items={list.items}\n inputValue={inputValue}\n onInputChange={handleInputChange}\n isLoading={isLoading}\n getKey={(character) => character.name}\n // Disable client-side filtering since server already filters results\n filter={(nodes) => nodes}\n // Keep menu open during async loading to show loading/empty states\n allowsEmptyMenu={true}\n renderEmptyState={({ isLoading }) => {\n if (isLoading) {\n return <Text padding=\"300\">Loading...</Text>;\n }\n if (!inputValue || inputValue.length < minSearchLength) {\n return <Text padding=\"300\">Type at least {minSearchLength} characters to search</Text>;\n }\n return <Text padding=\"300\">No results found</Text>;\n }}\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(character) => (\n <ComboBox.Option textValue={character.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{character.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">\n {character.gender} • Born {character.birth_year}\n </Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n {error && <FormField.ErrorMessage>{error}</FormField.ErrorMessage>}\n </FormField.Root>\n );\n};\n```\n",
153
+ "toc": [
154
+ {
155
+ "value": "Overview",
156
+ "href": "#overview",
157
+ "depth": 2,
158
+ "numbering": [
159
+ 1,
160
+ 1
161
+ ],
162
+ "parent": "root"
163
+ },
164
+ {
165
+ "value": "Key features",
166
+ "href": "#key-features",
167
+ "depth": 3,
168
+ "numbering": [
169
+ 1,
170
+ 1,
171
+ 1
172
+ ],
173
+ "parent": "root"
174
+ },
175
+ {
176
+ "value": "Resources",
177
+ "href": "#resources",
178
+ "depth": 3,
179
+ "numbering": [
180
+ 1,
181
+ 1,
182
+ 2
183
+ ],
184
+ "parent": "root"
185
+ },
186
+ {
187
+ "value": "Variables",
188
+ "href": "#variables",
189
+ "depth": 2,
190
+ "numbering": [
191
+ 1,
192
+ 2
193
+ ],
194
+ "parent": "root"
195
+ },
196
+ {
197
+ "value": "Visual options",
198
+ "href": "#visual-options",
199
+ "depth": 3,
200
+ "numbering": [
201
+ 1,
202
+ 2,
203
+ 1
204
+ ],
205
+ "parent": "root"
206
+ },
207
+ {
208
+ "value": "Size",
209
+ "href": "#size",
210
+ "depth": 4,
211
+ "numbering": [
212
+ 1,
213
+ 2,
214
+ 1,
215
+ 1
216
+ ],
217
+ "parent": "root"
218
+ },
219
+ {
220
+ "value": "Appearances",
221
+ "href": "#appearances",
222
+ "depth": 4,
223
+ "numbering": [
224
+ 1,
225
+ 2,
226
+ 1,
227
+ 2
228
+ ],
229
+ "parent": "root"
230
+ },
231
+ {
232
+ "value": "Selection types",
233
+ "href": "#selection-types",
234
+ "depth": 4,
235
+ "numbering": [
236
+ 1,
237
+ 2,
238
+ 1,
239
+ 3
240
+ ],
241
+ "parent": "root"
242
+ },
243
+ {
244
+ "value": "Async Loading",
245
+ "href": "#async-loading",
246
+ "depth": 3,
247
+ "numbering": [
248
+ 1,
249
+ 2,
250
+ 2
251
+ ],
252
+ "parent": "root"
253
+ }
254
+ ]
255
+ },
256
+ "a11y": {
257
+ "mdx": "\n## Accessibility\n\nAccessibility ensures that digital content and functionality are usable by\neveryone, including people with disabilities, by addressing visual, auditory,\ncognitive, and physical limitations.\n\n```jsx live\nconst App = () => {\n const options = [\n { id: \"seasonal\", name: \"Seasonal\", description: \"Grower's choice\" },\n { id: \"greens\", name: \"Greens\", description: \"Leafy and nutritious\" },\n { id: \"fruit\", name: \"Fruit\", description: \"Sweet and tart\" },\n { id: \"root-vegetables\", name: \"Root Vegetables\", description: \"Firm and hearty\" }\n ];\n\n return (\n <ComboBox.Root size=\"md\" items={options} placeholder=\"Search...\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">{item.description}</Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n )\n}\n```\n\n### Accessibility standards\n\n- Always provide a visible label for the input.\n- If a visible label is not used, provide an `aria-label` or `aria-labelledby`\n for screen readers.\n- Ensure proper ARIA roles (`combobox`, `listbox`, `option`) and attributes\n (`aria-expanded`, `aria-autocomplete`, `aria-activedescendant`) are used.\n- Ensure sufficient color contrast for all elements (text, borders, icons).\n- Communicate errors clearly, using both visual cues (e.g., color) and\n descriptive error messages.\n- Clearly indicate the focused and selected states.\n\n### Resources\n\n- [Adobe Spectrum ComboBox](https://spectrum.adobe.com/page/combo-box/)\n- [React Spectrum ComboBox](https://react-spectrum.adobe.com/react-spectrum/ComboBox.html)\n- [W3C ARIA Authoring Practices Guide (APG) - Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)\n",
258
+ "toc": [
259
+ {
260
+ "value": "Accessibility",
261
+ "href": "#accessibility",
262
+ "depth": 2,
263
+ "numbering": [
264
+ 1,
265
+ 1
266
+ ],
267
+ "parent": "root"
268
+ },
269
+ {
270
+ "value": "Accessibility standards",
271
+ "href": "#accessibility-standards",
272
+ "depth": 3,
273
+ "numbering": [
274
+ 1,
275
+ 1,
276
+ 1
277
+ ],
278
+ "parent": "root"
279
+ },
280
+ {
281
+ "value": "Resources",
282
+ "href": "#resources",
283
+ "depth": 3,
284
+ "numbering": [
285
+ 1,
286
+ 1,
287
+ 2
288
+ ],
289
+ "parent": "root"
290
+ }
291
+ ]
292
+ },
293
+ "dev": {
294
+ "mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { ComboBox, type ComboBoxRootProps } from \"@commercetools/nimbus\";\n```\n\n### Basic usage\n\nComboBox supports two patterns for defining options:\n\n#### With items prop (recommended for dynamic data)\n\nUse the `items` prop when working with data from APIs, state, or dynamic\nsources:\n\n```jsx live-dev\nconst App = () => {\n const animals = [\n { id: \"koala\", name: \"Koala\" },\n { id: \"panda\", name: \"Panda\" },\n { id: \"giraffe\", name: \"Giraffe\" },\n ];\n\n return (\n <ComboBox.Root\n items={animals}\n placeholder=\"Select an animal...\"\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n#### With static children (requires textValue)\n\nFor static lists, you **must** include the `textValue` prop on each option for\nselections to display correctly:\n\n```jsx live-dev\nconst App = () => (\n <ComboBox.Root\n placeholder=\"Select an animal...\"\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\" textValue=\"Koala\">\n Koala\n </ComboBox.Option>\n <ComboBox.Option id=\"panda\" textValue=\"Panda\">\n Panda\n </ComboBox.Option>\n <ComboBox.Option id=\"giraffe\" textValue=\"Giraffe\">\n Giraffe\n </ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n);\n```\n\n> [!WARNING]\\\n> Without `textValue` on static children, selected values will not display in\n> the input field or as tags. Always include `textValue` when not using the\n> `items` prop.\n\n## Usage examples\n\n### Size options\n\nThe `sm` and `md` size variants are available to match your interface density:\n\n```jsx live-dev\nconst App = () => {\n const options = [\n { id: \"1\", name: \"Option 1\" },\n { id: \"2\", name: \"Option 2\" },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n size=\"sm\"\n items={options}\n placeholder=\"Small size...\"\n aria-label=\"Small combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <ComboBox.Root\n size=\"md\"\n items={options}\n placeholder=\"Medium size...\"\n aria-label=\"Medium combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n );\n};\n```\n\n### Visual variants\n\nChoose between `solid` and `ghost` variants to match your design context:\n\n```jsx live-dev\nconst App = () => {\n const options = [\n { id: \"1\", name: \"Option 1\" },\n { id: \"2\", name: \"Option 2\" },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n variant=\"solid\"\n items={options}\n placeholder=\"Solid variant...\"\n aria-label=\"Solid combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <ComboBox.Root\n variant=\"ghost\"\n items={options}\n placeholder=\"Ghost variant...\"\n aria-label=\"Ghost combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Stack>\n );\n};\n```\n\n### Single-select mode\n\nBy default, ComboBox operates in single-select mode where users can select one\noption at a time:\n\n```jsx live-dev\nconst App = () => {\n const [selectedKeys, setSelectedKeys] = useState([]);\n\n const fruits = [\n { id: \"apple\", name: \"Apple\" },\n { id: \"banana\", name: \"Banana\" },\n { id: \"orange\", name: \"Orange\" },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n items={fruits}\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"single\"\n placeholder=\"Select a fruit...\"\n aria-label=\"Select a fruit\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <Text fontSize=\"sm\">\n {selectedKeys.length > 0\n ? `Selected: ${selectedKeys[0]}`\n : \"No selection\"}\n </Text>\n </Stack>\n );\n};\n```\n\n### Multi-select mode\n\nEnable multi-select to allow users to choose multiple options displayed as tags:\n\n```jsx live-dev\nconst App = () => {\n const [selectedKeys, setSelectedKeys] = useState([]);\n\n const animals = [\n { id: \"koala\", name: \"Koala\" },\n { id: \"panda\", name: \"Panda\" },\n { id: \"giraffe\", name: \"Giraffe\" },\n { id: \"elephant\", name: \"Elephant\" },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n items={animals}\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"multiple\"\n placeholder=\"Select animals...\"\n aria-label=\"Select animals\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <Text fontSize=\"sm\">\n Selected: {selectedKeys.length} item\n {selectedKeys.length !== 1 ? \"s\" : \"\"}\n </Text>\n </Stack>\n );\n};\n```\n\n### Leading element\n\nAdd a leading icon to enhance the visual context:\n\n```jsx live-dev\nconst App = () => {\n const animals = [\n { id: \"koala\", name: \"Koala\" },\n { id: \"panda\", name: \"Panda\" },\n { id: \"giraffe\", name: \"Giraffe\" },\n ];\n\n return (\n <ComboBox.Root\n items={animals}\n leadingElement={<Icons.Search aria-hidden=\"true\" />}\n placeholder=\"Search...\"\n aria-label=\"Search for an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Disabled state\n\nUse `isDisabled` to disable the combobox:\n\n```jsx live-dev\nconst App = () => {\n const options = [\n { id: \"1\", name: \"Option 1\" },\n { id: \"2\", name: \"Option 2\" },\n ];\n\n return (\n <ComboBox.Root\n items={options}\n isDisabled\n placeholder=\"Disabled...\"\n aria-label=\"Disabled combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Invalid state\n\nMark the combobox as invalid for form validation:\n\n```jsx live-dev\nconst App = () => {\n const options = [\n { id: \"1\", name: \"Option 1\" },\n { id: \"2\", name: \"Option 2\" },\n ];\n\n return (\n <ComboBox.Root\n items={options}\n isInvalid\n placeholder=\"Select an option...\"\n aria-label=\"Invalid combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Read-only state\n\nUse `isReadOnly` for display-only scenarios:\n\n```jsx live-dev\nconst App = () => {\n const fruits = [\n { id: \"apple\", name: \"Apple\" },\n { id: \"banana\", name: \"Banana\" },\n ];\n\n return (\n <ComboBox.Root\n items={fruits}\n isReadOnly\n selectedKeys={[\"apple\"]}\n aria-label=\"Read-only combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Option groups\n\nOrganize options into sections with headers:\n\n```jsx live-dev\nconst App = () => {\n const foodCategories = [\n {\n id: \"fruits\",\n name: \"Fruits\",\n children: [\n { id: \"apple\", name: \"Apple\" },\n { id: \"banana\", name: \"Banana\" },\n { id: \"orange\", name: \"Orange\" },\n ],\n },\n {\n id: \"vegetables\",\n name: \"Vegetables\",\n children: [\n { id: \"carrot\", name: \"Carrot\" },\n { id: \"broccoli\", name: \"Broccoli\" },\n { id: \"spinach\", name: \"Spinach\" },\n ],\n },\n ];\n\n return (\n <ComboBox.Root\n items={foodCategories}\n placeholder=\"Select a food...\"\n aria-label=\"Select a food\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(category) => (\n <ComboBox.Section\n id={category.id}\n label={category.name}\n items={category.children}\n >\n {(item) => (\n <ComboBox.Option id={item.id} textValue={item.name}>\n {item.name}\n </ComboBox.Option>\n )}\n </ComboBox.Section>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Dynamic options with items\n\n`ComboBox.ListBox` supports the `items` prop, a pattern unique to React Aria\nComponents (inherited from React Aria's `ListBox` component). This pattern\nprovides an efficient way to render dynamic or large lists of options.\n\n**When to use `items` vs static children:**\n\n- **Use `items` when:**\n - Rendering options from dynamic data (API responses, filtered lists, computed\n arrays)\n - Working with large lists (better performance through virtualization support)\n - Options change frequently based on user input or state\n - You need to map over data structures\n\n- **Use static children when:**\n - Options are known at compile time\n - You have a small, fixed set of options\n - You prefer the simpler JSX syntax for readability\n\n**Static children example:**\n\n```jsx live-dev\nconst App = () => (\n <ComboBox.Root\n placeholder=\"Select an animal...\"\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\" textValue=\"Koala\">\n Koala\n </ComboBox.Option>\n <ComboBox.Option id=\"panda\" textValue=\"Panda\">\n Panda\n </ComboBox.Option>\n <ComboBox.Option id=\"giraffe\" textValue=\"Giraffe\">\n Giraffe\n </ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n);\n```\n\n**Dynamic items example:**\n\nWhen using `items`, children must be a render function that receives each item\nand returns a `ComboBox.Option`. This example demonstrates using `items` with\ndata that could come from an API, state, or computed values:\n\n```jsx live-dev\nconst App = () => {\n // In a real app, this data might come from an API, props, or state.\n const animals = [\n { id: \"koala\", name: \"Koala\" },\n { id: \"panda\", name: \"Panda\" },\n { id: \"giraffe\", name: \"Giraffe\" },\n { id: \"elephant\", name: \"Elephant\" },\n ];\n\n return (\n <ComboBox.Root\n items={animals}\n placeholder=\"Select an animal...\"\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\nThe `items` pattern enables React Aria to optimize rendering and provides better\nsupport for features like virtualization, which can improve performance with\nlarge option lists.\n\n> [!TIP]\\\n> See [React Aria's ComboBox documentation](https://react-spectrum.adobe.com/react-aria/ComboBox.html#sections)\n> for complete API reference and advanced usage, including static and dynamic\n> items.\n\n### Custom filtering\n\nCustomize how options are filtered using the `filter` prop:\n\n```jsx live-dev\nconst App = () => {\n const animals = [\n { id: 1, name: \"Koala\", category: \"Marsupial\" },\n { id: 2, name: \"Panda\", category: \"Bear\" },\n { id: 3, name: \"Giraffe\", category: \"Mammal\" },\n { id: 4, name: \"Elephant\", category: \"Mammal\" },\n ];\n\n // Use the built-in multi-property filter\n const multiPropertyFilter = useCallback(\n ComboBox.filters.createMultiPropertyFilter([\"name\", \"category\"]),\n []\n );\n\n return (\n <ComboBox.Root\n items={animals}\n filter={multiPropertyFilter}\n aria-label=\"Search animals\"\n placeholder=\"Search by name or category...\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option key={item.id} id={item.id}>\n {item.name} ({item.category})\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Custom options creation\n\nAllow users to create new options by typing and pressing Enter:\n\n```jsx live-dev\nconst App = () => {\n const [items, setItems] = useState([\n { id: 1, name: \"Apple\" },\n { id: 2, name: \"Banana\" },\n { id: 3, name: \"Orange\" },\n ]);\n const [selectedKeys, setSelectedKeys] = useState([]);\n\n const handleCreateOption = useCallback((newItem) => {\n console.log(\"Created:\", newItem.name);\n }, []);\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n items={items}\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"multiple\"\n allowsCustomOptions\n getNewOptionData={(inputValue) => ({\n id: Date.now(),\n name: inputValue,\n })}\n onCreateOption={handleCreateOption}\n aria-label=\"Select or create fruits\"\n placeholder=\"Type to search or create...\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option key={item.id} id={item.id}>\n {item.name}\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <Text fontSize=\"sm\">\n Type a custom fruit and press Enter to create it!\n </Text>\n </Stack>\n );\n};\n```\n\n### Async data loading\n\nLoad options asynchronously with built-in debouncing and loading states:\n\n```jsx live-dev\nconst App = () => {\n // Simulated API call\n const loadPokemon = useCallback(async (filterText, signal) => {\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n const allPokemon = [\n { id: 1, name: \"Bulbasaur\" },\n { id: 2, name: \"Charmander\" },\n { id: 3, name: \"Squirtle\" },\n { id: 4, name: \"Pikachu\" },\n { id: 5, name: \"Jigglypuff\" },\n ];\n\n return allPokemon.filter((p) =>\n p.name.toLowerCase().includes(filterText.toLowerCase())\n );\n }, []);\n\n return (\n <ComboBox.Root\n async={{\n load: loadPokemon,\n minSearchLength: 2,\n debounce: 300,\n }}\n aria-label=\"Search Pokemon\"\n placeholder=\"Type to search...\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option key={item.id} id={item.id}>\n {item.name}\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n### Controlled input value\n\nControl the input text for advanced scenarios:\n\n```jsx live-dev\nconst App = () => {\n const [inputValue, setInputValue] = useState(\"\");\n const [selectedKeys, setSelectedKeys] = useState([]);\n\n const fruits = [\n { id: \"apple\", name: \"Apple\" },\n { id: \"banana\", name: \"Banana\" },\n { id: \"orange\", name: \"Orange\" },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n items={fruits}\n inputValue={inputValue}\n onInputChange={setInputValue}\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"single\"\n placeholder=\"Select a fruit...\"\n aria-label=\"Controlled combobox\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <Text fontSize=\"sm\">Input: {inputValue || \"(empty)\"}</Text>\n </Stack>\n );\n};\n```\n\n### Menu trigger behavior\n\nControl when the dropdown menu opens using `menuTrigger`:\n\n```jsx live-dev\nconst App = () => {\n const options = [\n { id: \"1\", name: \"Option 1\" },\n { id: \"2\", name: \"Option 2\" },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <Box>\n <Text fontSize=\"sm\" fontWeight=\"medium\" mb=\"200\">\n menuTrigger=\"focus\" (default) - Opens when clicking input or toggle\n button\n </Text>\n <ComboBox.Root\n items={options}\n menuTrigger=\"focus\"\n placeholder=\"Click to open...\"\n aria-label=\"Focus trigger\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Box>\n <Box>\n <Text fontSize=\"sm\" fontWeight=\"medium\" mb=\"200\">\n menuTrigger=\"input\" - Opens only when typing (try typing in the input)\n </Text>\n <ComboBox.Root\n items={options}\n menuTrigger=\"input\"\n placeholder=\"Type to open...\"\n aria-label=\"Input trigger\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Box>\n <Box>\n <Text fontSize=\"sm\" fontWeight=\"medium\" mb=\"200\">\n menuTrigger=\"manual\" - Opens only via toggle button (input click does\n nothing)\n </Text>\n <ComboBox.Root\n items={options}\n menuTrigger=\"manual\"\n placeholder=\"Use button to open...\"\n aria-label=\"Manual trigger\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>{item.name}</ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </Box>\n </Stack>\n );\n};\n```\n\n## Component requirements\n\n### Accessibility\n\nThe ComboBox handles most accessibility requirements internally via React Aria.\n\nIf your use case requires tracking and analytics for this component, it is good\npractice to add a **persistent**, **unique** id:\n\n```tsx\nconst PERSISTENT_ID = \"example-combobox\";\n\nexport const Example = () => (\n <ComboBox.Root id={PERSISTENT_ID} aria-label=\"Select an option\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"1\">Option 1</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n);\n```\n\n#### Keyboard navigation\n\nThe component supports full keyboard interaction:\n\n- `Tab` / `Shift+Tab`: Navigate to/from the combobox\n- `Arrow Down`: Opens the menu and moves focus to the next option\n- `Arrow Up`: Opens the menu and moves focus to the previous option\n- `Enter`: Selects the focused option (or creates custom option if\n `allowsCustomOptions` is enabled)\n- `Escape`: Closes the menu\n- `Home` / `End`: Jump to first/last option\n- Type to filter: Type characters to filter options in real-time\n\n## API reference\n\n<PropsTable id=\"ComboBox\" />\n\n## Common patterns\n\n### Product search with categories\n\nA common e-commerce pattern combining filtering, grouping, and multi-select:\n\n```jsx live-dev\nconst App = () => {\n const [selectedKeys, setSelectedKeys] = useState([]);\n\n const products = [\n { id: 1, name: \"MacBook Pro\", category: \"Electronics\" },\n { id: 2, name: \"iPhone\", category: \"Electronics\" },\n { id: 3, name: \"Desk Chair\", category: \"Furniture\" },\n { id: 4, name: \"Standing Desk\", category: \"Furniture\" },\n { id: 5, name: \"Coffee Maker\", category: \"Appliances\" },\n ];\n\n // Group products by category\n const groupedByCategory = products.reduce((acc, product) => {\n if (!acc[product.category]) {\n acc[product.category] = [];\n }\n acc[product.category].push(product);\n return acc;\n }, {});\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ComboBox.Root\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"multiple\"\n placeholder=\"Search products...\"\n aria-label=\"Select products\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {Object.entries(groupedByCategory).map(([category, items]) => (\n <ComboBox.Section key={category} label={category}>\n {items.map((item) => (\n <ComboBox.Option key={item.id} id={item.id}>\n {item.name}\n </ComboBox.Option>\n ))}\n </ComboBox.Section>\n ))}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n <Text fontSize=\"sm\">\n {selectedKeys.length} product{selectedKeys.length !== 1 ? \"s\" : \"\"}{\" \"}\n selected\n </Text>\n </Stack>\n );\n};\n```\n\n### Tag selection with creation\n\nEnable users to select from existing tags or create new ones:\n\n```jsx live-dev\nconst App = () => {\n const [tags, setTags] = useState([\n { id: 1, label: \"React\" },\n { id: 2, label: \"TypeScript\" },\n { id: 3, label: \"JavaScript\" },\n ]);\n const [selectedKeys, setSelectedKeys] = useState([]);\n\n const handleCreateTag = useCallback((newTag) => {\n setTags((prev) => [...prev, newTag]);\n }, []);\n\n return (\n <ComboBox.Root\n items={tags}\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"multiple\"\n allowsCustomOptions\n getNewOptionData={(inputValue) => ({\n id: Date.now(),\n label: inputValue,\n })}\n onCreateOption={handleCreateTag}\n getTextValue={(item) => item.label}\n placeholder=\"Add tags...\"\n aria-label=\"Select or create tags\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option key={item.id} id={item.id}>\n {item.label}\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using ComboBox\nwithin your application. As the component's internal functionality is already\ntested by Nimbus, these patterns help you verify your integration and\napplication-specific logic.\n\n### Basic Rendering Tests\n\nVerify the component renders with expected elements\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Basic rendering\", () => {\n it(\"renders combobox input\", async () => {\n render(\n <NimbusProvider>\n <ComboBox.Root aria-label=\"Select an animal\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n <ComboBox.Option id=\"panda\">Panda</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n await waitFor(() => {\n expect(screen.getByRole(\"combobox\")).toBeInTheDocument();\n });\n });\n\n it(\"renders with placeholder text\", async () => {\n render(\n <NimbusProvider>\n <ComboBox.Root\n placeholder=\"Search animals...\"\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n await waitFor(() => {\n expect(\n screen.getByPlaceholderText(\"Search animals...\")\n ).toBeInTheDocument();\n });\n });\n});\n```\n\n### Interaction Tests\n\nTest user interactions with the component\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Interactions\", () => {\n it(\"opens dropdown when input is clicked\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <ComboBox.Root aria-label=\"Select an animal\" menuTrigger=\"focus\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n <ComboBox.Option id=\"panda\">Panda</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n const combobox = screen.getByRole(\"combobox\");\n await user.click(combobox);\n\n // The listbox is rendered in a portal outside the component tree\n // Query document directly and wait for it to appear\n await waitFor(() => {\n expect(document.querySelector('[role=\"listbox\"]')).toBeInTheDocument();\n });\n });\n\n it(\"calls onInputChange when typing\", async () => {\n const user = userEvent.setup();\n const handleInputChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ComboBox.Root\n onInputChange={handleInputChange}\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n <ComboBox.Option id=\"panda\">Panda</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n const combobox = screen.getByRole(\"combobox\");\n await user.type(combobox, \"test\");\n\n expect(handleInputChange).toHaveBeenCalled();\n });\n\n it(\"selects option when clicked\", async () => {\n const user = userEvent.setup();\n const handleSelectionChange = vi.fn();\n render(\n <NimbusProvider>\n <ComboBox.Root\n onSelectionChange={handleSelectionChange}\n menuTrigger=\"focus\"\n aria-label=\"Select an animal\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n <ComboBox.Option id=\"panda\">Panda</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n const combobox = screen.getByRole(\"combobox\");\n await user.click(combobox);\n\n // Wait for options to appear in the portal\n await waitFor(() => {\n expect(\n document.querySelectorAll('[role=\"option\"]').length\n ).toBeGreaterThan(0);\n });\n\n // Get options after they've rendered\n const options = document.querySelectorAll('[role=\"option\"]');\n await user.click(options[0]);\n\n expect(handleSelectionChange).toHaveBeenCalledWith([\"koala\"]);\n });\n});\n```\n\n### Single-Select Mode Tests\n\nTest single-select behavior\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Single-select mode\", () => {\n it(\"supports single selection mode\", async () => {\n const handleSelectionChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ComboBox.Root\n selectionMode=\"single\"\n onSelectionChange={handleSelectionChange}\n aria-label=\"Select a fruit\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"apple\">Apple</ComboBox.Option>\n <ComboBox.Option id=\"banana\">Banana</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n // Verify single-select mode renders correctly\n await waitFor(() => {\n expect(screen.getByRole(\"combobox\")).toBeInTheDocument();\n });\n expect(handleSelectionChange).not.toHaveBeenCalled();\n });\n});\n```\n\n### Multi-Select Mode Tests\n\nTest multi-select behavior with tags\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Multi-select mode\", () => {\n it(\"supports multiple selection mode\", async () => {\n const handleSelectionChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ComboBox.Root\n selectionMode=\"multiple\"\n onSelectionChange={handleSelectionChange}\n aria-label=\"Select animals\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n <ComboBox.Option id=\"panda\">Panda</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n // Verify multi-select mode renders correctly\n await waitFor(() => {\n expect(screen.getByRole(\"combobox\")).toBeInTheDocument();\n });\n expect(handleSelectionChange).not.toHaveBeenCalled();\n });\n});\n```\n\n### Disabled State Tests\n\nTest that the combobox is properly disabled\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Disabled state\", () => {\n it(\"renders disabled combobox\", async () => {\n render(\n <NimbusProvider>\n <ComboBox.Root isDisabled aria-label=\"Select an animal\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n await waitFor(() => {\n expect(screen.getByRole(\"combobox\")).toBeDisabled();\n });\n });\n});\n```\n\n### Invalid State Tests\n\nTest that the combobox is properly marked as invalid\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Invalid state\", () => {\n it(\"renders invalid combobox\", async () => {\n render(\n <NimbusProvider>\n <ComboBox.Root isInvalid aria-label=\"Select an animal\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Option id=\"koala\">Koala</ComboBox.Option>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n // Invalid state is on the root element\n await waitFor(() => {\n const combobox = screen.getByRole(\"combobox\");\n const comboboxRoot = combobox.closest('[data-invalid=\"true\"]');\n expect(comboboxRoot).toBeInTheDocument();\n });\n });\n});\n```\n\n### Custom Options Creation Tests\n\nTest creating custom options with allowsCustomOptions\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Custom options\", () => {\n it(\"creates new option when Enter is pressed\", async () => {\n const user = userEvent.setup();\n\n type FruitItem = {\n id: number;\n name: string;\n };\n\n const handleCreateOption = vi.fn();\n\n const TestComponent = () => {\n const [items, setItems] = useState<FruitItem[]>([\n { id: 1, name: \"Apple\" },\n { id: 2, name: \"Banana\" },\n ]);\n\n const getNewOptionData = useCallback(\n (inputValue: string): FruitItem => ({\n id: Date.now(),\n name: inputValue,\n }),\n []\n );\n\n const onCreateOption = useCallback((newItem: FruitItem) => {\n setItems((prev) => [...prev, newItem]);\n handleCreateOption(newItem);\n }, []);\n\n return (\n <NimbusProvider>\n <ComboBox.Root\n items={items}\n selectionMode=\"multiple\"\n allowsCustomOptions\n getNewOptionData={getNewOptionData}\n onCreateOption={onCreateOption}\n aria-label=\"Select or create fruits\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item: FruitItem) => (\n <ComboBox.Option key={item.id} id={item.id}>\n {item.name}\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n };\n\n render(<TestComponent />);\n\n const combobox = screen.getByRole(\"combobox\");\n await user.click(combobox);\n await user.type(combobox, \"Orange\");\n await user.keyboard(\"{Enter}\");\n\n expect(handleCreateOption).toHaveBeenCalled();\n // TypeScript knows the mock call argument type from the implementation\n const createdItem = handleCreateOption.mock.calls[0][0] as FruitItem;\n expect(createdItem.name).toBe(\"Orange\");\n });\n});\n```\n\n### Option Groups Tests\n\nTest rendering with option groups\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { useState, useCallback } from \"react\";\nimport { ComboBox, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ComboBox - Option groups\", () => {\n it(\"renders grouped options\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <ComboBox.Root menuTrigger=\"focus\" aria-label=\"Select a food\">\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n <ComboBox.Section label=\"Fruits\">\n <ComboBox.Option id=\"apple\">Apple</ComboBox.Option>\n <ComboBox.Option id=\"banana\">Banana</ComboBox.Option>\n </ComboBox.Section>\n <ComboBox.Section label=\"Vegetables\">\n <ComboBox.Option id=\"carrot\">Carrot</ComboBox.Option>\n </ComboBox.Section>\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n </NimbusProvider>\n );\n\n const combobox = screen.getByRole(\"combobox\");\n await user.click(combobox);\n\n // Wait for listbox to appear in portal, then check for section labels\n await waitFor(() => {\n expect(document.querySelector('[role=\"listbox\"]')).toBeInTheDocument();\n });\n\n // Section labels should be visible in the document\n await waitFor(() => {\n expect(screen.getByText(\"Fruits\")).toBeInTheDocument();\n expect(screen.getByText(\"Vegetables\")).toBeInTheDocument();\n });\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-combobox--docs)\n- [React Aria ComboBox](https://react-spectrum.adobe.com/react-aria/ComboBox.html)\n- [ARIA Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)\n",
295
+ "toc": [
296
+ {
297
+ "value": "Getting started",
298
+ "href": "#getting-started",
299
+ "depth": 2,
300
+ "numbering": [
301
+ 1,
302
+ 1
303
+ ],
304
+ "parent": "root"
305
+ },
306
+ {
307
+ "value": "Import",
308
+ "href": "#import",
309
+ "depth": 3,
310
+ "numbering": [
311
+ 1,
312
+ 1,
313
+ 1
314
+ ],
315
+ "parent": "root"
316
+ },
317
+ {
318
+ "value": "Basic usage",
319
+ "href": "#basic-usage",
320
+ "depth": 3,
321
+ "numbering": [
322
+ 1,
323
+ 1,
324
+ 2
325
+ ],
326
+ "parent": "root"
327
+ },
328
+ {
329
+ "value": "With items prop (recommended for dynamic data)",
330
+ "href": "#with-items-prop-recommended-for-dynamic-data",
331
+ "depth": 4,
332
+ "numbering": [
333
+ 1,
334
+ 1,
335
+ 2,
336
+ 1
337
+ ],
338
+ "parent": "root"
339
+ },
340
+ {
341
+ "value": "With static children (requires textValue)",
342
+ "href": "#with-static-children-requires-textvalue",
343
+ "depth": 4,
344
+ "numbering": [
345
+ 1,
346
+ 1,
347
+ 2,
348
+ 2
349
+ ],
350
+ "parent": "root"
351
+ },
352
+ {
353
+ "value": "Usage examples",
354
+ "href": "#usage-examples",
355
+ "depth": 2,
356
+ "numbering": [
357
+ 1,
358
+ 2
359
+ ],
360
+ "parent": "root"
361
+ },
362
+ {
363
+ "value": "Size options",
364
+ "href": "#size-options",
365
+ "depth": 3,
366
+ "numbering": [
367
+ 1,
368
+ 2,
369
+ 1
370
+ ],
371
+ "parent": "root"
372
+ },
373
+ {
374
+ "value": "Visual variants",
375
+ "href": "#visual-variants",
376
+ "depth": 3,
377
+ "numbering": [
378
+ 1,
379
+ 2,
380
+ 2
381
+ ],
382
+ "parent": "root"
383
+ },
384
+ {
385
+ "value": "Single-select mode",
386
+ "href": "#single-select-mode",
387
+ "depth": 3,
388
+ "numbering": [
389
+ 1,
390
+ 2,
391
+ 3
392
+ ],
393
+ "parent": "root"
394
+ },
395
+ {
396
+ "value": "Multi-select mode",
397
+ "href": "#multi-select-mode",
398
+ "depth": 3,
399
+ "numbering": [
400
+ 1,
401
+ 2,
402
+ 4
403
+ ],
404
+ "parent": "root"
405
+ },
406
+ {
407
+ "value": "Leading element",
408
+ "href": "#leading-element",
409
+ "depth": 3,
410
+ "numbering": [
411
+ 1,
412
+ 2,
413
+ 5
414
+ ],
415
+ "parent": "root"
416
+ },
417
+ {
418
+ "value": "Disabled state",
419
+ "href": "#disabled-state",
420
+ "depth": 3,
421
+ "numbering": [
422
+ 1,
423
+ 2,
424
+ 6
425
+ ],
426
+ "parent": "root"
427
+ },
428
+ {
429
+ "value": "Invalid state",
430
+ "href": "#invalid-state",
431
+ "depth": 3,
432
+ "numbering": [
433
+ 1,
434
+ 2,
435
+ 7
436
+ ],
437
+ "parent": "root"
438
+ },
439
+ {
440
+ "value": "Read-only state",
441
+ "href": "#read-only-state",
442
+ "depth": 3,
443
+ "numbering": [
444
+ 1,
445
+ 2,
446
+ 8
447
+ ],
448
+ "parent": "root"
449
+ },
450
+ {
451
+ "value": "Option groups",
452
+ "href": "#option-groups",
453
+ "depth": 3,
454
+ "numbering": [
455
+ 1,
456
+ 2,
457
+ 9
458
+ ],
459
+ "parent": "root"
460
+ },
461
+ {
462
+ "value": "Dynamic options with items",
463
+ "href": "#dynamic-options-with-items",
464
+ "depth": 3,
465
+ "numbering": [
466
+ 1,
467
+ 2,
468
+ 10
469
+ ],
470
+ "parent": "root"
471
+ },
472
+ {
473
+ "value": "Custom filtering",
474
+ "href": "#custom-filtering",
475
+ "depth": 3,
476
+ "numbering": [
477
+ 1,
478
+ 2,
479
+ 11
480
+ ],
481
+ "parent": "root"
482
+ },
483
+ {
484
+ "value": "Custom options creation",
485
+ "href": "#custom-options-creation",
486
+ "depth": 3,
487
+ "numbering": [
488
+ 1,
489
+ 2,
490
+ 12
491
+ ],
492
+ "parent": "root"
493
+ },
494
+ {
495
+ "value": "Async data loading",
496
+ "href": "#async-data-loading",
497
+ "depth": 3,
498
+ "numbering": [
499
+ 1,
500
+ 2,
501
+ 13
502
+ ],
503
+ "parent": "root"
504
+ },
505
+ {
506
+ "value": "Controlled input value",
507
+ "href": "#controlled-input-value",
508
+ "depth": 3,
509
+ "numbering": [
510
+ 1,
511
+ 2,
512
+ 14
513
+ ],
514
+ "parent": "root"
515
+ },
516
+ {
517
+ "value": "Menu trigger behavior",
518
+ "href": "#menu-trigger-behavior",
519
+ "depth": 3,
520
+ "numbering": [
521
+ 1,
522
+ 2,
523
+ 15
524
+ ],
525
+ "parent": "root"
526
+ },
527
+ {
528
+ "value": "Component requirements",
529
+ "href": "#component-requirements",
530
+ "depth": 2,
531
+ "numbering": [
532
+ 1,
533
+ 3
534
+ ],
535
+ "parent": "root"
536
+ },
537
+ {
538
+ "value": "Accessibility",
539
+ "href": "#accessibility",
540
+ "depth": 3,
541
+ "numbering": [
542
+ 1,
543
+ 3,
544
+ 1
545
+ ],
546
+ "parent": "root"
547
+ },
548
+ {
549
+ "value": "Keyboard navigation",
550
+ "href": "#keyboard-navigation",
551
+ "depth": 4,
552
+ "numbering": [
553
+ 1,
554
+ 3,
555
+ 1,
556
+ 1
557
+ ],
558
+ "parent": "root"
559
+ },
560
+ {
561
+ "value": "API reference",
562
+ "href": "#api-reference",
563
+ "depth": 2,
564
+ "numbering": [
565
+ 1,
566
+ 4
567
+ ],
568
+ "parent": "root"
569
+ },
570
+ {
571
+ "value": "Common patterns",
572
+ "href": "#common-patterns",
573
+ "depth": 2,
574
+ "numbering": [
575
+ 1,
576
+ 5
577
+ ],
578
+ "parent": "root"
579
+ },
580
+ {
581
+ "value": "Product search with categories",
582
+ "href": "#product-search-with-categories",
583
+ "depth": 3,
584
+ "numbering": [
585
+ 1,
586
+ 5,
587
+ 1
588
+ ],
589
+ "parent": "root"
590
+ },
591
+ {
592
+ "value": "Tag selection with creation",
593
+ "href": "#tag-selection-with-creation",
594
+ "depth": 3,
595
+ "numbering": [
596
+ 1,
597
+ 5,
598
+ 2
599
+ ],
600
+ "parent": "root"
601
+ },
602
+ {
603
+ "value": "Testing your implementation",
604
+ "href": "#testing-your-implementation",
605
+ "depth": 2,
606
+ "numbering": [
607
+ 1,
608
+ 6
609
+ ],
610
+ "parent": "root"
611
+ },
612
+ {
613
+ "value": "Basic Rendering Tests",
614
+ "href": "#basic-rendering-tests",
615
+ "depth": 3,
616
+ "numbering": [
617
+ 1,
618
+ 6,
619
+ 1
620
+ ],
621
+ "parent": "root"
622
+ },
623
+ {
624
+ "value": "Interaction Tests",
625
+ "href": "#interaction-tests",
626
+ "depth": 3,
627
+ "numbering": [
628
+ 1,
629
+ 6,
630
+ 2
631
+ ],
632
+ "parent": "root"
633
+ },
634
+ {
635
+ "value": "Single-Select Mode Tests",
636
+ "href": "#single-select-mode-tests",
637
+ "depth": 3,
638
+ "numbering": [
639
+ 1,
640
+ 6,
641
+ 3
642
+ ],
643
+ "parent": "root"
644
+ },
645
+ {
646
+ "value": "Multi-Select Mode Tests",
647
+ "href": "#multi-select-mode-tests",
648
+ "depth": 3,
649
+ "numbering": [
650
+ 1,
651
+ 6,
652
+ 4
653
+ ],
654
+ "parent": "root"
655
+ },
656
+ {
657
+ "value": "Disabled State Tests",
658
+ "href": "#disabled-state-tests",
659
+ "depth": 3,
660
+ "numbering": [
661
+ 1,
662
+ 6,
663
+ 5
664
+ ],
665
+ "parent": "root"
666
+ },
667
+ {
668
+ "value": "Invalid State Tests",
669
+ "href": "#invalid-state-tests",
670
+ "depth": 3,
671
+ "numbering": [
672
+ 1,
673
+ 6,
674
+ 6
675
+ ],
676
+ "parent": "root"
677
+ },
678
+ {
679
+ "value": "Custom Options Creation Tests",
680
+ "href": "#custom-options-creation-tests",
681
+ "depth": 3,
682
+ "numbering": [
683
+ 1,
684
+ 6,
685
+ 7
686
+ ],
687
+ "parent": "root"
688
+ },
689
+ {
690
+ "value": "Option Groups Tests",
691
+ "href": "#option-groups-tests",
692
+ "depth": 3,
693
+ "numbering": [
694
+ 1,
695
+ 6,
696
+ 8
697
+ ],
698
+ "parent": "root"
699
+ },
700
+ {
701
+ "value": "Resources",
702
+ "href": "#resources",
703
+ "depth": 2,
704
+ "numbering": [
705
+ 1,
706
+ 7
707
+ ],
708
+ "parent": "root"
709
+ }
710
+ ]
711
+ },
712
+ "guidelines": {
713
+ "mdx": "\n## Guidelines\n\nA combobox combines a text input with a dropdown list, allowing users to type a\nvalue or select from predefined options.\n\n### Best practices\n\n- **Provide Instant Feedback:** Filter results in real-time as users type.\n- **Handle No Results Graciously:** Display a clear message when no matching\n options are found.\n- **Optimize for Keyboard Navigation:** Ensure full functionality using only the\n keyboard.\n- **Consider Performance:** For very long lists, implement virtualization or\n lazy loading to prevent performance issues.\n- **Offer Clear/Reset Option:** Provide a way for users to clear their selection\n if needed.\n- **Match Input to Options:** If the user types a value that exactly matches an\n option, automatically select it.\n\n### Usage\n\nUsers interact with a combobox by typing to filter options or by selecting from\nthe dropdown list, expecting the component to dynamically narrow choices and\nallow either a selection or custom entry.\n\n> [!TIP]\\\n> When to use\n\n- Allowing custom input alongside predefined options.\n- Filtering long lists.\n- Providing suggestions as a user types.\n\n> [!CAUTION]\\\n> When not to use\n\n- Only predefined options are allowed.\n- The list of options is very short (e.g., 2-5 options).\n- Multi-selection is the primary goal without typing.\n- Real-time validation of complex custom input is difficult.\n\n### Intuitive interactions\n\nA well-designed combobox enhances user efficiency and accuracy by offering\nflexible input methods with clear, dynamic feedback.\n\n> [!TIP]\\\n> **Do**\n>\n> - Provide a clear and concise label.\n> - Filter options dynamically as the user types.\n> - Support both keyboard and mouse interactions.\n> - Offer clear feedback for all states (focus, hover, active, disabled,\n> invalid).\n> - Ensure accessibility with appropriate ARIA attributes.\n> - Pre-populate with common values if applicable.\n\n```jsx live\nconst App = () => {\n const [selectedKeys, setSelectedKeys] = useState([\"yellow\", \"blue\"]);\n\n const colors = [\n { id: \"red\", name: \"Red\" },\n { id: \"yellow\", name: \"Yellow\" },\n { id: \"blue\", name: \"Blue\" },\n { id: \"pink\", name: \"Pink\" },\n ];\n\n return (\n <ComboBox.Root\n items={colors}\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n selectionMode=\"multiple\"\n placeholder=\"Select colors...\"\n aria-label=\"Select colors\"\n >\n <ComboBox.Trigger />\n <ComboBox.Popover>\n <ComboBox.ListBox>\n {(item) => (\n <ComboBox.Option id={item.id}>\n <Stack gap=\"100\">\n <Text textStyle=\"sm\">{item.name}</Text>\n <Text textStyle=\"xs\" color=\"neutral.11\">\n key: {item.id}\n </Text>\n </Stack>\n </ComboBox.Option>\n )}\n </ComboBox.ListBox>\n </ComboBox.Popover>\n </ComboBox.Root>\n );\n};\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Use for simple dropdowns where no custom input is needed (use a select\n> component instead).\n> - Neglect to provide feedback when no options match the input.\n> - Force users to type an exact match if a selection from the list is expected.\n> - Rely solely on visual cues; provide ARIA attributes for screen readers.\n> - Display an overwhelming number of options without filtering or pagination.\n\n<img\n src=\"/images/combobox/combobox-dont.png\"\n alt=\"combobox do guideline\"\n width=\"316px\"\n/>\n",
714
+ "toc": [
715
+ {
716
+ "value": "Guidelines",
717
+ "href": "#guidelines",
718
+ "depth": 2,
719
+ "numbering": [
720
+ 1,
721
+ 1
722
+ ],
723
+ "parent": "root"
724
+ },
725
+ {
726
+ "value": "Best practices",
727
+ "href": "#best-practices",
728
+ "depth": 3,
729
+ "numbering": [
730
+ 1,
731
+ 1,
732
+ 1
733
+ ],
734
+ "parent": "root"
735
+ },
736
+ {
737
+ "value": "Usage",
738
+ "href": "#usage",
739
+ "depth": 3,
740
+ "numbering": [
741
+ 1,
742
+ 1,
743
+ 2
744
+ ],
745
+ "parent": "root"
746
+ },
747
+ {
748
+ "value": "Intuitive interactions",
749
+ "href": "#intuitive-interactions",
750
+ "depth": 3,
751
+ "numbering": [
752
+ 1,
753
+ 1,
754
+ 3
755
+ ],
756
+ "parent": "root"
757
+ }
758
+ ]
759
+ }
760
+ }
761
+ }