@astryxdesign/core 0.1.3 → 0.1.4-canary.01e5b29
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/CHANGELOG.md +51 -0
- package/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +20 -18
- package/dist/AlertDialog/useImperativeAlertDialog.js +3 -3
- package/dist/AppShell/AppShell.d.ts +1 -1
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +55 -53
- package/dist/AspectRatio/AspectRatio.js +5 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +27 -25
- package/dist/Avatar/AvatarStatusDot.js +5 -5
- package/dist/AvatarGroup/AvatarGroup.js +5 -5
- package/dist/AvatarGroup/AvatarGroupOverflow.js +5 -5
- package/dist/Badge/Badge.js +3 -3
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +32 -25
- package/dist/Blockquote/Blockquote.js +7 -7
- package/dist/Breadcrumbs/BreadcrumbItem.js +17 -17
- package/dist/Breadcrumbs/Breadcrumbs.js +7 -7
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +47 -26
- package/dist/ButtonGroup/ButtonGroup.js +7 -7
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +108 -64
- package/dist/Calendar/dayCellUtils.d.ts +67 -5
- package/dist/Calendar/dayCellUtils.d.ts.map +1 -1
- package/dist/Calendar/dayCellUtils.js +139 -18
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +2 -2
- package/dist/Card/Card.d.ts +4 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +18 -10
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +20 -19
- package/dist/Center/Center.js +3 -3
- package/dist/Chat/ChatComposer.js +23 -23
- package/dist/Chat/ChatComposerDrawer.js +14 -14
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +27 -15
- package/dist/Chat/ChatDictationButton.d.ts +1 -1
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +13 -11
- package/dist/Chat/ChatLayout.js +17 -17
- package/dist/Chat/ChatLayoutScrollButton.d.ts +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +12 -10
- package/dist/Chat/ChatMessage.js +12 -12
- package/dist/Chat/ChatMessageBubble.js +7 -7
- package/dist/Chat/ChatMessageList.js +14 -14
- package/dist/Chat/ChatMessageMetadata.d.ts +1 -1
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatMessageMetadata.js +14 -12
- package/dist/Chat/ChatPastedTextToken.js +13 -13
- package/dist/Chat/ChatSendButton.js +3 -3
- package/dist/Chat/ChatSystemMessage.d.ts +1 -1
- package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
- package/dist/Chat/ChatSystemMessage.js +17 -17
- package/dist/Chat/ChatTokenizedText.d.ts +1 -1
- package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
- package/dist/Chat/ChatTokenizedText.js +24 -15
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +81 -74
- package/dist/Chat/index.d.ts +1 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -15
- package/dist/CheckboxInput/CheckboxInput.d.ts +6 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +32 -24
- package/dist/CheckboxList/CheckboxList.js +9 -9
- package/dist/CheckboxList/CheckboxListItem.d.ts +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +8 -6
- package/dist/Citation/Citation.js +10 -10
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +32 -9
- package/dist/Code/Code.d.ts +15 -1
- package/dist/Code/Code.d.ts.map +1 -1
- package/dist/Code/Code.js +35 -5
- package/dist/Code/index.d.ts +1 -1
- package/dist/Code/index.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.d.ts +10 -1
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +88 -52
- package/dist/CodeBlock/index.d.ts +1 -1
- package/dist/CodeBlock/index.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.d.ts +9 -3
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +110 -22
- package/dist/Collapsible/CollapsibleGroup.d.ts +39 -7
- package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroup.js +73 -12
- package/dist/Collapsible/CollapsibleGroupContext.d.ts +31 -0
- package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroupContext.js +33 -3
- package/dist/Collapsible/index.d.ts +1 -0
- package/dist/Collapsible/index.d.ts.map +1 -1
- package/dist/Collapsible/useCollapsible.d.ts +4 -1
- package/dist/Collapsible/useCollapsible.d.ts.map +1 -1
- package/dist/Collapsible/useCollapsible.js +20 -6
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +36 -34
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.js +27 -7
- package/dist/CommandPalette/CommandPaletteFooter.js +16 -16
- package/dist/CommandPalette/CommandPaletteGroup.js +5 -5
- package/dist/CommandPalette/CommandPaletteInput.js +13 -13
- package/dist/CommandPalette/CommandPaletteItem.js +3 -3
- package/dist/CommandPalette/CommandPaletteList.js +3 -3
- package/dist/ContextMenu/ContextMenu.d.ts +9 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +16 -11
- package/dist/DateInput/DateInput.js +18 -18
- package/dist/DateRangeInput/DateRangeInput.js +23 -23
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -7
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +30 -26
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +9 -9
- package/dist/Dialog/DialogHeader.js +16 -16
- package/dist/Dialog/useImperativeDialog.js +3 -3
- package/dist/Divider/Divider.d.ts +0 -22
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +7 -7
- package/dist/DropdownMenu/DropdownMenu.js +10 -10
- package/dist/DropdownMenu/DropdownMenuItem.d.ts +2 -17
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +3 -3
- package/dist/DropdownMenu/renderDropdownItems.js +10 -10
- package/dist/EmptyState/EmptyState.js +9 -9
- package/dist/Field/Field.js +17 -17
- package/dist/Field/FieldLabel.js +15 -15
- package/dist/Field/InputClearButton.js +5 -5
- package/dist/FieldStatus/FieldStatus.js +3 -3
- package/dist/FileInput/FileInput.js +29 -29
- package/dist/FormLayout/FormLayout.js +5 -5
- package/dist/Grid/Grid.d.ts +4 -3
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +29 -12
- package/dist/Grid/GridSpan.js +3 -3
- package/dist/HStack/HStack.js +3 -3
- package/dist/Heading/Heading.js +10 -10
- package/dist/HoverCard/HoverCard.js +9 -9
- package/dist/HoverCard/useHoverCard.js +3 -3
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +16 -12
- package/dist/Icon/defaultIcons.js +116 -116
- package/dist/IconButton/IconButton.js +3 -3
- package/dist/InputGroup/InputGroup.js +11 -11
- package/dist/InputGroup/InputGroupText.js +3 -3
- package/dist/Item/Item.js +18 -18
- package/dist/Kbd/Kbd.js +5 -5
- package/dist/Layer/LayerProvider.js +7 -7
- package/dist/Layer/useLayer.js +5 -5
- package/dist/Layout/Layout.js +21 -21
- package/dist/Layout/LayoutContent.js +3 -3
- package/dist/Layout/LayoutFooter.js +5 -5
- package/dist/Layout/LayoutHeader.js +5 -5
- package/dist/Layout/LayoutPanel.js +3 -3
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +55 -31
- package/dist/Lightbox/useLightbox.js +3 -3
- package/dist/Link/Link.js +15 -15
- package/dist/Link/LinkProvider.js +3 -3
- package/dist/Link/useLinkify.js +3 -3
- package/dist/List/List.js +11 -11
- package/dist/List/ListItem.js +11 -11
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +102 -102
- package/dist/Markdown/parser.d.ts +7 -0
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +300 -6
- package/dist/MetadataList/MetadataList.js +10 -10
- package/dist/MetadataList/MetadataListItem.js +15 -15
- package/dist/MobileNav/MobileNav.js +15 -15
- package/dist/MobileNav/MobileNavToggle.js +5 -5
- package/dist/MoreMenu/MoreMenu.js +3 -3
- package/dist/MultiSelector/MultiSelector.d.ts +7 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +64 -55
- package/dist/NavIcon/NavIcon.js +3 -3
- package/dist/NavMenu/NavHeadingMenu.js +5 -5
- package/dist/NavMenu/NavHeadingMenuItem.js +8 -8
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +48 -32
- package/dist/Outline/Outline.js +15 -15
- package/dist/OverflowList/OverflowList.js +8 -8
- package/dist/Overlay/Overlay.js +3 -3
- package/dist/Overlay/OverlayScrim.js +5 -5
- package/dist/Overlay/useOverlay.js +3 -3
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +125 -72
- package/dist/Popover/Popover.js +16 -16
- package/dist/Popover/usePopover.js +7 -7
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +110 -92
- package/dist/PowerSearch/PowerSearchEditPopover.js +84 -84
- package/dist/PowerSearch/PowerSearchFilterEditor.js +3 -3
- package/dist/PowerSearch/PowerSearchToken.js +5 -5
- package/dist/PowerSearch/PowerSearchValueEditor.js +54 -54
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +25 -18
- package/dist/RadioList/RadioList.d.ts +7 -1
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +10 -8
- package/dist/RadioList/RadioListItem.d.ts +1 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +30 -22
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +28 -10
- package/dist/Section/Section.js +5 -5
- package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +32 -10
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +18 -12
- package/dist/SelectableCard/SelectableCard.js +5 -5
- package/dist/Selector/Selector.d.ts +6 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +54 -45
- package/dist/Selector/SelectorOption.js +3 -3
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +38 -34
- package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +8 -6
- package/dist/SideNav/SideNavHeading.js +87 -87
- package/dist/SideNav/SideNavItem.js +42 -42
- package/dist/SideNav/SideNavSection.d.ts +1 -1
- package/dist/SideNav/SideNavSection.d.ts.map +1 -1
- package/dist/SideNav/SideNavSection.js +18 -16
- package/dist/Skeleton/Skeleton.js +3 -3
- package/dist/Slider/Slider.d.ts +6 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +31 -22
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +10 -10
- package/dist/StatusDot/StatusDot.js +5 -5
- package/dist/Switch/Switch.d.ts +13 -4
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -30
- package/dist/TabList/Tab.js +18 -18
- package/dist/TabList/TabList.js +5 -5
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +57 -30
- package/dist/TabList/tab.markers.stylex.d.ts +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +51 -39
- package/dist/Table/Table.js +7 -7
- package/dist/Table/TableBody.d.ts +1 -1
- package/dist/Table/TableBody.d.ts.map +1 -1
- package/dist/Table/TableBody.js +9 -5
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +76 -7
- package/dist/Table/TableFooter.d.ts +1 -1
- package/dist/Table/TableFooter.d.ts.map +1 -1
- package/dist/Table/TableFooter.js +9 -5
- package/dist/Table/TableHeader.d.ts +1 -1
- package/dist/Table/TableHeader.d.ts.map +1 -1
- package/dist/Table/TableHeader.js +9 -5
- package/dist/Table/TableHeaderCell.js +3 -3
- package/dist/Table/TableRow.js +5 -5
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +14 -11
- package/dist/Table/plugins/filtering/useTableFiltering.js +71 -71
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +5 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +7 -7
- package/dist/Table/plugins/rowExpansion/index.d.ts +3 -0
- package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowExpansion/index.js +3 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +90 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +398 -0
- package/dist/Table/plugins/selection/useTableSelection.js +18 -18
- package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelectionState.js +11 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +17 -17
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +8 -4
- package/dist/Table/tableContextMenu.d.ts +5 -1
- package/dist/Table/tableContextMenu.d.ts.map +1 -1
- package/dist/Table/tableContextMenu.js +19 -12
- package/dist/Text/Text.d.ts +1 -1
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +14 -13
- package/dist/Text/text.stylex.d.ts +35 -0
- package/dist/Text/text.stylex.d.ts.map +1 -1
- package/dist/Text/text.stylex.js +52 -1
- package/dist/TextArea/TextArea.js +14 -14
- package/dist/TextInput/TextInput.js +15 -15
- package/dist/Thumbnail/Thumbnail.d.ts +3 -2
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +30 -29
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +26 -21
- package/dist/Timestamp/Timestamp.d.ts +1 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +37 -26
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +25 -17
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +23 -11
- package/dist/Toast/useToast.js +4 -4
- package/dist/ToggleButton/ToggleButton.js +12 -12
- package/dist/ToggleButton/ToggleButtonGroup.js +5 -5
- package/dist/Token/Token.js +21 -21
- package/dist/Tokenizer/Tokenizer.d.ts +6 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +38 -29
- package/dist/Toolbar/Toolbar.js +18 -18
- package/dist/Tooltip/Tooltip.js +11 -11
- package/dist/Tooltip/useTooltip.js +3 -3
- package/dist/TopNav/TopNav.js +32 -32
- package/dist/TopNav/TopNavHeading.js +67 -67
- package/dist/TopNav/TopNavItem.js +5 -5
- package/dist/TopNav/TopNavMegaMenu.js +30 -30
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +25 -14
- package/dist/TopNav/TopNavMegaMenuItem.js +16 -16
- package/dist/TopNav/TopNavMenu.js +32 -32
- package/dist/TreeList/TreeList.js +8 -8
- package/dist/TreeList/TreeListBranches.js +11 -11
- package/dist/TreeList/TreeListItem.js +30 -30
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +23 -20
- package/dist/Typeahead/Typeahead.js +12 -12
- package/dist/Typeahead/TypeaheadItem.js +10 -10
- package/dist/VStack/VStack.js +3 -3
- package/dist/astryx.css +23 -0
- package/dist/astryx.umd.js +60 -58
- package/dist/astryx.umd.js.map +4 -4
- package/dist/docs-types.d.ts +13 -1
- package/dist/docs-types.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.d.ts +1 -1
- package/dist/hooks/focusableSelector.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.js +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFocusTrap.d.ts +3 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +38 -1
- package/dist/hooks/useHotkeys.d.ts +51 -0
- package/dist/hooks/useHotkeys.d.ts.map +1 -0
- package/dist/hooks/useHotkeys.js +160 -0
- package/dist/hooks/useInteractiveRole.d.ts +6 -2
- package/dist/hooks/useInteractiveRole.d.ts.map +1 -1
- package/dist/hooks/useInteractiveRole.js +6 -2
- package/dist/hooks/useKeyboardHint.js +9 -9
- package/dist/hooks/useStreamingText.d.ts +3 -1
- package/dist/hooks/useStreamingText.d.ts.map +1 -1
- package/dist/hooks/useStreamingText.js +14 -5
- package/dist/theme/MediaTheme.js +3 -3
- package/dist/theme/Theme.js +7 -7
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +10 -3
- package/dist/theme/hct.d.ts +0 -15
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +9 -9
- package/dist/theme/syntax/SyntaxTheme.js +5 -5
- package/dist/theme/tokens.d.ts +10 -2
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +220 -4
- package/dist/utils/color.d.ts +42 -0
- package/dist/utils/color.d.ts.map +1 -0
- package/dist/utils/color.js +150 -0
- package/dist/utils/composeEventHandlers.d.ts +30 -0
- package/dist/utils/composeEventHandlers.d.ts.map +1 -0
- package/dist/utils/composeEventHandlers.js +40 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +8 -5
- package/package.json +3 -3
- package/src/AlertDialog/AlertDialog.tsx +2 -0
- package/src/AppShell/AppShell.tsx +2 -0
- package/src/AspectRatio/AspectRatio.doc.mjs +1 -1
- package/src/Avatar/Avatar.doc.mjs +13 -2
- package/src/Avatar/Avatar.test.tsx +32 -1
- package/src/Avatar/Avatar.tsx +12 -20
- package/src/Banner/Banner.doc.mjs +8 -0
- package/src/Banner/Banner.test.tsx +38 -0
- package/src/Banner/Banner.tsx +8 -1
- package/src/Button/Button.tsx +39 -6
- package/src/ButtonGroup/ButtonGroup.doc.mjs +1 -1
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +201 -17
- package/src/Calendar/Calendar.tsx +79 -26
- package/src/Calendar/dayCellUtils.test.ts +306 -1
- package/src/Calendar/dayCellUtils.ts +206 -13
- package/src/Calendar/styles.ts +12 -2
- package/src/Card/Card.doc.mjs +1 -1
- package/src/Card/Card.tsx +16 -6
- package/src/Carousel/Carousel.doc.mjs +12 -12
- package/src/Carousel/Carousel.test.tsx +13 -0
- package/src/Carousel/Carousel.tsx +1 -0
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.test.tsx +24 -0
- package/src/Chat/ChatComposerInput.tsx +17 -1
- package/src/Chat/ChatDictationButton.test.tsx +43 -0
- package/src/Chat/ChatDictationButton.tsx +4 -4
- package/src/Chat/ChatLayoutScrollButton.test.tsx +33 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
- package/src/Chat/ChatMessageMetadata.test.tsx +32 -0
- package/src/Chat/ChatMessageMetadata.tsx +4 -6
- package/src/Chat/ChatSystemMessage.test.tsx +26 -11
- package/src/Chat/ChatSystemMessage.tsx +7 -7
- package/src/Chat/ChatTokenizedText.test.tsx +29 -16
- package/src/Chat/ChatTokenizedText.tsx +15 -8
- package/src/Chat/ChatToolCalls.test.tsx +55 -0
- package/src/Chat/ChatToolCalls.tsx +10 -12
- package/src/Chat/index.ts +1 -4
- package/src/Chat/useChatNewMessages.test.tsx +4 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +12 -4
- package/src/CheckboxInput/CheckboxInput.test.tsx +103 -0
- package/src/CheckboxInput/CheckboxInput.tsx +13 -0
- package/src/CheckboxList/CheckboxList.doc.mjs +1 -1
- package/src/CheckboxList/CheckboxList.test.tsx +19 -0
- package/src/CheckboxList/CheckboxListItem.tsx +11 -2
- package/src/Citation/Citation.doc.mjs +5 -0
- package/src/ClickableCard/ClickableCard.tsx +45 -2
- package/src/Code/Code.test.tsx +54 -0
- package/src/Code/Code.tsx +48 -4
- package/src/Code/index.ts +1 -1
- package/src/CodeBlock/CodeBlock.doc.mjs +7 -2
- package/src/CodeBlock/CodeBlock.test.tsx +122 -3
- package/src/CodeBlock/CodeBlock.tsx +62 -4
- package/src/CodeBlock/index.ts +1 -1
- package/src/Collapsible/Collapsible.doc.mjs +8 -4
- package/src/Collapsible/Collapsible.test.tsx +405 -0
- package/src/Collapsible/Collapsible.tsx +86 -10
- package/src/Collapsible/CollapsibleGroup.doc.mjs +27 -3
- package/src/Collapsible/CollapsibleGroup.test.tsx +318 -0
- package/src/Collapsible/CollapsibleGroup.tsx +107 -9
- package/src/Collapsible/CollapsibleGroupContext.tsx +42 -2
- package/src/Collapsible/index.ts +4 -0
- package/src/Collapsible/useCollapsible.ts +20 -6
- package/src/CommandPalette/CommandPalette.tsx +5 -3
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +5 -0
- package/src/CommandPalette/CommandPaletteEmpty.tsx +15 -1
- package/src/CommandPalette/CommandPaletteGroup.doc.mjs +10 -0
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteList.doc.mjs +9 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +15 -0
- package/src/ContextMenu/ContextMenu.test.tsx +15 -0
- package/src/ContextMenu/ContextMenu.tsx +19 -1
- package/src/ContextMenu/ContextMenuItem.doc.mjs +6 -0
- package/src/DateInput/DateInput.doc.mjs +6 -1
- package/src/DateInput/DateInput.test.tsx +11 -20
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.test.tsx +31 -44
- package/src/DateRangeInput/DateRangeInput.tsx +2 -2
- package/src/DateTimeInput/DateTimeInput.doc.mjs +4 -3
- package/src/DateTimeInput/DateTimeInput.test.tsx +118 -16
- package/src/DateTimeInput/DateTimeInput.tsx +9 -8
- package/src/DateTimeInput/index.ts +1 -0
- package/src/Dialog/DialogHeader.doc.mjs +45 -0
- package/src/Divider/Divider.tsx +1 -23
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenu.test.tsx +5 -3
- package/src/DropdownMenu/DropdownMenuItem.tsx +5 -17
- package/src/EmptyState/EmptyState.doc.mjs +1 -1
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/FieldLabel.test.tsx +2 -2
- package/src/FieldStatus/FieldStatus.test.tsx +282 -0
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -3
- package/src/Grid/Grid.doc.mjs +3 -3
- package/src/Grid/Grid.test.tsx +43 -18
- package/src/Grid/Grid.tsx +26 -9
- package/src/Icon/Icon.test.tsx +41 -29
- package/src/Icon/Icon.tsx +6 -2
- package/src/InputGroup/InputGroup.tsx +2 -2
- package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +185 -0
- package/src/Item/Item.tsx +1 -1
- package/src/Layout/Layout.test.tsx +285 -0
- package/src/Layout/LayoutContent.doc.mjs +12 -0
- package/src/Layout/LayoutPanel.doc.mjs +40 -0
- package/src/Layout/LayoutSlots.test.tsx +364 -0
- package/src/Layout/__tests__/edgeCompensation.test.tsx +72 -0
- package/src/Lightbox/Lightbox.doc.mjs +5 -0
- package/src/Lightbox/Lightbox.test.tsx +123 -2
- package/src/Lightbox/Lightbox.tsx +29 -1
- package/src/Markdown/Markdown.test.tsx +23 -0
- package/src/Markdown/Markdown.tsx +9 -7
- package/src/Markdown/incremental.test.ts +36 -0
- package/src/Markdown/parser.perf.test.ts +34 -12
- package/src/Markdown/parser.test.ts +210 -0
- package/src/Markdown/parser.ts +301 -6
- package/src/MobileNav/MobileNav.doc.mjs +20 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +14 -1
- package/src/MultiSelector/MultiSelector.test.tsx +33 -0
- package/src/MultiSelector/MultiSelector.tsx +20 -0
- package/src/NavMenu/NavMenu.doc.mjs +15 -0
- package/src/NumberInput/NumberInput.test.tsx +48 -3
- package/src/NumberInput/NumberInput.tsx +32 -15
- package/src/Outline/parseOutlineFromMarkdown.test.ts +80 -0
- package/src/OverflowList/OverflowList.test.tsx +416 -0
- package/src/Overlay/Overlay.doc.mjs +6 -0
- package/src/Overlay/Overlay.test.tsx +356 -0
- package/src/Pagination/Pagination.test.tsx +162 -0
- package/src/Pagination/Pagination.tsx +80 -26
- package/src/Popover/Popover.test.tsx +65 -0
- package/src/PowerSearch/PowerSearch.doc.mjs +4 -4
- package/src/PowerSearch/PowerSearch.test.tsx +109 -1
- package/src/PowerSearch/PowerSearch.tsx +37 -16
- package/src/ProgressBar/ProgressBar.test.tsx +30 -0
- package/src/ProgressBar/ProgressBar.tsx +12 -5
- package/src/RadioList/RadioList.doc.mjs +7 -1
- package/src/RadioList/RadioList.test.tsx +65 -0
- package/src/RadioList/RadioList.tsx +10 -1
- package/src/RadioList/RadioListItem.tsx +13 -4
- package/src/Resizable/ResizeHandle.test.tsx +218 -0
- package/src/Resizable/ResizeHandle.tsx +22 -4
- package/src/Section/Section.test.tsx +4 -10
- package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
- package/src/SegmentedControl/SegmentedControl.test.tsx +192 -0
- package/src/SegmentedControl/SegmentedControl.tsx +27 -5
- package/src/SegmentedControl/SegmentedControlItem.tsx +9 -4
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +12 -1
- package/src/Selector/Selector.test.tsx +30 -0
- package/src/Selector/Selector.tsx +18 -0
- package/src/SideNav/SideNav.doc.mjs +7 -1
- package/src/SideNav/SideNav.test.tsx +66 -0
- package/src/SideNav/SideNav.tsx +6 -2
- package/src/SideNav/SideNavCollapseButton.tsx +4 -3
- package/src/SideNav/SideNavSection.tsx +6 -4
- package/src/SizeContext/SizeContext.test.tsx +226 -0
- package/src/Slider/Slider.doc.mjs +14 -2
- package/src/Slider/Slider.test.tsx +30 -0
- package/src/Slider/Slider.tsx +20 -0
- package/src/Spinner/Spinner.tsx +1 -0
- package/src/Switch/Switch.doc.mjs +21 -9
- package/src/Switch/Switch.test.tsx +137 -0
- package/src/Switch/Switch.tsx +32 -7
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +185 -1
- package/src/TabList/TabMenu.tsx +45 -9
- package/src/Table/BaseTable.tsx +16 -3
- package/src/Table/Table.test.tsx +87 -0
- package/src/Table/TableBody.tsx +15 -2
- package/src/Table/TableCell.tsx +87 -8
- package/src/Table/TableFooter.tsx +15 -2
- package/src/Table/TableHeader.tsx +15 -2
- package/src/Table/index.ts +5 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +13 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +3 -1
- package/src/Table/plugins/pagination/paginateData.ts +5 -1
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +13 -0
- package/src/Table/plugins/rowExpansion/index.ts +7 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +155 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +590 -0
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +23 -0
- package/src/Table/plugins/selection/useTableSelectionState.tsx +11 -6
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +35 -1
- package/src/Table/plugins/sortable/useTableSortableState.tsx +8 -4
- package/src/Table/tableContextMenu.test.tsx +27 -3
- package/src/Table/tableContextMenu.tsx +20 -2
- package/src/Table/useTablePagination.doc.mjs +4 -1
- package/src/Table/useTableRowExpansion.doc.mjs +80 -0
- package/src/Table/useTableSortable.doc.mjs +4 -1
- package/src/Text/Text.doc.mjs +2 -2
- package/src/Text/Text.test.tsx +11 -0
- package/src/Text/Text.tsx +4 -2
- package/src/Text/text.stylex.ts +41 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +3 -3
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/TextInput/TextInput.test.tsx +3 -3
- package/src/Thumbnail/Thumbnail.test.tsx +36 -2
- package/src/Thumbnail/Thumbnail.tsx +4 -2
- package/src/TimeInput/TimeInput.doc.mjs +11 -6
- package/src/TimeInput/TimeInput.test.tsx +37 -0
- package/src/TimeInput/TimeInput.tsx +11 -1
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Timestamp/Timestamp.test.tsx +52 -0
- package/src/Timestamp/Timestamp.tsx +30 -16
- package/src/Toast/Toast.tsx +11 -3
- package/src/Toast/ToastViewport.test.tsx +64 -0
- package/src/Toast/ToastViewport.tsx +12 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +1 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +16 -4
- package/src/Tokenizer/Tokenizer.test.tsx +33 -0
- package/src/Tokenizer/Tokenizer.tsx +19 -0
- package/src/Toolbar/Toolbar.test.tsx +1 -1
- package/src/TopNav/TopNav.doc.mjs +1 -1
- package/src/TopNav/TopNav.test.tsx +18 -0
- package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +9 -2
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +19 -0
- package/src/Typeahead/BaseTypeahead.tsx +5 -2
- package/src/Typeahead/Typeahead.doc.mjs +7 -2
- package/src/Typeahead/Typeahead.test.tsx +45 -1
- package/src/__tests__/TestIcon.tsx +26 -0
- package/src/__tests__/fastRoleQueries.ts +66 -0
- package/src/docPropReferences.test.ts +178 -0
- package/src/docs-types.ts +14 -0
- package/src/hooks/focusableSelector.ts +1 -1
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useFocusTrap.doc.mjs +4 -2
- package/src/hooks/useFocusTrap.test.tsx +149 -1
- package/src/hooks/useFocusTrap.ts +50 -1
- package/src/hooks/useHotkeys.doc.mjs +52 -0
- package/src/hooks/useHotkeys.test.ts +245 -0
- package/src/hooks/useHotkeys.ts +203 -0
- package/src/hooks/useInteractiveRole.ts +12 -4
- package/src/hooks/useKeyboardHint.doc.mjs +6 -6
- package/src/hooks/useStreamingText.test.ts +54 -19
- package/src/hooks/useStreamingText.ts +16 -5
- package/src/theme/SyntaxTheme.doc.mjs +4 -4
- package/src/theme/defineTheme.ts +4 -13
- package/src/theme/derivedVarRegistry.test.ts +2 -2
- package/src/theme/expandColorScale.test.ts +29 -0
- package/src/theme/expandColorScale.ts +11 -6
- package/src/theme/hct.ts +9 -17
- package/src/theme/tokens.test.ts +182 -9
- package/src/theme/tokens.ts +239 -4
- package/src/theme/useTheme.test.tsx +20 -0
- package/src/utils/color.test.ts +133 -0
- package/src/utils/color.ts +148 -0
- package/src/utils/composeEventHandlers.test.ts +71 -0
- package/src/utils/composeEventHandlers.ts +44 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/mergeProps.test.ts +166 -0
- package/src/utils/sharedResizeObserver.test.ts +2 -2
- package/src/utils/timeParser.test.ts +13 -0
- package/src/utils/timeParser.ts +8 -5
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useTableRowExpansion.tsx
|
|
7
|
+
* @input React, StyleX, Icon, Table types
|
|
8
|
+
* @output Exports useTableRowExpansion hook + config/state types
|
|
9
|
+
* @position Row-expansion plugin; consumed by Table via plugins prop
|
|
10
|
+
*
|
|
11
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
12
|
+
* - /packages/core/src/Table/index.ts (exports)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {useCallback, useMemo, useRef, type ReactNode} from 'react';
|
|
16
|
+
import * as stylex from '@stylexjs/stylex';
|
|
17
|
+
import {spacingVars, colorVars, radiusVars} from '../../../theme/tokens.stylex';
|
|
18
|
+
import {Icon} from '../../../Icon';
|
|
19
|
+
import {resolveContextActions} from '../../tableContextMenu';
|
|
20
|
+
import type {
|
|
21
|
+
TablePlugin,
|
|
22
|
+
TableColumn,
|
|
23
|
+
BodyCellRenderProps,
|
|
24
|
+
BodyRowRenderProps,
|
|
25
|
+
HeaderCellRenderProps,
|
|
26
|
+
} from '../../types';
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Config
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Configuration for useTableRowExpansion (inherited-columns mode).
|
|
34
|
+
*
|
|
35
|
+
* Child rows use the same columns as their parents, with indentation on the
|
|
36
|
+
* first content column. The consumer provides a **flat** data array (use
|
|
37
|
+
* {@link useTableRowExpansionState} to flatten a tree) and a `getDepth`
|
|
38
|
+
* function so the plugin knows each row's nesting level.
|
|
39
|
+
*/
|
|
40
|
+
export interface UseTableRowExpansionConfig<T extends Record<string, unknown>> {
|
|
41
|
+
/** Set of currently-expanded row keys. */
|
|
42
|
+
expandedKeys: Set<string>;
|
|
43
|
+
/** Called when a row's expansion is toggled. */
|
|
44
|
+
onToggle: (key: string) => void;
|
|
45
|
+
/** Derive a stable unique key from a row item. */
|
|
46
|
+
getRowKey: (item: T) => string;
|
|
47
|
+
/** Return the children of a row (used to determine expandability). */
|
|
48
|
+
getChildren: (item: T) => T[];
|
|
49
|
+
/** Return the depth of a row in the hierarchy (0 = top-level). */
|
|
50
|
+
getDepth?: (item: T) => number;
|
|
51
|
+
/** Optionally control which rows are expandable. @default checks getChildren length */
|
|
52
|
+
getIsItemExpandable?: (item: T) => boolean;
|
|
53
|
+
/**
|
|
54
|
+
* When true, clicking anywhere on the row toggles expansion (in addition to
|
|
55
|
+
* the chevron button). @default false — only the chevron triggers expansion.
|
|
56
|
+
*/
|
|
57
|
+
hasRowClickExpansion?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* State of the expand-all toggle in the header. `true` = all expanded,
|
|
60
|
+
* `false` = all collapsed, `'indeterminate'` = mixed. When provided
|
|
61
|
+
* (together with `onToggleExpandAll`), the header cell shows a toggle button.
|
|
62
|
+
*/
|
|
63
|
+
isAllExpanded?: boolean | 'indeterminate';
|
|
64
|
+
/** Called when the expand-all header toggle is clicked. */
|
|
65
|
+
onToggleExpandAll?: (expand: boolean) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Configuration for {@link useTableRowExpansionState}.
|
|
70
|
+
*
|
|
71
|
+
* Mirrors the shape of {@link useTableSelectionState}: you own the
|
|
72
|
+
* `expandedKeys` set (via `useState`), and the hook derives everything the
|
|
73
|
+
* plugin needs — the flattened `data`, per-row depth, expand/collapse
|
|
74
|
+
* handlers, and the expand-all toggle state.
|
|
75
|
+
*/
|
|
76
|
+
export interface UseTableRowExpansionStateConfig<
|
|
77
|
+
T extends Record<string, unknown>,
|
|
78
|
+
> {
|
|
79
|
+
/** The full, un-flattened tree. */
|
|
80
|
+
baseData: T[];
|
|
81
|
+
/** Return the children of a row. Leaf rows return an empty array. */
|
|
82
|
+
getChildren: (item: T) => T[];
|
|
83
|
+
/** Derive a stable unique key from a row item. */
|
|
84
|
+
getRowKey: (item: T) => string;
|
|
85
|
+
/**
|
|
86
|
+
* Should this row be expandable? Rows that return `false` never show a
|
|
87
|
+
* chevron and are skipped by expand-all. @default rows with children
|
|
88
|
+
*/
|
|
89
|
+
getIsItemExpandable?: (item: T) => boolean;
|
|
90
|
+
/** Controlled set of currently-expanded row keys. */
|
|
91
|
+
expandedKeys: Set<string>;
|
|
92
|
+
/** Setter for the controlled expanded keys. */
|
|
93
|
+
setExpandedKeys: React.Dispatch<React.SetStateAction<Set<string>>>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface UseTableRowExpansionStateResult<
|
|
97
|
+
T extends Record<string, unknown>,
|
|
98
|
+
> {
|
|
99
|
+
/** The flattened, currently-visible rows. Pass to `<Table data>`. */
|
|
100
|
+
data: T[];
|
|
101
|
+
/** Ready-to-use config for {@link useTableRowExpansion}. */
|
|
102
|
+
expansionConfig: UseTableRowExpansionConfig<T>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Manages row-expansion state and derives the config for
|
|
107
|
+
* {@link useTableRowExpansion}. This is the recommended entry point — it
|
|
108
|
+
* removes the boilerplate of flattening the tree, tracking depth, and
|
|
109
|
+
* computing the expand-all state.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* const [expandedKeys, setExpandedKeys] = useState<Set<string>>(new Set());
|
|
114
|
+
* const {data, expansionConfig} = useTableRowExpansionState({
|
|
115
|
+
* baseData: tree,
|
|
116
|
+
* getChildren: item => item.children ?? [],
|
|
117
|
+
* getRowKey: item => item.id,
|
|
118
|
+
* expandedKeys,
|
|
119
|
+
* setExpandedKeys,
|
|
120
|
+
* });
|
|
121
|
+
* const expansion = useTableRowExpansion(expansionConfig);
|
|
122
|
+
* <Table data={data} columns={columns} idKey="id" plugins={{expansion}} />;
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export function useTableRowExpansionState<T extends Record<string, unknown>>({
|
|
126
|
+
baseData,
|
|
127
|
+
getChildren,
|
|
128
|
+
getRowKey,
|
|
129
|
+
getIsItemExpandable,
|
|
130
|
+
expandedKeys,
|
|
131
|
+
setExpandedKeys,
|
|
132
|
+
}: UseTableRowExpansionStateConfig<T>): UseTableRowExpansionStateResult<T> {
|
|
133
|
+
const isExpandable = useCallback(
|
|
134
|
+
(item: T): boolean =>
|
|
135
|
+
getIsItemExpandable
|
|
136
|
+
? getIsItemExpandable(item)
|
|
137
|
+
: getChildren(item).length > 0,
|
|
138
|
+
[getIsItemExpandable, getChildren],
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const depthMap = useMemo(() => {
|
|
142
|
+
const map = new Map<string, number>();
|
|
143
|
+
function walk(items: T[], depth: number) {
|
|
144
|
+
for (const item of items) {
|
|
145
|
+
const key = getRowKey(item);
|
|
146
|
+
map.set(key, depth);
|
|
147
|
+
if (expandedKeys.has(key)) {
|
|
148
|
+
walk(getChildren(item), depth + 1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
walk(baseData, 0);
|
|
153
|
+
return map;
|
|
154
|
+
}, [baseData, getChildren, getRowKey, expandedKeys]);
|
|
155
|
+
|
|
156
|
+
const data = useMemo(() => {
|
|
157
|
+
const result: T[] = [];
|
|
158
|
+
function walk(items: T[]) {
|
|
159
|
+
for (const item of items) {
|
|
160
|
+
result.push(item);
|
|
161
|
+
const key = getRowKey(item);
|
|
162
|
+
if (expandedKeys.has(key)) {
|
|
163
|
+
walk(getChildren(item));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
walk(baseData);
|
|
168
|
+
return result;
|
|
169
|
+
}, [baseData, getChildren, getRowKey, expandedKeys]);
|
|
170
|
+
|
|
171
|
+
// Every expandable key across the whole tree (drives expand-all).
|
|
172
|
+
const allExpandableKeys = useMemo(() => {
|
|
173
|
+
const keys: string[] = [];
|
|
174
|
+
function walk(items: T[]) {
|
|
175
|
+
for (const item of items) {
|
|
176
|
+
if (isExpandable(item)) {
|
|
177
|
+
keys.push(getRowKey(item));
|
|
178
|
+
walk(getChildren(item));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
walk(baseData);
|
|
183
|
+
return keys;
|
|
184
|
+
}, [baseData, getChildren, getRowKey, isExpandable]);
|
|
185
|
+
|
|
186
|
+
const getDepth = useCallback(
|
|
187
|
+
(item: T) => depthMap.get(getRowKey(item)) ?? 0,
|
|
188
|
+
[depthMap, getRowKey],
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const onToggle = useCallback(
|
|
192
|
+
(key: string) => {
|
|
193
|
+
setExpandedKeys(prev => {
|
|
194
|
+
const next = new Set(prev);
|
|
195
|
+
if (next.has(key)) {
|
|
196
|
+
next.delete(key);
|
|
197
|
+
} else {
|
|
198
|
+
next.add(key);
|
|
199
|
+
}
|
|
200
|
+
return next;
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
[setExpandedKeys],
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const isAllExpanded: boolean | 'indeterminate' = useMemo(() => {
|
|
207
|
+
if (allExpandableKeys.length === 0) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const expandedCount = allExpandableKeys.filter(k =>
|
|
211
|
+
expandedKeys.has(k),
|
|
212
|
+
).length;
|
|
213
|
+
if (expandedCount === 0) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (expandedCount === allExpandableKeys.length) {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
return 'indeterminate';
|
|
220
|
+
}, [allExpandableKeys, expandedKeys]);
|
|
221
|
+
|
|
222
|
+
const onToggleExpandAll = useCallback(
|
|
223
|
+
(expand: boolean) => {
|
|
224
|
+
setExpandedKeys(expand ? new Set(allExpandableKeys) : new Set());
|
|
225
|
+
},
|
|
226
|
+
[setExpandedKeys, allExpandableKeys],
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const expansionConfig = useMemo(
|
|
230
|
+
(): UseTableRowExpansionConfig<T> => ({
|
|
231
|
+
expandedKeys,
|
|
232
|
+
onToggle,
|
|
233
|
+
getRowKey,
|
|
234
|
+
getChildren,
|
|
235
|
+
getDepth,
|
|
236
|
+
getIsItemExpandable,
|
|
237
|
+
isAllExpanded,
|
|
238
|
+
onToggleExpandAll,
|
|
239
|
+
}),
|
|
240
|
+
[
|
|
241
|
+
expandedKeys,
|
|
242
|
+
onToggle,
|
|
243
|
+
getRowKey,
|
|
244
|
+
getChildren,
|
|
245
|
+
getDepth,
|
|
246
|
+
getIsItemExpandable,
|
|
247
|
+
isAllExpanded,
|
|
248
|
+
onToggleExpandAll,
|
|
249
|
+
],
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return {data, expansionConfig};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// =============================================================================
|
|
256
|
+
// Styles
|
|
257
|
+
// =============================================================================
|
|
258
|
+
|
|
259
|
+
const EXPANSION_COLUMN_WIDTH = {type: 'pixel' as const, value: 40};
|
|
260
|
+
|
|
261
|
+
/** Indentation applied per depth level, in pixels. */
|
|
262
|
+
const INDENT_PER_DEPTH = 24;
|
|
263
|
+
|
|
264
|
+
const expansionStyles = stylex.create({
|
|
265
|
+
chevronButton: {
|
|
266
|
+
display: 'inline-flex',
|
|
267
|
+
alignItems: 'center',
|
|
268
|
+
justifyContent: 'center',
|
|
269
|
+
width: '24px',
|
|
270
|
+
height: '24px',
|
|
271
|
+
background: 'transparent',
|
|
272
|
+
border: 'none',
|
|
273
|
+
borderRadius: radiusVars['--radius-inner'],
|
|
274
|
+
cursor: 'pointer',
|
|
275
|
+
color: colorVars['--color-icon-secondary'],
|
|
276
|
+
transitionProperty: 'transform, color, background-color',
|
|
277
|
+
transitionDuration: '150ms',
|
|
278
|
+
padding: 0,
|
|
279
|
+
flexShrink: '0',
|
|
280
|
+
// Match IconButton ghost hover: subtle overlay background
|
|
281
|
+
backgroundImage: {
|
|
282
|
+
default: null,
|
|
283
|
+
':hover': {
|
|
284
|
+
'@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
':hover': {
|
|
288
|
+
color: colorVars['--color-icon-primary'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
chevronExpanded: {
|
|
292
|
+
transform: 'rotate(90deg)',
|
|
293
|
+
},
|
|
294
|
+
chevronIcon: {
|
|
295
|
+
display: 'inline-flex',
|
|
296
|
+
transitionProperty: 'transform',
|
|
297
|
+
transitionDuration: '150ms',
|
|
298
|
+
},
|
|
299
|
+
indentedCell: {
|
|
300
|
+
display: 'flex',
|
|
301
|
+
alignItems: 'center',
|
|
302
|
+
gap: spacingVars['--spacing-1'],
|
|
303
|
+
},
|
|
304
|
+
indent: (px: number) => ({
|
|
305
|
+
paddingInlineStart: `${px}px`,
|
|
306
|
+
}),
|
|
307
|
+
placeholder: {
|
|
308
|
+
display: 'inline-block',
|
|
309
|
+
width: '24px',
|
|
310
|
+
height: '24px',
|
|
311
|
+
flexShrink: '0',
|
|
312
|
+
},
|
|
313
|
+
clickableRow: {
|
|
314
|
+
cursor: 'pointer',
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// =============================================================================
|
|
319
|
+
// Chevron
|
|
320
|
+
// =============================================================================
|
|
321
|
+
|
|
322
|
+
function ExpansionChevron({
|
|
323
|
+
isExpanded,
|
|
324
|
+
onToggle,
|
|
325
|
+
ariaLabel,
|
|
326
|
+
}: {
|
|
327
|
+
isExpanded: boolean;
|
|
328
|
+
onToggle: () => void;
|
|
329
|
+
ariaLabel: string;
|
|
330
|
+
}) {
|
|
331
|
+
return (
|
|
332
|
+
<button
|
|
333
|
+
type="button"
|
|
334
|
+
{...stylex.props(expansionStyles.chevronButton)}
|
|
335
|
+
onClick={e => {
|
|
336
|
+
e.stopPropagation();
|
|
337
|
+
onToggle();
|
|
338
|
+
}}
|
|
339
|
+
aria-label={ariaLabel}
|
|
340
|
+
aria-expanded={isExpanded}>
|
|
341
|
+
<span
|
|
342
|
+
{...stylex.props(
|
|
343
|
+
expansionStyles.chevronIcon,
|
|
344
|
+
isExpanded && expansionStyles.chevronExpanded,
|
|
345
|
+
)}>
|
|
346
|
+
<Icon icon="chevronRight" size="xsm" />
|
|
347
|
+
</span>
|
|
348
|
+
</button>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// =============================================================================
|
|
353
|
+
// Hook
|
|
354
|
+
// =============================================================================
|
|
355
|
+
|
|
356
|
+
export function useTableRowExpansion<T extends Record<string, unknown>>(
|
|
357
|
+
config: UseTableRowExpansionConfig<T>,
|
|
358
|
+
): TablePlugin<T> {
|
|
359
|
+
const {
|
|
360
|
+
expandedKeys,
|
|
361
|
+
onToggle,
|
|
362
|
+
getRowKey,
|
|
363
|
+
getChildren,
|
|
364
|
+
getDepth,
|
|
365
|
+
getIsItemExpandable,
|
|
366
|
+
hasRowClickExpansion = false,
|
|
367
|
+
isAllExpanded,
|
|
368
|
+
onToggleExpandAll,
|
|
369
|
+
} = config;
|
|
370
|
+
|
|
371
|
+
// Track the first non-plugin column key for indentation.
|
|
372
|
+
const firstUserColumnKeyRef = useRef<string | null>(null);
|
|
373
|
+
|
|
374
|
+
const expansionColumn = useMemo(
|
|
375
|
+
(): TableColumn<T> => ({
|
|
376
|
+
key: '__expansion',
|
|
377
|
+
header: '',
|
|
378
|
+
width: EXPANSION_COLUMN_WIDTH,
|
|
379
|
+
resizable: false,
|
|
380
|
+
renderCell: (item: T) => {
|
|
381
|
+
// Child rows (depth > 0) show their chevron inline in the first user
|
|
382
|
+
// column instead — don't double up here.
|
|
383
|
+
const depth = getDepth ? getDepth(item) : 0;
|
|
384
|
+
if (depth > 0) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const key = getRowKey(item);
|
|
389
|
+
const expandable = getIsItemExpandable
|
|
390
|
+
? getIsItemExpandable(item)
|
|
391
|
+
: getChildren(item).length > 0;
|
|
392
|
+
if (!expandable) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
const isExpanded = expandedKeys.has(key);
|
|
396
|
+
return (
|
|
397
|
+
<ExpansionChevron
|
|
398
|
+
isExpanded={isExpanded}
|
|
399
|
+
onToggle={() => onToggle(key)}
|
|
400
|
+
ariaLabel={isExpanded ? 'Collapse row' : 'Expand row'}
|
|
401
|
+
/>
|
|
402
|
+
);
|
|
403
|
+
},
|
|
404
|
+
}),
|
|
405
|
+
[
|
|
406
|
+
expandedKeys,
|
|
407
|
+
onToggle,
|
|
408
|
+
getRowKey,
|
|
409
|
+
getChildren,
|
|
410
|
+
getIsItemExpandable,
|
|
411
|
+
getDepth,
|
|
412
|
+
],
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
return useMemo(
|
|
416
|
+
(): TablePlugin<T> => ({
|
|
417
|
+
transformColumns(columns: TableColumn<T>[]) {
|
|
418
|
+
// Track the first user column for indentation.
|
|
419
|
+
const firstUserCol = columns.find(c => !c.key.startsWith('__'));
|
|
420
|
+
firstUserColumnKeyRef.current = firstUserCol?.key ?? null;
|
|
421
|
+
|
|
422
|
+
// Wrap the first user column's renderCell to add depth indentation +
|
|
423
|
+
// an inline chevron for child rows. This is the inherited-columns
|
|
424
|
+
// pattern: child rows use the same columns but indent their first
|
|
425
|
+
// content cell.
|
|
426
|
+
const wrappedColumns = columns.map(col => {
|
|
427
|
+
if (col.key !== firstUserColumnKeyRef.current) {
|
|
428
|
+
return col;
|
|
429
|
+
}
|
|
430
|
+
const originalRenderCell = col.renderCell;
|
|
431
|
+
return {
|
|
432
|
+
...col,
|
|
433
|
+
renderCell: (item: T): ReactNode => {
|
|
434
|
+
const depth = getDepth ? getDepth(item) : 0;
|
|
435
|
+
const originalContent = originalRenderCell
|
|
436
|
+
? originalRenderCell(item)
|
|
437
|
+
: String(
|
|
438
|
+
((item as Record<string, unknown>)[col.key] as
|
|
439
|
+
| string
|
|
440
|
+
| number
|
|
441
|
+
| null
|
|
442
|
+
| undefined) ?? '',
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
if (depth === 0) {
|
|
446
|
+
return originalContent;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const indent = (depth - 1) * INDENT_PER_DEPTH;
|
|
450
|
+
const key = getRowKey(item);
|
|
451
|
+
const isExpanded = expandedKeys.has(key);
|
|
452
|
+
const expandable = getIsItemExpandable
|
|
453
|
+
? getIsItemExpandable(item)
|
|
454
|
+
: getChildren(item).length > 0;
|
|
455
|
+
|
|
456
|
+
const chevron = expandable ? (
|
|
457
|
+
<ExpansionChevron
|
|
458
|
+
isExpanded={isExpanded}
|
|
459
|
+
onToggle={() => onToggle(key)}
|
|
460
|
+
ariaLabel={isExpanded ? 'Collapse row' : 'Expand row'}
|
|
461
|
+
/>
|
|
462
|
+
) : (
|
|
463
|
+
<span {...stylex.props(expansionStyles.placeholder)} />
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
return (
|
|
467
|
+
<div
|
|
468
|
+
{...stylex.props(
|
|
469
|
+
expansionStyles.indentedCell,
|
|
470
|
+
indent > 0 && expansionStyles.indent(indent),
|
|
471
|
+
)}>
|
|
472
|
+
{chevron}
|
|
473
|
+
{originalContent}
|
|
474
|
+
</div>
|
|
475
|
+
);
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
return [expansionColumn, ...wrappedColumns];
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
transformHeaderCell(
|
|
484
|
+
props: HeaderCellRenderProps,
|
|
485
|
+
column: TableColumn<T>,
|
|
486
|
+
): HeaderCellRenderProps {
|
|
487
|
+
if (column.key !== '__expansion') {
|
|
488
|
+
return props;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Show expand-all toggle when the consumer provides the state + callback.
|
|
492
|
+
if (isAllExpanded !== undefined && onToggleExpandAll) {
|
|
493
|
+
const allExpanded = isAllExpanded === true;
|
|
494
|
+
return {
|
|
495
|
+
...props,
|
|
496
|
+
content: (
|
|
497
|
+
<button
|
|
498
|
+
type="button"
|
|
499
|
+
{...stylex.props(expansionStyles.chevronButton)}
|
|
500
|
+
onClick={() => onToggleExpandAll(!allExpanded)}
|
|
501
|
+
aria-label={
|
|
502
|
+
allExpanded ? 'Collapse all rows' : 'Expand all rows'
|
|
503
|
+
}>
|
|
504
|
+
<span
|
|
505
|
+
{...stylex.props(
|
|
506
|
+
expansionStyles.chevronIcon,
|
|
507
|
+
allExpanded && expansionStyles.chevronExpanded,
|
|
508
|
+
)}>
|
|
509
|
+
<Icon icon="chevronRight" size="xsm" />
|
|
510
|
+
</span>
|
|
511
|
+
</button>
|
|
512
|
+
),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return {...props, content: null};
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
transformBodyCell(
|
|
520
|
+
props: BodyCellRenderProps,
|
|
521
|
+
column: TableColumn<T>,
|
|
522
|
+
item: T,
|
|
523
|
+
): BodyCellRenderProps {
|
|
524
|
+
// Contribute "Expand/Collapse row" context-menu action on every cell
|
|
525
|
+
// so right-clicking anywhere in the row shows the option.
|
|
526
|
+
const expandable = getIsItemExpandable
|
|
527
|
+
? getIsItemExpandable(item)
|
|
528
|
+
: getChildren(item).length > 0;
|
|
529
|
+
if (!expandable) {
|
|
530
|
+
return props;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const key = getRowKey(item);
|
|
534
|
+
const isExpanded = expandedKeys.has(key);
|
|
535
|
+
return {
|
|
536
|
+
...props,
|
|
537
|
+
contextMenuActions: () => [
|
|
538
|
+
...resolveContextActions(props.contextMenuActions),
|
|
539
|
+
{
|
|
540
|
+
id: 'row-expansion-toggle',
|
|
541
|
+
group: 'row-expansion',
|
|
542
|
+
label: isExpanded ? 'Collapse row' : 'Expand row',
|
|
543
|
+
icon: (
|
|
544
|
+
<Icon
|
|
545
|
+
icon={isExpanded ? 'chevronDown' : 'chevronRight'}
|
|
546
|
+
size="xsm"
|
|
547
|
+
aria-hidden
|
|
548
|
+
/>
|
|
549
|
+
),
|
|
550
|
+
onSelect: () => onToggle(key),
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
};
|
|
554
|
+
},
|
|
555
|
+
|
|
556
|
+
transformBodyRow(props: BodyRowRenderProps, item: T): BodyRowRenderProps {
|
|
557
|
+
if (!hasRowClickExpansion) {
|
|
558
|
+
return props;
|
|
559
|
+
}
|
|
560
|
+
const expandable = getIsItemExpandable
|
|
561
|
+
? getIsItemExpandable(item)
|
|
562
|
+
: getChildren(item).length > 0;
|
|
563
|
+
if (!expandable) {
|
|
564
|
+
return props;
|
|
565
|
+
}
|
|
566
|
+
const key = getRowKey(item);
|
|
567
|
+
return {
|
|
568
|
+
...props,
|
|
569
|
+
htmlProps: {
|
|
570
|
+
...props.htmlProps,
|
|
571
|
+
onClick: () => onToggle(key),
|
|
572
|
+
},
|
|
573
|
+
styles: [...props.styles, expansionStyles.clickableRow],
|
|
574
|
+
};
|
|
575
|
+
},
|
|
576
|
+
}),
|
|
577
|
+
[
|
|
578
|
+
expandedKeys,
|
|
579
|
+
getRowKey,
|
|
580
|
+
onToggle,
|
|
581
|
+
getChildren,
|
|
582
|
+
getDepth,
|
|
583
|
+
getIsItemExpandable,
|
|
584
|
+
hasRowClickExpansion,
|
|
585
|
+
isAllExpanded,
|
|
586
|
+
onToggleExpandAll,
|
|
587
|
+
expansionColumn,
|
|
588
|
+
],
|
|
589
|
+
);
|
|
590
|
+
}
|
|
@@ -302,6 +302,29 @@ describe('useTableSelectionState with filtered data', () => {
|
|
|
302
302
|
expect(screen.getByTestId('selected-keys')).toHaveTextContent('1,3');
|
|
303
303
|
});
|
|
304
304
|
|
|
305
|
+
it('select-all is unchecked when the filter matches nothing (#3591)', async () => {
|
|
306
|
+
const user = userEvent.setup();
|
|
307
|
+
render(<FilteredSelectionTable />);
|
|
308
|
+
|
|
309
|
+
// Select Bob, then filter to an empty result set.
|
|
310
|
+
const checkboxes = screen.getAllByLabelText('Select row');
|
|
311
|
+
await user.click(checkboxes[1]);
|
|
312
|
+
expect(screen.getByTestId('selected-keys')).toHaveTextContent('2');
|
|
313
|
+
|
|
314
|
+
const input = screen.getByTestId('filter-input');
|
|
315
|
+
await user.clear(input);
|
|
316
|
+
await user.type(input, 'zzz');
|
|
317
|
+
|
|
318
|
+
// Zero visible rows must not read as "all selected" — previously the
|
|
319
|
+
// header checkbox rendered checked here and deselect-all was a no-op.
|
|
320
|
+
const selectAll = screen.getByLabelText('Select all rows');
|
|
321
|
+
expect(selectAll).not.toBeChecked();
|
|
322
|
+
|
|
323
|
+
// The hidden selection itself is preserved (frozen), as designed.
|
|
324
|
+
await user.clear(input);
|
|
325
|
+
expect(screen.getByTestId('selected-keys')).toHaveTextContent('2');
|
|
326
|
+
});
|
|
327
|
+
|
|
305
328
|
it('selections persist when filter changes', async () => {
|
|
306
329
|
const user = userEvent.setup();
|
|
307
330
|
render(<FilteredSelectionTable />);
|
|
@@ -125,12 +125,17 @@ export function useTableSelectionState<T extends Record<string, unknown>>(
|
|
|
125
125
|
[selectedKeys, selectableIDs],
|
|
126
126
|
);
|
|
127
127
|
|
|
128
|
-
const getAllSelected = useCallback(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
const getAllSelected = useCallback(() => {
|
|
129
|
+
// Require at least one actionable row: with zero visible selectable
|
|
130
|
+
// rows (e.g. a filter with no matches while a selection exists), the
|
|
131
|
+
// union-based size comparison would read any non-empty selection as
|
|
132
|
+
// "all selected" — a checked header checkbox over an empty table that
|
|
133
|
+
// deselect-all can't clear, since the invisible keys count as frozen.
|
|
134
|
+
if (selectableIDs.size === 0) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return allSelectableIDs.size === selectedKeys.size;
|
|
138
|
+
}, [allSelectableIDs, selectableIDs, selectedKeys]);
|
|
134
139
|
|
|
135
140
|
// Use refs to keep onSelectAll stable
|
|
136
141
|
const frozenSelectedIDsRef = useRef(frozenSelectedIDs);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {describe, it, expect, vi} from 'vitest';
|
|
11
|
-
import {render, screen, within} from '@testing-library/react';
|
|
11
|
+
import {render, renderHook, screen, within} from '@testing-library/react';
|
|
12
12
|
import userEvent from '@testing-library/user-event';
|
|
13
13
|
import {useState} from 'react';
|
|
14
14
|
import {Table} from '../../Table';
|
|
@@ -421,6 +421,40 @@ describe('useTableSortableState', () => {
|
|
|
421
421
|
});
|
|
422
422
|
});
|
|
423
423
|
|
|
424
|
+
describe('NaN handling', () => {
|
|
425
|
+
it('keeps valid numbers sorted when a NaN cell is present', () => {
|
|
426
|
+
const data: Employee[] = [
|
|
427
|
+
{id: '1', name: 'A', age: 5, department: 'X', salary: 1},
|
|
428
|
+
{id: '2', name: 'B', age: NaN, department: 'X', salary: 1},
|
|
429
|
+
{id: '3', name: 'C', age: 1, department: 'X', salary: 1},
|
|
430
|
+
{id: '4', name: 'D', age: 3, department: 'X', salary: 1},
|
|
431
|
+
];
|
|
432
|
+
const {result} = renderHook(() =>
|
|
433
|
+
useTableSortableState({
|
|
434
|
+
data,
|
|
435
|
+
defaultSort: [{sortKey: 'age', direction: 'ascending'}],
|
|
436
|
+
}),
|
|
437
|
+
);
|
|
438
|
+
// NaN groups at the end like null; the valid numbers stay ordered.
|
|
439
|
+
expect(result.current.sortedData.map(e => e.age)).toEqual([1, 3, 5, NaN]);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('sorts NaN to the start in descending order, like null', () => {
|
|
443
|
+
const data: Employee[] = [
|
|
444
|
+
{id: '1', name: 'A', age: 5, department: 'X', salary: 1},
|
|
445
|
+
{id: '2', name: 'B', age: NaN, department: 'X', salary: 1},
|
|
446
|
+
{id: '3', name: 'C', age: 1, department: 'X', salary: 1},
|
|
447
|
+
];
|
|
448
|
+
const {result} = renderHook(() =>
|
|
449
|
+
useTableSortableState({
|
|
450
|
+
data,
|
|
451
|
+
defaultSort: [{sortKey: 'age', direction: 'descending'}],
|
|
452
|
+
}),
|
|
453
|
+
);
|
|
454
|
+
expect(result.current.sortedData.map(e => e.age)).toEqual([NaN, 5, 1]);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
424
458
|
describe('null/undefined handling', () => {
|
|
425
459
|
it('sorts null values to the end', () => {
|
|
426
460
|
const data: Employee[] = [
|