@astryxdesign/core 0.4.7 → 0.5.0-canary.54bb922
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 +161 -1
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +3 -2
- package/dist/AspectRatio/AspectRatio.d.ts +24 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +23 -4
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +15 -10
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +3 -2
- package/dist/Banner/Banner.d.ts +40 -15
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +51 -18
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +17 -4
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +5 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +26 -7
- package/dist/Calendar/Calendar.d.ts +3 -1
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +13 -14
- package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
- package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
- package/dist/Calendar/getInitialFocusDate.js +62 -0
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +3 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +2 -3
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +3 -3
- package/dist/Chat/ChatMessageList.d.ts +22 -3
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +6 -3
- package/dist/Chat/ChatTokenizedText.d.ts +2 -0
- package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
- package/dist/Chat/ChatTokenizedText.js +11 -6
- package/dist/Chat/useChatComposerTokens.js +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +3 -2
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -3
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +3 -2
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +3 -2
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +3 -2
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +38 -4
- package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +406 -12
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/Dialog/Dialog.d.ts +7 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +89 -58
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +3 -2
- package/dist/Heading/Heading.d.ts.map +1 -1
- package/dist/Heading/Heading.js +6 -2
- package/dist/HoverCard/HoverCard.d.ts +6 -0
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.d.ts +5 -0
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +67 -25
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -2
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +2 -19
- package/dist/Layer/LayerDepthContext.d.ts +52 -0
- package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
- package/dist/Layer/LayerDepthContext.js +66 -0
- package/dist/Layer/index.d.ts +3 -0
- package/dist/Layer/index.d.ts.map +1 -1
- package/dist/Layer/index.js +4 -0
- package/dist/Layer/layerStack.d.ts +98 -0
- package/dist/Layer/layerStack.d.ts.map +1 -0
- package/dist/Layer/layerStack.js +306 -0
- package/dist/Layer/useLayerDismissal.d.ts +93 -0
- package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
- package/dist/Layer/useLayerDismissal.js +91 -0
- package/dist/Lightbox/Lightbox.d.ts +1 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +103 -81
- package/dist/Markdown/index.d.ts +1 -1
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/parser.d.ts +38 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +177 -29
- package/dist/MobileNav/MobileNav.d.ts +6 -1
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +61 -36
- package/dist/MultiSelector/MultiSelector.d.ts +15 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +17 -6
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +3 -2
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +3 -2
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +3 -2
- package/dist/Overlay/Overlay.d.ts.map +1 -1
- package/dist/Overlay/Overlay.js +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +20 -17
- package/dist/PowerSearch/PowerSearch.d.ts +8 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +19 -6
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
- package/dist/PowerSearch/types.d.ts +2 -0
- package/dist/PowerSearch/types.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.js +14 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +3 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +3 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +3 -3
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +5 -0
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +4 -3
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +6 -5
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +4 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +3 -3
- package/dist/Spinner/Spinner.d.ts +3 -3
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +130 -78
- package/dist/Stepper/Step.d.ts +140 -0
- package/dist/Stepper/Step.d.ts.map +1 -0
- package/dist/Stepper/Step.js +1045 -0
- package/dist/Stepper/StepStatus.d.ts +24 -0
- package/dist/Stepper/StepStatus.d.ts.map +1 -0
- package/dist/Stepper/StepStatus.js +1 -0
- package/dist/Stepper/Stepper.d.ts +93 -0
- package/dist/Stepper/Stepper.d.ts.map +1 -0
- package/dist/Stepper/Stepper.js +184 -0
- package/dist/Stepper/StepperContext.d.ts +41 -0
- package/dist/Stepper/StepperContext.d.ts.map +1 -0
- package/dist/Stepper/StepperContext.js +34 -0
- package/dist/Stepper/index.d.ts +8 -0
- package/dist/Stepper/index.d.ts.map +1 -0
- package/dist/Stepper/index.js +7 -0
- package/dist/Stepper/stepper.stylex.d.ts +15 -0
- package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
- package/dist/Stepper/stepper.stylex.js +20 -0
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +3 -2
- package/dist/TabList/Tab.d.ts +15 -2
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +32 -7
- package/dist/TabList/TabList.d.ts +46 -7
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +353 -23
- package/dist/TabList/TabListContext.d.ts +11 -1
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +11 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +3 -2
- package/dist/TabList/index.d.ts +2 -2
- package/dist/TabList/index.d.ts.map +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +4 -1
- package/dist/Table/TableRow.d.ts +16 -5
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +16 -5
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
- package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
- package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +19 -5
- package/dist/Table/types.d.ts +22 -4
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -0
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +6 -2
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +3 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +3 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +5 -3
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +2 -2
- package/dist/Tokenizer/Tokenizer.d.ts +3 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +2 -0
- package/dist/Tooltip/Tooltip.d.ts +5 -0
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +5 -0
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +39 -24
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +3 -2
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +3 -3
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +4 -3
- package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +68 -29
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +3 -2
- package/dist/astryx.css +68 -2
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFocusTrap.d.ts +7 -4
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +60 -95
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +3 -18
- package/dist/hooks/useMergedRefs.d.ts +18 -0
- package/dist/hooks/useMergedRefs.d.ts.map +1 -0
- package/dist/hooks/useMergedRefs.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/utils/groupItems.d.ts +5 -1
- package/dist/utils/groupItems.d.ts.map +1 -1
- package/dist/utils/groupItems.js +9 -9
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/isApplePlatform.d.ts +18 -0
- package/dist/utils/isApplePlatform.d.ts.map +1 -0
- package/dist/utils/isApplePlatform.js +37 -0
- package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
- package/dist/utils/sharedResizeObserver.js +10 -1
- package/dist/utils/timeParser.d.ts +1 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +26 -12
- package/locales/en.json +31 -7
- package/locales/pseudo.json +18 -0
- package/package.json +8 -3
- package/src/AppShell/AppShell.tsx +3 -2
- package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
- package/src/AspectRatio/AspectRatio.test.tsx +57 -10
- package/src/AspectRatio/AspectRatio.tsx +40 -1
- package/src/Avatar/Avatar.doc.mjs +2 -1
- package/src/Avatar/Avatar.test.tsx +17 -0
- package/src/Avatar/Avatar.tsx +61 -38
- package/src/AvatarGroup/AvatarGroup.tsx +3 -2
- package/src/Banner/Banner.doc.mjs +13 -11
- package/src/Banner/Banner.test.tsx +102 -15
- package/src/Banner/Banner.tsx +72 -25
- package/src/BottomSheet/BottomSheet.test.tsx +123 -0
- package/src/BottomSheet/BottomSheet.tsx +17 -4
- package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
- package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
- package/src/Button/Button.doc.mjs +22 -0
- package/src/Button/Button.test.tsx +13 -0
- package/src/Button/Button.tsx +5 -4
- package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
- package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
- package/src/ButtonGroup/ButtonGroup.tsx +23 -5
- package/src/Calendar/Calendar.doc.mjs +4 -3
- package/src/Calendar/Calendar.test.tsx +52 -0
- package/src/Calendar/Calendar.tsx +18 -15
- package/src/Calendar/getInitialFocusDate.test.ts +94 -0
- package/src/Calendar/getInitialFocusDate.ts +93 -0
- package/src/Carousel/Carousel.tsx +3 -2
- package/src/Chat/ChatComposerInput.tsx +5 -1
- package/src/Chat/ChatLayout.tsx +3 -2
- package/src/Chat/ChatMessageList.doc.mjs +9 -1
- package/src/Chat/ChatMessageList.test.tsx +46 -0
- package/src/Chat/ChatMessageList.tsx +28 -4
- package/src/Chat/ChatTokenizedText.tsx +17 -10
- package/src/Chat/useChatComposerTokens.ts +1 -1
- package/src/CheckboxInput/CheckboxInput.tsx +3 -2
- package/src/ClickableCard/ClickableCard.tsx +3 -2
- package/src/CodeBlock/CodeBlock.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
- package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
- package/src/ContextMenu/ContextMenu.tsx +4 -3
- package/src/DateInput/DateInput.tsx +3 -2
- package/src/DateInput/DateInputTouch.test.tsx +36 -0
- package/src/DateInput/TouchDateField.tsx +38 -4
- package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
- package/src/DateTimeInput/DateTimeInput.tsx +556 -24
- package/src/DateTimeInput/index.ts +1 -0
- package/src/Dialog/Dialog.doc.mjs +8 -3
- package/src/Dialog/Dialog.test.tsx +261 -1
- package/src/Dialog/Dialog.tsx +109 -58
- package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
- package/src/Field/Field.doc.mjs +13 -2
- package/src/FileInput/FileInput.tsx +3 -2
- package/src/Heading/Heading.test.tsx +23 -1
- package/src/Heading/Heading.tsx +6 -2
- package/src/HoverCard/HoverCard.doc.mjs +5 -0
- package/src/HoverCard/HoverCard.tsx +6 -0
- package/src/HoverCard/useHoverCard.tsx +81 -30
- package/src/Item/Item.tsx +5 -4
- package/src/Kbd/Kbd.tsx +2 -19
- package/src/Layer/LayerDepthContext.tsx +62 -0
- package/src/Layer/index.ts +9 -0
- package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
- package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
- package/src/Layer/layerStack.ts +362 -0
- package/src/Layer/useLayerDismissal.test.tsx +469 -0
- package/src/Layer/useLayerDismissal.ts +173 -0
- package/src/Layout/Layout.doc.mjs +2 -2
- package/src/Lightbox/Lightbox.doc.mjs +12 -0
- package/src/Lightbox/Lightbox.tsx +124 -102
- package/src/Link/Link.doc.mjs +39 -0
- package/src/Markdown/Markdown.doc.mjs +5 -0
- package/src/Markdown/incremental.test.ts +16 -2
- package/src/Markdown/index.ts +1 -0
- package/src/Markdown/parser.perf.test.ts +96 -1
- package/src/Markdown/parser.test.ts +145 -2
- package/src/Markdown/parser.ts +244 -29
- package/src/MobileNav/MobileNav.tsx +62 -36
- package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
- package/src/MultiSelector/MultiSelector.test.tsx +106 -0
- package/src/MultiSelector/MultiSelector.tsx +50 -6
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavMenu/NavHeadingMenu.tsx +3 -2
- package/src/NumberInput/NumberInput.doc.mjs +5 -0
- package/src/Outline/Outline.tsx +3 -2
- package/src/OverflowList/OverflowList.tsx +3 -2
- package/src/Overlay/Overlay.tsx +3 -2
- package/src/Popover/usePopover.tsx +31 -28
- package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
- package/src/PowerSearch/PowerSearch.test.tsx +240 -1
- package/src/PowerSearch/PowerSearch.tsx +34 -5
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
- package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
- package/src/PowerSearch/types.ts +2 -0
- package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
- package/src/PowerSearch/usePowerSearchSource.ts +13 -3
- package/src/Resizable/ResizeHandle.tsx +3 -2
- package/src/SegmentedControl/SegmentedControl.tsx +3 -2
- package/src/SelectableCard/SelectableCard.tsx +3 -2
- package/src/Selector/Selector.doc.mjs +12 -1
- package/src/Selector/Selector.test.tsx +45 -1
- package/src/Selector/Selector.tsx +11 -1
- package/src/SideNav/SideNav.doc.mjs +6 -6
- package/src/SideNav/SideNav.tsx +4 -2
- package/src/SideNav/SideNavHeading.tsx +10 -10
- package/src/SideNav/SideNavItem.tsx +4 -2
- package/src/Slider/Slider.tsx +3 -2
- package/src/Spinner/Spinner.test.tsx +147 -1
- package/src/Spinner/Spinner.tsx +120 -101
- package/src/Stepper/Step.doc.mjs +98 -0
- package/src/Stepper/Step.tsx +1638 -0
- package/src/Stepper/StepStatus.ts +26 -0
- package/src/Stepper/Stepper.doc.mjs +370 -0
- package/src/Stepper/Stepper.test.tsx +1024 -0
- package/src/Stepper/Stepper.tsx +258 -0
- package/src/Stepper/StepperContext.ts +71 -0
- package/src/Stepper/index.ts +18 -0
- package/src/Stepper/stepper.stylex.ts +19 -0
- package/src/Switch/Switch.tsx +3 -2
- package/src/TabList/Tab.doc.mjs +13 -1
- package/src/TabList/Tab.tsx +67 -7
- package/src/TabList/TabList.doc.mjs +22 -6
- package/src/TabList/TabList.test.tsx +559 -0
- package/src/TabList/TabList.tsx +490 -22
- package/src/TabList/TabListContext.ts +13 -2
- package/src/TabList/TabMenu.tsx +3 -2
- package/src/TabList/index.ts +6 -2
- package/src/Table/BaseTable.tsx +6 -3
- package/src/Table/Table.doc.mjs +6 -1
- package/src/Table/Table.test.tsx +132 -14
- package/src/Table/TableBody.doc.mjs +40 -0
- package/src/Table/TableFooter.doc.mjs +40 -0
- package/src/Table/TableHeader.doc.mjs +40 -0
- package/src/Table/TableRow.tsx +16 -5
- package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
- package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
- package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
- package/src/Table/types.ts +22 -4
- package/src/Table/useBaseTablePlugins.ts +5 -0
- package/src/Table/useTableGroupedRows.doc.mjs +5 -4
- package/src/Table/useTablePagination.doc.mjs +7 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/Table/useTableSelection.doc.mjs +31 -0
- package/src/Text/Text.test.tsx +23 -1
- package/src/Text/Text.tsx +6 -2
- package/src/TextArea/TextArea.tsx +3 -2
- package/src/TextInput/TextInput.doc.mjs +10 -0
- package/src/TextInput/TextInput.tsx +3 -2
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.tsx +2 -2
- package/src/Timestamp/Timestamp.tsx +4 -2
- package/src/Toast/Toast.doc.mjs +6 -0
- package/src/Token/TokenLink.tsx +2 -2
- package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
- package/src/Tokenizer/Tokenizer.tsx +4 -0
- package/src/Toolbar/Toolbar.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.test.tsx +32 -0
- package/src/Tooltip/Tooltip.tsx +5 -0
- package/src/Tooltip/useTooltip.tsx +47 -24
- package/src/TopNav/TopNavHeading.tsx +3 -2
- package/src/TopNav/TopNavMegaMenu.tsx +3 -2
- package/src/TopNav/TopNavMenu.tsx +4 -3
- package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
- package/src/Typeahead/BaseTypeahead.tsx +85 -33
- package/src/Typeahead/Typeahead.doc.mjs +5 -0
- package/src/Typeahead/Typeahead.test.tsx +49 -0
- package/src/Typeahead/Typeahead.tsx +3 -2
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useContainerReveal.doc.mjs +2 -2
- package/src/hooks/useFocusTrap.test.tsx +14 -0
- package/src/hooks/useFocusTrap.ts +62 -111
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
- package/src/hooks/useHotkeys.ts +3 -18
- package/src/hooks/useMergedRefs.doc.mjs +79 -0
- package/src/hooks/useMergedRefs.test.tsx +62 -0
- package/src/hooks/useMergedRefs.ts +36 -0
- package/src/index.ts +1 -0
- package/src/theme/MediaTheme.doc.mjs +5 -5
- package/src/utils/groupItems.ts +12 -6
- package/src/utils/index.ts +1 -1
- package/src/utils/isApplePlatform.test.ts +61 -0
- package/src/utils/isApplePlatform.ts +37 -0
- package/src/utils/sharedResizeObserver.ts +10 -2
- package/src/utils/timeParser.test.ts +6 -1
- package/src/utils/timeParser.ts +35 -13
- package/src/__tests__/inputWidthContract.test.tsx +0 -69
package/src/TabList/index.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
export {TabList} from './TabList';
|
|
15
|
-
export type {TabListProps} from './TabList';
|
|
15
|
+
export type {TabListProps, TabListOverflow} from './TabList';
|
|
16
16
|
|
|
17
17
|
export {Tab} from './Tab';
|
|
18
18
|
export type {TabProps} from './Tab';
|
|
@@ -21,4 +21,8 @@ export {TabMenu} from './TabMenu';
|
|
|
21
21
|
export type {TabMenuProps, TabMenuOption} from './TabMenu';
|
|
22
22
|
|
|
23
23
|
export {useTabListContext} from './TabListContext';
|
|
24
|
-
export type {
|
|
24
|
+
export type {
|
|
25
|
+
TabListSize,
|
|
26
|
+
TabListLayout,
|
|
27
|
+
TabListPattern,
|
|
28
|
+
} from './TabListContext';
|
package/src/Table/BaseTable.tsx
CHANGED
|
@@ -183,9 +183,12 @@ function TableRowInner<T extends Record<string, unknown>>({
|
|
|
183
183
|
);
|
|
184
184
|
|
|
185
185
|
const isDefaultRenderer = !col.renderCell;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
let rawContent: ReactNode = null;
|
|
187
|
+
if (!cellRenderProps.isContentSuppressed) {
|
|
188
|
+
rawContent = isDefaultRenderer
|
|
189
|
+
? defaultCellRenderer(item, col.key)
|
|
190
|
+
: (col.renderCell?.(item) ?? null);
|
|
191
|
+
}
|
|
189
192
|
|
|
190
193
|
// In truncate mode, wrap default-rendered string content in
|
|
191
194
|
// <Text maxLines={1}> for smart tooltips that only appear
|
package/src/Table/Table.doc.mjs
CHANGED
|
@@ -107,7 +107,7 @@ export const docs = {
|
|
|
107
107
|
{
|
|
108
108
|
name: 'children',
|
|
109
109
|
type: 'ReactNode',
|
|
110
|
-
description: 'Children mode:
|
|
110
|
+
description: 'Children mode: compose the table yourself from TableHeader / TableBody / TableFooter, each holding TableRow and TableCell, instead of using data-driven rendering. The children are passed straight to the <table>, so the section is yours to supply — a TableRow placed directly in Table emits <table><tr>, which is invalid HTML and mismatches on hydration (the parser inserts an implied <tbody> for server-rendered markup; React does not on the client). Data-driven mode renders the sections for you.',
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
name: 'xstyle',
|
|
@@ -116,6 +116,9 @@ export const docs = {
|
|
|
116
116
|
},
|
|
117
117
|
],
|
|
118
118
|
components: [
|
|
119
|
+
{name: 'TableHeader'},
|
|
120
|
+
{name: 'TableBody'},
|
|
121
|
+
{name: 'TableFooter'},
|
|
119
122
|
{name: 'TableRow'},
|
|
120
123
|
{name: 'TableCell'},
|
|
121
124
|
{name: 'TableHeaderCell'},
|
|
@@ -135,6 +138,7 @@ export const docs = {
|
|
|
135
138
|
bestPractices: [
|
|
136
139
|
{ guidance: true, description: 'Use density and divider variants to match the information density and scanning needs of your data.' },
|
|
137
140
|
{ guidance: true, description: 'Compose rich cell content with Astryx components like Badge, StatusDot, and Avatar via renderCell.' },
|
|
141
|
+
{ guidance: true, description: 'In children mode, put every row inside TableHeader, TableBody, or TableFooter. <table> cannot contain a <tr> directly: the HTML parser inserts an implied <tbody> for server-rendered markup and React does not on the client, so unwrapped rows mismatch on hydration.' },
|
|
138
142
|
{ guidance: true, description: 'Set explicit width on every column using proportional() or pixel(). proportional(1) gives equal flex distribution with a 120px minimum that prevents columns from collapsing on narrow viewports. Omitting width skips the minimum.' },
|
|
139
143
|
{ guidance: true, description: 'Use the data-driven API from React Server Components: proportional(), pixel(), and column definitions without function props are server-safe. Columns using renderCell (or any function prop) need the table wrapped in a "use client" component, since functions cannot cross the server-client boundary.' },
|
|
140
144
|
{ guidance: false, description: 'Use a table for data without consistent columns. Use a list or card layout for heterogeneous content.' },
|
|
@@ -183,6 +187,7 @@ export const docsDense = {
|
|
|
183
187
|
bestPractices: [
|
|
184
188
|
{ guidance: true, description: 'Use density and divider variants to match the information density and scanning needs of your data.' },
|
|
185
189
|
{ guidance: true, description: 'Compose rich cell content with Astryx components like Badge, StatusDot, and Avatar via renderCell.' },
|
|
190
|
+
{ guidance: true, description: 'Children mode: wrap rows in TableHeader/TableBody/TableFooter. <table> cannot hold a <tr> directly — parser adds an implied <tbody> for SSR markup, React does not on the client, so unwrapped rows mismatch on hydration.' },
|
|
186
191
|
{ guidance: true, description: 'Set explicit width on every column via proportional() or pixel(). proportional(1) = equal flex w/ 120px min preventing collapse on narrow viewports. Omitting width skips the minimum.' },
|
|
187
192
|
{ guidance: true, description: 'Data-driven API is RSC-safe: proportional(), pixel(), column defs w/o function props work in Server Components. renderCell (any function prop) requires a "use client" wrapper.' },
|
|
188
193
|
{ guidance: false, description: 'Use a table for data without consistent columns. Use a list or card layout for heterogeneous content.' },
|
package/src/Table/Table.test.tsx
CHANGED
|
@@ -369,9 +369,11 @@ describe('BaseTable', () => {
|
|
|
369
369
|
it('renders children mode instead of data', () => {
|
|
370
370
|
render(
|
|
371
371
|
<BaseTable>
|
|
372
|
-
<
|
|
373
|
-
<
|
|
374
|
-
|
|
372
|
+
<tbody>
|
|
373
|
+
<tr>
|
|
374
|
+
<td>Manual cell</td>
|
|
375
|
+
</tr>
|
|
376
|
+
</tbody>
|
|
375
377
|
</BaseTable>,
|
|
376
378
|
);
|
|
377
379
|
expect(screen.getByText('Manual cell')).toBeInTheDocument();
|
|
@@ -380,9 +382,11 @@ describe('BaseTable', () => {
|
|
|
380
382
|
it('does not render thead in children mode without columns', () => {
|
|
381
383
|
const {container} = render(
|
|
382
384
|
<BaseTable>
|
|
383
|
-
<
|
|
384
|
-
<
|
|
385
|
-
|
|
385
|
+
<tbody>
|
|
386
|
+
<tr>
|
|
387
|
+
<td>Content</td>
|
|
388
|
+
</tr>
|
|
389
|
+
</tbody>
|
|
386
390
|
</BaseTable>,
|
|
387
391
|
);
|
|
388
392
|
expect(container.querySelector('thead')).toBeNull();
|
|
@@ -602,6 +606,41 @@ describe('BaseTable', () => {
|
|
|
602
606
|
expect(calls[0]).toEqual({col: 'name', name: 'Alice'});
|
|
603
607
|
});
|
|
604
608
|
|
|
609
|
+
it('isContentSuppressed renders an empty cell and never calls the column renderer', () => {
|
|
610
|
+
const rendered: string[] = [];
|
|
611
|
+
const withRenderer: TableColumn<User>[] = [
|
|
612
|
+
{
|
|
613
|
+
key: 'name',
|
|
614
|
+
header: 'Name',
|
|
615
|
+
renderCell: item => {
|
|
616
|
+
rendered.push(item.name);
|
|
617
|
+
return <b>{item.name}</b>;
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
];
|
|
621
|
+
const plugin: TablePlugin<User> = {
|
|
622
|
+
transformBodyCell: (props, _column, item) =>
|
|
623
|
+
item.name === 'Bob' ? {...props, isContentSuppressed: true} : props,
|
|
624
|
+
};
|
|
625
|
+
render(
|
|
626
|
+
<BaseTable data={users} columns={withRenderer} plugins={[plugin]} />,
|
|
627
|
+
);
|
|
628
|
+
expect(rendered).toEqual(['Alice', 'Charlie']);
|
|
629
|
+
const cells = screen.getAllByRole('cell');
|
|
630
|
+
expect(cells[1]).toBeEmptyDOMElement();
|
|
631
|
+
expect(cells[0]).toHaveTextContent('Alice');
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('isContentSuppressed also suppresses the default renderer', () => {
|
|
635
|
+
const plugin: TablePlugin<User> = {
|
|
636
|
+
transformBodyCell: props => ({...props, isContentSuppressed: true}),
|
|
637
|
+
};
|
|
638
|
+
render(<BaseTable data={users} columns={columns} plugins={[plugin]} />);
|
|
639
|
+
for (const cell of screen.getAllByRole('cell')) {
|
|
640
|
+
expect(cell).toBeEmptyDOMElement();
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
|
|
605
644
|
it('composes multiple plugins sequentially', () => {
|
|
606
645
|
const plugin1: TablePlugin<User> = {
|
|
607
646
|
transformTable: props => ({
|
|
@@ -988,14 +1027,16 @@ describe('Table', () => {
|
|
|
988
1027
|
it('renders children mode with TableRow and TableCell', () => {
|
|
989
1028
|
render(
|
|
990
1029
|
<Table density="balanced" dividers="rows">
|
|
991
|
-
<
|
|
992
|
-
<
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
<
|
|
997
|
-
|
|
998
|
-
|
|
1030
|
+
<TableBody>
|
|
1031
|
+
<TableRow>
|
|
1032
|
+
<TableCell>Streamed A</TableCell>
|
|
1033
|
+
<TableCell>Streamed B</TableCell>
|
|
1034
|
+
</TableRow>
|
|
1035
|
+
<TableRow>
|
|
1036
|
+
<TableCell>Streamed C</TableCell>
|
|
1037
|
+
<TableCell>Streamed D</TableCell>
|
|
1038
|
+
</TableRow>
|
|
1039
|
+
</TableBody>
|
|
999
1040
|
</Table>,
|
|
1000
1041
|
);
|
|
1001
1042
|
expect(screen.getByText('Streamed A')).toBeInTheDocument();
|
|
@@ -1004,6 +1045,83 @@ describe('Table', () => {
|
|
|
1004
1045
|
expect(screen.getAllByRole('cell')).toHaveLength(4);
|
|
1005
1046
|
});
|
|
1006
1047
|
|
|
1048
|
+
// Children mode hands the children straight to <table> — it does not wrap
|
|
1049
|
+
// them the way the data-driven path does. That is deliberate (it is what
|
|
1050
|
+
// makes the section components composable), but it was never written down,
|
|
1051
|
+
// and a caller who skipped the section shipped `<table><tr>` (#5277).
|
|
1052
|
+
it("children mode renders no tbody of its own — the section is the caller's", () => {
|
|
1053
|
+
const {container} = render(
|
|
1054
|
+
<Table density="balanced">
|
|
1055
|
+
{/* The unwrapped shape is the subject of this test, not a mistake. */}
|
|
1056
|
+
{/* eslint-disable-next-line @astryx/require-table-section */}
|
|
1057
|
+
<TableRow>
|
|
1058
|
+
<TableCell>Unwrapped</TableCell>
|
|
1059
|
+
</TableRow>
|
|
1060
|
+
</Table>,
|
|
1061
|
+
);
|
|
1062
|
+
const table = container.querySelector('table');
|
|
1063
|
+
expect(table?.querySelector('tbody')).toBeNull();
|
|
1064
|
+
expect(Array.from(table?.children ?? []).map(el => el.tagName)).toContain(
|
|
1065
|
+
'TR',
|
|
1066
|
+
);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
// Columns do not bring a thead with them in children mode — BaseTable's
|
|
1070
|
+
// `children ? children : <>...</>` short-circuits the whole data-driven
|
|
1071
|
+
// branch, header included. The header is TableHeader's job here.
|
|
1072
|
+
it('children mode renders no thead even when columns are supplied', () => {
|
|
1073
|
+
const {container} = render(
|
|
1074
|
+
<Table columns={columns}>
|
|
1075
|
+
<TableBody>
|
|
1076
|
+
<TableRow>
|
|
1077
|
+
<TableCell>Only body</TableCell>
|
|
1078
|
+
</TableRow>
|
|
1079
|
+
</TableBody>
|
|
1080
|
+
</Table>,
|
|
1081
|
+
);
|
|
1082
|
+
expect(container.querySelector('thead')).toBeNull();
|
|
1083
|
+
expect(container.querySelector('tbody > tr')).not.toBeNull();
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
it('children mode renders the thead TableHeader supplies', () => {
|
|
1087
|
+
const {container} = render(
|
|
1088
|
+
<Table columns={columns}>
|
|
1089
|
+
<TableHeader>
|
|
1090
|
+
<TableRow isHeaderRow>
|
|
1091
|
+
<TableCell>H</TableCell>
|
|
1092
|
+
</TableRow>
|
|
1093
|
+
</TableHeader>
|
|
1094
|
+
<TableBody>
|
|
1095
|
+
<TableRow>
|
|
1096
|
+
<TableCell>B</TableCell>
|
|
1097
|
+
</TableRow>
|
|
1098
|
+
</TableBody>
|
|
1099
|
+
</Table>,
|
|
1100
|
+
);
|
|
1101
|
+
const table = container.querySelector('table');
|
|
1102
|
+
expect(Array.from(table?.children ?? []).map(el => el.tagName)).toEqual([
|
|
1103
|
+
'THEAD',
|
|
1104
|
+
'TBODY',
|
|
1105
|
+
]);
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
it('children mode puts the rows in the tbody TableBody renders', () => {
|
|
1109
|
+
const {container} = render(
|
|
1110
|
+
<Table density="balanced">
|
|
1111
|
+
<TableBody>
|
|
1112
|
+
<TableRow>
|
|
1113
|
+
<TableCell>Wrapped</TableCell>
|
|
1114
|
+
</TableRow>
|
|
1115
|
+
</TableBody>
|
|
1116
|
+
</Table>,
|
|
1117
|
+
);
|
|
1118
|
+
const table = container.querySelector('table');
|
|
1119
|
+
expect(
|
|
1120
|
+
Array.from(table?.children ?? []).map(el => el.tagName),
|
|
1121
|
+
).not.toContain('TR');
|
|
1122
|
+
expect(table?.querySelector('tbody > tr')).not.toBeNull();
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1007
1125
|
it('passes through idKey string to base table', () => {
|
|
1008
1126
|
render(<Table data={users} columns={columns} idKey="email" />);
|
|
1009
1127
|
expect(screen.getAllByRole('row')).toHaveLength(4);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
|
+
|
|
5
|
+
export const docs = {
|
|
6
|
+
name: 'TableBody',
|
|
7
|
+
subComponentOf: 'Table',
|
|
8
|
+
displayName: 'Table Body',
|
|
9
|
+
isHiddenFromOverview: true,
|
|
10
|
+
description:
|
|
11
|
+
'<tbody> wrapper for children mode. Holds the data rows. A row must sit inside a section — <table> cannot contain a <tr> directly, because the HTML parser inserts an implied <tbody> when it parses server-rendered markup and React does not when it renders on the client, so rows written straight into Table mismatch on hydration. The data-driven data={...} mode renders this element itself; in children mode it is yours to supply.',
|
|
12
|
+
props: [
|
|
13
|
+
{
|
|
14
|
+
name: 'children',
|
|
15
|
+
type: 'ReactNode',
|
|
16
|
+
description: 'The <tbody> rows.',
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'xstyle',
|
|
21
|
+
type: 'StyleXStyles',
|
|
22
|
+
description:
|
|
23
|
+
'StyleX styles for layout customization. Must be a stylex.create() value: not an inline style object like style={{}}.',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
theming: {
|
|
27
|
+
targets: [{className: 'astryx-table-body'}],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const docsDense = {
|
|
32
|
+
name: 'TableBody',
|
|
33
|
+
isHiddenFromOverview: true,
|
|
34
|
+
displayName: 'Table Body',
|
|
35
|
+
description: '<tbody> wrapper for children mode; holds the data rows — children mode does not add one for you',
|
|
36
|
+
propDescriptions: {
|
|
37
|
+
children: 'The <tbody> rows.',
|
|
38
|
+
xstyle: 'StyleX styles for layout customization',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
|
+
|
|
5
|
+
export const docs = {
|
|
6
|
+
name: 'TableFooter',
|
|
7
|
+
subComponentOf: 'Table',
|
|
8
|
+
displayName: 'Table Footer',
|
|
9
|
+
isHiddenFromOverview: true,
|
|
10
|
+
description:
|
|
11
|
+
'<tfoot> wrapper for children mode. Holds summary or total rows beneath the body. A row must sit inside a section — <table> cannot contain a <tr> directly, because the HTML parser inserts an implied <tbody> when it parses server-rendered markup and React does not when it renders on the client, so the two trees mismatch on hydration. Children mode only: the data-driven data={...} path renders a header and a body, never a footer.',
|
|
12
|
+
props: [
|
|
13
|
+
{
|
|
14
|
+
name: 'children',
|
|
15
|
+
type: 'ReactNode',
|
|
16
|
+
description: 'The <tfoot> rows.',
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'xstyle',
|
|
21
|
+
type: 'StyleXStyles',
|
|
22
|
+
description:
|
|
23
|
+
'StyleX styles for layout customization. Must be a stylex.create() value: not an inline style object like style={{}}.',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
theming: {
|
|
27
|
+
targets: [{className: 'astryx-table-footer'}],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const docsDense = {
|
|
32
|
+
name: 'TableFooter',
|
|
33
|
+
isHiddenFromOverview: true,
|
|
34
|
+
displayName: 'Table Footer',
|
|
35
|
+
description: '<tfoot> wrapper for children mode; holds summary rows',
|
|
36
|
+
propDescriptions: {
|
|
37
|
+
children: 'The <tfoot> rows.',
|
|
38
|
+
xstyle: 'StyleX styles for layout customization',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
|
+
|
|
5
|
+
export const docs = {
|
|
6
|
+
name: 'TableHeader',
|
|
7
|
+
subComponentOf: 'Table',
|
|
8
|
+
displayName: 'Table Header',
|
|
9
|
+
isHiddenFromOverview: true,
|
|
10
|
+
description:
|
|
11
|
+
'<thead> wrapper for children mode. Holds the header row, whose cells are TableHeaderCell. A row must sit inside a section — <table> cannot contain a <tr> directly, because the HTML parser inserts an implied <tbody> when it parses server-rendered markup and React does not when it renders on the client, so the two trees mismatch on hydration. The data-driven data={...} mode renders this element itself whenever columns are supplied; in children mode it is yours to supply.',
|
|
12
|
+
props: [
|
|
13
|
+
{
|
|
14
|
+
name: 'children',
|
|
15
|
+
type: 'ReactNode',
|
|
16
|
+
description: 'The <thead> rows.',
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'xstyle',
|
|
21
|
+
type: 'StyleXStyles',
|
|
22
|
+
description:
|
|
23
|
+
'StyleX styles for layout customization. Must be a stylex.create() value: not an inline style object like style={{}}.',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
theming: {
|
|
27
|
+
targets: [{className: 'astryx-table-header'}],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const docsDense = {
|
|
32
|
+
name: 'TableHeader',
|
|
33
|
+
isHiddenFromOverview: true,
|
|
34
|
+
displayName: 'Table Header',
|
|
35
|
+
description: '<thead> wrapper for children mode; holds the header row',
|
|
36
|
+
propDescriptions: {
|
|
37
|
+
children: 'The <thead> rows.',
|
|
38
|
+
xstyle: 'StyleX styles for layout customization',
|
|
39
|
+
},
|
|
40
|
+
};
|
package/src/Table/TableRow.tsx
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* @file TableRow.tsx
|
|
7
7
|
* @input React, StyleX, TableContext, theme tokens
|
|
8
8
|
* @output Exports TableRow component, TableRowProps
|
|
9
|
-
* @position Sub-component; used inside Table
|
|
9
|
+
* @position Sub-component; used inside a Table section (TableHeader /
|
|
10
|
+
* TableBody / TableFooter) in children mode
|
|
10
11
|
*
|
|
11
12
|
* SYNC: When modified, update:
|
|
12
13
|
* - /packages/core/src/Table/Table.doc.mjs
|
|
@@ -105,13 +106,23 @@ const stripedHoverRowStyles = stylex.create({
|
|
|
105
106
|
* When used inside `<Table>`, inherits styling from the table context
|
|
106
107
|
* (striped, hover, divider overrides). When used standalone, renders a plain `<tr>`.
|
|
107
108
|
*
|
|
109
|
+
* Rows go inside a section — `<TableBody>`, `<TableHeader>`, or
|
|
110
|
+
* `<TableFooter>`. `<table>` cannot contain a `<tr>` directly: the HTML parser
|
|
111
|
+
* inserts an implied `<tbody>` when it parses server-rendered markup and React
|
|
112
|
+
* does not when it renders on the client, so the two trees mismatch on
|
|
113
|
+
* hydration. Children mode passes children straight through, so the section is
|
|
114
|
+
* the caller's to supply — the data-driven `data={...}` mode renders it for
|
|
115
|
+
* you.
|
|
116
|
+
*
|
|
108
117
|
* @example
|
|
109
118
|
* ```
|
|
110
119
|
* <Table>
|
|
111
|
-
* <
|
|
112
|
-
* <
|
|
113
|
-
*
|
|
114
|
-
*
|
|
120
|
+
* <TableBody>
|
|
121
|
+
* <TableRow>
|
|
122
|
+
* <TableCell>Alice</TableCell>
|
|
123
|
+
* <TableCell>30</TableCell>
|
|
124
|
+
* </TableRow>
|
|
125
|
+
* </TableBody>
|
|
115
126
|
* </Table>
|
|
116
127
|
* ```
|
|
117
128
|
*/
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useTableGroupedRows-perf.test.tsx
|
|
5
|
+
* @input Table, useTableGroupedRows, React testing utilities
|
|
6
|
+
* @output Performance tests for grouped-rows render behavior
|
|
7
|
+
* @position Test file; validates that grouping costs no extra row renders
|
|
8
|
+
*
|
|
9
|
+
* The plugin keeps group headers out of cell renderers per cell, at render
|
|
10
|
+
* time. Doing it by rewriting the columns instead would hand BaseTable a new
|
|
11
|
+
* column object on every render, and its element-by-element check on the
|
|
12
|
+
* resolved column array is what stops every row re-rendering.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {describe, it, expect} from 'vitest';
|
|
16
|
+
import {render, screen, act} from '@testing-library/react';
|
|
17
|
+
import {useCallback, useMemo, useState} from 'react';
|
|
18
|
+
import userEvent from '@testing-library/user-event';
|
|
19
|
+
import {Table} from '../../Table';
|
|
20
|
+
import type {TableColumn} from '../../types';
|
|
21
|
+
import {useTableGroupedRows} from './useTableGroupedRows';
|
|
22
|
+
|
|
23
|
+
interface Person extends Record<string, unknown> {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
team: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const people: Person[] = [
|
|
30
|
+
{id: 'a', name: 'Alice', team: 'Core'},
|
|
31
|
+
{id: 'b', name: 'Bob', team: 'Core'},
|
|
32
|
+
{id: 'c', name: 'Carol', team: 'Infra'},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const EMPTY = new Set<string>();
|
|
36
|
+
|
|
37
|
+
function GroupedRenderCountTable({
|
|
38
|
+
renderCounts,
|
|
39
|
+
}: {
|
|
40
|
+
renderCounts: Record<string, number>;
|
|
41
|
+
}) {
|
|
42
|
+
const [tick, setTick] = useState(0);
|
|
43
|
+
const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(EMPTY);
|
|
44
|
+
|
|
45
|
+
const columns = useMemo<TableColumn<Person>[]>(
|
|
46
|
+
() => [
|
|
47
|
+
{
|
|
48
|
+
key: 'name',
|
|
49
|
+
header: 'Name',
|
|
50
|
+
renderCell: (item: Person) => {
|
|
51
|
+
renderCounts[item.id] = (renderCounts[item.id] ?? 0) + 1;
|
|
52
|
+
return item.name;
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
[renderCounts],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const grouped = useTableGroupedRows<Person>({
|
|
60
|
+
data: people,
|
|
61
|
+
groupBy: useCallback((p: Person) => p.team, []),
|
|
62
|
+
collapsedGroups,
|
|
63
|
+
onToggleGroup: useCallback(
|
|
64
|
+
(key: string) =>
|
|
65
|
+
setCollapsedGroups(prev => {
|
|
66
|
+
const next = new Set(prev);
|
|
67
|
+
if (!next.delete(key)) {
|
|
68
|
+
next.add(key);
|
|
69
|
+
}
|
|
70
|
+
return next;
|
|
71
|
+
}),
|
|
72
|
+
[],
|
|
73
|
+
),
|
|
74
|
+
getRowKey: useCallback((p: Person) => p.id, []),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<button type="button" onClick={() => setTick(t => t + 1)}>
|
|
80
|
+
bump {tick}
|
|
81
|
+
</button>
|
|
82
|
+
<Table
|
|
83
|
+
data={grouped.data}
|
|
84
|
+
columns={columns}
|
|
85
|
+
idKey={grouped.idKey}
|
|
86
|
+
plugins={{grouped: grouped.plugin}}
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe('Grouped rows render performance', () => {
|
|
93
|
+
it('renders each real row once and no group header at all', () => {
|
|
94
|
+
const renderCounts: Record<string, number> = {};
|
|
95
|
+
render(<GroupedRenderCountTable renderCounts={renderCounts} />);
|
|
96
|
+
expect(renderCounts).toEqual({a: 1, b: 1, c: 1});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('a re-render of the surrounding component re-renders no rows', async () => {
|
|
100
|
+
const user = userEvent.setup();
|
|
101
|
+
const renderCounts: Record<string, number> = {};
|
|
102
|
+
render(<GroupedRenderCountTable renderCounts={renderCounts} />);
|
|
103
|
+
const before = {...renderCounts};
|
|
104
|
+
|
|
105
|
+
await act(async () => {
|
|
106
|
+
await user.click(screen.getByRole('button', {name: /bump/}));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(screen.getByRole('button', {name: 'bump 1'})).toBeInTheDocument();
|
|
110
|
+
expect(renderCounts).toEqual(before);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -5,6 +5,7 @@ import {render, screen, fireEvent, within} from '@testing-library/react';
|
|
|
5
5
|
import {useState, useCallback} from 'react';
|
|
6
6
|
import {Table} from '../../Table';
|
|
7
7
|
import type {TableColumn} from '../../types';
|
|
8
|
+
import {useTableRowStatus, type TableRowStatus} from '../rowStatus';
|
|
8
9
|
import {useTableGroupedRows} from './useTableGroupedRows';
|
|
9
10
|
|
|
10
11
|
interface Person extends Record<string, unknown> {
|
|
@@ -175,6 +176,105 @@ describe('useTableGroupedRows', () => {
|
|
|
175
176
|
).not.toBeInTheDocument();
|
|
176
177
|
});
|
|
177
178
|
|
|
179
|
+
it('never runs a column renderCell against a group header row', () => {
|
|
180
|
+
// The renderer that breaks: it keys a lookup off a field rather than
|
|
181
|
+
// printing it, so the header Proxy answering `''` is no better than
|
|
182
|
+
// `undefined`.
|
|
183
|
+
const TEAM_META: Record<string, {dot: string}> = {
|
|
184
|
+
Core: {dot: 'green'},
|
|
185
|
+
Infra: {dot: 'blue'},
|
|
186
|
+
};
|
|
187
|
+
const seen: Person[] = [];
|
|
188
|
+
const lookupColumns: TableColumn<Person>[] = [
|
|
189
|
+
{
|
|
190
|
+
key: 'team',
|
|
191
|
+
header: 'Team',
|
|
192
|
+
renderCell: item => {
|
|
193
|
+
seen.push(item);
|
|
194
|
+
return <span>{TEAM_META[item.team].dot}</span>;
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
function LookupHarness() {
|
|
200
|
+
const [collapsed, setCollapsed] = useState<Set<string>>(EMPTY);
|
|
201
|
+
const grouped = useTableGroupedRows<Person>({
|
|
202
|
+
data: people,
|
|
203
|
+
groupBy: p => p.team,
|
|
204
|
+
collapsedGroups: collapsed,
|
|
205
|
+
onToggleGroup: k => setCollapsed(new Set([k])),
|
|
206
|
+
getRowKey: p => p.id,
|
|
207
|
+
});
|
|
208
|
+
return (
|
|
209
|
+
<Table
|
|
210
|
+
data={grouped.data}
|
|
211
|
+
columns={lookupColumns}
|
|
212
|
+
idKey={grouped.idKey}
|
|
213
|
+
plugins={{grouped: grouped.plugin}}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
expect(() => render(<LookupHarness />)).not.toThrow();
|
|
219
|
+
expect(seen.map(p => p.name)).toEqual(['Alice', 'Bob', 'Carol']);
|
|
220
|
+
expect(screen.getByText('Core')).toBeInTheDocument();
|
|
221
|
+
expect(screen.getByText('Infra')).toBeInTheDocument();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe.each([
|
|
225
|
+
['grouped first', true],
|
|
226
|
+
['rowStatus first', false],
|
|
227
|
+
])(
|
|
228
|
+
'with a column contributed by another plugin (%s)',
|
|
229
|
+
(_label, groupedFirst) => {
|
|
230
|
+
// `grouped` and `rowStatus` are both absent from PLUGIN_ORDER, so the
|
|
231
|
+
// record's insertion order is the pipeline order. Whichever way round
|
|
232
|
+
// the consumer types it, the status column's renderCell must not see a
|
|
233
|
+
// header row.
|
|
234
|
+
const STATUS_BY_TEAM: Record<string, TableRowStatus> = {
|
|
235
|
+
Core: {color: 'success', label: 'Healthy'},
|
|
236
|
+
Infra: {color: 'error', label: 'Paging'},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
function StatusHarness() {
|
|
240
|
+
const [collapsed, setCollapsed] = useState<Set<string>>(EMPTY);
|
|
241
|
+
const grouped = useTableGroupedRows<Person>({
|
|
242
|
+
data: people,
|
|
243
|
+
groupBy: p => p.team,
|
|
244
|
+
collapsedGroups: collapsed,
|
|
245
|
+
onToggleGroup: k => setCollapsed(new Set([k])),
|
|
246
|
+
getRowKey: p => p.id,
|
|
247
|
+
});
|
|
248
|
+
const rowStatus = useTableRowStatus<Person>({
|
|
249
|
+
getStatus: useCallback((item: Person) => {
|
|
250
|
+
const {color, label} = STATUS_BY_TEAM[item.team];
|
|
251
|
+
return {color, label};
|
|
252
|
+
}, []),
|
|
253
|
+
});
|
|
254
|
+
return (
|
|
255
|
+
<Table
|
|
256
|
+
data={grouped.data}
|
|
257
|
+
columns={columns}
|
|
258
|
+
idKey={grouped.idKey}
|
|
259
|
+
plugins={
|
|
260
|
+
groupedFirst
|
|
261
|
+
? {grouped: grouped.plugin, rowStatus}
|
|
262
|
+
: {rowStatus, grouped: grouped.plugin}
|
|
263
|
+
}
|
|
264
|
+
/>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
it('renders the group headers without throwing', () => {
|
|
269
|
+
expect(() => render(<StatusHarness />)).not.toThrow();
|
|
270
|
+
expect(screen.getByText('Core')).toBeInTheDocument();
|
|
271
|
+
expect(screen.getByText('Infra')).toBeInTheDocument();
|
|
272
|
+
// One indicator per real row, none on the two headers.
|
|
273
|
+
expect(screen.getAllByRole('img')).toHaveLength(3);
|
|
274
|
+
});
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
|
|
178
278
|
it('keeps a group collapsed across a data change (state keyed by group)', () => {
|
|
179
279
|
function ChangingHarness() {
|
|
180
280
|
const [collapsed, setCollapsed] = useState<Set<string>>(
|