@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,855 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"id": "Components-DataTable",
|
|
4
|
+
"title": "Data table",
|
|
5
|
+
"exportName": "DataTable",
|
|
6
|
+
"description": "Data tables display sets of data across rows and columns. Use them to organize information, enable comparisons, and allow users to inspect and interact with large datasets.",
|
|
7
|
+
"lifecycleState": "Stable",
|
|
8
|
+
"order": 999,
|
|
9
|
+
"repoPath": "packages/nimbus/src/components/data-table/data-table.mdx",
|
|
10
|
+
"menu": [
|
|
11
|
+
"Components",
|
|
12
|
+
"Data Display",
|
|
13
|
+
"Data table"
|
|
14
|
+
],
|
|
15
|
+
"route": "components/data-display/data-table",
|
|
16
|
+
"tags": [
|
|
17
|
+
"component",
|
|
18
|
+
"table",
|
|
19
|
+
"data"
|
|
20
|
+
],
|
|
21
|
+
"toc": [
|
|
22
|
+
{
|
|
23
|
+
"value": "Overview",
|
|
24
|
+
"href": "#overview",
|
|
25
|
+
"depth": 3,
|
|
26
|
+
"numbering": [
|
|
27
|
+
1,
|
|
28
|
+
1,
|
|
29
|
+
1
|
|
30
|
+
],
|
|
31
|
+
"parent": "root"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"value": "Resources",
|
|
35
|
+
"href": "#resources",
|
|
36
|
+
"depth": 2,
|
|
37
|
+
"numbering": [
|
|
38
|
+
1,
|
|
39
|
+
2
|
|
40
|
+
],
|
|
41
|
+
"parent": "root"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"value": "Variables",
|
|
45
|
+
"href": "#variables",
|
|
46
|
+
"depth": 3,
|
|
47
|
+
"numbering": [
|
|
48
|
+
1,
|
|
49
|
+
2,
|
|
50
|
+
1
|
|
51
|
+
],
|
|
52
|
+
"parent": "root"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"value": "Visual options",
|
|
56
|
+
"href": "#visual-options",
|
|
57
|
+
"depth": 4,
|
|
58
|
+
"numbering": [
|
|
59
|
+
1,
|
|
60
|
+
2,
|
|
61
|
+
1,
|
|
62
|
+
1
|
|
63
|
+
],
|
|
64
|
+
"parent": "root"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"value": "Data table manager",
|
|
68
|
+
"href": "#data-table-manager",
|
|
69
|
+
"depth": 3,
|
|
70
|
+
"numbering": [
|
|
71
|
+
1,
|
|
72
|
+
2,
|
|
73
|
+
2
|
|
74
|
+
],
|
|
75
|
+
"parent": "root"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"figmaLink": "https://www.figma.com/design/gHbAJGfcrCv7f2bgzUQgHq/NIMBUS-Guidelines?node-id=4256-4285&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\nData tables are essential tools for presenting sets of data in a structured\nformat, utilizing rows and columns to enhance clarity. They serve multiple\npurposes: organizing information systematically, facilitating comparisons\nbetween different data points, and providing users with the ability to\nthoroughly inspect and interact with extensive datasets. By incorporating\nfeatures such as sorting, filtering, and pagination, data tables empower users\nto navigate through large volumes of information efficiently, making them\ninvaluable in data analysis and reporting.\n\n\n\n## Resources\n\nDeep dive into details and access the Nimbus design library\n\n[Figma library](https://www.figma.com/design/gHbAJGfcrCv7f2bgzUQgHq/NIMBUS-Guidelines?node-id=4256-4285&m=dev)\n\n**Related components**\n\n- **[Table Component](/components/data-display/table)** - Use the simpler Table component for static, read-only data without interactive features\n\n### Variables\n\nGet familiar with the features.\n\n#### Visual options\n\n**Data table base:** Click on any row to open the product details modal. Edit\nthe fields and click Save to see the changes reflected in the data table. Table\nis sorted by Date Modified (newest first) by default.\n\n**Bulk selection:** Users can make bulk selections by selecting the checkbox in\nthe table header to select all visible rows, or by choosing multiple rows at\nonce. To clear, users can uncheck the boxes or click on the header checkbox to\nselect all and then unselect all at once.\n\n**Text highlight and copy:** We’ve made improvements to this feature, you can\nnow highlight text to make easier to copy and use for users workflows. Clicking\noutside the text within the box will open details, but clicks made over the text\nwill allow for selection, highlight and copying.\n\n**Column resizing**\n\nThis component allows for columns to be resized. It is helpful when there is a\nlot of data and columns can be restricted or grown by the user to show more\ninformation.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isResizable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isResizable: true,\n },\n ];\n return <DataTable rows={data} columns={columns} />;\n}\n```\n\n**Search and highlight**\n\nAdding a search field close to the data table that will refine the shown data to\nshow the user relevant information is extremely helpful to users.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n\n const [search, setSearch] = useState(\"\");\n\n return <Stack gap={16}>\n <TextInput\n value={search}\n onChange={setSearch}\n placeholder=\"Search...\"\n width=\"1/3\"\n aria-label=\"search-rows\"\n data-testid=\"search-input\"\n />\n <DataTable\n rows={data}\n columns={columns}\n search={search}\n />\n </Stack>\n}\n```\n\n**Controlled sorting**\n\nThe sorting state is controlled externally and can be programmatically changed.\nThis can be applied to some and not all the headers if more control is needed,\nor to simplify if the need is less for some columns of data.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isSortable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isSortable: true,\n },\n ];\n const [sortDescriptor, setSortDescriptor] = useState({\n column: \"modified\",\n direction: \"ascending\",\n });\n\n return <Stack gap={16}>\n <Button\n onPress={() =>\n setSortDescriptor({ column: \"name\", direction: \"ascending\" })\n }\n variant=\"outline\"\n width=\"2xs\"\n >\n Sort by Name (A-Z)\n </Button>\n <DataTable\n rows={data}\n columns={columns}\n onSortChange={setSortDescriptor}\n sortDescriptor={sortDescriptor}\n />\n </Stack>\n}\n```\n\n**Condensed**\n\nTo show more data in smaller spaces, condense the vertical padding. This setting\nis also included in the data table manager if giving control to users to choose\na more condensed styling is appealing for the placement of a data table.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n\n const [condensed, setCondensed] = useState(false);\n\n return <Stack gap={16}>\n <Checkbox\n isSelected={condensed}\n onChange={setCondensed}\n >\n Condensed\n </Checkbox>\n <DataTable\n rows={data}\n columns={columns}\n density={condensed ? \"condensed\" : \"default\"}\n />\n </Stack>\n}\n```\n\n**Sticky header**\n\nWe encourage using sticky headers as default on data tables. Keeping context by\nutilizing sticky users is important when scrolling long rows of data. Make sure\nto give it a maxHeight value when you use the sticky header feature.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n\n const [sticky, setSticky] = useState(false);\n\n return <Stack gap={16}>\n <Checkbox\n isSelected={sticky}\n onChange={setSticky}\n >\n Sticky header (with max height)\n </Checkbox>\n <DataTable\n rows={data}\n columns={columns}\n maxHeight={sticky ? \"400px\" : undefined}\n />\n </Stack>\n}\n```\n\n**Nested content**\n\nNested tables can be used to show related or detailed information about a given\nrow in a data table. Make it obvious to users that there is more information in\nthis area.\n\n```jsx live\nconst App = () => {\n const data = [\n {\n id: 1,\n name: 'Product A',\n modified: '2023-10-27',\n children: (\n <Box p={16}>\n <DataTable\n rows={[\n { id: 'a1', variant: 'Size S', stock: 150 },\n { id: 'a2', variant: 'Size M', stock: 200 },\n { id: 'a3', variant: 'Size L', stock: 180 },\n ]}\n columns={[\n { id: 'variant', header: 'Variant', accessor: (row) => row.variant },\n { id: 'stock', header: 'Stock', accessor: (row) => row.stock },\n ]}\n />\n </Box>\n ),\n },\n {\n id: 2,\n name: 'Product B',\n modified: '2023-10-26',\n children: (\n <Box p={16}>\n <DataTable\n rows={[\n { id: 'b1', variant: 'Color Red', stock: 75 },\n { id: 'b2', variant: 'Color Blue', stock: 90 },\n ]}\n columns={[\n { id: 'variant', header: 'Variant', accessor: (row) => row.variant },\n { id: 'stock', header: 'Stock', accessor: (row) => row.stock },\n ]}\n />\n </Box>\n ),\n },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isResizable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isResizable: true,\n },\n ];\n return <DataTable rows={data} columns={columns} nestedKey=\"children\" />;\n}\n```\n\n**Pinned rows**\n\nUsing pins can allow users to keep certain rows of data to the top of the data\ntable. These keep commonly referenced information in a predictable areas for\nusers to return to often.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isResizable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isResizable: true,\n },\n ];\n return <DataTable rows={data} columns={columns} />;\n}\n```\n\n### Data table manager\n\nData table managerThe data table manager enables customization of the displayed\ndata and structure within a Data table. It provides a single interface for users\nto personalize complex data views to match their specific workflow needs. It\norganizes customization options into two primary tabs: **Visible columns** and\n**Layout settings**.\n\n- **Visible columns:** This tab controls the visibility and order of available\n data columns within the data table.\n- **Layout settings:** This tab controls structural and display properties for\n the entire table view. Users have control over the row density and text\n truncation.\n\n```jsx live\nconst App = () => {\n const allColumns = [\n {\n id: \"product-name\",\n header: \"Product name\",\n accessor: (row) => row.name,\n isSortable: true,\n },\n {\n id: \"sku\",\n header: \"SKU\",\n accessor: (row) => row.sku,\n isSortable: true,\n },\n {\n id: \"status\",\n header: \"Status\",\n accessor: (row) => (\n <Badge\n colorPalette={\n row.status === \"Published\"\n ? \"success\"\n : row.status === \"Modified\"\n ? \"warning\"\n : \"neutral\"\n }\n size=\"xs\"\n >\n {row.status}\n </Badge>\n ),\n },\n {\n id: \"category\",\n header: \"Category\",\n accessor: (row) => row.category,\n },\n {\n id: \"inventory\",\n header: \"Inventory\",\n accessor: (row) => row.inventory,\n },\n {\n id: \"price\",\n header: \"Price\",\n accessor: (row) => row.price,\n },\n {\n id: \"store\",\n header: \"Store\",\n accessor: (row) => row.store,\n },\n ];\n\n const initialVisibleColumns = [\n allColumns[0], // product-name\n allColumns[1], // sku\n allColumns[2], // status\n allColumns[3], // category\n allColumns[4], // inventory\n ];\n\n const data = [\n {\n id: \"1\",\n name: \"Midnight Bloom Silk Blouse\",\n sku: \"MID-BLM-SLK-BLS\",\n status: \"Published\",\n category: \"Clothing\",\n inventory: 120,\n price: \"$89.99\",\n store: \"Main Store\",\n },\n {\n id: \"2\",\n name: \"Urban Canvas Denim\",\n sku: \"URB-CAN-DNM\",\n status: \"Modified\",\n category: \"Clothing\",\n inventory: 85,\n price: \"$129.99\",\n store: \"Downtown\",\n },\n {\n id: \"3\",\n name: \"Coastal Breeze Linen Pants\",\n sku: \"COS-BRZ-LIN-PNT\",\n status: \"Published\",\n category: \"Clothing\",\n inventory: 200,\n price: \"$79.99\",\n store: \"Beach Shop\",\n },\n ];\n\n const [visibleColumns, setVisibleColumns] = useState(initialVisibleColumns);\n const [isTruncated, setIsTruncated] = useState(false);\n const [density, setDensity] = useState(\"default\");\n\n const handleColumnsChange = (updatedColumns) => {\n setVisibleColumns(updatedColumns);\n };\n\n const handleSettingsChange = (action) => {\n if (!action) {\n return;\n }\n switch (action) {\n case \"toggleTextVisibility\":\n setIsTruncated(!isTruncated);\n break;\n case \"toggleRowDensity\":\n setDensity(density === \"condensed\" ? \"default\" : \"condensed\");\n break;\n }\n };\n\n return (\n <Stack direction=\"column\" gap={16}>\n <DataTable.Root\n columns={allColumns}\n rows={data}\n visibleColumns={visibleColumns.map((col) => col.id)}\n allowsSorting={true}\n isTruncated={isTruncated}\n density={density}\n onColumnsChange={handleColumnsChange}\n onSettingsChange={handleSettingsChange}\n >\n <Flex\n justifyContent=\"space-between\"\n alignItems=\"center\"\n width=\"100%\"\n >\n <Text>Table settings</Text>\n <Box p={8}>\n <DataTable.Manager />\n </Box>\n </Flex>\n <DataTable.Table aria-label=\"Products table\">\n <DataTable.Header aria-label=\"Products table header\" />\n <DataTable.Body aria-label=\"Products table body\" />\n </DataTable.Table>\n </DataTable.Root>\n </Stack>\n );\n}\n```\n",
|
|
104
|
+
"views": {
|
|
105
|
+
"overview": {
|
|
106
|
+
"mdx": "\n### Overview\n\nData tables are essential tools for presenting sets of data in a structured\nformat, utilizing rows and columns to enhance clarity. They serve multiple\npurposes: organizing information systematically, facilitating comparisons\nbetween different data points, and providing users with the ability to\nthoroughly inspect and interact with extensive datasets. By incorporating\nfeatures such as sorting, filtering, and pagination, data tables empower users\nto navigate through large volumes of information efficiently, making them\ninvaluable in data analysis and reporting.\n\n\n\n## Resources\n\nDeep dive into details and access the Nimbus design library\n\n[Figma library](https://www.figma.com/design/gHbAJGfcrCv7f2bgzUQgHq/NIMBUS-Guidelines?node-id=4256-4285&m=dev)\n\n**Related components**\n\n- **[Table Component](/components/data-display/table)** - Use the simpler Table component for static, read-only data without interactive features\n\n### Variables\n\nGet familiar with the features.\n\n#### Visual options\n\n**Data table base:** Click on any row to open the product details modal. Edit\nthe fields and click Save to see the changes reflected in the data table. Table\nis sorted by Date Modified (newest first) by default.\n\n**Bulk selection:** Users can make bulk selections by selecting the checkbox in\nthe table header to select all visible rows, or by choosing multiple rows at\nonce. To clear, users can uncheck the boxes or click on the header checkbox to\nselect all and then unselect all at once.\n\n**Text highlight and copy:** We’ve made improvements to this feature, you can\nnow highlight text to make easier to copy and use for users workflows. Clicking\noutside the text within the box will open details, but clicks made over the text\nwill allow for selection, highlight and copying.\n\n**Column resizing**\n\nThis component allows for columns to be resized. It is helpful when there is a\nlot of data and columns can be restricted or grown by the user to show more\ninformation.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isResizable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isResizable: true,\n },\n ];\n return <DataTable rows={data} columns={columns} />;\n}\n```\n\n**Search and highlight**\n\nAdding a search field close to the data table that will refine the shown data to\nshow the user relevant information is extremely helpful to users.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n\n const [search, setSearch] = useState(\"\");\n\n return <Stack gap={16}>\n <TextInput\n value={search}\n onChange={setSearch}\n placeholder=\"Search...\"\n width=\"1/3\"\n aria-label=\"search-rows\"\n data-testid=\"search-input\"\n />\n <DataTable\n rows={data}\n columns={columns}\n search={search}\n />\n </Stack>\n}\n```\n\n**Controlled sorting**\n\nThe sorting state is controlled externally and can be programmatically changed.\nThis can be applied to some and not all the headers if more control is needed,\nor to simplify if the need is less for some columns of data.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isSortable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isSortable: true,\n },\n ];\n const [sortDescriptor, setSortDescriptor] = useState({\n column: \"modified\",\n direction: \"ascending\",\n });\n\n return <Stack gap={16}>\n <Button\n onPress={() =>\n setSortDescriptor({ column: \"name\", direction: \"ascending\" })\n }\n variant=\"outline\"\n width=\"2xs\"\n >\n Sort by Name (A-Z)\n </Button>\n <DataTable\n rows={data}\n columns={columns}\n onSortChange={setSortDescriptor}\n sortDescriptor={sortDescriptor}\n />\n </Stack>\n}\n```\n\n**Condensed**\n\nTo show more data in smaller spaces, condense the vertical padding. This setting\nis also included in the data table manager if giving control to users to choose\na more condensed styling is appealing for the placement of a data table.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n\n const [condensed, setCondensed] = useState(false);\n\n return <Stack gap={16}>\n <Checkbox\n isSelected={condensed}\n onChange={setCondensed}\n >\n Condensed\n </Checkbox>\n <DataTable\n rows={data}\n columns={columns}\n density={condensed ? \"condensed\" : \"default\"}\n />\n </Stack>\n}\n```\n\n**Sticky header**\n\nWe encourage using sticky headers as default on data tables. Keeping context by\nutilizing sticky users is important when scrolling long rows of data. Make sure\nto give it a maxHeight value when you use the sticky header feature.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n\n const [sticky, setSticky] = useState(false);\n\n return <Stack gap={16}>\n <Checkbox\n isSelected={sticky}\n onChange={setSticky}\n >\n Sticky header (with max height)\n </Checkbox>\n <DataTable\n rows={data}\n columns={columns}\n maxHeight={sticky ? \"400px\" : undefined}\n />\n </Stack>\n}\n```\n\n**Nested content**\n\nNested tables can be used to show related or detailed information about a given\nrow in a data table. Make it obvious to users that there is more information in\nthis area.\n\n```jsx live\nconst App = () => {\n const data = [\n {\n id: 1,\n name: 'Product A',\n modified: '2023-10-27',\n children: (\n <Box p={16}>\n <DataTable\n rows={[\n { id: 'a1', variant: 'Size S', stock: 150 },\n { id: 'a2', variant: 'Size M', stock: 200 },\n { id: 'a3', variant: 'Size L', stock: 180 },\n ]}\n columns={[\n { id: 'variant', header: 'Variant', accessor: (row) => row.variant },\n { id: 'stock', header: 'Stock', accessor: (row) => row.stock },\n ]}\n />\n </Box>\n ),\n },\n {\n id: 2,\n name: 'Product B',\n modified: '2023-10-26',\n children: (\n <Box p={16}>\n <DataTable\n rows={[\n { id: 'b1', variant: 'Color Red', stock: 75 },\n { id: 'b2', variant: 'Color Blue', stock: 90 },\n ]}\n columns={[\n { id: 'variant', header: 'Variant', accessor: (row) => row.variant },\n { id: 'stock', header: 'Stock', accessor: (row) => row.stock },\n ]}\n />\n </Box>\n ),\n },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isResizable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isResizable: true,\n },\n ];\n return <DataTable rows={data} columns={columns} nestedKey=\"children\" />;\n}\n```\n\n**Pinned rows**\n\nUsing pins can allow users to keep certain rows of data to the top of the data\ntable. These keep commonly referenced information in a predictable areas for\nusers to return to often.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isResizable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isResizable: true,\n },\n ];\n return <DataTable rows={data} columns={columns} />;\n}\n```\n\n### Data table manager\n\nData table managerThe data table manager enables customization of the displayed\ndata and structure within a Data table. It provides a single interface for users\nto personalize complex data views to match their specific workflow needs. It\norganizes customization options into two primary tabs: **Visible columns** and\n**Layout settings**.\n\n- **Visible columns:** This tab controls the visibility and order of available\n data columns within the data table.\n- **Layout settings:** This tab controls structural and display properties for\n the entire table view. Users have control over the row density and text\n truncation.\n\n```jsx live\nconst App = () => {\n const allColumns = [\n {\n id: \"product-name\",\n header: \"Product name\",\n accessor: (row) => row.name,\n isSortable: true,\n },\n {\n id: \"sku\",\n header: \"SKU\",\n accessor: (row) => row.sku,\n isSortable: true,\n },\n {\n id: \"status\",\n header: \"Status\",\n accessor: (row) => (\n <Badge\n colorPalette={\n row.status === \"Published\"\n ? \"success\"\n : row.status === \"Modified\"\n ? \"warning\"\n : \"neutral\"\n }\n size=\"xs\"\n >\n {row.status}\n </Badge>\n ),\n },\n {\n id: \"category\",\n header: \"Category\",\n accessor: (row) => row.category,\n },\n {\n id: \"inventory\",\n header: \"Inventory\",\n accessor: (row) => row.inventory,\n },\n {\n id: \"price\",\n header: \"Price\",\n accessor: (row) => row.price,\n },\n {\n id: \"store\",\n header: \"Store\",\n accessor: (row) => row.store,\n },\n ];\n\n const initialVisibleColumns = [\n allColumns[0], // product-name\n allColumns[1], // sku\n allColumns[2], // status\n allColumns[3], // category\n allColumns[4], // inventory\n ];\n\n const data = [\n {\n id: \"1\",\n name: \"Midnight Bloom Silk Blouse\",\n sku: \"MID-BLM-SLK-BLS\",\n status: \"Published\",\n category: \"Clothing\",\n inventory: 120,\n price: \"$89.99\",\n store: \"Main Store\",\n },\n {\n id: \"2\",\n name: \"Urban Canvas Denim\",\n sku: \"URB-CAN-DNM\",\n status: \"Modified\",\n category: \"Clothing\",\n inventory: 85,\n price: \"$129.99\",\n store: \"Downtown\",\n },\n {\n id: \"3\",\n name: \"Coastal Breeze Linen Pants\",\n sku: \"COS-BRZ-LIN-PNT\",\n status: \"Published\",\n category: \"Clothing\",\n inventory: 200,\n price: \"$79.99\",\n store: \"Beach Shop\",\n },\n ];\n\n const [visibleColumns, setVisibleColumns] = useState(initialVisibleColumns);\n const [isTruncated, setIsTruncated] = useState(false);\n const [density, setDensity] = useState(\"default\");\n\n const handleColumnsChange = (updatedColumns) => {\n setVisibleColumns(updatedColumns);\n };\n\n const handleSettingsChange = (action) => {\n if (!action) {\n return;\n }\n switch (action) {\n case \"toggleTextVisibility\":\n setIsTruncated(!isTruncated);\n break;\n case \"toggleRowDensity\":\n setDensity(density === \"condensed\" ? \"default\" : \"condensed\");\n break;\n }\n };\n\n return (\n <Stack direction=\"column\" gap={16}>\n <DataTable.Root\n columns={allColumns}\n rows={data}\n visibleColumns={visibleColumns.map((col) => col.id)}\n allowsSorting={true}\n isTruncated={isTruncated}\n density={density}\n onColumnsChange={handleColumnsChange}\n onSettingsChange={handleSettingsChange}\n >\n <Flex\n justifyContent=\"space-between\"\n alignItems=\"center\"\n width=\"100%\"\n >\n <Text>Table settings</Text>\n <Box p={8}>\n <DataTable.Manager />\n </Box>\n </Flex>\n <DataTable.Table aria-label=\"Products table\">\n <DataTable.Header aria-label=\"Products table header\" />\n <DataTable.Body aria-label=\"Products table body\" />\n </DataTable.Table>\n </DataTable.Root>\n </Stack>\n );\n}\n```\n",
|
|
107
|
+
"toc": [
|
|
108
|
+
{
|
|
109
|
+
"value": "Overview",
|
|
110
|
+
"href": "#overview",
|
|
111
|
+
"depth": 3,
|
|
112
|
+
"numbering": [
|
|
113
|
+
1,
|
|
114
|
+
1,
|
|
115
|
+
1
|
|
116
|
+
],
|
|
117
|
+
"parent": "root"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"value": "Resources",
|
|
121
|
+
"href": "#resources",
|
|
122
|
+
"depth": 2,
|
|
123
|
+
"numbering": [
|
|
124
|
+
1,
|
|
125
|
+
2
|
|
126
|
+
],
|
|
127
|
+
"parent": "root"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"value": "Variables",
|
|
131
|
+
"href": "#variables",
|
|
132
|
+
"depth": 3,
|
|
133
|
+
"numbering": [
|
|
134
|
+
1,
|
|
135
|
+
2,
|
|
136
|
+
1
|
|
137
|
+
],
|
|
138
|
+
"parent": "root"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"value": "Visual options",
|
|
142
|
+
"href": "#visual-options",
|
|
143
|
+
"depth": 4,
|
|
144
|
+
"numbering": [
|
|
145
|
+
1,
|
|
146
|
+
2,
|
|
147
|
+
1,
|
|
148
|
+
1
|
|
149
|
+
],
|
|
150
|
+
"parent": "root"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"value": "Data table manager",
|
|
154
|
+
"href": "#data-table-manager",
|
|
155
|
+
"depth": 3,
|
|
156
|
+
"numbering": [
|
|
157
|
+
1,
|
|
158
|
+
2,
|
|
159
|
+
2
|
|
160
|
+
],
|
|
161
|
+
"parent": "root"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
"a11y": {
|
|
166
|
+
"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 data = [\n { id: 1, name: 'Product A', modified: '2023-10-27' },\n { id: 2, name: 'Product B', modified: '2023-10-26' },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n },\n ];\n return <DataTable rows={data} columns={columns} />;\n}\n```\n\n### Accessibility standards\n\n- **Ensure proper semantic structure:** Use the `<table>`, `<th>`, and `<tr>`\n HTML elements correctly for screen reader compatibility.\n- **Associate data with headers:** Map table data cells `<td>` to the\n corresponding header cells `<th>` using appropriate ARIA attributes if the\n native structure is insufficient for complex tables.\n- **Provide clear context:** Use a table caption `<caption>` to give a concise\n overview of the table's content. Include descriptive column header text.\n- **Manage keyboard focus:** Ensure all interactive elements within the table\n (sort buttons, links, etc.) are reachable and operable via the keyboard.\n- **Maintain sufficient contrast:** Ensure text and background colors meet WCAG\n AA contrast standards for readability.\n",
|
|
167
|
+
"toc": [
|
|
168
|
+
{
|
|
169
|
+
"value": "Accessibility",
|
|
170
|
+
"href": "#accessibility",
|
|
171
|
+
"depth": 2,
|
|
172
|
+
"numbering": [
|
|
173
|
+
1,
|
|
174
|
+
1
|
|
175
|
+
],
|
|
176
|
+
"parent": "root"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"value": "Accessibility standards",
|
|
180
|
+
"href": "#accessibility-standards",
|
|
181
|
+
"depth": 3,
|
|
182
|
+
"numbering": [
|
|
183
|
+
1,
|
|
184
|
+
1,
|
|
185
|
+
1
|
|
186
|
+
],
|
|
187
|
+
"parent": "root"
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
"dev": {
|
|
192
|
+
"mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport {\n DataTable,\n type DataTableProps,\n type DataTableColumnItem,\n type DataTableRowItem,\n type SortDescriptor,\n} from \"@commercetools/nimbus\";\n```\n\n### Basic usage\n\nThe DataTable renders data in a tabular format with support for sorting,\nselection, resizing, and nested content. At minimum, you need to provide\n`columns` and `rows`:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n ];\n\n const rows = [\n { id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },\n { id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'User' },\n { id: '3', name: 'Carol Williams', email: 'carol@example.com', role: 'Editor' },\n ];\n\n return <DataTable columns={columns} rows={rows} />;\n}\n```\n\n## When to use DataTable\n\nUse the **DataTable** component when you need **interactive data management features** like sorting, filtering, selection, or pagination. It's built for complex, data-intensive interfaces.\n\n### Keep it simple\n\n**Don't use DataTable for static content.** If you only need to display read-only data without interactive features, use the simpler [Table component](/components/data-display/table) instead. It's lighter, faster, and easier to implement.\n\n**Use the Table component when you:**\n- Display static reference data (specifications, comparisons, pricing)\n- Have small datasets (between 10 - 20 rows)\n- Don't need sorting, filtering, or selection\n- Want a straightforward implementation without state management\n\n**Use DataTable when you need:**\n- Sorting columns in ascending or descending order\n- Row selection for bulk actions\n- Column management (show/hide, reorder, resize)\n- Search and filtering across data\n- Pagination for large datasets (> 50 rows)\n- Server-side data operations\n- Nested/hierarchical data with expansion\n- Dense data visualization with configurable row spacing\n\n**Example decision:**\n\n```tsx\n// ✗ Avoid - Overkill for static data\n<DataTable\n columns={threeStaticColumns}\n rows={fiveStaticRows}\n/>\n\n// ✓ Better - Use Table for static content\n<Table.Root>\n <Table.Header>{/* specification headers */}</Table.Header>\n <Table.Body>{/* specification rows */}</Table.Body>\n</Table.Root>\n```\n\n→ **[See Table component documentation](/components/data-display/table)** for simple table layouts.\n\n## Working with columns and rows\n\n### Column configuration\n\nEach column requires an `id`, `header`, and `accessor` function. Additional\noptions control sorting, resizing, and custom rendering:\n\n```tsx\nimport type { DataTableColumnItem } from \"@commercetools/nimbus\";\n\nconst columns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Product Name\",\n accessor: (row) => row.name,\n isSortable: true, // Enable sorting for this column\n isResizable: true, // Enable column resizing\n isRowHeader: true, // Mark as row header for accessibility\n width: 200, // Initial width in pixels\n minWidth: 100, // Minimum width when resizing\n maxWidth: 400, // Maximum width when resizing\n },\n {\n id: \"status\",\n header: \"Status\",\n accessor: (row) => row.status,\n // Custom rendering with the render prop\n render: ({ value }) => (\n <Badge colorPalette={value === \"Active\" ? \"positive\" : \"neutral\"}>\n {value}\n </Badge>\n ),\n },\n {\n id: \"actions\",\n header: \"Actions\",\n accessor: (row) => row.id,\n headerIcon: (\n <IconButton aria-label=\"Info\" size=\"2xs\" variant=\"ghost\">\n <Info />\n </IconButton>\n ),\n isSortable: false,\n },\n];\n```\n\n### Row data structure\n\nEach row must have a unique `id` property. Additional properties are accessed\nvia column `accessor` functions:\n\n```tsx\nimport type { DataTableRowItem } from \"@commercetools/nimbus\";\n\nconst rows: DataTableRowItem[] = [\n { id: \"1\", name: \"Product A\", status: \"Active\", price: 99.99 },\n {\n id: \"2\",\n name: \"Product B\",\n status: \"Draft\",\n price: 149.99,\n isDisabled: true,\n },\n { id: \"3\", name: \"Product C\", status: \"Active\", price: 79.99 },\n];\n```\n\n## Usage examples\n\n### Density options\n\nThe `density` prop controls row height and padding. Use `condensed` for\ndata-dense interfaces:\n\n```jsx live-dev\nconst App = () => {\n const [density, setDensity] = useState('default');\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', role: 'Admin' },\n { id: '2', name: 'Bob', role: 'User' },\n { id: '3', name: 'Carol', role: 'Editor' },\n ];\n\n return (\n <Stack gap=\"400\">\n <Stack direction=\"row\" gap=\"300\">\n <Button\n variant={density === 'default' ? 'solid' : 'outline'}\n colorPalette=\"primary\"\n onPress={() => setDensity('default')}\n >\n Default\n </Button>\n <Button\n variant={density === 'condensed' ? 'solid' : 'outline'}\n colorPalette=\"primary\"\n onPress={() => setDensity('condensed')}\n >\n Condensed\n </Button>\n </Stack>\n <DataTable columns={columns} rows={rows} density={density} />\n </Stack>\n );\n}\n```\n\n### Sorting\n\nEnable sorting with `allowsSorting`. Individual columns can opt-out using\n`isSortable: false`:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name, isSortable: true },\n { id: 'age', header: 'Age', accessor: (row) => row.age, isSortable: true },\n { id: 'role', header: 'Role', accessor: (row) => row.role, isSortable: false },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', age: 30, role: 'Admin' },\n { id: '2', name: 'Bob', age: 25, role: 'User' },\n { id: '3', name: 'Carol', age: 28, role: 'Editor' },\n ];\n\n return (\n <Stack gap=\"300\">\n <Text>Click column headers to sort. The \"Role\" column is not sortable.</Text>\n <DataTable\n columns={columns}\n rows={rows}\n allowsSorting\n defaultSortDescriptor={{ column: 'name', direction: 'ascending' }}\n />\n </Stack>\n );\n}\n```\n\n### Controlled sorting\n\nFor external sort state management, use `sortDescriptor` and `onSortChange`:\n\n```jsx live-dev\nconst App = () => {\n const [sortDescriptor, setSortDescriptor] = useState({\n column: 'name',\n direction: 'ascending',\n });\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name, isSortable: true },\n { id: 'age', header: 'Age', accessor: (row) => row.age, isSortable: true },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', age: 30 },\n { id: '2', name: 'Bob', age: 25 },\n { id: '3', name: 'Carol', age: 28 },\n ];\n\n return (\n <Stack gap=\"400\">\n <Text>\n Sorting by: <Text as=\"strong\">{sortDescriptor.column}</Text> ({sortDescriptor.direction})\n </Text>\n <Stack direction=\"row\" gap=\"300\">\n <Button\n variant=\"outline\"\n size=\"md\"\n colorPalette=\"primary\"\n onPress={() => setSortDescriptor({ column: 'name', direction: 'ascending' })}\n >\n Sort by Name (A-Z)\n </Button>\n <Button\n variant=\"outline\"\n size=\"md\"\n colorPalette=\"primary\"\n onPress={() => setSortDescriptor({ column: 'age', direction: 'descending' })}\n >\n Sort by Age (High-Low)\n </Button>\n </Stack>\n <DataTable\n columns={columns}\n rows={rows}\n allowsSorting\n sortDescriptor={sortDescriptor}\n onSortChange={setSortDescriptor}\n />\n </Stack>\n );\n}\n```\n\n### Row selection\n\nEnable selection with `selectionMode`. Supports `none`, `single`, or `multiple`:\n\n```jsx live-dev\nconst App = () => {\n const [selectedKeys, setSelectedKeys] = useState(new Set(['1']));\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', email: 'alice@example.com' },\n { id: '2', name: 'Bob', email: 'bob@example.com' },\n { id: '3', name: 'Carol', email: 'carol@example.com' },\n ];\n\n const selectedCount = selectedKeys === 'all' ? rows.length : selectedKeys.size;\n\n return (\n <Stack gap=\"400\">\n <Text>Selected: {selectedCount} row(s)</Text>\n <Stack direction=\"row\" gap=\"300\">\n <Button\n variant=\"outline\"\n colorPalette=\"primary\"\n size=\"md\"\n onPress={() => setSelectedKeys(new Set())}>\n Clear\n </Button>\n <Button size=\"md\" colorPalette=\"primary\" variant=\"outline\" onPress={() => setSelectedKeys('all')}>\n Select All\n </Button>\n </Stack>\n <DataTable\n columns={columns}\n rows={rows}\n selectionMode=\"multiple\"\n selectedKeys={selectedKeys}\n onSelectionChange={setSelectedKeys}\n />\n </Stack>\n );\n}\n```\n\n### Resizable columns\n\nEnable column resizing with `isResizable` on the table or individual columns:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name, isResizable: true, width: 150 },\n { id: 'description', header: 'Description', accessor: (row) => row.description, isResizable: true },\n { id: 'status', header: 'Status', accessor: (row) => row.status, isResizable: false },\n ];\n\n const rows = [\n { id: '1', name: 'Product A', description: 'A great product for everyday use', status: 'Active' },\n { id: '2', name: 'Product B', description: 'Premium quality item', status: 'Draft' },\n ];\n\n return (\n <Stack gap=\"300\">\n <Text>Drag column borders to resize. The \"Status\" column is fixed width.</Text>\n <DataTable columns={columns} rows={rows} isResizable />\n </Stack>\n );\n}\n```\n\n### Sticky header with scrolling\n\nUse `maxHeight` to enable vertical scrolling with a sticky header:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n ];\n\n const rows = Array.from({ length: 20 }, (_, i) => ({\n id: String(i + 1),\n name: `User ${i + 1}`,\n role: i % 3 === 0 ? 'Admin' : i % 3 === 1 ? 'Editor' : 'Viewer',\n }));\n\n return (\n <DataTable\n columns={columns}\n rows={rows}\n maxHeight=\"300px\"\n />\n );\n}\n```\n\n### Text truncation\n\nEnable `isTruncated` to truncate long text with ellipsis:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'description', header: 'Description', accessor: (row) => row.description },\n ];\n\n const rows = [\n {\n id: '1',\n name: 'Product A',\n description: 'This is a very long description that should be truncated when displayed in the table cell to prevent layout issues.',\n },\n {\n id: '2',\n name: 'Product B',\n description: 'Another lengthy description that demonstrates how truncation works with ellipsis and maintains a clean table layout.',\n },\n ];\n\n return <DataTable columns={columns} rows={rows} isTruncated />;\n}\n```\n\n### Clickable rows\n\nUse `onRowClick` to handle row click events for navigation or detail views:\n\n```jsx live-dev\nconst App = () => {\n const [clickedRow, setClickedRow] = useState(null);\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', email: 'alice@example.com' },\n { id: '2', name: 'Bob', email: 'bob@example.com' },\n { id: '3', name: 'Carol', email: 'carol@example.com' },\n ];\n\n return (\n <Stack gap=\"400\">\n <Text>\n {clickedRow ? `Clicked: ${clickedRow.name}` : 'Click a row to see details'}\n </Text>\n <DataTable\n columns={columns}\n rows={rows}\n onRowClick={(row) => setClickedRow(row)}\n />\n </Stack>\n );\n}\n```\n\n### Search and filtering\n\nUse the `search` prop to filter rows across all visible columns:\n\n```jsx live-dev\nconst App = () => {\n const [search, setSearch] = useState('');\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n { id: 'department', header: 'Department', accessor: (row) => row.department },\n ];\n\n const rows = [\n { id: '1', name: 'Alice Johnson', role: 'Admin', department: 'Engineering' },\n { id: '2', name: 'Bob Smith', role: 'Developer', department: 'Engineering' },\n { id: '3', name: 'Carol Williams', role: 'Designer', department: 'Design' },\n { id: '4', name: 'David Brown', role: 'Manager', department: 'Sales' },\n ];\n\n return (\n <Stack gap=\"400\">\n <TextInput\n value={search}\n onChange={setSearch}\n placeholder=\"Search...\"\n width=\"300px\"\n aria-label=\"Search table\"\n />\n <DataTable columns={columns} rows={rows} search={search} />\n </Stack>\n );\n}\n```\n\n### Column visibility\n\nControl which columns are displayed using `visibleColumns`:\n\n```jsx live-dev\nconst App = () => {\n const [visibleColumns, setVisibleColumns] = useState(['name', 'email']);\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n { id: 'department', header: 'Department', accessor: (row) => row.department },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', email: 'alice@example.com', role: 'Admin', department: 'Engineering' },\n { id: '2', name: 'Bob', email: 'bob@example.com', role: 'User', department: 'Sales' },\n ];\n\n const toggleColumn = (colId) => {\n setVisibleColumns((prev) =>\n prev.includes(colId) ? prev.filter((id) => id !== colId) : [...prev, colId]\n );\n };\n\n return (\n <Stack gap=\"400\">\n <Stack direction=\"row\" gap=\"300\" wrap=\"wrap\">\n {columns.map((col) => (\n <Checkbox\n key={col.id}\n isSelected={visibleColumns.includes(col.id)}\n onChange={() => toggleColumn(col.id)}\n >\n {col.header}\n </Checkbox>\n ))}\n </Stack>\n <DataTable columns={columns} rows={rows} visibleColumns={visibleColumns} />\n </Stack>\n );\n}\n```\n\n### Nested rows\n\nUse `nestedKey` to enable expandable nested content:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Category', accessor: (row) => row.name },\n { id: 'count', header: 'Products', accessor: (row) => row.count },\n ];\n\n const rows = [\n {\n id: '1',\n name: 'Electronics',\n count: 3,\n children: [\n { id: '1-1', name: 'Laptops', count: 15 },\n { id: '1-2', name: 'Phones', count: 25 },\n { id: '1-3', name: 'Tablets', count: 10 },\n ],\n },\n {\n id: '2',\n name: 'Clothing',\n count: 2,\n children: [\n { id: '2-1', name: 'Shirts', count: 50 },\n { id: '2-2', name: 'Pants', count: 30 },\n ],\n },\n { id: '3', name: 'Books', count: 0 },\n ];\n\n return (\n <Stack gap=\"300\">\n <Text>Click the expand button to view nested items.</Text>\n <DataTable columns={columns} rows={rows} nestedKey=\"children\" />\n </Stack>\n );\n}\n```\n\n### With footer\n\nAdd pagination or summary content using the `footer` prop:\n\n```jsx live-dev\nconst App = () => {\n const [page, setPage] = useState(1);\n const pageSize = 3;\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n ];\n\n const allRows = Array.from({ length: 10 }, (_, i) => ({\n id: String(i + 1),\n name: `User ${i + 1}`,\n role: i % 2 === 0 ? 'Admin' : 'User',\n }));\n\n const totalPages = Math.ceil(allRows.length / pageSize);\n const rows = allRows.slice((page - 1) * pageSize, page * pageSize);\n\n return (\n <DataTable\n columns={columns}\n rows={rows}\n footer={\n <Stack direction=\"row\" justify=\"space-between\" align=\"center\" width=\"full\" px=\"400\" py=\"300\">\n <Text fontSize=\"sm\">\n Showing {(page - 1) * pageSize + 1}-{Math.min(page * pageSize, allRows.length)} of {allRows.length}\n </Text>\n <Stack direction=\"row\" gap=\"200\">\n <Button\n size=\"md\"\n colorPalette=\"primary\"\n variant=\"outline\"\n onPress={() => setPage((p) => Math.max(1, p - 1))}\n isDisabled={page === 1}\n >\n Previous\n </Button>\n <Button\n size=\"md\"\n variant=\"outline\"\n colorPalette=\"primary\"\n onPress={() => setPage((p) => Math.min(totalPages, p + 1))}\n isDisabled={page === totalPages}\n >\n Next\n </Button>\n </Stack>\n </Stack>\n }\n />\n );\n}\n```\n\n### Disabled rows\n\nUse `disabledKeys` to prevent interaction with specific rows:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'status', header: 'Status', accessor: (row) => row.status },\n ];\n\n const rows = [\n { id: '1', name: 'Active Item', status: 'Available' },\n { id: '2', name: 'Disabled Item', status: 'Unavailable' },\n { id: '3', name: 'Another Active', status: 'Available' },\n ];\n\n return (\n <DataTable\n columns={columns}\n rows={rows}\n selectionMode=\"multiple\"\n disabledKeys={new Set(['2'])}\n />\n );\n}\n```\n\n### Row pinning\n\nPin important rows to the top of the table:\n\n```jsx live-dev\nconst App = () => {\n const [pinnedRows, setPinnedRows] = useState(new Set(['1']));\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'priority', header: 'Priority', accessor: (row) => row.priority },\n ];\n\n const rows = [\n { id: '1', name: 'High Priority Task', priority: 'High' },\n { id: '2', name: 'Normal Task', priority: 'Normal' },\n { id: '3', name: 'Low Priority Task', priority: 'Low' },\n ];\n\n return (\n <Stack gap=\"400\">\n <Text>Row 1 is pinned to the top. Pinned rows stay at top when sorting.</Text>\n <DataTable\n columns={columns}\n rows={rows}\n pinnedRows={pinnedRows}\n onPinToggle={(rowId) => {\n setPinnedRows((prev) => {\n const next = new Set(prev);\n if (next.has(rowId)) {\n next.delete(rowId);\n } else {\n next.add(rowId);\n }\n return next;\n });\n }}\n allowsSorting\n />\n </Stack>\n );\n}\n```\n\n## Compound component API\n\nFor advanced customization, use the compound component pattern:\n\n```jsx live-dev\nconst App = () => {\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n ];\n\n const rows = [\n { id: '1', name: 'Alice', role: 'Admin' },\n { id: '2', name: 'Bob', role: 'User' },\n ];\n\n return (\n <DataTable.Root columns={columns} rows={rows} allowsSorting>\n <DataTable.Manager />\n <DataTable.Table aria-label=\"Users table\">\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n <DataTable.Footer>\n <Stack direction=\"row\" justify=\"center\" py=\"300\">\n <Text fontSize=\"sm\" color=\"neutral.11\">Custom footer content</Text>\n </Stack>\n </DataTable.Footer>\n </DataTable.Root>\n );\n}\n```\n\n### Available sub-components\n\n| Component | Description |\n| ------------------- | ------------------------------------------------------------------------------------------------------- |\n| `DataTable.Root` | Provider component that manages state and context |\n| `DataTable.Table` | The main table element |\n| `DataTable.Header` | Table header with column headers |\n| `DataTable.Body` | Table body containing rows |\n| `DataTable.Footer` | Footer section for pagination or summaries |\n| `DataTable.Manager` | Settings panel for column visibility and layout - [see detailed docs below](#advanced-datatablemanager) |\n\n## Advanced: DataTable.Manager\n\nThe `DataTable.Manager` component provides a settings drawer for managing table\nconfiguration. It enables users to control column visibility, reorder columns,\nand adjust layout settings through an intuitive interface.\n\n### Manager overview\n\nDataTable.Manager must be used within a DataTable.Root to access table state and\ncolumns. It renders as a settings button that opens a drawer with multiple\nconfiguration tabs:\n\n```jsx live-dev\nconst App = () => {\n const [visibleColumns, setVisibleColumns] = useState(['name', 'email', 'role']);\n const [isTruncated, setIsTruncated] = useState(false);\n const [density, setDensity] = useState('default');\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n ];\n\n const rows = [\n { id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },\n { id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'User' },\n { id: '3', name: 'Carol Williams', email: 'carol@example.com', role: 'Editor' },\n ];\n\n const handleSettingsChange = (action) => {\n if (action === 'toggleTextVisibility') {\n setIsTruncated(!isTruncated);\n } else if (action === 'toggleRowDensity') {\n setDensity(density === 'default' ? 'condensed' : 'default');\n }\n };\n\n return (\n <DataTable.Root\n columns={columns}\n rows={rows}\n visibleColumns={visibleColumns}\n onColumnsChange={(updatedColumns) => {\n setVisibleColumns(updatedColumns.map(col => col.id));\n }}\n isTruncated={isTruncated}\n density={density}\n onSettingsChange={handleSettingsChange}\n >\n <Stack direction=\"row\" justifyContent=\"space-between\" mb=\"400\">\n <Text fontSize=\"lg\" fontWeight=\"600\">Users</Text>\n <DataTable.Manager />\n </Stack>\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n );\n}\n```\n\n### Manager: Column visibility\n\nThe Manager provides a dual-list interface for managing visible and hidden\ncolumns. Users can drag columns between lists, reorder visible columns, and\nsearch hidden columns:\n\n```jsx live-dev\nconst App = () => {\n const [visibleColumns, setVisibleColumns] = useState(['name', 'email']);\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n { id: 'department', header: 'Department', accessor: (row) => row.department },\n ];\n\n const rows = [\n { id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin', department: 'Engineering' },\n { id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'User', department: 'Sales' },\n ];\n\n return (\n <DataTable.Root\n columns={columns}\n rows={rows}\n visibleColumns={visibleColumns}\n onColumnsChange={(updatedColumns) => {\n setVisibleColumns(updatedColumns.map(col => col.id));\n }}\n >\n <Stack direction=\"row\" justifyContent=\"space-between\" mb=\"400\">\n <Text fontSize=\"lg\" fontWeight=\"600\">Team Members</Text>\n <DataTable.Manager />\n </Stack>\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n );\n}\n```\n\n### Manager: Layout settings\n\nThe Layout Settings tab allows users to control text truncation and row density.\nThese settings update the table's visual appearance in real-time:\n\n```jsx live-dev\nconst App = () => {\n const [visibleColumns, setVisibleColumns] = useState(['name', 'description']);\n const [isTruncated, setIsTruncated] = useState(false);\n const [density, setDensity] = useState('default');\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'description', header: 'Description', accessor: (row) => row.description },\n ];\n\n const rows = [\n {\n id: '1',\n name: 'Product A',\n description: 'This is a very long description that will demonstrate text truncation when enabled'\n },\n {\n id: '2',\n name: 'Product B',\n description: 'Another lengthy description to show how the layout settings affect content display'\n },\n ];\n\n const handleSettingsChange = (action) => {\n if (action === 'toggleTextVisibility') {\n setIsTruncated(!isTruncated);\n } else if (action === 'toggleRowDensity') {\n setDensity(density === 'default' ? 'condensed' : 'default');\n }\n };\n\n return (\n <DataTable.Root\n columns={columns}\n rows={rows}\n visibleColumns={visibleColumns}\n onColumnsChange={(updatedColumns) => {\n setVisibleColumns(updatedColumns.map(col => col.id));\n }}\n isTruncated={isTruncated}\n density={density}\n onSettingsChange={handleSettingsChange}\n >\n <Stack direction=\"row\" justifyContent=\"space-between\" mb=\"400\">\n <Text fontSize=\"lg\" fontWeight=\"600\">Products</Text>\n <DataTable.Manager />\n </Stack>\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n );\n}\n```\n\n### Manager: Custom settings tab\n\nAdd a custom settings tab by providing the `customSettings` prop to\nDataTable.Root. This allows extending the Manager with application-specific\nconfiguration:\n\n```jsx live-dev\nconst App = () => {\n const [visibleColumns, setVisibleColumns] = useState(['name', 'status']);\n const [filterStatus, setFilterStatus] = useState('all');\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'status', header: 'Status', accessor: (row) => row.status },\n ];\n\n const allRows = [\n { id: '1', name: 'Task A', status: 'Active' },\n { id: '2', name: 'Task B', status: 'Inactive' },\n { id: '3', name: 'Task C', status: 'Active' },\n ];\n\n const filteredRows = filterStatus === 'all'\n ? allRows\n : allRows.filter(row => row.status.toLowerCase() === filterStatus);\n\n const customSettingsPanel = (\n <Stack direction=\"column\" gap=\"400\">\n <Text fontWeight=\"600\">Filter Options</Text>\n <ToggleButtonGroup.Root\n selectedKeys={[filterStatus]}\n onSelectionChange={(keys) => setFilterStatus(Array.from(keys)[0])}\n >\n <ToggleButton id=\"all\">All</ToggleButton>\n <ToggleButton id=\"active\">Active</ToggleButton>\n <ToggleButton id=\"inactive\">Inactive</ToggleButton>\n </ToggleButtonGroup.Root>\n </Stack>\n );\n\n return (\n <DataTable.Root\n columns={columns}\n rows={filteredRows}\n visibleColumns={visibleColumns}\n onColumnsChange={(updatedColumns) => {\n setVisibleColumns(updatedColumns.map(col => col.id));\n }}\n customSettings={{\n label: 'Filters',\n icon: <Icons.FilterList />,\n panel: customSettingsPanel,\n }}\n >\n <Stack direction=\"row\" justifyContent=\"space-between\" mb=\"400\">\n <Text fontSize=\"lg\" fontWeight=\"600\">Tasks</Text>\n <DataTable.Manager />\n </Stack>\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n );\n}\n```\n\n### Manager: Controlled column state with persistence\n\nManage column configuration externally for features like saving user\npreferences:\n\n```jsx live-dev\nconst App = () => {\n const [visibleColumns, setVisibleColumns] = useState(['name', 'email', 'role']);\n\n const columns = [\n { id: 'name', header: 'Name', accessor: (row) => row.name },\n { id: 'email', header: 'Email', accessor: (row) => row.email },\n { id: 'role', header: 'Role', accessor: (row) => row.role },\n { id: 'department', header: 'Department', accessor: (row) => row.department },\n { id: 'location', header: 'Location', accessor: (row) => row.location },\n ];\n\n const rows = [\n {\n id: '1',\n name: 'Alice Johnson',\n email: 'alice@example.com',\n role: 'Admin',\n department: 'Engineering',\n location: 'New York'\n },\n ];\n\n const handleColumnsChange = (updatedColumns) => {\n const newVisibleColumns = updatedColumns.map(col => col.id);\n setVisibleColumns(newVisibleColumns);\n\n // Example: Save to localStorage\n localStorage.setItem('tableColumns', JSON.stringify(newVisibleColumns));\n };\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <DataTable.Root\n columns={columns}\n rows={rows}\n visibleColumns={visibleColumns}\n onColumnsChange={handleColumnsChange}\n >\n <Stack direction=\"row\" justifyContent=\"space-between\" mb=\"400\">\n <Text fontSize=\"lg\" fontWeight=\"600\">Team Directory</Text>\n <DataTable.Manager />\n </Stack>\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n <Text fontSize=\"sm\" color=\"gray.9\">\n Visible columns: {visibleColumns.join(', ')}\n </Text>\n </Stack>\n );\n}\n```\n\n### Manager: Context requirements\n\nDataTable.Manager **must** be used within a DataTable.Root component. It relies\non the DataTable context for:\n\n- Column configuration (`columns`, `visibleColumns`)\n- Layout settings (`isTruncated`, `density`)\n- Callbacks (`onColumnsChange`, `onSettingsChange`)\n- Custom settings configuration\n\nUsing Manager outside of DataTable.Root will throw an error.\n\n## Component requirements\n\n## Accessibility\n\nThe DataTable uses React Aria's Table components for full accessibility support:\n\n- **ARIA grid pattern**: The table implements the ARIA grid pattern with proper\n roles\n- **Keyboard navigation**: Full keyboard support with arrow keys, Tab, Enter,\n and Space\n- **Screen reader support**: Proper announcements for sorting, selection, and\n expansion\n\nAlways provide an accessible label for the table:\n\n```tsx\n<DataTable columns={columns} rows={rows} aria-label=\"Product inventory table\" />\n```\n\nOr with the compound component API:\n\n```tsx\n<DataTable.Root columns={columns} rows={rows}>\n <DataTable.Table aria-label=\"Product inventory table\">\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n</DataTable.Root>\n```\n\n#### Persistent ID\n\nIf your use case requires tracking and analytics for this component, it is good\npractice to add a **persistent**, **unique** id to the component:\n\n```tsx\nconst PERSISTENT_ID = \"product-inventory-table\";\n\nexport const Example = () => (\n <DataTable\n id={PERSISTENT_ID}\n columns={columns}\n rows={rows}\n aria-label=\"Product inventory table\"\n />\n);\n```\n\n#### Keyboard navigation\n\nThe component supports full keyboard interaction:\n\n- `Tab` / `Shift+Tab`: Move focus between interactive elements\n- `Arrow keys`: Navigate between cells and rows\n- `Enter` / `Space`: Activate sorting, selection, or expansion\n- `Escape`: Cancel current interaction\n\nThe Manager drawer also supports keyboard navigation:\n\n- `Tab` / `Shift+Tab`: Navigate between the settings button, tabs, and controls\n- `Enter` / `Space`: Open the Manager drawer, activate tabs, toggle buttons\n- `Arrow keys`: Navigate between tab panels, reorder items in drag-and-drop\n lists\n- `Escape`: Close the Manager drawer\n- Drag-and-drop keyboard operations: `Space` to pick up, arrow keys to move,\n `Space` to drop\n\n## API reference\n\n<PropsTable id=\"DataTable\" />\n\n### DataTableColumnItem\n\n```tsx\ntype DataTableColumnItem<T extends object = Record<string, unknown>> = {\n id: string; // Unique column identifier\n header: ReactNode; // Column header content\n accessor: (row: T) => ReactNode; // Function to extract cell value\n render?: (cell: {\n // Custom cell renderer\n value: unknown;\n row: T;\n column: DataTableColumnItem<T>;\n }) => ReactNode;\n isResizable?: boolean; // Enable column resizing\n width?: number | null; // Initial width in pixels\n defaultWidth?: number | null; // Default width\n minWidth?: number | null; // Minimum width when resizing\n maxWidth?: number | null; // Maximum width when resizing\n sticky?: boolean; // Stick column to edge\n isSortable?: boolean; // Enable sorting for column\n isRowHeader?: boolean; // Mark as row header (accessibility)\n headerIcon?: ReactNode; // Icon in column header\n};\n```\n\n### DataTableRowItem\n\n```tsx\ntype DataTableRowItem<T extends object = Record<string, unknown>> = T & {\n id: string; // Unique row identifier (required)\n isDisabled?: boolean; // Disable row interactions\n [key: string]: unknown;\n};\n```\n\n### SortDescriptor\n\n```tsx\ntype SortDescriptor = {\n column: string; // Column ID to sort by\n direction: \"ascending\" | \"descending\"; // Sort direction\n};\n```\n\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using DataTable\nin your application. As the component's internal functionality is already tested\nby Nimbus, these patterns help you verify your integration and\napplication-specific logic.\n\n### Basic Rendering Tests\n\nVerify the DataTable renders with correct structure\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Basic rendering\", () => {\n it(\"renders table with grid role\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} />\n </NimbusProvider>\n );\n\n expect(screen.getByRole(\"grid\")).toBeInTheDocument();\n });\n\n it(\"renders column headers\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Name\")).toBeInTheDocument();\n expect(screen.getByText(\"Email\")).toBeInTheDocument();\n expect(screen.getByText(\"Role\")).toBeInTheDocument();\n });\n\n it(\"renders all data rows\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Alice Johnson\")).toBeInTheDocument();\n expect(screen.getByText(\"Bob Smith\")).toBeInTheDocument();\n expect(screen.getByText(\"Carol Williams\")).toBeInTheDocument();\n });\n\n it(\"renders correct number of rows\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} />\n </NimbusProvider>\n );\n\n // 1 header row + 3 data rows\n const allRows = screen.getAllByRole(\"row\");\n expect(allRows.length).toBe(4);\n });\n});\n```\n\n### Sorting Tests\n\nTest column sorting functionality\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Sorting\", () => {\n it(\"renders sortable column headers when allowsSorting is true\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} allowsSorting />\n </NimbusProvider>\n );\n\n const nameHeader = screen\n .getByText(\"Name\")\n .closest('[role=\"columnheader\"]');\n expect(nameHeader).toBeInTheDocument();\n });\n\n it(\"calls onSortChange when column header is clicked\", async () => {\n const user = userEvent.setup();\n const handleSortChange = vi.fn();\n\n render(\n <NimbusProvider>\n <DataTable\n columns={columns}\n rows={rows}\n allowsSorting\n onSortChange={handleSortChange}\n />\n </NimbusProvider>\n );\n\n await user.click(screen.getByText(\"Name\"));\n\n expect(handleSortChange).toHaveBeenCalledWith({\n column: \"name\",\n direction: expect.any(String),\n });\n });\n\n it(\"displays sort indicator on sorted column\", async () => {\n const user = userEvent.setup();\n\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} allowsSorting />\n </NimbusProvider>\n );\n\n await user.click(screen.getByText(\"Name\"));\n\n await waitFor(() => {\n const columnHeader = screen\n .getByText(\"Name\")\n .closest('[role=\"columnheader\"]');\n expect(columnHeader).toHaveAttribute(\"aria-sort\");\n });\n });\n});\n```\n\n### Selection Tests\n\nTest row selection functionality\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Selection\", () => {\n it(\"renders checkboxes when selectionMode is multiple\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} selectionMode=\"multiple\" />\n </NimbusProvider>\n );\n\n const checkboxes = screen.getAllByRole(\"checkbox\");\n // 1 select-all checkbox + 3 row checkboxes\n expect(checkboxes.length).toBe(4);\n });\n\n it(\"allows selecting a row\", async () => {\n const user = userEvent.setup();\n const handleSelectionChange = vi.fn();\n\n render(\n <NimbusProvider>\n <DataTable\n columns={columns}\n rows={rows}\n selectionMode=\"multiple\"\n onSelectionChange={handleSelectionChange}\n />\n </NimbusProvider>\n );\n\n const checkboxes = screen.getAllByRole(\"checkbox\");\n await user.click(checkboxes[1]); // First data row checkbox\n\n expect(handleSelectionChange).toHaveBeenCalled();\n });\n\n it(\"allows selecting all rows with header checkbox\", async () => {\n const user = userEvent.setup();\n const handleSelectionChange = vi.fn();\n\n render(\n <NimbusProvider>\n <DataTable\n columns={columns}\n rows={rows}\n selectionMode=\"multiple\"\n onSelectionChange={handleSelectionChange}\n />\n </NimbusProvider>\n );\n\n const selectAllCheckbox = screen.getAllByRole(\"checkbox\")[0];\n await user.click(selectAllCheckbox);\n\n await waitFor(() => {\n expect(handleSelectionChange).toHaveBeenCalledWith(\"all\");\n });\n });\n\n it(\"supports single selection mode\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} selectionMode=\"single\" />\n </NimbusProvider>\n );\n\n const radios = screen.getAllByRole(\"checkbox\");\n expect(radios.length).toBe(3);\n });\n});\n```\n\n### Row Interaction Tests\n\nTest row click and interaction functionality\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Row interactions\", () => {\n it(\"calls onRowClick when a row is clicked\", async () => {\n const user = userEvent.setup();\n const handleRowClick = vi.fn();\n\n render(\n <NimbusProvider>\n <DataTable\n columns={columns}\n rows={rows}\n search=\"\"\n onRowClick={handleRowClick}\n />\n </NimbusProvider>\n );\n\n const allRows = screen.getAllByRole(\"row\");\n const firstDataRow = allRows[1]; // Second data row (index 2 because of header)\n const clickableCell = within(firstDataRow).getAllByRole(\"rowheader\");\n await user.click(clickableCell[0]);\n\n await waitFor(() => {\n expect(handleRowClick).toHaveBeenCalled();\n });\n });\n\n it(\"applies disabled state to specified rows\", () => {\n render(\n <NimbusProvider>\n <DataTable\n columns={columns}\n rows={rows}\n selectionMode=\"multiple\"\n disabledKeys={new Set([\"2\"])}\n />\n </NimbusProvider>\n );\n\n const allRows = screen.getAllByRole(\"row\");\n const disabledRow = allRows[2]; // Second data row (index 2 because of header)\n const checkbox = within(disabledRow).getByRole(\"checkbox\");\n expect(checkbox).toBeDisabled();\n });\n});\n```\n\n### Search and Filtering Tests\n\nTest search functionality\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Search and filtering\", () => {\n it(\"filters rows based on search term\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} search=\"Alice\" />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Alice\", { selector: \"mark\" })).toBeInTheDocument();\n expect(screen.getByText(\"Johnson\")).toBeInTheDocument();\n expect(screen.queryByText(\"Bob Smith\")).not.toBeInTheDocument();\n expect(screen.queryByText(\"Carol Williams\")).not.toBeInTheDocument();\n });\n\n it(\"shows all rows when search is empty\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} search=\"\" />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Alice Johnson\")).toBeInTheDocument();\n expect(screen.getByText(\"Bob Smith\")).toBeInTheDocument();\n expect(screen.getByText(\"Carol Williams\")).toBeInTheDocument();\n });\n\n it(\"filters across multiple columns\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} search=\"Admin\" />\n </NimbusProvider>\n );\n\n // Alice is the Admin\n expect(screen.getByText(\"Alice Johnson\")).toBeInTheDocument();\n expect(screen.queryByText(\"Bob Smith\")).not.toBeInTheDocument();\n });\n});\n```\n\n### Column Visibility Tests\n\nTest column visibility control\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Column visibility\", () => {\n it(\"shows only specified visible columns\", () => {\n render(\n <NimbusProvider>\n <DataTable\n columns={columns}\n rows={rows}\n visibleColumns={[\"name\", \"role\"]}\n />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Name\")).toBeInTheDocument();\n expect(screen.getByText(\"Role\")).toBeInTheDocument();\n expect(screen.queryByText(\"Email\")).not.toBeInTheDocument();\n });\n\n it(\"shows all columns when visibleColumns is not specified\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Name\")).toBeInTheDocument();\n expect(screen.getByText(\"Email\")).toBeInTheDocument();\n expect(screen.getByText(\"Role\")).toBeInTheDocument();\n });\n});\n```\n\n### Density Tests\n\nTest density variants\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Density\", () => {\n it(\"renders with default density\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} density=\"default\" />\n </NimbusProvider>\n );\n\n const table = screen.getByRole(\"grid\");\n expect(table).toBeInTheDocument();\n });\n\n it(\"renders with condensed density\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} density=\"condensed\" />\n </NimbusProvider>\n );\n\n const table = screen.getByRole(\"grid\");\n expect(table).toBeInTheDocument();\n });\n});\n```\n\n### Nested Rows Tests\n\nTest expandable nested row functionality\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Nested rows\", () => {\n const nestedRows: DataTableRowItem[] = [\n {\n id: \"1\",\n name: \"Parent Item\",\n email: \"parent@example.com\",\n role: \"Admin\",\n children: (\n <Box\n p=\"500\"\n bg=\"neutral.2\"\n borderRadius=\"md\"\n border=\"1px solid\"\n borderColor=\"neutral.6\"\n m=\"200 0\"\n >\n Child Item 1\n </Box>\n ),\n },\n {\n id: \"2\",\n name: \"No Children\",\n email: \"single@example.com\",\n role: \"User\",\n },\n ];\n\n it(\"renders expand button for rows with children\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={nestedRows} nestedKey=\"children\" />\n </NimbusProvider>\n );\n\n const expandButton = screen.getByRole(\"button\", { name: /expand/i });\n expect(expandButton).toBeInTheDocument();\n });\n\n it(\"expands nested content when expand button is clicked\", async () => {\n const user = userEvent.setup();\n\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={nestedRows} nestedKey=\"children\" />\n </NimbusProvider>\n );\n\n // Initially nested content should not be visible\n expect(screen.queryByText(\"Child Item 1\")).not.toBeInTheDocument();\n\n const expandButton = screen.getByRole(\"button\", { name: /expand/i });\n expect(expandButton).toBeInTheDocument();\n await user.click(expandButton);\n\n await waitFor(async () => {\n await expect(screen.getByText(\"Child Item 1\")).toBeInTheDocument();\n });\n });\n\n it(\"collapses nested content when collapse button is clicked\", async () => {\n const user = userEvent.setup();\n\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={nestedRows} nestedKey=\"children\" />\n </NimbusProvider>\n );\n\n // Expand first\n const expandButton = screen.getByRole(\"button\", { name: /expand/i });\n await user.click(expandButton);\n\n await waitFor(() => {\n expect(screen.getByText(\"Child Item 1\")).toBeInTheDocument();\n });\n\n // Collapse\n const collapseButton = screen.getByRole(\"button\", { name: /collapse/i });\n await user.click(collapseButton);\n\n await waitFor(() => {\n expect(screen.queryByText(\"Child Item 1\")).not.toBeInTheDocument();\n });\n });\n});\n```\n\n### Empty State Tests\n\nTest empty state rendering\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Empty state\", () => {\n it(\"renders custom empty state when no rows\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={[]} />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"No Data\")).toBeInTheDocument();\n });\n\n it(\"renders empty state when search matches no rows\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} search=\"nonexistent\" />\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"No Data\")).toBeInTheDocument();\n });\n});\n```\n\n### Accessibility Tests\n\nTest accessibility features\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable - Accessibility\", () => {\n it(\"has accessible grid role\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} aria-label=\"Users table\" />\n </NimbusProvider>\n );\n\n const table = screen.getByRole(\"grid\");\n expect(table).toBeInTheDocument();\n });\n\n it(\"has proper row structure\", () => {\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} />\n </NimbusProvider>\n );\n\n const tableRows = screen.getAllByRole(\"row\");\n expect(tableRows.length).toBeGreaterThan(0);\n\n // Check that rows contain cells\n const firstDataRow = tableRows[1];\n const cells = within(firstDataRow).getAllByRole(\"gridcell\");\n expect(cells.length).toBeGreaterThan(0);\n });\n\n it(\"has sortable columns with aria-sort attribute\", async () => {\n const user = userEvent.setup();\n\n render(\n <NimbusProvider>\n <DataTable columns={columns} rows={rows} allowsSorting />\n </NimbusProvider>\n );\n\n await user.click(screen.getByText(\"Name\"));\n\n await waitFor(() => {\n const columnHeader = screen\n .getByText(\"Name\")\n .closest('[role=\"columnheader\"]');\n expect(columnHeader).toHaveAttribute(\"aria-sort\");\n });\n });\n});\n```\n\n### Manager: Basic Rendering Tests\n\nVerify the Manager button renders and opens the settings drawer\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable.Manager - Basic rendering\", () => {\n it(\"renders the settings button\", () => {\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n expect(\n screen.getByRole(\"button\", { name: /settings/i })\n ).toBeInTheDocument();\n });\n\n it(\"settings button is clickable\", async () => {\n const user = userEvent.setup();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n\n // Verify button can be clicked\n await user.click(settingsButton);\n\n // Button should still be in the document after click\n expect(settingsButton).toBeInTheDocument();\n });\n});\n```\n\n### Manager: Column Management Tests\n\nTest column visibility and reordering functionality\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable.Manager - Column management\", () => {\n it(\"displays visible and hidden columns in separate lists\", async () => {\n const user = userEvent.setup();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n {\n id: \"role\",\n header: \"Role\",\n accessor: (row: Record<string, unknown>) => row.role as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\", role: \"Admin\" },\n ];\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\", \"email\"]}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n await user.click(settingsButton);\n\n await waitFor(() => {\n const visibleList = screen.getByTestId(\"visible-columns-list\");\n const hiddenList = screen.getByTestId(\"hidden-columns-list\");\n\n expect(within(visibleList).getByText(\"Name\")).toBeInTheDocument();\n expect(within(visibleList).getByText(\"Email\")).toBeInTheDocument();\n expect(within(hiddenList).getByText(\"Role\")).toBeInTheDocument();\n });\n });\n\n it(\"calls onColumnsChange when column visibility changes\", async () => {\n const user = userEvent.setup();\n const handleColumnsChange = vi.fn();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n onColumnsChange={handleColumnsChange}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n await user.click(settingsButton);\n\n // Note: Full drag-and-drop testing would require more complex setup\n // This verifies the callback is available\n expect(handleColumnsChange).toBeDefined();\n });\n});\n```\n\n### Manager: Layout Settings Tests\n\nTest text visibility and row density controls\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable.Manager - Layout settings\", () => {\n it(\"displays layout settings tab\", async () => {\n const user = userEvent.setup();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n await user.click(settingsButton);\n\n await waitFor(() => {\n expect(screen.getByText(/layout settings/i)).toBeInTheDocument();\n });\n });\n\n it(\"calls onSettingsChange when layout settings change\", async () => {\n const user = userEvent.setup();\n const handleSettingsChange = vi.fn();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n onSettingsChange={handleSettingsChange}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n await user.click(settingsButton);\n\n // Note: Actual toggle interaction testing would require finding specific buttons\n // This verifies the callback is wired up\n expect(handleSettingsChange).toBeDefined();\n });\n});\n```\n\n### Manager: Custom Settings Tests\n\nTest custom settings tab integration\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { render, screen, waitFor, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { DataTable, NimbusProvider, Box } from \"@commercetools/nimbus\";\nimport type { DataTableColumnItem, DataTableRowItem } from \"./data-table.types\";\n\ndescribe(\"DataTable.Manager - Custom settings\", () => {\n it(\"displays custom settings tab when provided\", async () => {\n const user = userEvent.setup();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n const customSettings = {\n label: \"Filters\",\n panel: <div>Custom Filter Panel</div>,\n };\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n customSettings={customSettings}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n await user.click(settingsButton);\n\n await waitFor(() => {\n expect(screen.getByText(\"Filters\")).toBeInTheDocument();\n });\n });\n\n it(\"renders custom settings panel content\", async () => {\n const user = userEvent.setup();\n const managerColumns: DataTableColumnItem[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row: Record<string, unknown>) => row.name as React.ReactNode,\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row: Record<string, unknown>) =>\n row.email as React.ReactNode,\n },\n ];\n const managerRows: DataTableRowItem[] = [\n { id: \"1\", name: \"Alice\", email: \"alice@example.com\" },\n ];\n\n const customSettings = {\n label: \"Advanced\",\n panel: <div data-testid=\"custom-panel\">Custom Panel Content</div>,\n };\n\n render(\n <NimbusProvider>\n <DataTable.Root\n columns={managerColumns}\n rows={managerRows}\n visibleColumns={[\"name\"]}\n customSettings={customSettings}\n >\n <DataTable.Manager />\n <DataTable.Table>\n <DataTable.Header />\n <DataTable.Body />\n </DataTable.Table>\n </DataTable.Root>\n </NimbusProvider>\n );\n\n const settingsButton = screen.getByRole(\"button\", { name: /settings/i });\n await user.click(settingsButton);\n\n await waitFor(() => {\n const advancedTab = screen.getByText(\"Advanced\");\n expect(advancedTab).toBeInTheDocument();\n });\n\n // Click the custom tab\n const advancedTab = screen.getByText(\"Advanced\");\n await user.click(advancedTab);\n\n await waitFor(() => {\n expect(screen.getByTestId(\"custom-panel\")).toBeInTheDocument();\n expect(screen.getByText(\"Custom Panel Content\")).toBeInTheDocument();\n });\n });\n});\n```\n\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-datatable--docs)\n- [React Aria Table](https://react-spectrum.adobe.com/react-aria/Table.html)\n- [ARIA Grid Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/)\n\n**Related components**\n\n- **[Table Component](/components/data-display/table)** - Use the simpler Table component for static, read-only data without interactive features\n",
|
|
193
|
+
"toc": [
|
|
194
|
+
{
|
|
195
|
+
"value": "Getting started",
|
|
196
|
+
"href": "#getting-started",
|
|
197
|
+
"depth": 2,
|
|
198
|
+
"numbering": [
|
|
199
|
+
1,
|
|
200
|
+
1
|
|
201
|
+
],
|
|
202
|
+
"parent": "root"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"value": "Import",
|
|
206
|
+
"href": "#import",
|
|
207
|
+
"depth": 3,
|
|
208
|
+
"numbering": [
|
|
209
|
+
1,
|
|
210
|
+
1,
|
|
211
|
+
1
|
|
212
|
+
],
|
|
213
|
+
"parent": "root"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"value": "Basic usage",
|
|
217
|
+
"href": "#basic-usage",
|
|
218
|
+
"depth": 3,
|
|
219
|
+
"numbering": [
|
|
220
|
+
1,
|
|
221
|
+
1,
|
|
222
|
+
2
|
|
223
|
+
],
|
|
224
|
+
"parent": "root"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"value": "When to use DataTable",
|
|
228
|
+
"href": "#when-to-use-datatable",
|
|
229
|
+
"depth": 2,
|
|
230
|
+
"numbering": [
|
|
231
|
+
1,
|
|
232
|
+
2
|
|
233
|
+
],
|
|
234
|
+
"parent": "root"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"value": "Keep it simple",
|
|
238
|
+
"href": "#keep-it-simple",
|
|
239
|
+
"depth": 3,
|
|
240
|
+
"numbering": [
|
|
241
|
+
1,
|
|
242
|
+
2,
|
|
243
|
+
1
|
|
244
|
+
],
|
|
245
|
+
"parent": "root"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"value": "Working with columns and rows",
|
|
249
|
+
"href": "#working-with-columns-and-rows",
|
|
250
|
+
"depth": 2,
|
|
251
|
+
"numbering": [
|
|
252
|
+
1,
|
|
253
|
+
3
|
|
254
|
+
],
|
|
255
|
+
"parent": "root"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"value": "Column configuration",
|
|
259
|
+
"href": "#column-configuration",
|
|
260
|
+
"depth": 3,
|
|
261
|
+
"numbering": [
|
|
262
|
+
1,
|
|
263
|
+
3,
|
|
264
|
+
1
|
|
265
|
+
],
|
|
266
|
+
"parent": "root"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"value": "Row data structure",
|
|
270
|
+
"href": "#row-data-structure",
|
|
271
|
+
"depth": 3,
|
|
272
|
+
"numbering": [
|
|
273
|
+
1,
|
|
274
|
+
3,
|
|
275
|
+
2
|
|
276
|
+
],
|
|
277
|
+
"parent": "root"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"value": "Usage examples",
|
|
281
|
+
"href": "#usage-examples",
|
|
282
|
+
"depth": 2,
|
|
283
|
+
"numbering": [
|
|
284
|
+
1,
|
|
285
|
+
4
|
|
286
|
+
],
|
|
287
|
+
"parent": "root"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"value": "Density options",
|
|
291
|
+
"href": "#density-options",
|
|
292
|
+
"depth": 3,
|
|
293
|
+
"numbering": [
|
|
294
|
+
1,
|
|
295
|
+
4,
|
|
296
|
+
1
|
|
297
|
+
],
|
|
298
|
+
"parent": "root"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"value": "Sorting",
|
|
302
|
+
"href": "#sorting",
|
|
303
|
+
"depth": 3,
|
|
304
|
+
"numbering": [
|
|
305
|
+
1,
|
|
306
|
+
4,
|
|
307
|
+
2
|
|
308
|
+
],
|
|
309
|
+
"parent": "root"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"value": "Controlled sorting",
|
|
313
|
+
"href": "#controlled-sorting",
|
|
314
|
+
"depth": 3,
|
|
315
|
+
"numbering": [
|
|
316
|
+
1,
|
|
317
|
+
4,
|
|
318
|
+
3
|
|
319
|
+
],
|
|
320
|
+
"parent": "root"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"value": "Row selection",
|
|
324
|
+
"href": "#row-selection",
|
|
325
|
+
"depth": 3,
|
|
326
|
+
"numbering": [
|
|
327
|
+
1,
|
|
328
|
+
4,
|
|
329
|
+
4
|
|
330
|
+
],
|
|
331
|
+
"parent": "root"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"value": "Resizable columns",
|
|
335
|
+
"href": "#resizable-columns",
|
|
336
|
+
"depth": 3,
|
|
337
|
+
"numbering": [
|
|
338
|
+
1,
|
|
339
|
+
4,
|
|
340
|
+
5
|
|
341
|
+
],
|
|
342
|
+
"parent": "root"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"value": "Sticky header with scrolling",
|
|
346
|
+
"href": "#sticky-header-with-scrolling",
|
|
347
|
+
"depth": 3,
|
|
348
|
+
"numbering": [
|
|
349
|
+
1,
|
|
350
|
+
4,
|
|
351
|
+
6
|
|
352
|
+
],
|
|
353
|
+
"parent": "root"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"value": "Text truncation",
|
|
357
|
+
"href": "#text-truncation",
|
|
358
|
+
"depth": 3,
|
|
359
|
+
"numbering": [
|
|
360
|
+
1,
|
|
361
|
+
4,
|
|
362
|
+
7
|
|
363
|
+
],
|
|
364
|
+
"parent": "root"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"value": "Clickable rows",
|
|
368
|
+
"href": "#clickable-rows",
|
|
369
|
+
"depth": 3,
|
|
370
|
+
"numbering": [
|
|
371
|
+
1,
|
|
372
|
+
4,
|
|
373
|
+
8
|
|
374
|
+
],
|
|
375
|
+
"parent": "root"
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
"value": "Search and filtering",
|
|
379
|
+
"href": "#search-and-filtering",
|
|
380
|
+
"depth": 3,
|
|
381
|
+
"numbering": [
|
|
382
|
+
1,
|
|
383
|
+
4,
|
|
384
|
+
9
|
|
385
|
+
],
|
|
386
|
+
"parent": "root"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"value": "Column visibility",
|
|
390
|
+
"href": "#column-visibility",
|
|
391
|
+
"depth": 3,
|
|
392
|
+
"numbering": [
|
|
393
|
+
1,
|
|
394
|
+
4,
|
|
395
|
+
10
|
|
396
|
+
],
|
|
397
|
+
"parent": "root"
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"value": "Nested rows",
|
|
401
|
+
"href": "#nested-rows",
|
|
402
|
+
"depth": 3,
|
|
403
|
+
"numbering": [
|
|
404
|
+
1,
|
|
405
|
+
4,
|
|
406
|
+
11
|
|
407
|
+
],
|
|
408
|
+
"parent": "root"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"value": "With footer",
|
|
412
|
+
"href": "#with-footer",
|
|
413
|
+
"depth": 3,
|
|
414
|
+
"numbering": [
|
|
415
|
+
1,
|
|
416
|
+
4,
|
|
417
|
+
12
|
|
418
|
+
],
|
|
419
|
+
"parent": "root"
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
"value": "Disabled rows",
|
|
423
|
+
"href": "#disabled-rows",
|
|
424
|
+
"depth": 3,
|
|
425
|
+
"numbering": [
|
|
426
|
+
1,
|
|
427
|
+
4,
|
|
428
|
+
13
|
|
429
|
+
],
|
|
430
|
+
"parent": "root"
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
"value": "Row pinning",
|
|
434
|
+
"href": "#row-pinning",
|
|
435
|
+
"depth": 3,
|
|
436
|
+
"numbering": [
|
|
437
|
+
1,
|
|
438
|
+
4,
|
|
439
|
+
14
|
|
440
|
+
],
|
|
441
|
+
"parent": "root"
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"value": "Compound component API",
|
|
445
|
+
"href": "#compound-component-api",
|
|
446
|
+
"depth": 2,
|
|
447
|
+
"numbering": [
|
|
448
|
+
1,
|
|
449
|
+
5
|
|
450
|
+
],
|
|
451
|
+
"parent": "root"
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
"value": "Available sub-components",
|
|
455
|
+
"href": "#available-sub-components",
|
|
456
|
+
"depth": 3,
|
|
457
|
+
"numbering": [
|
|
458
|
+
1,
|
|
459
|
+
5,
|
|
460
|
+
1
|
|
461
|
+
],
|
|
462
|
+
"parent": "root"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"value": "Advanced: DataTable.Manager",
|
|
466
|
+
"href": "#advanced-datatablemanager",
|
|
467
|
+
"depth": 2,
|
|
468
|
+
"numbering": [
|
|
469
|
+
1,
|
|
470
|
+
6
|
|
471
|
+
],
|
|
472
|
+
"parent": "root"
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
"value": "Manager overview",
|
|
476
|
+
"href": "#manager-overview",
|
|
477
|
+
"depth": 3,
|
|
478
|
+
"numbering": [
|
|
479
|
+
1,
|
|
480
|
+
6,
|
|
481
|
+
1
|
|
482
|
+
],
|
|
483
|
+
"parent": "root"
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"value": "Manager: Column visibility",
|
|
487
|
+
"href": "#manager-column-visibility",
|
|
488
|
+
"depth": 3,
|
|
489
|
+
"numbering": [
|
|
490
|
+
1,
|
|
491
|
+
6,
|
|
492
|
+
2
|
|
493
|
+
],
|
|
494
|
+
"parent": "root"
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"value": "Manager: Layout settings",
|
|
498
|
+
"href": "#manager-layout-settings",
|
|
499
|
+
"depth": 3,
|
|
500
|
+
"numbering": [
|
|
501
|
+
1,
|
|
502
|
+
6,
|
|
503
|
+
3
|
|
504
|
+
],
|
|
505
|
+
"parent": "root"
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
"value": "Manager: Custom settings tab",
|
|
509
|
+
"href": "#manager-custom-settings-tab",
|
|
510
|
+
"depth": 3,
|
|
511
|
+
"numbering": [
|
|
512
|
+
1,
|
|
513
|
+
6,
|
|
514
|
+
4
|
|
515
|
+
],
|
|
516
|
+
"parent": "root"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"value": "Manager: Controlled column state with persistence",
|
|
520
|
+
"href": "#manager-controlled-column-state-with-persistence",
|
|
521
|
+
"depth": 3,
|
|
522
|
+
"numbering": [
|
|
523
|
+
1,
|
|
524
|
+
6,
|
|
525
|
+
5
|
|
526
|
+
],
|
|
527
|
+
"parent": "root"
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
"value": "Manager: Context requirements",
|
|
531
|
+
"href": "#manager-context-requirements",
|
|
532
|
+
"depth": 3,
|
|
533
|
+
"numbering": [
|
|
534
|
+
1,
|
|
535
|
+
6,
|
|
536
|
+
6
|
|
537
|
+
],
|
|
538
|
+
"parent": "root"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
"value": "Component requirements",
|
|
542
|
+
"href": "#component-requirements",
|
|
543
|
+
"depth": 2,
|
|
544
|
+
"numbering": [
|
|
545
|
+
1,
|
|
546
|
+
7
|
|
547
|
+
],
|
|
548
|
+
"parent": "root"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"value": "Accessibility",
|
|
552
|
+
"href": "#accessibility",
|
|
553
|
+
"depth": 2,
|
|
554
|
+
"numbering": [
|
|
555
|
+
1,
|
|
556
|
+
8
|
|
557
|
+
],
|
|
558
|
+
"parent": "root"
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
"value": "Persistent ID",
|
|
562
|
+
"href": "#persistent-id",
|
|
563
|
+
"depth": 4,
|
|
564
|
+
"numbering": [
|
|
565
|
+
1,
|
|
566
|
+
8,
|
|
567
|
+
1,
|
|
568
|
+
1
|
|
569
|
+
],
|
|
570
|
+
"parent": "root"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
"value": "Keyboard navigation",
|
|
574
|
+
"href": "#keyboard-navigation",
|
|
575
|
+
"depth": 4,
|
|
576
|
+
"numbering": [
|
|
577
|
+
1,
|
|
578
|
+
8,
|
|
579
|
+
1,
|
|
580
|
+
2
|
|
581
|
+
],
|
|
582
|
+
"parent": "root"
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
"value": "API reference",
|
|
586
|
+
"href": "#api-reference",
|
|
587
|
+
"depth": 2,
|
|
588
|
+
"numbering": [
|
|
589
|
+
1,
|
|
590
|
+
9
|
|
591
|
+
],
|
|
592
|
+
"parent": "root"
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
"value": "DataTableColumnItem",
|
|
596
|
+
"href": "#datatablecolumnitem",
|
|
597
|
+
"depth": 3,
|
|
598
|
+
"numbering": [
|
|
599
|
+
1,
|
|
600
|
+
9,
|
|
601
|
+
1
|
|
602
|
+
],
|
|
603
|
+
"parent": "root"
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
"value": "DataTableRowItem",
|
|
607
|
+
"href": "#datatablerowitem",
|
|
608
|
+
"depth": 3,
|
|
609
|
+
"numbering": [
|
|
610
|
+
1,
|
|
611
|
+
9,
|
|
612
|
+
2
|
|
613
|
+
],
|
|
614
|
+
"parent": "root"
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
"value": "SortDescriptor",
|
|
618
|
+
"href": "#sortdescriptor",
|
|
619
|
+
"depth": 3,
|
|
620
|
+
"numbering": [
|
|
621
|
+
1,
|
|
622
|
+
9,
|
|
623
|
+
3
|
|
624
|
+
],
|
|
625
|
+
"parent": "root"
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"value": "Testing your implementation",
|
|
629
|
+
"href": "#testing-your-implementation",
|
|
630
|
+
"depth": 2,
|
|
631
|
+
"numbering": [
|
|
632
|
+
1,
|
|
633
|
+
10
|
|
634
|
+
],
|
|
635
|
+
"parent": "root"
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
"value": "Basic Rendering Tests",
|
|
639
|
+
"href": "#basic-rendering-tests",
|
|
640
|
+
"depth": 3,
|
|
641
|
+
"numbering": [
|
|
642
|
+
1,
|
|
643
|
+
10,
|
|
644
|
+
1
|
|
645
|
+
],
|
|
646
|
+
"parent": "root"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
"value": "Sorting Tests",
|
|
650
|
+
"href": "#sorting-tests",
|
|
651
|
+
"depth": 3,
|
|
652
|
+
"numbering": [
|
|
653
|
+
1,
|
|
654
|
+
10,
|
|
655
|
+
2
|
|
656
|
+
],
|
|
657
|
+
"parent": "root"
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
"value": "Selection Tests",
|
|
661
|
+
"href": "#selection-tests",
|
|
662
|
+
"depth": 3,
|
|
663
|
+
"numbering": [
|
|
664
|
+
1,
|
|
665
|
+
10,
|
|
666
|
+
3
|
|
667
|
+
],
|
|
668
|
+
"parent": "root"
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
"value": "Row Interaction Tests",
|
|
672
|
+
"href": "#row-interaction-tests",
|
|
673
|
+
"depth": 3,
|
|
674
|
+
"numbering": [
|
|
675
|
+
1,
|
|
676
|
+
10,
|
|
677
|
+
4
|
|
678
|
+
],
|
|
679
|
+
"parent": "root"
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
"value": "Search and Filtering Tests",
|
|
683
|
+
"href": "#search-and-filtering-tests",
|
|
684
|
+
"depth": 3,
|
|
685
|
+
"numbering": [
|
|
686
|
+
1,
|
|
687
|
+
10,
|
|
688
|
+
5
|
|
689
|
+
],
|
|
690
|
+
"parent": "root"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"value": "Column Visibility Tests",
|
|
694
|
+
"href": "#column-visibility-tests",
|
|
695
|
+
"depth": 3,
|
|
696
|
+
"numbering": [
|
|
697
|
+
1,
|
|
698
|
+
10,
|
|
699
|
+
6
|
|
700
|
+
],
|
|
701
|
+
"parent": "root"
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
"value": "Density Tests",
|
|
705
|
+
"href": "#density-tests",
|
|
706
|
+
"depth": 3,
|
|
707
|
+
"numbering": [
|
|
708
|
+
1,
|
|
709
|
+
10,
|
|
710
|
+
7
|
|
711
|
+
],
|
|
712
|
+
"parent": "root"
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
"value": "Nested Rows Tests",
|
|
716
|
+
"href": "#nested-rows-tests",
|
|
717
|
+
"depth": 3,
|
|
718
|
+
"numbering": [
|
|
719
|
+
1,
|
|
720
|
+
10,
|
|
721
|
+
8
|
|
722
|
+
],
|
|
723
|
+
"parent": "root"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
"value": "Empty State Tests",
|
|
727
|
+
"href": "#empty-state-tests",
|
|
728
|
+
"depth": 3,
|
|
729
|
+
"numbering": [
|
|
730
|
+
1,
|
|
731
|
+
10,
|
|
732
|
+
9
|
|
733
|
+
],
|
|
734
|
+
"parent": "root"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
"value": "Accessibility Tests",
|
|
738
|
+
"href": "#accessibility-tests",
|
|
739
|
+
"depth": 3,
|
|
740
|
+
"numbering": [
|
|
741
|
+
1,
|
|
742
|
+
10,
|
|
743
|
+
10
|
|
744
|
+
],
|
|
745
|
+
"parent": "root"
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
"value": "Manager: Basic Rendering Tests",
|
|
749
|
+
"href": "#manager-basic-rendering-tests",
|
|
750
|
+
"depth": 3,
|
|
751
|
+
"numbering": [
|
|
752
|
+
1,
|
|
753
|
+
10,
|
|
754
|
+
11
|
|
755
|
+
],
|
|
756
|
+
"parent": "root"
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
"value": "Manager: Column Management Tests",
|
|
760
|
+
"href": "#manager-column-management-tests",
|
|
761
|
+
"depth": 3,
|
|
762
|
+
"numbering": [
|
|
763
|
+
1,
|
|
764
|
+
10,
|
|
765
|
+
12
|
|
766
|
+
],
|
|
767
|
+
"parent": "root"
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"value": "Manager: Layout Settings Tests",
|
|
771
|
+
"href": "#manager-layout-settings-tests",
|
|
772
|
+
"depth": 3,
|
|
773
|
+
"numbering": [
|
|
774
|
+
1,
|
|
775
|
+
10,
|
|
776
|
+
13
|
|
777
|
+
],
|
|
778
|
+
"parent": "root"
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
"value": "Manager: Custom Settings Tests",
|
|
782
|
+
"href": "#manager-custom-settings-tests",
|
|
783
|
+
"depth": 3,
|
|
784
|
+
"numbering": [
|
|
785
|
+
1,
|
|
786
|
+
10,
|
|
787
|
+
14
|
|
788
|
+
],
|
|
789
|
+
"parent": "root"
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
"value": "Resources",
|
|
793
|
+
"href": "#resources",
|
|
794
|
+
"depth": 2,
|
|
795
|
+
"numbering": [
|
|
796
|
+
1,
|
|
797
|
+
11
|
|
798
|
+
],
|
|
799
|
+
"parent": "root"
|
|
800
|
+
}
|
|
801
|
+
]
|
|
802
|
+
},
|
|
803
|
+
"guidelines": {
|
|
804
|
+
"mdx": "\n## Guidelines\n\nThese guidelines provide comprehensive direction for designing and developing\ndata tables within Nimbus.\n\n#### Best practices\n\n- **Display relevant data:** Only include columns and data necessary for the\n user's current task. Avoid overwhelming users with excessive information.\n- **Allow for customization:** Allow users to organize and display important\n information in a way that suits their preferences, whether that means viewing\n more data on a single screen or having a table manager to add or reorder\n columns. The more customizable the workspace, the more effective users will be\n in completing their tasks.\n- **Keep column headers concise:** Use clear, brief labels for column headers to\n ensure quick understanding.\n- **Use sticky column headers:** Keep headers visible when users scroll\n vertically through a long table. Offer sorting and filtering: Include\n mechanisms to sort data by any column and filter the dataset to help users\n find specific information.\n\n#### Usage\n\nData tables are great for displaying large sets of information that can have\ndifferent statuses or data that needs to be compared. Users can click through\nfor more information held in modals or further detail pages.\n\n> [!TIP]\\\n> When to use\n\n- Presenting complex, structured data.\n- Comparing different attributes of multiple items.\n- Allowing users to analyze and manipulate large datasets.\n- Where bulk actions are being made, it can make sense to have users make\n selections from a data table.\n\n> [!CAUTION]\\\n> When not to use\n\n- **For static, read-only data**: Use the simpler [Table component](/components/data-display/table) instead for static content without interactive features (sorting, filtering, selection). DataTable adds unnecessary complexity for simple displays.\n- Presenting simple lists of items.\n- Displaying content with complex, varying layouts (use card or a grid layout).\n- Presenting an action-focused list where the primary interaction is on the\n whole item.\n- Within a small space where a user will need to scroll multiple directions in a\n frustrating way to understand the information presented.\n- To display information that is not meant to be read in the format of a data\n table like long blocks of text.\n\n> [!TIP]\\\n> **Use Table for simple cases**\n>\n> If you're displaying fewer than 50 rows of static data without needing sorting, filtering, or selection, use the [Table component](/components/data-display/table) instead. It's lighter, simpler, and faster to implement for straightforward data presentation.\n\n#### Tags and tag group in use\n\n> [!TIP]\\\n> **Do**\n>\n> - Do keep the table design simple and focused on data.\n> - Do provide clear visual feedback on the currently sorted column.\n> - Do ensure all interactive elements within the table are accessible (e.g.,\n> buttons, links).\n> - Do allow users to select one or multiple rows where needed.\n\n```jsx live\nconst App = () => {\n const data = [\n { id: 1, name: 'Product A', modified: '2023-10-27', status: \"Published\" },\n { id: 2, name: 'Product B', modified: '2023-10-26', status: \"Modified\" },\n ];\n const columns = [\n {\n id: 'name',\n header: \"Name\",\n accessor: (row) => row.name,\n isSortable: true,\n },\n {\n id: 'modified',\n header: \"Date Modified\",\n accessor: (row) => row.modified,\n isSortable: true,\n },\n {\n id: \"status\",\n header: \"Status\",\n isSortable: true,\n accessor: (row) => row.status,\n render: ({ value }) => {\n let colorPalette;\n switch (value) {\n case \"Published\":\n colorPalette = \"positive\";\n break;\n case \"Modified\":\n colorPalette = \"warning\";\n break;\n default:\n colorPalette = \"neutral\";\n }\n\n return (\n <Badge colorPalette={colorPalette} size=\"xs\">\n {value}\n </Badge>\n );\n },\n },\n ];\n return <DataTable\n rows={data}\n columns={columns}\n selectionMode={\"multiple\"}\n />;\n}\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Don’t overcomplicate the layout with too many visual elements or unnecessary\n> actions.\n> - Don’t force horizontal scrolling on the user unless absolutely necessary for\n> the data.\n> - Don’t use tables only for layout purposes; they are for data only.\n\n```jsx live\nconst App = () => {\n const data = [\n {\n id: 1,\n blogTitle: \"How to increase sales with abandoned cart discounts.\",\n readBlog:\n \"One highly effective strategy to recover revenue from abandoned shopping carts is the targeted use of cart discounts, which serve as a final, personalized incentive to encourage conversion. By analyzing the items in the abandoned cart and the customer's shopping history, retailers can deploy an automated, time-sensitive email offering a specific discount—such as a percentage off, a fixed dollar amount, or free shipping—designed to overcome the specific price sensitivity or hesitation that caused the abandonment. This direct appeal mitigates the perceived cost or barrier, making the original purchase seem more valuable and urgent, significantly increasing the likelihood that the customer will return to complete their transaction...\",\n },\n {\n id: 2,\n blogTitle :\"Want to know how Buy One Get One works? \",\n readBlog:\n \"Want to know how Buy One Get One works? ', readBlog: 'You know those Buy One, Get One (BOGO) deals? They're super popular because they're basically magic for getting people to shop. The idea is simple: you buy one thing at the regular price and get the second one for free or maybe half-off. It works so well because everyone loves getting something for nothing, which makes us buy more than we planned. That's a win-win: the customer feels like they got a steal, and the store boosts its sales and clears out all that extra stuff that's just sitting on the shelves.\",\n },\n];\n const columns = [\n {\n id: 'blogTitle',\n header: \"Blog title\",\n accessor: (row) => row.blogTitle,\n render: ({value}) => {\n return (\n <Box fontWeight=\"800\">\n {value}\n </Box>\n );\n },\n isResizable: true,\n },\n {\n id: 'readBlog',\n header: \"Read blog\",\n accessor: (row) => row.readBlog,\n isResizable: true,\n },\n ];\n return <DataTable\n rows={data}\n columns={columns}\n selectionMode={\"multiple\"}\n />;\n}\n```\n",
|
|
805
|
+
"toc": [
|
|
806
|
+
{
|
|
807
|
+
"value": "Guidelines",
|
|
808
|
+
"href": "#guidelines",
|
|
809
|
+
"depth": 2,
|
|
810
|
+
"numbering": [
|
|
811
|
+
1,
|
|
812
|
+
1
|
|
813
|
+
],
|
|
814
|
+
"parent": "root"
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
"value": "Best practices",
|
|
818
|
+
"href": "#best-practices",
|
|
819
|
+
"depth": 4,
|
|
820
|
+
"numbering": [
|
|
821
|
+
1,
|
|
822
|
+
1,
|
|
823
|
+
1,
|
|
824
|
+
1
|
|
825
|
+
],
|
|
826
|
+
"parent": "root"
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"value": "Usage",
|
|
830
|
+
"href": "#usage",
|
|
831
|
+
"depth": 4,
|
|
832
|
+
"numbering": [
|
|
833
|
+
1,
|
|
834
|
+
1,
|
|
835
|
+
1,
|
|
836
|
+
2
|
|
837
|
+
],
|
|
838
|
+
"parent": "root"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
"value": "Tags and tag group in use",
|
|
842
|
+
"href": "#tags-and-tag-group-in-use",
|
|
843
|
+
"depth": 4,
|
|
844
|
+
"numbering": [
|
|
845
|
+
1,
|
|
846
|
+
1,
|
|
847
|
+
1,
|
|
848
|
+
3
|
|
849
|
+
],
|
|
850
|
+
"parent": "root"
|
|
851
|
+
}
|
|
852
|
+
]
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|