@astryxdesign/core 0.4.7 → 0.5.0-canary.32a62fd
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 +25 -1
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +58 -19
- package/dist/Avatar/index.d.ts +2 -2
- package/dist/Avatar/index.d.ts.map +1 -1
- package/dist/Avatar/index.js +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +8 -4
- package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
- package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
- 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 +10 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -3
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +15 -4
- package/dist/CodeBlock/highlightRanges.js +1 -1
- package/dist/CodeBlock/highlightStyles.js +13 -13
- 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 +43 -0
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +22 -2
- package/dist/DateInput/NativeDateField.d.ts +10 -0
- package/dist/DateInput/NativeDateField.d.ts.map +1 -0
- package/dist/DateInput/NativeDateField.js +477 -0
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +38 -4
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- 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/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -1
- 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/HoverCard.js +52 -3
- package/dist/HoverCard/useHoverCard.d.ts +16 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +74 -26
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +6 -2
- 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/NavIcon/NavIcon.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.js +5 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +3 -2
- package/dist/NumberInput/NumberInput.d.ts +1 -0
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +27 -37
- package/dist/NumberInput/numberParser.d.ts +43 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -0
- package/dist/NumberInput/numberParser.js +293 -0
- 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/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +18 -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 +8 -7
- 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/StatusDot/StatusDot.d.ts.map +1 -1
- package/dist/StatusDot/StatusDot.js +6 -1
- 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 +8 -3
- 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 +10 -2
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +3 -6
- 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/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
- 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 +9 -3
- 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 +5 -4
- 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 +94 -7
- 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/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +24 -4
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +5 -2
- 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 +14 -3
- package/src/Avatar/Avatar.test.tsx +97 -0
- package/src/Avatar/Avatar.tsx +125 -52
- package/src/Avatar/index.ts +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
- package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
- package/src/AvatarGroup/AvatarGroup.tsx +15 -6
- package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
- 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.doc.mjs +2 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
- package/src/CheckboxInput/CheckboxInput.tsx +8 -2
- package/src/ClickableCard/ClickableCard.tsx +3 -2
- package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
- package/src/CodeBlock/CodeBlock.test.tsx +37 -0
- package/src/CodeBlock/CodeBlock.tsx +20 -4
- package/src/CodeBlock/highlightRanges.ts +1 -1
- package/src/CodeBlock/highlightStyles.ts +13 -13
- 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.doc.mjs +16 -0
- package/src/DateInput/DateInput.tsx +55 -2
- package/src/DateInput/DateInputTouch.test.tsx +68 -9
- package/src/DateInput/NativeDateField.test.tsx +565 -0
- package/src/DateInput/NativeDateField.tsx +623 -0
- package/src/DateInput/TouchDateField.tsx +38 -4
- package/src/DateInput/index.ts +1 -0
- 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/Field/FieldLabel.test.tsx +29 -0
- package/src/Field/FieldLabel.tsx +7 -0
- 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 +13 -2
- package/src/HoverCard/HoverCard.test.tsx +101 -4
- package/src/HoverCard/HoverCard.tsx +71 -3
- package/src/HoverCard/useHoverCard.tsx +101 -33
- package/src/Indicator/CheckboxIndicator.tsx +14 -8
- package/src/Indicator/Indicator.doc.mjs +2 -0
- 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/NavIcon/NavIcon.doc.mjs +8 -2
- package/src/NavIcon/NavIcon.test.tsx +9 -0
- package/src/NavIcon/NavIcon.tsx +5 -1
- package/src/NavMenu/NavHeadingMenu.tsx +3 -2
- package/src/NumberInput/NumberInput.doc.mjs +20 -0
- package/src/NumberInput/NumberInput.tsx +28 -29
- package/src/NumberInput/numberParser.test.ts +137 -0
- package/src/NumberInput/numberParser.ts +346 -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/ProgressBar/ProgressBar.doc.mjs +42 -6
- package/src/ProgressBar/ProgressBar.test.tsx +24 -0
- package/src/ProgressBar/ProgressBar.tsx +28 -8
- 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 +12 -12
- 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/StatusDot/StatusDot.doc.mjs +8 -2
- package/src/StatusDot/StatusDot.test.tsx +9 -0
- package/src/StatusDot/StatusDot.tsx +7 -1
- 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.doc.mjs +2 -0
- package/src/Switch/Switch.test.tsx +10 -0
- package/src/Switch/Switch.tsx +6 -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 +12 -4
- package/src/Table/Table.doc.mjs +9 -2
- package/src/Table/Table.test.tsx +132 -14
- package/src/Table/Table.tsx +3 -8
- 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/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
- 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.doc.mjs +8 -2
- package/src/TextArea/TextArea.test.tsx +11 -0
- package/src/TextArea/TextArea.tsx +15 -8
- 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 +5 -4
- 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/theme/derivedVarRegistry.test.ts +19 -3
- package/src/theme/derivedVarRegistry.ts +23 -4
- package/src/theme/generateThemeRules.test.ts +37 -0
- package/src/theme/syntax/tokens.ts +5 -2
- 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
|
@@ -38,7 +38,7 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
38
38
|
import type {BaseProps} from '../BaseProps';
|
|
39
39
|
import type {DialogPurpose} from '../Dialog';
|
|
40
40
|
import {colorVars, durationVars, easeVars} from '../theme/tokens.stylex';
|
|
41
|
-
import {useDevWarning, useScrollLock} from '../hooks';
|
|
41
|
+
import {isImeKeyEvent, useDevWarning, useScrollLock} from '../hooks';
|
|
42
42
|
import {
|
|
43
43
|
BottomSheetPanel,
|
|
44
44
|
type BottomSheetPanelMotion,
|
|
@@ -314,6 +314,9 @@ function StandaloneBottomSheet({
|
|
|
314
314
|
|
|
315
315
|
const handleCancel = useCallback(
|
|
316
316
|
(event: React.SyntheticEvent<HTMLDialogElement>) => {
|
|
317
|
+
// No IME guard here: `cancel` is a plain Event carrying no composition
|
|
318
|
+
// state, and handleKeyDown claims a composing Escape before the browser
|
|
319
|
+
// can raise the close request that would arrive here.
|
|
317
320
|
event.preventDefault();
|
|
318
321
|
dismissOnEscape();
|
|
319
322
|
},
|
|
@@ -321,10 +324,20 @@ function StandaloneBottomSheet({
|
|
|
321
324
|
);
|
|
322
325
|
const handleKeyDown = useCallback(
|
|
323
326
|
(event: React.KeyboardEvent<HTMLDialogElement>) => {
|
|
324
|
-
if (event.key
|
|
325
|
-
|
|
326
|
-
dismissOnEscape();
|
|
327
|
+
if (event.key !== 'Escape') {
|
|
328
|
+
return;
|
|
327
329
|
}
|
|
330
|
+
// Claim the key before reading it: an unclaimed Escape lets the browser
|
|
331
|
+
// raise its own close request, which lands on handleCancel and dismisses
|
|
332
|
+
// on the same keypress.
|
|
333
|
+
event.preventDefault();
|
|
334
|
+
// An IME fires this keydown to cancel an in-progress composition, ahead
|
|
335
|
+
// of compositionend. It is a composition cancel, not a dismissal command
|
|
336
|
+
// — see utils/ime; Dialog and BottomSheetSwitcher guard the same way.
|
|
337
|
+
if (isImeKeyEvent(event.nativeEvent)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
dismissOnEscape();
|
|
328
341
|
},
|
|
329
342
|
[dismissOnEscape],
|
|
330
343
|
);
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
useFocusTrap,
|
|
52
52
|
useScrollLock,
|
|
53
53
|
} from '../hooks';
|
|
54
|
-
import {composeEventHandlers, mergeProps
|
|
54
|
+
import {composeEventHandlers, mergeProps} from '../utils';
|
|
55
55
|
import {BottomSheetEdgeTint} from './BottomSheetEdgeTint';
|
|
56
56
|
import {
|
|
57
57
|
BottomSheetSwitcherContext,
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
type BottomSheetSwitcherTransitionEvent,
|
|
61
61
|
} from './BottomSheetSwitcherContext';
|
|
62
62
|
|
|
63
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
63
64
|
const styles = stylex.create({
|
|
64
65
|
dialog: {
|
|
65
66
|
position: 'fixed',
|
|
@@ -621,7 +622,7 @@ export function BottomSheetSwitcher({
|
|
|
621
622
|
<dialog
|
|
622
623
|
{...props}
|
|
623
624
|
{...dialogPresentationProps}
|
|
624
|
-
ref={
|
|
625
|
+
ref={useMergedRefs(ref, dialogRef, containerRef)}
|
|
625
626
|
aria-label={ariaLabel}
|
|
626
627
|
aria-modal={isModal ? 'true' : undefined}
|
|
627
628
|
onCancel={composeEventHandlers(onCancel, handleCancel)}
|
|
@@ -38,11 +38,12 @@ import {
|
|
|
38
38
|
spacingVars,
|
|
39
39
|
typeScaleVars,
|
|
40
40
|
radiusVars,
|
|
41
|
+
fontWeightVars,
|
|
41
42
|
} from '../theme/tokens.stylex';
|
|
42
43
|
import {BreadcrumbContext, type BreadcrumbsVariant} from './Breadcrumbs';
|
|
43
44
|
import {useLinkComponent} from '../Link/useLinkComponent';
|
|
44
45
|
import type {LinkComponentType} from '../Link/types';
|
|
45
|
-
import {mergeProps
|
|
46
|
+
import {mergeProps} from '../utils';
|
|
46
47
|
import type {BaseProps} from '../BaseProps';
|
|
47
48
|
import {themeProps} from '../utils/themeProps';
|
|
48
49
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
} from '../DropdownMenu/menuItemRoles';
|
|
65
66
|
import type {DropdownMenuOption} from '../DropdownMenu/DropdownMenu';
|
|
66
67
|
|
|
68
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
67
69
|
// =============================================================================
|
|
68
70
|
// Props
|
|
69
71
|
// =============================================================================
|
|
@@ -186,7 +188,7 @@ const itemStyles = stylex.create({
|
|
|
186
188
|
color: colorVars['--color-text-secondary'],
|
|
187
189
|
},
|
|
188
190
|
current: {
|
|
189
|
-
fontWeight: '
|
|
191
|
+
fontWeight: fontWeightVars['--font-weight-semibold'],
|
|
190
192
|
},
|
|
191
193
|
defaultCurrent: {
|
|
192
194
|
color: colorVars['--color-text-primary'],
|
|
@@ -346,6 +348,8 @@ export function BreadcrumbItem({
|
|
|
346
348
|
}
|
|
347
349
|
});
|
|
348
350
|
|
|
351
|
+
const mergedItemRef = useMergedRefs(ref, liRef);
|
|
352
|
+
|
|
349
353
|
const content = (
|
|
350
354
|
<>
|
|
351
355
|
{startIcon && <span {...stylex.props(itemStyles.icon)}>{startIcon}</span>}
|
|
@@ -356,7 +360,7 @@ export function BreadcrumbItem({
|
|
|
356
360
|
if (isCurrent) {
|
|
357
361
|
return (
|
|
358
362
|
<li
|
|
359
|
-
ref={
|
|
363
|
+
ref={mergedItemRef}
|
|
360
364
|
{...mergeProps(
|
|
361
365
|
themeProps('breadcrumb-item', {variant: ctx.variant}),
|
|
362
366
|
stylex.props(
|
|
@@ -405,7 +409,7 @@ export function BreadcrumbItem({
|
|
|
405
409
|
// The effect handles adding aria-current for auto-last detection.
|
|
406
410
|
return (
|
|
407
411
|
<li
|
|
408
|
-
ref={
|
|
412
|
+
ref={mergedItemRef}
|
|
409
413
|
{...mergeProps(
|
|
410
414
|
themeProps('breadcrumb-item', {variant: ctx.variant}),
|
|
411
415
|
stylex.props(
|
|
@@ -615,7 +619,7 @@ function BreadcrumbMenuTrigger({
|
|
|
615
619
|
return (
|
|
616
620
|
<>
|
|
617
621
|
<button
|
|
618
|
-
ref={
|
|
622
|
+
ref={useMergedRefs(
|
|
619
623
|
ref as React.Ref<HTMLButtonElement>,
|
|
620
624
|
buttonRef,
|
|
621
625
|
popover.triggerRef,
|
|
@@ -150,6 +150,28 @@ export const docs = {
|
|
|
150
150
|
description:
|
|
151
151
|
'Async click handler. Shows loading state while the returned promise is pending.',
|
|
152
152
|
},
|
|
153
|
+
{
|
|
154
|
+
name: 'href',
|
|
155
|
+
type: 'string',
|
|
156
|
+
description:
|
|
157
|
+
'When provided, renders the button as a link element (<a> or custom link component).',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'as',
|
|
161
|
+
type: 'ComponentType',
|
|
162
|
+
description:
|
|
163
|
+
'Custom link component to use when href is provided (e.g. Next.js Link).',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'target',
|
|
167
|
+
type: 'string',
|
|
168
|
+
description: 'HTML target attribute when rendered as a link (e.g. "_blank").',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'rel',
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'HTML rel attribute when rendered as a link (e.g. "noopener noreferrer").',
|
|
174
|
+
},
|
|
153
175
|
],
|
|
154
176
|
playground: {
|
|
155
177
|
defaults: {
|
|
@@ -151,6 +151,19 @@ describe('Button', () => {
|
|
|
151
151
|
expect(ref).toHaveBeenCalledWith(expect.any(HTMLButtonElement));
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
+
it('keeps its merged ref attached across unrelated rerenders', () => {
|
|
155
|
+
const ref = vi.fn();
|
|
156
|
+
const {rerender} = render(<Button label="Test" ref={ref} />);
|
|
157
|
+
const button = screen.getByRole('button');
|
|
158
|
+
expect(ref).toHaveBeenLastCalledWith(button);
|
|
159
|
+
ref.mockClear();
|
|
160
|
+
|
|
161
|
+
rerender(<Button label="Test" variant="primary" ref={ref} />);
|
|
162
|
+
|
|
163
|
+
expect(ref).not.toHaveBeenCalled();
|
|
164
|
+
expect(screen.getByRole('button')).toBe(button);
|
|
165
|
+
});
|
|
166
|
+
|
|
154
167
|
// endContent tests
|
|
155
168
|
it('renders endContent after label', () => {
|
|
156
169
|
render(
|
package/src/Button/Button.tsx
CHANGED
|
@@ -42,7 +42,8 @@ import {VisuallyHidden} from '../VisuallyHidden';
|
|
|
42
42
|
import {EDGE_COMP_ATTR} from '../Layout/edgeCompensation.stylex';
|
|
43
43
|
import {useSize} from '../SizeContext/SizeContext';
|
|
44
44
|
import {useButtonGroup} from '../ButtonGroup/ButtonGroupContext';
|
|
45
|
-
import {mergeProps
|
|
45
|
+
import {mergeProps} from '../utils';
|
|
46
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
46
47
|
import {useLinkComponent} from '../Link/useLinkComponent';
|
|
47
48
|
import type {LinkComponentType} from '../Link/types';
|
|
48
49
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -757,9 +758,9 @@ export function Button({
|
|
|
757
758
|
: null;
|
|
758
759
|
|
|
759
760
|
// Merge the consumer ref with the tooltip hook's trigger ref so both point at
|
|
760
|
-
// the same element.
|
|
761
|
-
// tooltip side when no tooltip is set.
|
|
762
|
-
const mergedButtonRef =
|
|
761
|
+
// the same element. useMergedRefs tolerates undefined, so this is a no-op for
|
|
762
|
+
// the tooltip side when no tooltip is set.
|
|
763
|
+
const mergedButtonRef = useMergedRefs(
|
|
763
764
|
ref,
|
|
764
765
|
tooltip != null ? tooltipHook.ref : undefined,
|
|
765
766
|
);
|
|
@@ -15,7 +15,7 @@ export const docs = {
|
|
|
15
15
|
},
|
|
16
16
|
theming: {
|
|
17
17
|
targets: [
|
|
18
|
-
{className: 'astryx-button-group', visualProps: ['size', 'orientation']},
|
|
18
|
+
{className: 'astryx-button-group', visualProps: ['size', 'orientation', 'elevation']},
|
|
19
19
|
],
|
|
20
20
|
},
|
|
21
21
|
components: [
|
|
@@ -46,6 +46,9 @@ export const docs = {
|
|
|
46
46
|
{guidance: false, description: "Don't mix wildly different actions. A Save button next to a Delete button in the same group is confusing."},
|
|
47
47
|
{guidance: false, description: "Don't use ButtonGroup for navigation. Use SegmentedControl or TabList for switching between views."},
|
|
48
48
|
{guidance: false, description: "Don't nest ButtonGroups. If you need multiple groups, place them side by side with a gap."},
|
|
49
|
+
{guidance: true, description: 'Name the group for what its buttons act on. The label is the group\u2019s accessible name and a screen reader reads it before each member.'},
|
|
50
|
+
{guidance: true, description: 'Keep the group a single Tab stop. Arrow keys move between members along the orientation, Home/End jump to the ends, and disabled members are skipped — the WAI-ARIA APG roving tabindex technique: https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex'},
|
|
51
|
+
{guidance: false, description: "Don't disable the group to show that an action is in flight. A disabled member drops focus, so a keyboard user loses their place; leave the group enabled and show progress on the button that started the work."},
|
|
49
52
|
],
|
|
50
53
|
anatomy: [
|
|
51
54
|
{name: 'Button', required: true, description: 'One or more Button or IconButton children that form the connected group.'},
|
|
@@ -84,6 +87,9 @@ export const docsZh = {
|
|
|
84
87
|
{guidance: false, description: '不要混合差异很大的操作,将保存按钮和删除按钮放在同一组中会令人困惑。'},
|
|
85
88
|
{guidance: false, description: '不要使用 ButtonGroup 进行导航,使用 SegmentedControl 或 TabList 切换视图。'},
|
|
86
89
|
{guidance: false, description: '不要嵌套 ButtonGroup。如需多个组,请并排放置并留有间隔。'},
|
|
90
|
+
{guidance: true, description: '按钮组的标签应说明这些按钮作用于什么。该标签是按钮组的无障碍名称,屏幕阅读器会在每个成员之前朗读它。'},
|
|
91
|
+
{guidance: true, description: '按钮组是单个 Tab 停靠点。方向键沿排列方向在成员间移动焦点,Home/End 跳到首尾,禁用成员会被跳过——即 WAI-ARIA APG 的 roving tabindex 技术。'},
|
|
92
|
+
{guidance: false, description: '不要用禁用整个按钮组来表示操作进行中。禁用的成员会失去焦点,键盘用户会丢失位置;请保持按钮组可用,并在发起操作的按钮上显示进度。'},
|
|
87
93
|
],
|
|
88
94
|
anatomy: [
|
|
89
95
|
{name: '按钮', required: true, description: '一个或多个 Button 或 IconButton 子元素,形成连接的组。'},
|
|
@@ -105,6 +111,9 @@ export const docsDense = {
|
|
|
105
111
|
{guidance: false, description: "Don't mix unrelated actions in one group."},
|
|
106
112
|
{guidance: false, description: "Don't use for navigation. Use SegmentedControl or TabList."},
|
|
107
113
|
{guidance: false, description: "Don't nest ButtonGroups."},
|
|
114
|
+
{guidance: true, description: 'Label the group for what its buttons act on; it is the accessible name.'},
|
|
115
|
+
{guidance: true, description: 'Single Tab stop; arrows move between members, Home/End to ends, disabled skipped (APG roving tabindex).'},
|
|
116
|
+
{guidance: false, description: "Don't disable the group for an in-flight action; a disabled member drops focus."},
|
|
108
117
|
],
|
|
109
118
|
},
|
|
110
119
|
components: [
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import {describe, it, expect} from 'vitest';
|
|
15
|
-
import {
|
|
15
|
+
import type {ReactNode} from 'react';
|
|
16
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
17
|
+
import userEvent from '@testing-library/user-event';
|
|
16
18
|
import {transformSync} from '@babel/core';
|
|
17
19
|
import stylexBabelPlugin from '@stylexjs/babel-plugin';
|
|
18
20
|
import {readFileSync} from 'node:fs';
|
|
@@ -611,5 +613,393 @@ describe('ButtonGroup', () => {
|
|
|
611
613
|
none.querySelector('[role="group"]')!.className,
|
|
612
614
|
);
|
|
613
615
|
});
|
|
616
|
+
|
|
617
|
+
it('exposes the elevation to a theme as a data attribute', () => {
|
|
618
|
+
render(
|
|
619
|
+
<ButtonGroup label="Actions" elevation="high">
|
|
620
|
+
<Button label="One" />
|
|
621
|
+
</ButtonGroup>,
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
expect(screen.getByRole('group')).toHaveAttribute(
|
|
625
|
+
'data-elevation',
|
|
626
|
+
'high',
|
|
627
|
+
);
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// ===========================================================================
|
|
632
|
+
// Keyboard navigation
|
|
633
|
+
//
|
|
634
|
+
// The group wires useListFocus, so arrow keys move focus between members.
|
|
635
|
+
// The last two tests cover the boundary: a member can own a layer, whose
|
|
636
|
+
// keys belong to that layer, and the group itself can sit inside a layer,
|
|
637
|
+
// whose keys are still the group's own. Both must hold at once.
|
|
638
|
+
// ===========================================================================
|
|
639
|
+
describe('keyboard navigation', () => {
|
|
640
|
+
const clipboard = (
|
|
641
|
+
<ButtonGroup label="Actions">
|
|
642
|
+
<Button label="Copy" />
|
|
643
|
+
<Button label="Cut" />
|
|
644
|
+
<Button label="Paste" />
|
|
645
|
+
</ButtonGroup>
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
it('moves focus between members with the arrow keys, wrapping at the end', async () => {
|
|
649
|
+
const user = userEvent.setup();
|
|
650
|
+
render(clipboard);
|
|
651
|
+
|
|
652
|
+
screen.getByRole('button', {name: 'Copy'}).focus();
|
|
653
|
+
|
|
654
|
+
await user.keyboard('{ArrowRight}');
|
|
655
|
+
expect(screen.getByRole('button', {name: 'Cut'})).toHaveFocus();
|
|
656
|
+
|
|
657
|
+
await user.keyboard('{ArrowRight}{ArrowRight}');
|
|
658
|
+
expect(screen.getByRole('button', {name: 'Copy'})).toHaveFocus();
|
|
659
|
+
|
|
660
|
+
await user.keyboard('{ArrowLeft}');
|
|
661
|
+
expect(screen.getByRole('button', {name: 'Paste'})).toHaveFocus();
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('jumps to the first and last member with Home and End', async () => {
|
|
665
|
+
const user = userEvent.setup();
|
|
666
|
+
render(clipboard);
|
|
667
|
+
|
|
668
|
+
screen.getByRole('button', {name: 'Cut'}).focus();
|
|
669
|
+
|
|
670
|
+
await user.keyboard('{End}');
|
|
671
|
+
expect(screen.getByRole('button', {name: 'Paste'})).toHaveFocus();
|
|
672
|
+
|
|
673
|
+
await user.keyboard('{Home}');
|
|
674
|
+
expect(screen.getByRole('button', {name: 'Copy'})).toHaveFocus();
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('uses the vertical arrows when the group is vertical', async () => {
|
|
678
|
+
const user = userEvent.setup();
|
|
679
|
+
render(
|
|
680
|
+
<ButtonGroup label="Actions" orientation="vertical">
|
|
681
|
+
<Button label="Copy" />
|
|
682
|
+
<Button label="Cut" />
|
|
683
|
+
</ButtonGroup>,
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
screen.getByRole('button', {name: 'Copy'}).focus();
|
|
687
|
+
|
|
688
|
+
await user.keyboard('{ArrowDown}');
|
|
689
|
+
expect(screen.getByRole('button', {name: 'Cut'})).toHaveFocus();
|
|
690
|
+
|
|
691
|
+
await user.keyboard('{ArrowUp}');
|
|
692
|
+
expect(screen.getByRole('button', {name: 'Copy'})).toHaveFocus();
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('follows visual direction in RTL', async () => {
|
|
696
|
+
const user = userEvent.setup();
|
|
697
|
+
// dir sits on the group because jsdom resolves computed direction from
|
|
698
|
+
// the element's own attribute, not from an ancestor.
|
|
699
|
+
render(
|
|
700
|
+
<ButtonGroup label="Actions" dir="rtl">
|
|
701
|
+
<Button label="Copy" />
|
|
702
|
+
<Button label="Cut" />
|
|
703
|
+
<Button label="Paste" />
|
|
704
|
+
</ButtonGroup>,
|
|
705
|
+
);
|
|
706
|
+
screen.getByRole('button', {name: 'Copy'}).focus();
|
|
707
|
+
|
|
708
|
+
await user.keyboard('{ArrowLeft}');
|
|
709
|
+
expect(screen.getByRole('button', {name: 'Cut'})).toHaveFocus();
|
|
710
|
+
|
|
711
|
+
await user.keyboard('{ArrowRight}');
|
|
712
|
+
expect(screen.getByRole('button', {name: 'Copy'})).toHaveFocus();
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it('skips a disabled member', async () => {
|
|
716
|
+
const user = userEvent.setup();
|
|
717
|
+
render(
|
|
718
|
+
<ButtonGroup label="Actions">
|
|
719
|
+
<Button label="Copy" />
|
|
720
|
+
<Button label="Cut" isDisabled />
|
|
721
|
+
<Button label="Paste" />
|
|
722
|
+
</ButtonGroup>,
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
screen.getByRole('button', {name: 'Copy'}).focus();
|
|
726
|
+
|
|
727
|
+
await user.keyboard('{ArrowRight}');
|
|
728
|
+
expect(screen.getByRole('button', {name: 'Paste'})).toHaveFocus();
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('leaves arrow keys to a member\u2019s open menu', async () => {
|
|
732
|
+
const user = userEvent.setup();
|
|
733
|
+
render(
|
|
734
|
+
<ButtonGroup label="Approve action">
|
|
735
|
+
<Button label="Allow once" />
|
|
736
|
+
<DropdownMenu
|
|
737
|
+
hasChevron={false}
|
|
738
|
+
button={{label: 'Allow options', isIconOnly: true}}
|
|
739
|
+
items={[{label: 'Allow for 30 minutes'}, {label: 'Always allow'}]}
|
|
740
|
+
/>
|
|
741
|
+
</ButtonGroup>,
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
await user.click(screen.getByRole('button', {name: /Allow options/}));
|
|
745
|
+
const item = screen.getByRole('menuitem', {
|
|
746
|
+
name: 'Allow for 30 minutes',
|
|
747
|
+
hidden: true,
|
|
748
|
+
});
|
|
749
|
+
item.focus();
|
|
750
|
+
|
|
751
|
+
// Assert positively: the menu item keeps focus. `.not.toHaveFocus()` on
|
|
752
|
+
// the group's buttons would also pass if the group had thrown focus
|
|
753
|
+
// somewhere else entirely.
|
|
754
|
+
fireEvent.keyDown(item, {key: 'ArrowRight'});
|
|
755
|
+
expect(item).toHaveFocus();
|
|
756
|
+
|
|
757
|
+
fireEvent.keyDown(item, {key: 'ArrowLeft'});
|
|
758
|
+
expect(item).toHaveFocus();
|
|
759
|
+
|
|
760
|
+
// End belongs to the menu's own list focus, so it lands on the last menu
|
|
761
|
+
// item rather than the group's last button.
|
|
762
|
+
fireEvent.keyDown(item, {key: 'End'});
|
|
763
|
+
expect(
|
|
764
|
+
screen.getByRole('menuitem', {name: 'Always allow', hidden: true}),
|
|
765
|
+
).toHaveFocus();
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it('leaves a link member in the arrow order', async () => {
|
|
769
|
+
const user = userEvent.setup();
|
|
770
|
+
render(
|
|
771
|
+
<ButtonGroup label="Docs actions">
|
|
772
|
+
<Button label="Save" />
|
|
773
|
+
<Button label="Docs" href="https://example.com" />
|
|
774
|
+
<Button label="Print" />
|
|
775
|
+
</ButtonGroup>,
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
screen.getByRole('button', {name: 'Save'}).focus();
|
|
779
|
+
|
|
780
|
+
await user.keyboard('{ArrowRight}');
|
|
781
|
+
expect(screen.getByRole('link', {name: 'Docs'})).toHaveFocus();
|
|
782
|
+
|
|
783
|
+
await user.keyboard('{ArrowRight}');
|
|
784
|
+
expect(screen.getByRole('button', {name: 'Print'})).toHaveFocus();
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
it('still moves between its own members when the group sits inside a popover', async () => {
|
|
788
|
+
const user = userEvent.setup();
|
|
789
|
+
// A group rendered inside a Popover, ContextMenu, HoverCard or Toast has
|
|
790
|
+
// a `[popover]` ancestor, because that is how useLayer mounts a layer.
|
|
791
|
+
// A bare `popover` div is the same DOM fact without the layer harness.
|
|
792
|
+
render(
|
|
793
|
+
<div popover="manual">
|
|
794
|
+
<ButtonGroup label="Actions">
|
|
795
|
+
<Button label="Copy" />
|
|
796
|
+
<Button label="Cut" />
|
|
797
|
+
<Button label="Paste" />
|
|
798
|
+
</ButtonGroup>
|
|
799
|
+
</div>,
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
// A closed popover is display:none in jsdom, so its contents read as
|
|
803
|
+
// inaccessible; focus and key handling still work on them.
|
|
804
|
+
const button = (name: string) =>
|
|
805
|
+
screen.getByRole('button', {name, hidden: true});
|
|
806
|
+
|
|
807
|
+
button('Copy').focus();
|
|
808
|
+
|
|
809
|
+
await user.keyboard('{ArrowRight}');
|
|
810
|
+
expect(button('Cut')).toHaveFocus();
|
|
811
|
+
|
|
812
|
+
await user.keyboard('{End}');
|
|
813
|
+
expect(button('Paste')).toHaveFocus();
|
|
814
|
+
|
|
815
|
+
await user.keyboard('{Home}');
|
|
816
|
+
expect(button('Copy')).toHaveFocus();
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// ===========================================================================
|
|
821
|
+
// Roving tabindex
|
|
822
|
+
//
|
|
823
|
+
// The group is a single tab stop: exactly one enabled member carries
|
|
824
|
+
// tabindex="0", the rest carry -1, and the stop moves with arrow navigation.
|
|
825
|
+
// Entering and leaving is asserted through the real tab order as well as the
|
|
826
|
+
// attribute, because the attribute alone says nothing about a member the
|
|
827
|
+
// itemSelector missed — such a member stays natively tabbable and quietly
|
|
828
|
+
// adds a second stop.
|
|
829
|
+
// ===========================================================================
|
|
830
|
+
describe('roving tabindex', () => {
|
|
831
|
+
const tabIndexOf = (name: string) =>
|
|
832
|
+
screen.getByRole('button', {name}).getAttribute('tabindex');
|
|
833
|
+
|
|
834
|
+
const bracketed = (group: ReactNode) => (
|
|
835
|
+
<>
|
|
836
|
+
<button type="button">before</button>
|
|
837
|
+
{group}
|
|
838
|
+
<button type="button">after</button>
|
|
839
|
+
</>
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
const clipboard = (
|
|
843
|
+
<ButtonGroup label="Actions">
|
|
844
|
+
<Button label="Copy" />
|
|
845
|
+
<Button label="Cut" />
|
|
846
|
+
<Button label="Paste" />
|
|
847
|
+
</ButtonGroup>
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
it('gives the group one tab stop, on the first member', () => {
|
|
851
|
+
render(clipboard);
|
|
852
|
+
|
|
853
|
+
expect(tabIndexOf('Copy')).toBe('0');
|
|
854
|
+
expect(tabIndexOf('Cut')).toBe('-1');
|
|
855
|
+
expect(tabIndexOf('Paste')).toBe('-1');
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
it('takes one Tab to enter and one to leave', async () => {
|
|
859
|
+
const user = userEvent.setup();
|
|
860
|
+
render(bracketed(clipboard));
|
|
861
|
+
|
|
862
|
+
await user.tab();
|
|
863
|
+
expect(screen.getByRole('button', {name: 'before'})).toHaveFocus();
|
|
864
|
+
|
|
865
|
+
await user.tab();
|
|
866
|
+
expect(screen.getByRole('button', {name: 'Copy'})).toHaveFocus();
|
|
867
|
+
|
|
868
|
+
await user.tab();
|
|
869
|
+
expect(screen.getByRole('button', {name: 'after'})).toHaveFocus();
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
it('moves the tab stop with arrow navigation', async () => {
|
|
873
|
+
const user = userEvent.setup();
|
|
874
|
+
render(clipboard);
|
|
875
|
+
|
|
876
|
+
screen.getByRole('button', {name: 'Copy'}).focus();
|
|
877
|
+
await user.keyboard('{ArrowRight}');
|
|
878
|
+
|
|
879
|
+
expect(tabIndexOf('Copy')).toBe('-1');
|
|
880
|
+
expect(tabIndexOf('Cut')).toBe('0');
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it('never parks the tab stop on a disabled member', () => {
|
|
884
|
+
render(
|
|
885
|
+
<ButtonGroup label="Actions">
|
|
886
|
+
<Button label="Copy" isDisabled />
|
|
887
|
+
<Button label="Cut" />
|
|
888
|
+
<Button label="Paste" />
|
|
889
|
+
</ButtonGroup>,
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
expect(tabIndexOf('Copy')).toBe('-1');
|
|
893
|
+
expect(tabIndexOf('Cut')).toBe('0');
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it('repairs the tab stop when the member holding it unmounts', () => {
|
|
897
|
+
const {rerender} = render(
|
|
898
|
+
<ButtonGroup label="Actions">
|
|
899
|
+
<Button label="Copy" />
|
|
900
|
+
<Button label="Cut" />
|
|
901
|
+
</ButtonGroup>,
|
|
902
|
+
);
|
|
903
|
+
expect(tabIndexOf('Copy')).toBe('0');
|
|
904
|
+
|
|
905
|
+
rerender(
|
|
906
|
+
<ButtonGroup label="Actions">
|
|
907
|
+
<Button label="Cut" />
|
|
908
|
+
</ButtonGroup>,
|
|
909
|
+
);
|
|
910
|
+
|
|
911
|
+
expect(tabIndexOf('Cut')).toBe('0');
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
it('repairs the tab stop when the member holding it becomes disabled', () => {
|
|
915
|
+
const {rerender} = render(
|
|
916
|
+
<ButtonGroup label="Actions">
|
|
917
|
+
<Button label="Copy" />
|
|
918
|
+
<Button label="Cut" />
|
|
919
|
+
</ButtonGroup>,
|
|
920
|
+
);
|
|
921
|
+
expect(tabIndexOf('Copy')).toBe('0');
|
|
922
|
+
|
|
923
|
+
rerender(
|
|
924
|
+
<ButtonGroup label="Actions">
|
|
925
|
+
<Button label="Copy" isDisabled />
|
|
926
|
+
<Button label="Cut" />
|
|
927
|
+
</ButtonGroup>,
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
expect(tabIndexOf('Copy')).toBe('-1');
|
|
931
|
+
expect(tabIndexOf('Cut')).toBe('0');
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
it('counts a link member as the tab stop', async () => {
|
|
935
|
+
const user = userEvent.setup();
|
|
936
|
+
render(
|
|
937
|
+
bracketed(
|
|
938
|
+
<ButtonGroup label="Docs actions">
|
|
939
|
+
<Button label="Docs" href="https://example.com" />
|
|
940
|
+
<Button label="Print" />
|
|
941
|
+
</ButtonGroup>,
|
|
942
|
+
),
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
expect(
|
|
946
|
+
screen.getByRole('link', {name: 'Docs'}).getAttribute('tabindex'),
|
|
947
|
+
).toBe('0');
|
|
948
|
+
expect(tabIndexOf('Print')).toBe('-1');
|
|
949
|
+
|
|
950
|
+
await user.tab();
|
|
951
|
+
await user.tab();
|
|
952
|
+
expect(screen.getByRole('link', {name: 'Docs'})).toHaveFocus();
|
|
953
|
+
|
|
954
|
+
await user.tab();
|
|
955
|
+
expect(screen.getByRole('button', {name: 'after'})).toHaveFocus();
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
it('contributes no tab stop when the whole group is disabled', async () => {
|
|
959
|
+
const user = userEvent.setup();
|
|
960
|
+
render(
|
|
961
|
+
bracketed(
|
|
962
|
+
<ButtonGroup label="Actions" isDisabled>
|
|
963
|
+
<Button label="Copy" />
|
|
964
|
+
<Button label="Cut" />
|
|
965
|
+
</ButtonGroup>,
|
|
966
|
+
),
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
await user.tab();
|
|
970
|
+
expect(screen.getByRole('button', {name: 'before'})).toHaveFocus();
|
|
971
|
+
|
|
972
|
+
await user.tab();
|
|
973
|
+
expect(screen.getByRole('button', {name: 'after'})).toHaveFocus();
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
it('leaves the tab stop on its own members when a member owns an open menu', async () => {
|
|
977
|
+
const user = userEvent.setup();
|
|
978
|
+
render(
|
|
979
|
+
<ButtonGroup label="Approve action">
|
|
980
|
+
<Button label="Allow once" />
|
|
981
|
+
<DropdownMenu
|
|
982
|
+
hasChevron={false}
|
|
983
|
+
button={{label: 'Allow options', isIconOnly: true}}
|
|
984
|
+
items={[{label: 'Allow for 30 minutes'}, {label: 'Always allow'}]}
|
|
985
|
+
/>
|
|
986
|
+
</ButtonGroup>,
|
|
987
|
+
);
|
|
988
|
+
|
|
989
|
+
const trigger = screen.getByRole('button', {name: /Allow options/});
|
|
990
|
+
expect(tabIndexOf('Allow once')).toBe('0');
|
|
991
|
+
expect(trigger.getAttribute('tabindex')).toBe('-1');
|
|
992
|
+
|
|
993
|
+
await user.click(trigger);
|
|
994
|
+
|
|
995
|
+
// The menu's own items are a different list level (the boundary), so the
|
|
996
|
+
// group must not stamp them or hand them its tab stop.
|
|
997
|
+
const item = screen.getByRole('menuitem', {
|
|
998
|
+
name: 'Allow for 30 minutes',
|
|
999
|
+
hidden: true,
|
|
1000
|
+
});
|
|
1001
|
+
expect(item.getAttribute('tabindex')).not.toBe('0');
|
|
1002
|
+
expect(tabIndexOf('Allow once')).toBe('0');
|
|
1003
|
+
});
|
|
614
1004
|
});
|
|
615
1005
|
});
|