@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.
- package/README.md +63 -14
- package/data/docs/route-manifest.json +10998 -0
- package/data/docs/routes/components-accessibility-visually-hidden.json +388 -0
- package/data/docs/routes/components-accessibility.json +34 -0
- package/data/docs/routes/components-buttons-button.json +715 -0
- package/data/docs/routes/components-buttons-icon-button.json +852 -0
- package/data/docs/routes/components-buttons-icon-toggle-button.json +594 -0
- package/data/docs/routes/components-buttons-split-button.json +670 -0
- package/data/docs/routes/components-buttons-toggle-button-group.json +722 -0
- package/data/docs/routes/components-buttons-toggle-button.json +689 -0
- package/data/docs/routes/components-buttons.json +36 -0
- package/data/docs/routes/components-data-display-badge.json +555 -0
- package/data/docs/routes/components-data-display-card.json +338 -0
- package/data/docs/routes/components-data-display-data-table.json +855 -0
- package/data/docs/routes/components-data-display-draggable-list.json +596 -0
- package/data/docs/routes/components-data-display-table.json +472 -0
- package/data/docs/routes/components-data-display-tag-group.json +535 -0
- package/data/docs/routes/components-data-display.json +34 -0
- package/data/docs/routes/components-feedback-alert.json +696 -0
- package/data/docs/routes/components-feedback-dialog.json +682 -0
- package/data/docs/routes/components-feedback-drawer.json +600 -0
- package/data/docs/routes/components-feedback-loading-spinner.json +415 -0
- package/data/docs/routes/components-feedback-progress-bar.json +661 -0
- package/data/docs/routes/components-feedback-toast.json +1040 -0
- package/data/docs/routes/components-feedback-tooltip.json +510 -0
- package/data/docs/routes/components-feedback.json +34 -0
- package/data/docs/routes/components-forms-field-errors.json +557 -0
- package/data/docs/routes/components-forms-form-field.json +848 -0
- package/data/docs/routes/components-forms-group.json +427 -0
- package/data/docs/routes/components-forms-localized-field.json +770 -0
- package/data/docs/routes/components-forms.json +37 -0
- package/data/docs/routes/components-inputs-calendar.json +611 -0
- package/data/docs/routes/components-inputs-checkbox.json +774 -0
- package/data/docs/routes/components-inputs-combo-box.json +761 -0
- package/data/docs/routes/components-inputs-date-input.json +628 -0
- package/data/docs/routes/components-inputs-date-picker.json +709 -0
- package/data/docs/routes/components-inputs-date-range-picker.json +599 -0
- package/data/docs/routes/components-inputs-money-input.json +721 -0
- package/data/docs/routes/components-inputs-multiline-text-input.json +611 -0
- package/data/docs/routes/components-inputs-number-input.json +647 -0
- package/data/docs/routes/components-inputs-password-input.json +576 -0
- package/data/docs/routes/components-inputs-radio-input.json +583 -0
- package/data/docs/routes/components-inputs-range-calendar.json +607 -0
- package/data/docs/routes/components-inputs-rich-text-input.json +599 -0
- package/data/docs/routes/components-inputs-scoped-search-input.json +570 -0
- package/data/docs/routes/components-inputs-search-input.json +588 -0
- package/data/docs/routes/components-inputs-select-input.json +960 -0
- package/data/docs/routes/components-inputs-switch.json +720 -0
- package/data/docs/routes/components-inputs-text-input.json +566 -0
- package/data/docs/routes/components-inputs-time-input.json +775 -0
- package/data/docs/routes/components-inputs.json +34 -0
- package/data/docs/routes/components-layout-box.json +501 -0
- package/data/docs/routes/components-layout-defaultpage.json +748 -0
- package/data/docs/routes/components-layout-flex.json +587 -0
- package/data/docs/routes/components-layout-grid.json +393 -0
- package/data/docs/routes/components-layout-modalpage.json +716 -0
- package/data/docs/routes/components-layout-pagecontent.json +673 -0
- package/data/docs/routes/components-layout-scrollarea.json +428 -0
- package/data/docs/routes/components-layout-separator.json +461 -0
- package/data/docs/routes/components-layout-simple-grid.json +519 -0
- package/data/docs/routes/components-layout-spacer.json +573 -0
- package/data/docs/routes/components-layout-stack.json +481 -0
- package/data/docs/routes/components-layout.json +34 -0
- package/data/docs/routes/components-media-avatar.json +427 -0
- package/data/docs/routes/components-media-icon.json +663 -0
- package/data/docs/routes/components-media-image.json +511 -0
- package/data/docs/routes/components-media-inline-svg.json +586 -0
- package/data/docs/routes/components-media.json +34 -0
- package/data/docs/routes/components-navigation-accordion.json +643 -0
- package/data/docs/routes/components-navigation-collapsible-motion.json +628 -0
- package/data/docs/routes/components-navigation-link.json +554 -0
- package/data/docs/routes/components-navigation-menu.json +546 -0
- package/data/docs/routes/components-navigation-pagination.json +502 -0
- package/data/docs/routes/components-navigation-steps.json +629 -0
- package/data/docs/routes/components-navigation-tabnav.json +546 -0
- package/data/docs/routes/components-navigation-tabs.json +635 -0
- package/data/docs/routes/components-navigation-toolbar.json +549 -0
- package/data/docs/routes/components-navigation.json +34 -0
- package/data/docs/routes/components-typography-code.json +39 -0
- package/data/docs/routes/components-typography-heading.json +402 -0
- package/data/docs/routes/components-typography-kbd.json +399 -0
- package/data/docs/routes/components-typography-list.json +593 -0
- package/data/docs/routes/components-typography-text.json +444 -0
- package/data/docs/routes/components-typography.json +34 -0
- package/data/docs/routes/components-utilities-nimbus-i18n-provider.json +295 -0
- package/data/docs/routes/components-utilities-nimbus-provider.json +663 -0
- package/data/docs/routes/components-utilities.json +34 -0
- package/data/docs/routes/components.json +33 -0
- package/data/docs/routes/home-contribute-adrs-adr0001-consumer-component-apis.json +314 -0
- package/data/docs/routes/home-contribute-adrs-adr0002-compound-component-extraction.json +160 -0
- package/data/docs/routes/home-contribute-adrs-adr0003-component-lifecycle-states.json +460 -0
- package/data/docs/routes/home-contribute-adrs.json +205 -0
- package/data/docs/routes/home-contribute-development-setup.json +213 -0
- package/data/docs/routes/home-contribute-stats.json +36 -0
- package/data/docs/routes/home-contribute.json +36 -0
- package/data/docs/routes/home-design-tokens-aspect-ratios.json +36 -0
- package/data/docs/routes/home-design-tokens-borders.json +35 -0
- package/data/docs/routes/home-design-tokens-colors.json +157 -0
- package/data/docs/routes/home-design-tokens-other-animations.json +119 -0
- package/data/docs/routes/home-design-tokens-other-blurs.json +36 -0
- package/data/docs/routes/home-design-tokens-other-breakpoints.json +61 -0
- package/data/docs/routes/home-design-tokens-other-cursors.json +36 -0
- package/data/docs/routes/home-design-tokens-other-z-indices.json +39 -0
- package/data/docs/routes/home-design-tokens-other.json +35 -0
- package/data/docs/routes/home-design-tokens-radii.json +59 -0
- package/data/docs/routes/home-design-tokens-shadows.json +57 -0
- package/data/docs/routes/home-design-tokens-sizes.json +137 -0
- package/data/docs/routes/home-design-tokens-spacing.json +36 -0
- package/data/docs/routes/home-design-tokens-typography.json +184 -0
- package/data/docs/routes/home-design-tokens.json +34 -0
- package/data/docs/routes/home-getting-started-core-concepts.json +301 -0
- package/data/docs/routes/home-getting-started-installation.json +621 -0
- package/data/docs/routes/home-getting-started-mcp-server-overview.json +139 -0
- package/data/docs/routes/home-getting-started-mcp-server-setup.json +316 -0
- package/data/docs/routes/home-getting-started-release-process.json +294 -0
- package/data/docs/routes/home-getting-started-testing-setup.json +296 -0
- package/data/docs/routes/home-playground-markdown.json +638 -0
- package/data/docs/routes/home-playground-toc.json +169 -0
- package/data/docs/routes/home-playground.json +34 -0
- package/data/docs/routes/home-style-props-background.json +236 -0
- package/data/docs/routes/home-style-props-border.json +310 -0
- package/data/docs/routes/home-style-props-display.json +120 -0
- package/data/docs/routes/home-style-props-effects.json +116 -0
- package/data/docs/routes/home-style-props-filters.json +396 -0
- package/data/docs/routes/home-style-props-flex-and-grid.json +496 -0
- package/data/docs/routes/home-style-props-interactivity.json +356 -0
- package/data/docs/routes/home-style-props-layout.json +422 -0
- package/data/docs/routes/home-style-props-list.json +116 -0
- package/data/docs/routes/home-style-props-sizing.json +244 -0
- package/data/docs/routes/home-style-props-spacing.json +228 -0
- package/data/docs/routes/home-style-props-svg.json +96 -0
- package/data/docs/routes/home-style-props-tables.json +116 -0
- package/data/docs/routes/home-style-props-transforms.json +216 -0
- package/data/docs/routes/home-style-props-transitions.json +216 -0
- package/data/docs/routes/home-style-props-typography.json +536 -0
- package/data/docs/routes/home-style-props.json +33 -0
- package/data/docs/routes/home.json +32 -0
- package/data/docs/routes/hooks-usecopytoclipboard.json +76 -0
- package/data/docs/routes/hooks-usehotkeys.json +117 -0
- package/data/docs/routes/hooks.json +33 -0
- package/data/docs/routes/icons.json +32 -0
- package/data/docs/routes/patterns-fields-date-range-picker-field.json +393 -0
- package/data/docs/routes/patterns-fields-money-input-field.json +415 -0
- package/data/docs/routes/patterns-fields-multiline-text-input-field.json +404 -0
- package/data/docs/routes/patterns-fields-number-input-field.json +470 -0
- package/data/docs/routes/patterns-fields-password-input-field.json +319 -0
- package/data/docs/routes/patterns-fields-search-input-field.json +382 -0
- package/data/docs/routes/patterns-fields-text-input-field.json +404 -0
- package/data/docs/routes/patterns-fields.json +78 -0
- package/data/docs/routes/patterns.json +34 -0
- package/data/docs/search-index.json +1 -0
- package/data/docs/types/Accordion.json +12 -0
- package/data/docs/types/AccordionContent.json +286 -0
- package/data/docs/types/AccordionHeader.json +891 -0
- package/data/docs/types/AccordionHeaderRightContent.json +27 -0
- package/data/docs/types/AccordionItem.json +242 -0
- package/data/docs/types/AccordionRoot.json +162 -0
- package/data/docs/types/Alert.json +12 -0
- package/data/docs/types/AlertActions.json +11 -0
- package/data/docs/types/AlertDescription.json +118 -0
- package/data/docs/types/AlertDismissButton.json +937 -0
- package/data/docs/types/AlertRoot.json +42 -0
- package/data/docs/types/AlertTitle.json +118 -0
- package/data/docs/types/Avatar.json +125 -0
- package/data/docs/types/Badge.json +64 -0
- package/data/docs/types/Body.json +67 -0
- package/data/docs/types/Box.json +85 -0
- package/data/docs/types/Button.json +1015 -0
- package/data/docs/types/Calendar.json +565 -0
- package/data/docs/types/Caption.json +67 -0
- package/data/docs/types/Card.json +12 -0
- package/data/docs/types/CardContent.json +27 -0
- package/data/docs/types/CardHeader.json +27 -0
- package/data/docs/types/CardRoot.json +106 -0
- package/data/docs/types/Cell.json +227 -0
- package/data/docs/types/Checkbox.json +897 -0
- package/data/docs/types/Code.json +112 -0
- package/data/docs/types/CollapsibleMotionContent.json +35 -0
- package/data/docs/types/CollapsibleMotionRoot.json +99 -0
- package/data/docs/types/CollapsibleMotionTrigger.json +71 -0
- package/data/docs/types/Column.json +101 -0
- package/data/docs/types/ColumnGroup.json +101 -0
- package/data/docs/types/ColumnHeader.json +193 -0
- package/data/docs/types/ComboBoxListBox.json +751 -0
- package/data/docs/types/ComboBoxOption.json +672 -0
- package/data/docs/types/ComboBoxPopover.json +786 -0
- package/data/docs/types/ComboBoxRoot.json +747 -0
- package/data/docs/types/ComboBoxSection.json +277 -0
- package/data/docs/types/ComboBoxTrigger.json +70 -0
- package/data/docs/types/Content.json +33 -0
- package/data/docs/types/DataTable.json +596 -0
- package/data/docs/types/DataTableBody.json +223 -0
- package/data/docs/types/DataTableFooter.json +27 -0
- package/data/docs/types/DataTableHeader.json +269 -0
- package/data/docs/types/DataTableManager.json +11 -0
- package/data/docs/types/DataTableRoot.json +590 -0
- package/data/docs/types/DataTableTable.json +271 -0
- package/data/docs/types/DateInput.json +792 -0
- package/data/docs/types/DatePicker.json +700 -0
- package/data/docs/types/DateRangePicker.json +936 -0
- package/data/docs/types/DateRangePickerField.json +1047 -0
- package/data/docs/types/DefaultPage.json +12 -0
- package/data/docs/types/DefaultPageActions.json +27 -0
- package/data/docs/types/DefaultPageBackLink.json +213 -0
- package/data/docs/types/DefaultPageContent.json +27 -0
- package/data/docs/types/DefaultPageFooter.json +27 -0
- package/data/docs/types/DefaultPageHeader.json +27 -0
- package/data/docs/types/DefaultPageRoot.json +106 -0
- package/data/docs/types/DefaultPageSubtitle.json +27 -0
- package/data/docs/types/DefaultPageTabNav.json +28 -0
- package/data/docs/types/DefaultPageTitle.json +27 -0
- package/data/docs/types/DialogBody.json +27 -0
- package/data/docs/types/DialogCloseTrigger.json +939 -0
- package/data/docs/types/DialogContent.json +27 -0
- package/data/docs/types/DialogFooter.json +27 -0
- package/data/docs/types/DialogHeader.json +27 -0
- package/data/docs/types/DialogRoot.json +138 -0
- package/data/docs/types/DialogTitle.json +27 -0
- package/data/docs/types/DialogTrigger.json +80 -0
- package/data/docs/types/DraggableList.json +12 -0
- package/data/docs/types/DraggableListField.json +894 -0
- package/data/docs/types/DraggableListItem.json +574 -0
- package/data/docs/types/DraggableListRoot.json +745 -0
- package/data/docs/types/Drawer.json +12 -0
- package/data/docs/types/DrawerBody.json +27 -0
- package/data/docs/types/DrawerCloseTrigger.json +939 -0
- package/data/docs/types/DrawerContent.json +27 -0
- package/data/docs/types/DrawerFooter.json +27 -0
- package/data/docs/types/DrawerHeader.json +27 -0
- package/data/docs/types/DrawerRoot.json +142 -0
- package/data/docs/types/DrawerTitle.json +27 -0
- package/data/docs/types/DrawerTrigger.json +80 -0
- package/data/docs/types/FieldErrors.getBuiltInMessage.json +11 -0
- package/data/docs/types/FieldErrors.getCustomMessage.json +9 -0
- package/data/docs/types/FieldErrors.json +109 -0
- package/data/docs/types/Flex.json +238 -0
- package/data/docs/types/Footer.json +67 -0
- package/data/docs/types/FormFieldDescription.json +11 -0
- package/data/docs/types/FormFieldError.json +11 -0
- package/data/docs/types/FormFieldInfoBox.json +27 -0
- package/data/docs/types/FormFieldInput.json +11 -0
- package/data/docs/types/FormFieldLabel.json +11 -0
- package/data/docs/types/FormFieldRoot.json +148 -0
- package/data/docs/types/Grid.json +253 -0
- package/data/docs/types/GridProps.json +11 -0
- package/data/docs/types/Group.json +143 -0
- package/data/docs/types/Header.json +67 -0
- package/data/docs/types/Heading.json +109 -0
- package/data/docs/types/Icon.json +112 -0
- package/data/docs/types/IconButton.json +1019 -0
- package/data/docs/types/IconToggleButton.json +787 -0
- package/data/docs/types/Image.json +373 -0
- package/data/docs/types/Indicator.json +67 -0
- package/data/docs/types/InlineSvg.json +98 -0
- package/data/docs/types/Item.json +67 -0
- package/data/docs/types/Kbd.json +118 -0
- package/data/docs/types/Link.json +380 -0
- package/data/docs/types/List.json +12 -0
- package/data/docs/types/ListIndicator.json +70 -0
- package/data/docs/types/ListItem.json +70 -0
- package/data/docs/types/ListRoot.json +124 -0
- package/data/docs/types/LoadingSpinner.json +87 -0
- package/data/docs/types/LocalizedField.json +460 -0
- package/data/docs/types/LocalizedStringFormatter.json +9 -0
- package/data/docs/types/MakeElementFocusable.json +196 -0
- package/data/docs/types/MenuContent.json +111 -0
- package/data/docs/types/MenuItem.json +671 -0
- package/data/docs/types/MenuRoot.json +670 -0
- package/data/docs/types/MenuSection.json +364 -0
- package/data/docs/types/MenuSubmenu.json +111 -0
- package/data/docs/types/MenuSubmenuTrigger.json +67 -0
- package/data/docs/types/MenuTrigger.json +906 -0
- package/data/docs/types/ModalPage.json +12 -0
- package/data/docs/types/ModalPageActions.json +27 -0
- package/data/docs/types/ModalPageContent.json +27 -0
- package/data/docs/types/ModalPageFooter.json +27 -0
- package/data/docs/types/ModalPageHeader.json +27 -0
- package/data/docs/types/ModalPageRoot.json +87 -0
- package/data/docs/types/ModalPageSubtitle.json +27 -0
- package/data/docs/types/ModalPageTabNav.json +28 -0
- package/data/docs/types/ModalPageTitle.json +27 -0
- package/data/docs/types/ModalPageTopBar.json +57 -0
- package/data/docs/types/MoneyInput.isEmpty.json +40 -0
- package/data/docs/types/MoneyInput.json +282 -0
- package/data/docs/types/MoneyInputField.json +379 -0
- package/data/docs/types/MoneyInputFieldProps.json +9 -0
- package/data/docs/types/MultilineTextInput.json +1194 -0
- package/data/docs/types/MultilineTextInputField.json +1269 -0
- package/data/docs/types/MultilineTextInputFieldProps.json +9 -0
- package/data/docs/types/NimbusI18nProvider.json +42 -0
- package/data/docs/types/NimbusI18nProviderProps.json +9 -0
- package/data/docs/types/NimbusProvider.json +270 -0
- package/data/docs/types/NumberInput.json +952 -0
- package/data/docs/types/NumberInputField.json +1004 -0
- package/data/docs/types/NumberInputFieldProps.json +9 -0
- package/data/docs/types/PageContent.json +11 -0
- package/data/docs/types/PageContentColumn.json +99 -0
- package/data/docs/types/PageContentRoot.json +114 -0
- package/data/docs/types/Pagination.json +159 -0
- package/data/docs/types/PasswordInput.json +1120 -0
- package/data/docs/types/PasswordInputField.json +1216 -0
- package/data/docs/types/PasswordInputFieldProps.json +9 -0
- package/data/docs/types/ProgressBar.json +280 -0
- package/data/docs/types/RadioInputOption.json +550 -0
- package/data/docs/types/RadioInputRoot.json +514 -0
- package/data/docs/types/RangeCalendar.json +618 -0
- package/data/docs/types/RichTextInput.json +134 -0
- package/data/docs/types/Root.json +122 -0
- package/data/docs/types/Row.json +67 -0
- package/data/docs/types/ScopedSearchInput.isEmpty.json +40 -0
- package/data/docs/types/ScopedSearchInput.json +253 -0
- package/data/docs/types/ScrollArea.json +179 -0
- package/data/docs/types/ScrollAreaElementIds.json +9 -0
- package/data/docs/types/ScrollAreaProps.json +9 -0
- package/data/docs/types/SearchInput.json +1165 -0
- package/data/docs/types/SearchInputField.json +1240 -0
- package/data/docs/types/Select.json +12 -0
- package/data/docs/types/SelectOption.json +572 -0
- package/data/docs/types/SelectOptionGroup.json +215 -0
- package/data/docs/types/SelectOptions.json +693 -0
- package/data/docs/types/SelectRoot.json +926 -0
- package/data/docs/types/Separator.json +65 -0
- package/data/docs/types/SimpleGrid.json +291 -0
- package/data/docs/types/Spacer.json +27 -0
- package/data/docs/types/SpacerProps.json +9 -0
- package/data/docs/types/SplitButton.json +203 -0
- package/data/docs/types/Stack.json +144 -0
- package/data/docs/types/Steps.json +12 -0
- package/data/docs/types/StepsChangeDetails.json +9 -0
- package/data/docs/types/StepsCompletedContent.json +28 -0
- package/data/docs/types/StepsCompletedContentProps.json +9 -0
- package/data/docs/types/StepsContent.json +43 -0
- package/data/docs/types/StepsContentProps.json +9 -0
- package/data/docs/types/StepsDescription.json +28 -0
- package/data/docs/types/StepsDescriptionProps.json +9 -0
- package/data/docs/types/StepsIndicator.json +28 -0
- package/data/docs/types/StepsIndicatorProps.json +9 -0
- package/data/docs/types/StepsItem.json +43 -0
- package/data/docs/types/StepsItemProps.json +9 -0
- package/data/docs/types/StepsList.json +28 -0
- package/data/docs/types/StepsListProps.json +9 -0
- package/data/docs/types/StepsNextTrigger.json +62 -0
- package/data/docs/types/StepsNextTriggerProps.json +9 -0
- package/data/docs/types/StepsNumber.json +28 -0
- package/data/docs/types/StepsNumberProps.json +9 -0
- package/data/docs/types/StepsPrevTrigger.json +62 -0
- package/data/docs/types/StepsPrevTriggerProps.json +9 -0
- package/data/docs/types/StepsRoot.json +183 -0
- package/data/docs/types/StepsRootProps.json +11 -0
- package/data/docs/types/StepsSeparator.json +28 -0
- package/data/docs/types/StepsSeparatorProps.json +9 -0
- package/data/docs/types/StepsStatus.json +57 -0
- package/data/docs/types/StepsStatusProps.json +9 -0
- package/data/docs/types/StepsTitle.json +28 -0
- package/data/docs/types/StepsTitleProps.json +9 -0
- package/data/docs/types/StepsTrigger.json +47 -0
- package/data/docs/types/StepsTriggerProps.json +9 -0
- package/data/docs/types/Switch.json +371 -0
- package/data/docs/types/TabListProps.json +9 -0
- package/data/docs/types/TabNav.json +12 -0
- package/data/docs/types/TabNavItem.json +300 -0
- package/data/docs/types/TabNavItemProps.json +9 -0
- package/data/docs/types/TabNavProps.json +9 -0
- package/data/docs/types/TabNavRoot.json +80 -0
- package/data/docs/types/TabPanelProps.json +9 -0
- package/data/docs/types/TabPanelsProps.json +9 -0
- package/data/docs/types/TabProps.json +9 -0
- package/data/docs/types/Table.json +9 -0
- package/data/docs/types/TableBody.json +67 -0
- package/data/docs/types/TableBodyProps.json +9 -0
- package/data/docs/types/TableCaption.json +67 -0
- package/data/docs/types/TableCaptionProps.json +9 -0
- package/data/docs/types/TableCell.json +227 -0
- package/data/docs/types/TableCellProps.json +9 -0
- package/data/docs/types/TableColumn.json +101 -0
- package/data/docs/types/TableColumnGroup.json +101 -0
- package/data/docs/types/TableColumnGroupProps.json +9 -0
- package/data/docs/types/TableColumnHeader.json +193 -0
- package/data/docs/types/TableColumnHeaderProps.json +9 -0
- package/data/docs/types/TableColumnProps.json +9 -0
- package/data/docs/types/TableFooter.json +67 -0
- package/data/docs/types/TableFooterProps.json +9 -0
- package/data/docs/types/TableHeader.json +67 -0
- package/data/docs/types/TableHeaderProps.json +9 -0
- package/data/docs/types/TableRoot.json +365 -0
- package/data/docs/types/TableRootProps.json +12 -0
- package/data/docs/types/TableRow.json +67 -0
- package/data/docs/types/TableRowProps.json +9 -0
- package/data/docs/types/TableScrollArea.json +82 -0
- package/data/docs/types/TableScrollAreaProps.json +9 -0
- package/data/docs/types/Tabs.json +12 -0
- package/data/docs/types/TabsList.json +110 -0
- package/data/docs/types/TabsPanel.json +112 -0
- package/data/docs/types/TabsPanels.json +108 -0
- package/data/docs/types/TabsRoot.json +211 -0
- package/data/docs/types/TabsTab.json +174 -0
- package/data/docs/types/TagGroup.json +12 -0
- package/data/docs/types/TagGroupRoot.json +306 -0
- package/data/docs/types/TagGroupTag.json +595 -0
- package/data/docs/types/TagGroupTagList.json +166 -0
- package/data/docs/types/Text.json +119 -0
- package/data/docs/types/TextInput.json +1156 -0
- package/data/docs/types/TextInputField.json +1263 -0
- package/data/docs/types/TimeInput.json +752 -0
- package/data/docs/types/ToastAction.json +9 -0
- package/data/docs/types/ToastManagerApi.json +9 -0
- package/data/docs/types/ToastOptions.json +9 -0
- package/data/docs/types/ToastOutlet.json +12 -0
- package/data/docs/types/ToastPlacement.json +9 -0
- package/data/docs/types/ToastPromiseOptions.json +9 -0
- package/data/docs/types/ToastType.json +9 -0
- package/data/docs/types/ToastVariant.json +9 -0
- package/data/docs/types/ToggleButton.json +789 -0
- package/data/docs/types/ToggleButtonGroup.json +9 -0
- package/data/docs/types/ToggleButtonGroupButton.json +331 -0
- package/data/docs/types/ToggleButtonGroupRoot.json +269 -0
- package/data/docs/types/Toolbar.json +176 -0
- package/data/docs/types/Tooltip.json +12 -0
- package/data/docs/types/TooltipContent.json +372 -0
- package/data/docs/types/TooltipRoot.json +179 -0
- package/data/docs/types/Trigger.json +69 -0
- package/data/docs/types/VisuallyHidden.json +93 -0
- package/data/docs/types/__object.json +12 -0
- package/data/docs/types/filters.json +11 -0
- package/data/docs/types/manifest.json +280 -0
- package/data/docs/types/toast.json +234 -0
- package/data/docs/types/useColorMode.json +13 -0
- package/data/docs/types/useColorModeValue.json +13 -0
- package/data/docs/types/useColorScheme.json +12 -0
- package/data/docs/types/useLocalizedStringFormatter.json +14 -0
- package/data/icons.json +21940 -0
- package/data/tokens.json +40061 -0
- package/dist/index.js +2516 -17
- package/package.json +25 -6
- package/dist/data-loader.d.ts +0 -102
- package/dist/data-loader.js +0 -104
- package/dist/index.d.ts +0 -13
- package/dist/server.d.ts +0 -9
- package/dist/server.js +0 -22
- package/dist/server.spec.d.ts +0 -1
- package/dist/server.spec.js +0 -69
- package/dist/tools/list-components.d.ts +0 -9
- package/dist/tools/list-components.js +0 -42
- package/dist/types.d.ts +0 -28
- package/dist/types.js +0 -4
- package/src/data-loader.ts +0 -226
- package/src/index.ts +0 -29
- package/src/server.spec.ts +0 -86
- package/src/server.ts +0 -28
- package/src/tools/list-components.ts +0 -49
- package/src/types.ts +0 -31
- package/tsconfig.json +0 -14
- package/vitest.config.ts +0 -9
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"id": "Components-ScopedSearchInput",
|
|
4
|
+
"title": "Scoped search input",
|
|
5
|
+
"exportName": "ScopedSearchInput",
|
|
6
|
+
"description": "Enables users to define where their search query is applied using a closely paired scope selector.",
|
|
7
|
+
"lifecycleState": "Stable",
|
|
8
|
+
"order": 999,
|
|
9
|
+
"repoPath": "packages/nimbus/src/components/scoped-search-input/scoped-search-input.mdx",
|
|
10
|
+
"menu": [
|
|
11
|
+
"Components",
|
|
12
|
+
"Inputs",
|
|
13
|
+
"Scoped search input"
|
|
14
|
+
],
|
|
15
|
+
"route": "components/inputs/scoped-search-input",
|
|
16
|
+
"tags": [
|
|
17
|
+
"component",
|
|
18
|
+
"input",
|
|
19
|
+
"search",
|
|
20
|
+
"select",
|
|
21
|
+
"scoped"
|
|
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": "Resources",
|
|
36
|
+
"href": "#resources",
|
|
37
|
+
"depth": 3,
|
|
38
|
+
"numbering": [
|
|
39
|
+
1,
|
|
40
|
+
1,
|
|
41
|
+
1
|
|
42
|
+
],
|
|
43
|
+
"parent": "root"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"value": "Variables",
|
|
47
|
+
"href": "#variables",
|
|
48
|
+
"depth": 2,
|
|
49
|
+
"numbering": [
|
|
50
|
+
1,
|
|
51
|
+
2
|
|
52
|
+
],
|
|
53
|
+
"parent": "root"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"value": "Size",
|
|
57
|
+
"href": "#size",
|
|
58
|
+
"depth": 3,
|
|
59
|
+
"numbering": [
|
|
60
|
+
1,
|
|
61
|
+
2,
|
|
62
|
+
1
|
|
63
|
+
],
|
|
64
|
+
"parent": "root"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"value": "With form fields",
|
|
68
|
+
"href": "#with-form-fields",
|
|
69
|
+
"depth": 3,
|
|
70
|
+
"numbering": [
|
|
71
|
+
1,
|
|
72
|
+
2,
|
|
73
|
+
2
|
|
74
|
+
],
|
|
75
|
+
"parent": "root"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"figmaLink": "https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=7190-35198&m=dev",
|
|
79
|
+
"layout": "app-frame",
|
|
80
|
+
"tabs": [
|
|
81
|
+
{
|
|
82
|
+
"key": "overview",
|
|
83
|
+
"title": "Overview",
|
|
84
|
+
"order": 0
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"key": "guidelines",
|
|
88
|
+
"title": "Guidelines",
|
|
89
|
+
"order": 2
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"key": "dev",
|
|
93
|
+
"title": "Implementation",
|
|
94
|
+
"order": 3
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"key": "a11y",
|
|
98
|
+
"title": "Accessibility",
|
|
99
|
+
"order": 4
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"mdx": "\n## Overview\n\nThe scoped search input combines an input field with a functional scope selector\n(often a dropdown or button group), ensuring users can precisely target their\nquery to a specific data segment, such as searching only 'Products' or only\n'Customers', yielding more accurate and relevant results.\n\n### Resources\n\nDeep dive into implementation details and access the Nimbus design library.\n\n[Figma library](https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=7190-35198&m=dev)\n[React Aria SearchField](https://react-spectrum.adobe.com/react-aria/SearchField.html)\n\n## Variables\n\nGet familiar with the features.\n\n### Size\n\nThere are two sizes, the default is medium (md) and supporting a more compact\nsize (sm).\n\n```jsx live\nconst App = () => {\n const [valueSm, setValueSm] = useState({ text: '', option: 'all' });\n const [valueMd, setValueMd] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ScopedSearchInput\n size=\"md\"\n value={valueMd}\n onValueChange={setValueMd}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n <ScopedSearchInput\n size=\"sm\"\n value={valueSm}\n onValueChange={setValueSm}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n </Stack>\n );\n};\n```\n\n### With form fields\n\nWhen used in combination with form field patterns there is more support for any\nlabel, description, and error states needed.\n\n```jsx live\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n return (\n <Box width=\"600px\">\n <FormField.Root isRequired>\n <FormField.Label>Label</FormField.Label>\n <FormField.Input>\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n </FormField.Input>\n <FormField.Description>Descriptive text</FormField.Description>\n <FormField.InfoBox>\n Info box content\n </FormField.InfoBox>\n </FormField.Root>\n </Box>\n );\n};\n```\n",
|
|
104
|
+
"views": {
|
|
105
|
+
"overview": {
|
|
106
|
+
"mdx": "\n## Overview\n\nThe scoped search input combines an input field with a functional scope selector\n(often a dropdown or button group), ensuring users can precisely target their\nquery to a specific data segment, such as searching only 'Products' or only\n'Customers', yielding more accurate and relevant results.\n\n### Resources\n\nDeep dive into implementation details and access the Nimbus design library.\n\n[Figma library](https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=7190-35198&m=dev)\n[React Aria SearchField](https://react-spectrum.adobe.com/react-aria/SearchField.html)\n\n## Variables\n\nGet familiar with the features.\n\n### Size\n\nThere are two sizes, the default is medium (md) and supporting a more compact\nsize (sm).\n\n```jsx live\nconst App = () => {\n const [valueSm, setValueSm] = useState({ text: '', option: 'all' });\n const [valueMd, setValueMd] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ScopedSearchInput\n size=\"md\"\n value={valueMd}\n onValueChange={setValueMd}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n <ScopedSearchInput\n size=\"sm\"\n value={valueSm}\n onValueChange={setValueSm}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n </Stack>\n );\n};\n```\n\n### With form fields\n\nWhen used in combination with form field patterns there is more support for any\nlabel, description, and error states needed.\n\n```jsx live\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n return (\n <Box width=\"600px\">\n <FormField.Root isRequired>\n <FormField.Label>Label</FormField.Label>\n <FormField.Input>\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n </FormField.Input>\n <FormField.Description>Descriptive text</FormField.Description>\n <FormField.InfoBox>\n Info box content\n </FormField.InfoBox>\n </FormField.Root>\n </Box>\n );\n};\n```\n",
|
|
107
|
+
"toc": [
|
|
108
|
+
{
|
|
109
|
+
"value": "Overview",
|
|
110
|
+
"href": "#overview",
|
|
111
|
+
"depth": 2,
|
|
112
|
+
"numbering": [
|
|
113
|
+
1,
|
|
114
|
+
1
|
|
115
|
+
],
|
|
116
|
+
"parent": "root"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"value": "Resources",
|
|
120
|
+
"href": "#resources",
|
|
121
|
+
"depth": 3,
|
|
122
|
+
"numbering": [
|
|
123
|
+
1,
|
|
124
|
+
1,
|
|
125
|
+
1
|
|
126
|
+
],
|
|
127
|
+
"parent": "root"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"value": "Variables",
|
|
131
|
+
"href": "#variables",
|
|
132
|
+
"depth": 2,
|
|
133
|
+
"numbering": [
|
|
134
|
+
1,
|
|
135
|
+
2
|
|
136
|
+
],
|
|
137
|
+
"parent": "root"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"value": "Size",
|
|
141
|
+
"href": "#size",
|
|
142
|
+
"depth": 3,
|
|
143
|
+
"numbering": [
|
|
144
|
+
1,
|
|
145
|
+
2,
|
|
146
|
+
1
|
|
147
|
+
],
|
|
148
|
+
"parent": "root"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"value": "With form fields",
|
|
152
|
+
"href": "#with-form-fields",
|
|
153
|
+
"depth": 3,
|
|
154
|
+
"numbering": [
|
|
155
|
+
1,
|
|
156
|
+
2,
|
|
157
|
+
2
|
|
158
|
+
],
|
|
159
|
+
"parent": "root"
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
"a11y": {
|
|
164
|
+
"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 [value, setValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n return (\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n );\n};\n```\n\n### Accessibility standards\n\n- **Keyboard flow**: Ensure the Tab order moves logically between the scope\n selector and the text input field.\n- **ARIA roles**: The scope selector (if a dropdown) must use the appropriate\n ARIA roles (role=\"combobox\", aria-expanded).\n- **Labeling**: Provide clear programmatic labels for both the search input\n (`aria-label` or visible `<label>`) and the scope selector to explain their\n function to screen readers.\n- **State communication**: Use aria-live regions if search results appear\n dynamically, to announce the updated results count after a scope change or\n query submission.\n",
|
|
165
|
+
"toc": [
|
|
166
|
+
{
|
|
167
|
+
"value": "Accessibility",
|
|
168
|
+
"href": "#accessibility",
|
|
169
|
+
"depth": 2,
|
|
170
|
+
"numbering": [
|
|
171
|
+
1,
|
|
172
|
+
1
|
|
173
|
+
],
|
|
174
|
+
"parent": "root"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"value": "Accessibility standards",
|
|
178
|
+
"href": "#accessibility-standards",
|
|
179
|
+
"depth": 3,
|
|
180
|
+
"numbering": [
|
|
181
|
+
1,
|
|
182
|
+
1,
|
|
183
|
+
1
|
|
184
|
+
],
|
|
185
|
+
"parent": "root"
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
"dev": {
|
|
190
|
+
"mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { ScopedSearchInput, type ScopedSearchInputProps, type ScopedSearchInputValue } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nThe ScopedSearchInput combines a select dropdown with a search input field, enabling users to filter searches by category or field type. It requires controlled mode with `value` and `onSubmit`:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All Fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n { label: 'Phone', value: 'phone' },\n ];\n\n return (\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n selectPlaceholder=\"Select field\"\n searchPlaceholder=\"Enter search term...\"\n />\n );\n}\n```\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 [valueSm, setValueSm] = useState({ text: '', option: 'name' });\n const [valueMd, setValueMd] = useState({ text: '', option: 'name' });\n\n const options = [\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n { label: 'Phone', value: 'phone' },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\" alignItems=\"flex-start\">\n <ScopedSearchInput\n size=\"sm\"\n value={valueSm}\n onValueChange={setValueSm}\n onSubmit={console.log}\n options={options}\n searchPlaceholder=\"Small size...\"\n />\n <ScopedSearchInput\n size=\"md\"\n value={valueMd}\n onValueChange={setValueMd}\n onSubmit={console.log}\n options={options}\n searchPlaceholder=\"Medium size...\"\n />\n </Stack>\n );\n}\n```\n\n### Grouped options\n\nOrganize filter categories using option groups for better organization:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'email' });\n\n const groupedOptions = [\n {\n label: 'Contact Info',\n options: [\n { label: 'Email', value: 'email' },\n { label: 'Phone', value: 'phone' },\n ],\n },\n {\n label: 'Personal Info',\n options: [\n { label: 'Name', value: 'name' },\n { label: 'Address', value: 'address' },\n ],\n },\n ];\n\n return (\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={groupedOptions}\n searchPlaceholder=\"Search...\"\n />\n );\n}\n```\n\n### Auto-focus behavior\n\nAfter selecting a filter option, the search input automatically receives focus, allowing users to immediately start typing without clicking:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All Fields', value: 'all' },\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"200\">\n <Text fontSize=\"sm\" color=\"fg.muted\">\n Select a different option to see auto-focus in action\n </Text>\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={console.log}\n options={options}\n searchPlaceholder=\"Type after selecting...\"\n />\n </Stack>\n );\n}\n```\n\n### State management patterns\n\nUse separate callbacks for granular control over state changes:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'name' });\n const [history, setHistory] = useState([]);\n\n const options = [\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n { label: 'Phone', value: 'phone' },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ScopedSearchInput\n value={value}\n onTextChange={(text) => {\n setHistory((prev) => [...prev, `Text changed: \"${text}\"`]);\n }}\n onOptionChange={(option) => {\n setHistory((prev) => [...prev, `Option changed: \"${option}\"`]);\n }}\n onValueChange={setValue}\n onSubmit={(val) => {\n setHistory((prev) => [...prev, `Submitted: ${val.option} = \"${val.text}\"`]);\n }}\n options={options}\n searchPlaceholder=\"Type to track changes...\"\n />\n <Stack direction=\"column\" gap=\"100\" fontSize=\"sm\">\n <Text fontWeight=\"medium\">Current state:</Text>\n <Text>Option: {value.option}, Text: \"{value.text}\"</Text>\n {history.length > 0 && (\n <>\n <Text fontWeight=\"medium\" marginTop=\"200\">Event history:</Text>\n {history.slice(-3).map((event, i) => (\n <Text key={i} color=\"fg.muted\">{event}</Text>\n ))}\n </>\n )}\n </Stack>\n </Stack>\n );\n}\n```\n\n**Available callbacks:**\n- `onValueChange`: Fires when either text or option changes (receives complete value object)\n- `onTextChange`: Fires only when search text changes\n- `onOptionChange`: Fires only when selected option changes\n- `onSubmit`: Fires when user submits search (Enter key or search button)\n- `onReset`: Fires when user clears the search text\n\n### Component states\n\nThe component supports common input states:\n\n```jsx live-dev\nconst App = () => {\n const [normalValue, setNormalValue] = useState({ text: '', option: 'all' });\n const [disabledValue, setDisabledValue] = useState({ text: 'disabled', option: 'all' });\n const [readonlyValue, setReadonlyValue] = useState({ text: 'readonly', option: 'all' });\n const [invalidValue, setInvalidValue] = useState({ text: 'error', option: 'all' });\n const [requiredValue, setRequiredValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All Fields', value: 'all' },\n { label: 'Name', value: 'name' },\n ];\n\n return (\n <Stack direction=\"column\" gap=\"400\" alignItems=\"flex-start\">\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"sm\" fontWeight=\"medium\">Normal</Text>\n <ScopedSearchInput\n value={normalValue}\n onValueChange={setNormalValue}\n onSubmit={console.log}\n options={options}\n searchPlaceholder=\"Normal state...\"\n />\n </Stack>\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"sm\" fontWeight=\"medium\">Disabled</Text>\n <ScopedSearchInput\n value={disabledValue}\n onValueChange={setDisabledValue}\n onSubmit={console.log}\n options={options}\n isDisabled\n />\n </Stack>\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"sm\" fontWeight=\"medium\">Read-only</Text>\n <ScopedSearchInput\n value={readonlyValue}\n onValueChange={setReadonlyValue}\n onSubmit={console.log}\n options={options}\n isReadOnly\n />\n </Stack>\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"sm\" fontWeight=\"medium\">Invalid</Text>\n <ScopedSearchInput\n value={invalidValue}\n onValueChange={setInvalidValue}\n onSubmit={console.log}\n options={options}\n isInvalid\n />\n </Stack>\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"sm\" fontWeight=\"medium\">Required</Text>\n <ScopedSearchInput\n value={requiredValue}\n onValueChange={setRequiredValue}\n onSubmit={console.log}\n options={options}\n isRequired\n />\n </Stack>\n </Stack>\n );\n}\n```\n\n### isEmpty utility\n\nUse the static `isEmpty` method to validate search input before submission:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'name' });\n const [validationMessage, setValidationMessage] = useState('');\n\n const options = [\n { label: 'Name', value: 'name' },\n { label: 'Email', value: 'email' },\n ];\n\n const handleSubmit = (val) => {\n if (ScopedSearchInput.isEmpty(val)) {\n setValidationMessage('❌ Please enter a search term');\n } else {\n setValidationMessage(`✓ Searching ${val.option} for: \"${val.text}\"`);\n }\n };\n\n return (\n <Stack direction=\"column\" gap=\"300\">\n <ScopedSearchInput\n value={value}\n onValueChange={(val) => {\n setValue(val);\n setValidationMessage('');\n }}\n onSubmit={handleSubmit}\n options={options}\n searchPlaceholder=\"Type here...\"\n />\n <Stack direction=\"row\" gap=\"200\" alignItems=\"center\">\n <Button onClick={() => handleSubmit(value)}>\n Submit Search\n </Button>\n {validationMessage && (\n <Text\n fontSize=\"sm\"\n color={validationMessage.startsWith('❌') ? 'critical.11' : 'positive.11'}\n >\n {validationMessage}\n </Text>\n )}\n </Stack>\n </Stack>\n );\n}\n```\n\n### Controlled mode\n\nThe ScopedSearchInput requires controlled mode with explicit state management:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'products' });\n const [lastSubmission, setLastSubmission] = useState(null);\n\n const options = [\n { label: 'Products', value: 'products' },\n { label: 'Orders', value: 'orders' },\n { label: 'Customers', value: 'customers' },\n ];\n\n const handleSubmit = (val) => {\n setLastSubmission(val);\n };\n\n const handleClear = () => {\n setValue({ ...value, text: '' });\n setLastSubmission(null);\n };\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={handleSubmit}\n onReset={handleClear}\n options={options}\n selectPlaceholder=\"Select category\"\n searchPlaceholder=\"Enter search term...\"\n />\n <Stack direction=\"column\" gap=\"200\" fontSize=\"sm\">\n <Text fontWeight=\"medium\">Current state:</Text>\n <Text>Category: {value.option}</Text>\n <Text>Search text: {value.text || '(empty)'}</Text>\n {lastSubmission && (\n <>\n <Text fontWeight=\"medium\" marginTop=\"200\">Last submission:</Text>\n <Text>Searched {lastSubmission.option} for: \"{lastSubmission.text}\"</Text>\n </>\n )}\n </Stack>\n </Stack>\n );\n}\n```\n\n## Component requirements\n\n## Accessibility\n\nThe ScopedSearchInput is built with React Aria Components and follows WCAG 2.1 AA guidelines.\n\n#### Role\n\nBoth the select dropdown and search input maintain their respective semantic roles:\n- Select trigger: `role=\"button\"` with appropriate ARIA attributes\n- Search input: `role=\"searchbox\"`\n\n#### Labeling\n\nProvide accessible labels for both parts using `selectAriaLabel` and `searchAriaLabel`:\n\n```tsx\n<ScopedSearchInput\n selectAriaLabel=\"Filter search by field\"\n searchAriaLabel=\"Search term\"\n // ... other props\n/>\n```\n\nWhen using FormField, the field label automatically associates with both inputs via `aria-labelledby`.\n\n#### Keyboard navigation\n\n| Key | Action |\n|-----|--------|\n| `Tab` | Move focus between select dropdown and search input |\n| `Enter` (in select) | Open dropdown menu |\n| `Arrow Up/Down` (in select) | Navigate options |\n| `Enter` (in search) | Submit search |\n| `Escape` | Close dropdown (when open) |\n\n#### ARIA attributes\n\nThe component automatically manages:\n- `aria-controls`: Links select to search input\n- `aria-labelledby`: Associates with FormField label\n- `aria-describedby`: Associates with FormField description and errors\n- `aria-invalid`: Indicates validation state\n\n#### Persistent ID\n\nIf your use case requires tracking and analytics for this component, it is good practice to add a **persistent**, **unique** id to the component:\n\n```tsx\n<ScopedSearchInput\n id=\"product-search\"\n value={value}\n onValueChange={setValue}\n onSubmit={handleSearch}\n options={options}\n/>\n```\n\nThis ensures consistent tracking across sessions and makes it easier to target specific instances for analytics or testing.\n\n## API reference\n\n<PropsTable id=\"ScopedSearchInput\" />\n\n## Common patterns\n\n### Form integration with validation\n\nIntegrate with FormField for complete form functionality:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'products' });\n const [error, setError] = useState('');\n const [touched, setTouched] = useState(false);\n\n const options = [\n { label: 'Products', value: 'products' },\n { label: 'Orders', value: 'orders' },\n { label: 'Customers', value: 'customers' },\n ];\n\n const handleSubmit = (val) => {\n setTouched(true);\n if (ScopedSearchInput.isEmpty(val)) {\n setError('Search term is required');\n } else if (val.text.length < 3) {\n setError('Search term must be at least 3 characters');\n } else {\n setError('');\n console.log('Valid search:', val);\n }\n };\n\n const handleChange = (val) => {\n setValue(val);\n if (touched && error) {\n setError('');\n }\n };\n\n return (\n <FormField.Root isInvalid={!!(error && touched)}>\n <FormField.Label>Search</FormField.Label>\n <FormField.Input>\n <ScopedSearchInput\n value={value}\n onValueChange={handleChange}\n onSubmit={handleSubmit}\n options={options}\n selectPlaceholder=\"Category\"\n searchPlaceholder=\"Enter at least 3 characters...\"\n isInvalid={!!(error && touched)}\n />\n </FormField.Input>\n <FormField.Description>\n Select a category and enter your search term\n </FormField.Description>\n {touched && error && (\n <FormField.Error>{error}</FormField.Error>\n )}\n </FormField.Root>\n );\n}\n```\n\n### Advanced search with filters\n\nImplement a full-featured search interface with multiple filter categories:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'name' });\n const [results, setResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n\n const searchOptions = [\n {\n label: 'Product Info',\n options: [\n { label: 'Product Name', value: 'name' },\n { label: 'SKU', value: 'sku' },\n { label: 'Description', value: 'description' },\n ],\n },\n {\n label: 'Attributes',\n options: [\n { label: 'Brand', value: 'brand' },\n { label: 'Category', value: 'category' },\n ],\n },\n ];\n\n const mockSearch = (searchValue) => {\n setIsSearching(true);\n setTimeout(() => {\n // Simulate API response\n const mockResults = [\n `Found results in ${searchValue.option}: \"${searchValue.text}\"`,\n `Result 1 matching ${searchValue.text}`,\n `Result 2 matching ${searchValue.text}`,\n ];\n setResults(mockResults);\n setIsSearching(false);\n }, 500);\n };\n\n const handleSubmit = (val) => {\n if (!ScopedSearchInput.isEmpty(val)) {\n mockSearch(val);\n }\n };\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={handleSubmit}\n options={searchOptions}\n selectPlaceholder=\"Search by...\"\n searchPlaceholder=\"Enter search term...\"\n />\n {isSearching && (\n <Text fontSize=\"sm\" color=\"fg.muted\">Searching...</Text>\n )}\n {!isSearching && results.length > 0 && (\n <Stack direction=\"column\" gap=\"200\">\n <Text fontSize=\"sm\" fontWeight=\"medium\">Search Results:</Text>\n {results.map((result, i) => (\n <Text key={i} fontSize=\"sm\">{result}</Text>\n ))}\n </Stack>\n )}\n </Stack>\n );\n}\n```\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using ScopedSearchInput in your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Rendering Tests\n\nVerify the component renders with expected elements and structure\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 {\n ScopedSearchInput,\n NimbusProvider,\n type ScopedSearchInputValue,\n} from \"@commercetools/nimbus\";\n\ndescribe(\"ScopedSearchInput - Basic rendering\", () => {\n it(\"renders both select dropdown and search input\", () => {\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n // Verify select trigger is present\n expect(\n screen.getByRole(\"button\", { name: /all fields/i })\n ).toBeInTheDocument();\n\n // Verify search input is present\n expect(screen.getByRole(\"searchbox\")).toBeInTheDocument();\n });\n\n it(\"displays selected option in select trigger\", () => {\n const value: ScopedSearchInputValue = { text: \"john\", option: \"name\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n expect(screen.getByRole(\"button\", { name: /name/i })).toBeInTheDocument();\n });\n\n it(\"displays search text in input field\", () => {\n const value: ScopedSearchInputValue = { text: \"test query\", option: \"all\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n const searchInput = screen.getByRole(\"searchbox\");\n expect(searchInput).toHaveValue(\"test query\");\n });\n});\n```\n\n### User Interaction Tests\n\nTest user interactions including typing, selecting options, and submitting\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 {\n ScopedSearchInput,\n NimbusProvider,\n type ScopedSearchInputValue,\n} from \"@commercetools/nimbus\";\n\ndescribe(\"ScopedSearchInput - Interactions\", () => {\n it(\"allows typing in search input\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n const handleValueChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onValueChange={handleValueChange}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n const searchInput = screen.getByRole(\"searchbox\");\n await user.type(searchInput, \"test\");\n\n // Verify callback was called (typing happens character by character)\n expect(handleValueChange).toHaveBeenCalled();\n });\n\n it(\"allows selecting different option from dropdown\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n const handleValueChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onValueChange={handleValueChange}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n // Open dropdown\n const selectTrigger = screen.getByRole(\"button\", { name: /all fields/i });\n await user.click(selectTrigger);\n\n // Wait for dropdown to appear\n await waitFor(() => {\n expect(screen.getByRole(\"listbox\")).toBeInTheDocument();\n });\n\n // Select \"Name\" option\n const nameOption = screen.getByRole(\"option\", { name: \"Name\" });\n await user.click(nameOption);\n\n // Verify callback was called with new option\n expect(handleValueChange).toHaveBeenCalledWith(\n expect.objectContaining({ option: \"name\" })\n );\n });\n\n it(\"submits search when Enter is pressed in search input\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = {\n text: \"test query\",\n option: \"name\",\n };\n const handleSubmit = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={handleSubmit}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n const searchInput = screen.getByRole(\"searchbox\");\n await user.click(searchInput);\n await user.keyboard(\"{Enter}\");\n\n expect(handleSubmit).toHaveBeenCalledWith(value);\n });\n\n it(\"clears search text when clear button is clicked\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"test\", option: \"all\" };\n const handleValueChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onValueChange={handleValueChange}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n // Find and click clear button\n const clearButton = screen.getByRole(\"button\", { name: /clear/i });\n await user.click(clearButton);\n\n // Verify text was cleared (option remains)\n expect(handleValueChange).toHaveBeenCalledWith(\n expect.objectContaining({ text: \"\", option: \"all\" })\n );\n });\n});\n```\n\n### Callback Tests\n\nTest separate callbacks for text, option, and combined value changes\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 {\n ScopedSearchInput,\n NimbusProvider,\n type ScopedSearchInputValue,\n} from \"@commercetools/nimbus\";\n\ndescribe(\"ScopedSearchInput - Callbacks\", () => {\n it(\"calls onTextChange when search text changes\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n const handleTextChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onTextChange={handleTextChange}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n const searchInput = screen.getByRole(\"searchbox\");\n await user.type(searchInput, \"test\");\n\n // Verify callback was called for text changes\n expect(handleTextChange).toHaveBeenCalled();\n });\n\n it(\"calls onOptionChange when option is selected\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n const handleOptionChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onOptionChange={handleOptionChange}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n // Open and select from dropdown\n await user.click(screen.getByRole(\"button\", { name: /all fields/i }));\n await waitFor(() => {\n expect(screen.getByRole(\"listbox\")).toBeInTheDocument();\n });\n await user.click(screen.getByRole(\"option\", { name: \"Email\" }));\n\n expect(handleOptionChange).toHaveBeenCalledWith(\"email\");\n });\n\n it(\"calls onValueChange for both text and option changes\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n const handleValueChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onValueChange={handleValueChange}\n onSubmit={vi.fn()}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n // Type text\n const searchInput = screen.getByRole(\"searchbox\");\n await user.type(searchInput, \"test\");\n expect(handleValueChange).toHaveBeenCalled();\n\n // Change option\n handleValueChange.mockClear();\n await user.click(screen.getByRole(\"button\", { name: /all fields/i }));\n await waitFor(() => {\n expect(screen.getByRole(\"listbox\")).toBeInTheDocument();\n });\n await user.click(screen.getByRole(\"option\", { name: \"Name\" }));\n\n expect(handleValueChange).toHaveBeenCalledWith(\n expect.objectContaining({ option: \"name\" })\n );\n });\n});\n```\n\n### State Tests\n\nTest disabled, readonly, invalid, and required states\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 {\n ScopedSearchInput,\n NimbusProvider,\n type ScopedSearchInputValue,\n} from \"@commercetools/nimbus\";\n\ndescribe(\"ScopedSearchInput - States\", () => {\n it(\"disables both inputs when isDisabled is true\", () => {\n const value: ScopedSearchInputValue = { text: \"test\", option: \"all\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n isDisabled\n />\n </NimbusProvider>\n );\n\n // Check select button has disabled attribute\n const selectButton = screen.getByRole(\"button\", { name: /all fields/i });\n expect(selectButton).toBeDisabled();\n\n // Check search input is disabled\n expect(screen.getByRole(\"searchbox\")).toBeDisabled();\n });\n\n it(\"makes both inputs readonly when isReadOnly is true\", () => {\n const value: ScopedSearchInputValue = { text: \"test\", option: \"all\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n isReadOnly\n />\n </NimbusProvider>\n );\n\n // Search input should be readonly\n expect(screen.getByRole(\"searchbox\")).toHaveAttribute(\"readonly\");\n });\n\n it(\"applies invalid state styling when isInvalid is true\", () => {\n const value: ScopedSearchInputValue = { text: \"invalid\", option: \"all\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n isInvalid\n />\n </NimbusProvider>\n );\n\n const searchInput = screen.getByRole(\"searchbox\");\n expect(searchInput).toBeInvalid();\n });\n\n it(\"marks inputs as required when isRequired is true\", () => {\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={defaultOptions}\n isRequired\n />\n </NimbusProvider>\n );\n\n // Check search input has required attribute\n const searchInput = screen.getByRole(\"searchbox\");\n expect(searchInput).toBeRequired();\n });\n});\n```\n\n### Validation Tests\n\nTest the isEmpty utility and validation patterns\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 {\n ScopedSearchInput,\n NimbusProvider,\n type ScopedSearchInputValue,\n} from \"@commercetools/nimbus\";\n\ndescribe(\"ScopedSearchInput - Validation\", () => {\n it(\"isEmpty returns true for empty text\", () => {\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n expect(ScopedSearchInput.isEmpty(value)).toBe(true);\n });\n\n it(\"isEmpty returns true for whitespace-only text\", () => {\n const value: ScopedSearchInputValue = { text: \" \", option: \"all\" };\n expect(ScopedSearchInput.isEmpty(value)).toBe(true);\n });\n\n it(\"isEmpty returns false for non-empty text\", () => {\n const value: ScopedSearchInputValue = {\n text: \"search term\",\n option: \"all\",\n };\n expect(ScopedSearchInput.isEmpty(value)).toBe(false);\n });\n\n it(\"validates on submit using isEmpty utility\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"all\" };\n const handleSubmit = vi.fn((val: ScopedSearchInputValue) => {\n if (ScopedSearchInput.isEmpty(val)) {\n // Return validation error instead of throwing\n return { error: \"Search term required\" };\n }\n return { success: true };\n });\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={handleSubmit}\n options={defaultOptions}\n />\n </NimbusProvider>\n );\n\n const searchInput = screen.getByRole(\"searchbox\");\n await user.click(searchInput);\n await user.keyboard(\"{Enter}\");\n\n // Verify submit was called\n expect(handleSubmit).toHaveBeenCalled();\n // Verify it returned an error for empty value\n const result = handleSubmit.mock.results[0].value;\n expect(result).toEqual({ error: \"Search term required\" });\n });\n});\n```\n\n### Grouped Options Tests\n\nTest option group functionality\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 {\n ScopedSearchInput,\n NimbusProvider,\n type ScopedSearchInputValue,\n} from \"@commercetools/nimbus\";\n\ndescribe(\"ScopedSearchInput - Grouped options\", () => {\n const groupedOptions = [\n {\n label: \"Contact Info\",\n options: [\n { label: \"Email\", value: \"email\" },\n { label: \"Phone\", value: \"phone\" },\n ],\n },\n {\n label: \"Personal Info\",\n options: [\n { label: \"Name\", value: \"name\" },\n { label: \"Address\", value: \"address\" },\n ],\n },\n ];\n\n it(\"renders option groups correctly\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"email\" };\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onSubmit={vi.fn()}\n options={groupedOptions}\n />\n </NimbusProvider>\n );\n\n // Open dropdown\n await user.click(screen.getByRole(\"button\", { name: /email/i }));\n\n // Wait for dropdown\n await waitFor(() => {\n expect(screen.getByRole(\"listbox\")).toBeInTheDocument();\n });\n\n // Verify groups are present (groups typically render as separate elements)\n expect(screen.getByRole(\"option\", { name: \"Email\" })).toBeInTheDocument();\n expect(screen.getByRole(\"option\", { name: \"Phone\" })).toBeInTheDocument();\n expect(screen.getByRole(\"option\", { name: \"Name\" })).toBeInTheDocument();\n expect(screen.getByRole(\"option\", { name: \"Address\" })).toBeInTheDocument();\n });\n\n it(\"allows selecting options from different groups\", async () => {\n const user = userEvent.setup();\n const value: ScopedSearchInputValue = { text: \"\", option: \"email\" };\n const handleOptionChange = vi.fn();\n\n render(\n <NimbusProvider>\n <ScopedSearchInput\n aria-label=\"Scoped search\"\n value={value}\n onOptionChange={handleOptionChange}\n onSubmit={vi.fn()}\n options={groupedOptions}\n />\n </NimbusProvider>\n );\n\n // Select option from second group\n await user.click(screen.getByRole(\"button\", { name: /email/i }));\n await waitFor(() => {\n expect(screen.getByRole(\"listbox\")).toBeInTheDocument();\n });\n await user.click(screen.getByRole(\"option\", { name: \"Name\" }));\n\n expect(handleOptionChange).toHaveBeenCalledWith(\"name\");\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-scopedsearchinput--docs)\n- [SearchInput Component](/components/inputs/searchinput)\n- [Select Component](/components/inputs/select)\n- [React Aria SearchField](https://react-spectrum.adobe.com/react-aria/SearchField.html)\n- [React Aria Select](https://react-spectrum.adobe.com/react-aria/Select.html)\n",
|
|
191
|
+
"toc": [
|
|
192
|
+
{
|
|
193
|
+
"value": "Getting started",
|
|
194
|
+
"href": "#getting-started",
|
|
195
|
+
"depth": 2,
|
|
196
|
+
"numbering": [
|
|
197
|
+
1,
|
|
198
|
+
1
|
|
199
|
+
],
|
|
200
|
+
"parent": "root"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"value": "Import",
|
|
204
|
+
"href": "#import",
|
|
205
|
+
"depth": 3,
|
|
206
|
+
"numbering": [
|
|
207
|
+
1,
|
|
208
|
+
1,
|
|
209
|
+
1
|
|
210
|
+
],
|
|
211
|
+
"parent": "root"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"value": "Basic usage",
|
|
215
|
+
"href": "#basic-usage",
|
|
216
|
+
"depth": 3,
|
|
217
|
+
"numbering": [
|
|
218
|
+
1,
|
|
219
|
+
1,
|
|
220
|
+
2
|
|
221
|
+
],
|
|
222
|
+
"parent": "root"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"value": "Usage examples",
|
|
226
|
+
"href": "#usage-examples",
|
|
227
|
+
"depth": 2,
|
|
228
|
+
"numbering": [
|
|
229
|
+
1,
|
|
230
|
+
2
|
|
231
|
+
],
|
|
232
|
+
"parent": "root"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"value": "Size options",
|
|
236
|
+
"href": "#size-options",
|
|
237
|
+
"depth": 3,
|
|
238
|
+
"numbering": [
|
|
239
|
+
1,
|
|
240
|
+
2,
|
|
241
|
+
1
|
|
242
|
+
],
|
|
243
|
+
"parent": "root"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"value": "Grouped options",
|
|
247
|
+
"href": "#grouped-options",
|
|
248
|
+
"depth": 3,
|
|
249
|
+
"numbering": [
|
|
250
|
+
1,
|
|
251
|
+
2,
|
|
252
|
+
2
|
|
253
|
+
],
|
|
254
|
+
"parent": "root"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"value": "Auto-focus behavior",
|
|
258
|
+
"href": "#auto-focus-behavior",
|
|
259
|
+
"depth": 3,
|
|
260
|
+
"numbering": [
|
|
261
|
+
1,
|
|
262
|
+
2,
|
|
263
|
+
3
|
|
264
|
+
],
|
|
265
|
+
"parent": "root"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"value": "State management patterns",
|
|
269
|
+
"href": "#state-management-patterns",
|
|
270
|
+
"depth": 3,
|
|
271
|
+
"numbering": [
|
|
272
|
+
1,
|
|
273
|
+
2,
|
|
274
|
+
4
|
|
275
|
+
],
|
|
276
|
+
"parent": "root"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"value": "Component states",
|
|
280
|
+
"href": "#component-states",
|
|
281
|
+
"depth": 3,
|
|
282
|
+
"numbering": [
|
|
283
|
+
1,
|
|
284
|
+
2,
|
|
285
|
+
5
|
|
286
|
+
],
|
|
287
|
+
"parent": "root"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"value": "isEmpty utility",
|
|
291
|
+
"href": "#isempty-utility",
|
|
292
|
+
"depth": 3,
|
|
293
|
+
"numbering": [
|
|
294
|
+
1,
|
|
295
|
+
2,
|
|
296
|
+
6
|
|
297
|
+
],
|
|
298
|
+
"parent": "root"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"value": "Controlled mode",
|
|
302
|
+
"href": "#controlled-mode",
|
|
303
|
+
"depth": 3,
|
|
304
|
+
"numbering": [
|
|
305
|
+
1,
|
|
306
|
+
2,
|
|
307
|
+
7
|
|
308
|
+
],
|
|
309
|
+
"parent": "root"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"value": "Component requirements",
|
|
313
|
+
"href": "#component-requirements",
|
|
314
|
+
"depth": 2,
|
|
315
|
+
"numbering": [
|
|
316
|
+
1,
|
|
317
|
+
3
|
|
318
|
+
],
|
|
319
|
+
"parent": "root"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"value": "Accessibility",
|
|
323
|
+
"href": "#accessibility",
|
|
324
|
+
"depth": 2,
|
|
325
|
+
"numbering": [
|
|
326
|
+
1,
|
|
327
|
+
4
|
|
328
|
+
],
|
|
329
|
+
"parent": "root"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"value": "Role",
|
|
333
|
+
"href": "#role",
|
|
334
|
+
"depth": 4,
|
|
335
|
+
"numbering": [
|
|
336
|
+
1,
|
|
337
|
+
4,
|
|
338
|
+
1,
|
|
339
|
+
1
|
|
340
|
+
],
|
|
341
|
+
"parent": "root"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"value": "Labeling",
|
|
345
|
+
"href": "#labeling",
|
|
346
|
+
"depth": 4,
|
|
347
|
+
"numbering": [
|
|
348
|
+
1,
|
|
349
|
+
4,
|
|
350
|
+
1,
|
|
351
|
+
2
|
|
352
|
+
],
|
|
353
|
+
"parent": "root"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"value": "Keyboard navigation",
|
|
357
|
+
"href": "#keyboard-navigation",
|
|
358
|
+
"depth": 4,
|
|
359
|
+
"numbering": [
|
|
360
|
+
1,
|
|
361
|
+
4,
|
|
362
|
+
1,
|
|
363
|
+
3
|
|
364
|
+
],
|
|
365
|
+
"parent": "root"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"value": "ARIA attributes",
|
|
369
|
+
"href": "#aria-attributes",
|
|
370
|
+
"depth": 4,
|
|
371
|
+
"numbering": [
|
|
372
|
+
1,
|
|
373
|
+
4,
|
|
374
|
+
1,
|
|
375
|
+
4
|
|
376
|
+
],
|
|
377
|
+
"parent": "root"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"value": "Persistent ID",
|
|
381
|
+
"href": "#persistent-id",
|
|
382
|
+
"depth": 4,
|
|
383
|
+
"numbering": [
|
|
384
|
+
1,
|
|
385
|
+
4,
|
|
386
|
+
1,
|
|
387
|
+
5
|
|
388
|
+
],
|
|
389
|
+
"parent": "root"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"value": "API reference",
|
|
393
|
+
"href": "#api-reference",
|
|
394
|
+
"depth": 2,
|
|
395
|
+
"numbering": [
|
|
396
|
+
1,
|
|
397
|
+
5
|
|
398
|
+
],
|
|
399
|
+
"parent": "root"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"value": "Common patterns",
|
|
403
|
+
"href": "#common-patterns",
|
|
404
|
+
"depth": 2,
|
|
405
|
+
"numbering": [
|
|
406
|
+
1,
|
|
407
|
+
6
|
|
408
|
+
],
|
|
409
|
+
"parent": "root"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"value": "Form integration with validation",
|
|
413
|
+
"href": "#form-integration-with-validation",
|
|
414
|
+
"depth": 3,
|
|
415
|
+
"numbering": [
|
|
416
|
+
1,
|
|
417
|
+
6,
|
|
418
|
+
1
|
|
419
|
+
],
|
|
420
|
+
"parent": "root"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"value": "Advanced search with filters",
|
|
424
|
+
"href": "#advanced-search-with-filters",
|
|
425
|
+
"depth": 3,
|
|
426
|
+
"numbering": [
|
|
427
|
+
1,
|
|
428
|
+
6,
|
|
429
|
+
2
|
|
430
|
+
],
|
|
431
|
+
"parent": "root"
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"value": "Testing your implementation",
|
|
435
|
+
"href": "#testing-your-implementation",
|
|
436
|
+
"depth": 2,
|
|
437
|
+
"numbering": [
|
|
438
|
+
1,
|
|
439
|
+
7
|
|
440
|
+
],
|
|
441
|
+
"parent": "root"
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"value": "Basic Rendering Tests",
|
|
445
|
+
"href": "#basic-rendering-tests",
|
|
446
|
+
"depth": 3,
|
|
447
|
+
"numbering": [
|
|
448
|
+
1,
|
|
449
|
+
7,
|
|
450
|
+
1
|
|
451
|
+
],
|
|
452
|
+
"parent": "root"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"value": "User Interaction Tests",
|
|
456
|
+
"href": "#user-interaction-tests",
|
|
457
|
+
"depth": 3,
|
|
458
|
+
"numbering": [
|
|
459
|
+
1,
|
|
460
|
+
7,
|
|
461
|
+
2
|
|
462
|
+
],
|
|
463
|
+
"parent": "root"
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
"value": "Callback Tests",
|
|
467
|
+
"href": "#callback-tests",
|
|
468
|
+
"depth": 3,
|
|
469
|
+
"numbering": [
|
|
470
|
+
1,
|
|
471
|
+
7,
|
|
472
|
+
3
|
|
473
|
+
],
|
|
474
|
+
"parent": "root"
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"value": "State Tests",
|
|
478
|
+
"href": "#state-tests",
|
|
479
|
+
"depth": 3,
|
|
480
|
+
"numbering": [
|
|
481
|
+
1,
|
|
482
|
+
7,
|
|
483
|
+
4
|
|
484
|
+
],
|
|
485
|
+
"parent": "root"
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"value": "Validation Tests",
|
|
489
|
+
"href": "#validation-tests",
|
|
490
|
+
"depth": 3,
|
|
491
|
+
"numbering": [
|
|
492
|
+
1,
|
|
493
|
+
7,
|
|
494
|
+
5
|
|
495
|
+
],
|
|
496
|
+
"parent": "root"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"value": "Grouped Options Tests",
|
|
500
|
+
"href": "#grouped-options-tests",
|
|
501
|
+
"depth": 3,
|
|
502
|
+
"numbering": [
|
|
503
|
+
1,
|
|
504
|
+
7,
|
|
505
|
+
6
|
|
506
|
+
],
|
|
507
|
+
"parent": "root"
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"value": "Resources",
|
|
511
|
+
"href": "#resources",
|
|
512
|
+
"depth": 2,
|
|
513
|
+
"numbering": [
|
|
514
|
+
1,
|
|
515
|
+
8
|
|
516
|
+
],
|
|
517
|
+
"parent": "root"
|
|
518
|
+
}
|
|
519
|
+
]
|
|
520
|
+
},
|
|
521
|
+
"guidelines": {
|
|
522
|
+
"mdx": "\n## Guidelines\n\nScoped search guidelines establish how the component is used to manage\nmulti-domain data, ensuring consistency and accessibility in defining the search\ncontext.\n\n### Best practices\n\n- **Set a smart default**: Choose the default scope that will be used by the\n vast majority of users. This saves the user an extra click most of the time.\n- **Instant application of scope**: When the user changes the scope (e.g.,\n switches from \"All\" to \"Orders\"), that choice should be applied immediately\n without requiring the user to re-type the search query.\n- **Default scope clarity**: Always define a clear, logical default scope (e.g.,\n \"All\" or the most common category) and clearly display it before the user\n interacts with the component.\n- **Persistent scope**: Ensure the scope selector remains visible and accessible\n next to the input field, clearly communicating the current search context at\n all times.\n- **Intuitive scope options**: Keep scope options concise and meaningful to the\n area that they are being placed in.\n- **Display results contextually**: When the search results page loads,\n reiterate the search parameters clearly: \"Showing 15 results for 'Jackets'\n within the 'Products' scope.\"\n- **Handle zero results gracefully**: If the user selects a very narrow scope\n and gets zero results, suggest alternative, broader scopes they can try\n without leaving the current page.\n- **Clear iconography**: If the scope selector uses icons, ensure they are\n universally understood and directly relate to the content they filter.\n\n### Usage\n\nThe scoped search component is used to enable users to perform precise searches\nby filtering the query to a specific, predefined domain or content area, leading\nto more relevant results.\n\n> [!TIP]\\\n> When to use\n\n- **Multi-domain data**: When the application contains large, distinct data\n types that users often need to query separately (e.g., searching for \"Jackets\"\n within Products vs. searching for \"Jackets\" within Customers).\n- **Precision is key**: When users require highly precise results and filtering\n the search domain reduces irrelevant noise (common in back-office solutions).\n- **Restricted permissions**: To restrict searches based on user permissions\n (e.g., a manager can search \"All Teams,\" but a team member can only search \"My\n Team\").\n\n> [!CAUTION]\\\n> When not to use\n\n- **Homogeneous data**: When all data belongs to a single, unified domain and\n scoping would offer minimal benefit (e.g., searching within a simple, short\n blog archive).\n- **Global search default**: If the vast majority of users always search\n \"everything,\" avoid forcing them to define a scope. A single global search\n with filters applied after the results load is often better.\n- **Limited screen space**: If screen real estate is minimal, avoid the\n dual-component structure of scoped search; consider a single input with\n filtering options revealed on search results page.\n\n### Scoped search do's and don'ts\n\n> [!TIP]\\\n> **Do**\n>\n> - Ensure the scope selector is accessible and its current state is conveyed\n> via ARIA.\n> - Use concise labels for scope options, the short and descriptive is best.\n> - Do set a smart default for scopes.\n\n```jsx live\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'all' });\n\n const options = [\n { label: 'All fields', value: 'all' },\n { label: 'Products', value: 'products' },\n { label: 'Customers', value: 'customers' },\n ];\n\n return (\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n );\n};\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Don't use confusing or abstract icons for scopes\n> - Don't reset the scope back to the default after every search or page\n> refresh.\n> - Don't mix global and scoped results.\n> - Don't offer too many scopes.\n> - Don't force users to select a scope, determine a reasonable default for\n> users.\n> - Don't use highly technical or internal terms for scope labels (e.g.,\n> \"Database Table A\" instead of \"Products\").\n\n```jsx live\nconst App = () => {\n const [value, setValue] = useState({ text: '', option: 'complex-query' });\n\n const options = [\n { label: 'Products that were published in the last month but no other time', value: 'complex-query' },\n { label: 'Categories', value: 'categories' },\n { label: 'Products not published this month', value: 'not-published' },\n { label: 'Products published by Kevin that have been recalled in May', value: 'oh-kevin' },\n ];\n\n return (\n <ScopedSearchInput\n value={value}\n onValueChange={setValue}\n onSubmit={(val) => console.log('Search:', val)}\n options={options}\n searchPlaceholder=\"Search...\"\n />\n );\n};\n```\n",
|
|
523
|
+
"toc": [
|
|
524
|
+
{
|
|
525
|
+
"value": "Guidelines",
|
|
526
|
+
"href": "#guidelines",
|
|
527
|
+
"depth": 2,
|
|
528
|
+
"numbering": [
|
|
529
|
+
1,
|
|
530
|
+
1
|
|
531
|
+
],
|
|
532
|
+
"parent": "root"
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
"value": "Best practices",
|
|
536
|
+
"href": "#best-practices",
|
|
537
|
+
"depth": 3,
|
|
538
|
+
"numbering": [
|
|
539
|
+
1,
|
|
540
|
+
1,
|
|
541
|
+
1
|
|
542
|
+
],
|
|
543
|
+
"parent": "root"
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
"value": "Usage",
|
|
547
|
+
"href": "#usage",
|
|
548
|
+
"depth": 3,
|
|
549
|
+
"numbering": [
|
|
550
|
+
1,
|
|
551
|
+
1,
|
|
552
|
+
2
|
|
553
|
+
],
|
|
554
|
+
"parent": "root"
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
"value": "Scoped search do's and don'ts",
|
|
558
|
+
"href": "#scoped-search-dos-and-donts",
|
|
559
|
+
"depth": 3,
|
|
560
|
+
"numbering": [
|
|
561
|
+
1,
|
|
562
|
+
1,
|
|
563
|
+
3
|
|
564
|
+
],
|
|
565
|
+
"parent": "root"
|
|
566
|
+
}
|
|
567
|
+
]
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|