@astryxdesign/core 0.4.6 → 0.4.7-canary.019ae5a
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 +37 -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 +1 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +54 -15
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +6 -0
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetEdgeTint.js +62 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +1 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +1 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +13 -6
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +23 -5
- 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 +10 -3
- 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 -3
- 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 +72 -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 +10 -3
- package/dist/hooks/useListFocus.d.ts +5 -2
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +12 -6
- 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/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 +36 -4
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +225 -0
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +82 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +66 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +19 -0
- package/src/BottomSheet/BottomSheetSwitcher.tsx +16 -2
- package/src/BottomSheet/useSheetGestures.test.ts +27 -0
- package/src/BottomSheet/useSheetGestures.ts +25 -5
- 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.test.tsx +17 -0
- package/src/Kbd/Kbd.tsx +10 -3
- 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/Popover.test.tsx +27 -1
- 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 -3
- package/src/TabList/TabList.doc.mjs +22 -6
- package/src/TabList/TabList.test.tsx +580 -4
- 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.test.ts +18 -0
- package/src/hooks/useHotkeys.ts +10 -3
- package/src/hooks/useListFocus.doc.mjs +2 -2
- package/src/hooks/useListFocus.test.tsx +65 -3
- package/src/hooks/useListFocus.ts +15 -7
- 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/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
|
@@ -35,11 +35,12 @@ import {
|
|
|
35
35
|
spacingVars,
|
|
36
36
|
} from '../theme/tokens.stylex';
|
|
37
37
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
38
|
-
import {mergeProps,
|
|
38
|
+
import {mergeProps, rtlStyles} from '../utils';
|
|
39
39
|
import type {ResizableProps} from './useResizable';
|
|
40
40
|
import {themeProps} from '../utils/themeProps';
|
|
41
41
|
import {useTranslator} from '../i18n';
|
|
42
42
|
|
|
43
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
43
44
|
const KEYBOARD_STEP = 10;
|
|
44
45
|
const KEYBOARD_LARGE_STEP = 50;
|
|
45
46
|
|
|
@@ -549,7 +550,7 @@ export function ResizeHandle({
|
|
|
549
550
|
|
|
550
551
|
return (
|
|
551
552
|
<div
|
|
552
|
-
ref={
|
|
553
|
+
ref={useMergedRefs(ref, handleRef)}
|
|
553
554
|
role="separator"
|
|
554
555
|
aria-orientation={isHorizontal ? 'vertical' : 'horizontal'}
|
|
555
556
|
aria-valuenow={ariaValueNow}
|
|
@@ -26,11 +26,12 @@ import type {
|
|
|
26
26
|
SegmentedControlSize,
|
|
27
27
|
SegmentedControlLayout,
|
|
28
28
|
} from './SegmentedControlContext';
|
|
29
|
-
import {mergeProps,
|
|
29
|
+
import {mergeProps, composeEventHandlers} from '../utils';
|
|
30
30
|
import {useSize} from '../SizeContext/SizeContext';
|
|
31
31
|
import type {BaseProps} from '../BaseProps';
|
|
32
32
|
import {themeProps} from '../utils/themeProps';
|
|
33
33
|
|
|
34
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
34
35
|
export interface SegmentedControlProps extends Omit<
|
|
35
36
|
BaseProps<HTMLDivElement>,
|
|
36
37
|
'onChange'
|
|
@@ -258,7 +259,7 @@ export function SegmentedControl({
|
|
|
258
259
|
return (
|
|
259
260
|
<SegmentedControlContext value={contextValue}>
|
|
260
261
|
<div
|
|
261
|
-
ref={
|
|
262
|
+
ref={useMergedRefs(ref, listRef, disabledMessageTooltip.ref)}
|
|
262
263
|
{...rest}
|
|
263
264
|
role="radiogroup"
|
|
264
265
|
aria-label={label}
|
|
@@ -40,7 +40,7 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
40
40
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
41
41
|
import {colorVars, durationVars, easeVars} from '../theme/tokens.stylex';
|
|
42
42
|
import type {Elevation, SizeValue, SpacingStep} from '../utils/types';
|
|
43
|
-
import {mergeProps
|
|
43
|
+
import {mergeProps} from '../utils';
|
|
44
44
|
import {Card} from '../Card/Card';
|
|
45
45
|
import type {CardVariant} from '../Card/Card';
|
|
46
46
|
import {useClickableContainer} from '../hooks/useClickableContainer';
|
|
@@ -48,6 +48,7 @@ import type {BaseProps} from '../BaseProps';
|
|
|
48
48
|
import {themeProps} from '../utils/themeProps';
|
|
49
49
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
50
50
|
|
|
51
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
51
52
|
// =============================================================================
|
|
52
53
|
// Styles — selection + interaction; Card handles the rest
|
|
53
54
|
// =============================================================================
|
|
@@ -346,7 +347,7 @@ export function SelectableCard({
|
|
|
346
347
|
|
|
347
348
|
return (
|
|
348
349
|
<Card
|
|
349
|
-
ref={
|
|
350
|
+
ref={useMergedRefs(ref, containerRef)}
|
|
350
351
|
width={width}
|
|
351
352
|
height={height}
|
|
352
353
|
maxWidth={maxWidth}
|
|
@@ -171,7 +171,7 @@ export const docs = {
|
|
|
171
171
|
name: 'renderValue',
|
|
172
172
|
type: '(option: SelectorOptionData) => ReactNode',
|
|
173
173
|
description:
|
|
174
|
-
'Custom render function for the selected option inside the closed trigger. The trigger is sized by padding, so it is the size token for a one-line value (28/32/36) and exactly one text line taller for a two-line one (48/52/56)
|
|
174
|
+
'Custom render function for the selected option inside the closed trigger. The trigger is sized by padding, so it is the size token for a one-line value (28/32/36) and exactly one text line taller for a two-line one (48/52/56), always on the 4px rhythm, always aligned with the buttons and inputs beside it. Inside an InputGroup the group owns the row height: a SelectorOption folds onto one line and ellipsizes, and any taller node is cut off at the row.',
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
name: 'indicatorPosition',
|
|
@@ -186,6 +186,17 @@ export const docs = {
|
|
|
186
186
|
description:
|
|
187
187
|
'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
|
|
188
188
|
},
|
|
189
|
+
{
|
|
190
|
+
name: 'startIcon',
|
|
191
|
+
type: 'IconType | ReactNode',
|
|
192
|
+
description: 'Icon displayed at the start of the selector trigger.',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'isLoading',
|
|
196
|
+
type: 'boolean',
|
|
197
|
+
description: 'Shows a loading spinner in the trigger.',
|
|
198
|
+
default: 'false',
|
|
199
|
+
},
|
|
189
200
|
{
|
|
190
201
|
name: 'xstyle',
|
|
191
202
|
type: 'StyleXStyles',
|
|
@@ -961,7 +961,7 @@ describe('Selector', () => {
|
|
|
961
961
|
expect(onChange).toHaveBeenCalledWith('Banana');
|
|
962
962
|
});
|
|
963
963
|
|
|
964
|
-
it('closes dropdown on Tab without preventing default focus movement', async () => {
|
|
964
|
+
it('closes the search dropdown on Tab without preventing default focus movement', async () => {
|
|
965
965
|
const user = userEvent.setup();
|
|
966
966
|
render(
|
|
967
967
|
<>
|
|
@@ -1253,6 +1253,29 @@ describe('Selector', () => {
|
|
|
1253
1253
|
});
|
|
1254
1254
|
|
|
1255
1255
|
describe('keyboard accessibility', () => {
|
|
1256
|
+
it('Tab from the open listbox moves focus to the next control', async () => {
|
|
1257
|
+
const user = userEvent.setup();
|
|
1258
|
+
render(
|
|
1259
|
+
<>
|
|
1260
|
+
<Selector
|
|
1261
|
+
label="Fruit"
|
|
1262
|
+
options={OPTIONS}
|
|
1263
|
+
value="Apple"
|
|
1264
|
+
onChange={() => {}}
|
|
1265
|
+
/>
|
|
1266
|
+
<button type="button">Next</button>
|
|
1267
|
+
</>,
|
|
1268
|
+
);
|
|
1269
|
+
|
|
1270
|
+
const trigger = screen.getByRole('combobox');
|
|
1271
|
+
await user.click(trigger);
|
|
1272
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
1273
|
+
|
|
1274
|
+
await user.keyboard('{Tab}');
|
|
1275
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
|
1276
|
+
expect(screen.getByRole('button', {name: 'Next'})).toHaveFocus();
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1256
1279
|
it('trigger is focusable via Tab when enabled', async () => {
|
|
1257
1280
|
const user = userEvent.setup();
|
|
1258
1281
|
render(<Selector label="Fruit" options={OPTIONS} />);
|
|
@@ -2484,6 +2507,27 @@ describe('Selector section headings', () => {
|
|
|
2484
2507
|
// visible heading must not announce it a second time.
|
|
2485
2508
|
expect(heading).toHaveAttribute('aria-hidden', 'true');
|
|
2486
2509
|
});
|
|
2510
|
+
|
|
2511
|
+
it('hides a standalone divider from the accessibility tree (#4994)', async () => {
|
|
2512
|
+
const user = userEvent.setup();
|
|
2513
|
+
render(
|
|
2514
|
+
<Selector
|
|
2515
|
+
label="Fruit"
|
|
2516
|
+
options={['Apple', {type: 'divider'}, 'Banana']}
|
|
2517
|
+
value={undefined}
|
|
2518
|
+
onChange={() => {}}
|
|
2519
|
+
/>,
|
|
2520
|
+
);
|
|
2521
|
+
await user.click(screen.getByRole('combobox'));
|
|
2522
|
+
|
|
2523
|
+
// role="listbox" only permits option/group children. The divider still
|
|
2524
|
+
// renders role="separator" (unchanged visual/DOM), but must be excluded
|
|
2525
|
+
// from the accessibility tree so it never reaches the listbox's exposed
|
|
2526
|
+
// children (axe aria-required-children).
|
|
2527
|
+
const divider = document.querySelector('[role="separator"]');
|
|
2528
|
+
expect(divider).toBeTruthy();
|
|
2529
|
+
expect(divider).toHaveAttribute('aria-hidden', 'true');
|
|
2530
|
+
});
|
|
2487
2531
|
});
|
|
2488
2532
|
|
|
2489
2533
|
describe('Selector search focus ring', () => {
|
|
@@ -1344,7 +1344,17 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1344
1344
|
if (isSearching) {
|
|
1345
1345
|
continue;
|
|
1346
1346
|
}
|
|
1347
|
-
|
|
1347
|
+
// role="listbox" only permits option/group children; the divider
|
|
1348
|
+
// carries no information the options don't, so it's hidden from the
|
|
1349
|
+
// accessibility tree entirely rather than exposing role="separator"
|
|
1350
|
+
// as a disallowed listbox child (axe aria-required-children).
|
|
1351
|
+
elements.push(
|
|
1352
|
+
<Divider
|
|
1353
|
+
key={`divider-${i}`}
|
|
1354
|
+
aria-hidden="true"
|
|
1355
|
+
xstyle={styles.divider}
|
|
1356
|
+
/>,
|
|
1357
|
+
);
|
|
1348
1358
|
} else if (isSection(option)) {
|
|
1349
1359
|
const sectionItems: ReactNode[] = [];
|
|
1350
1360
|
for (const opt of option.options) {
|
|
@@ -131,8 +131,8 @@ export const docs = {
|
|
|
131
131
|
bestPractices: [
|
|
132
132
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
133
133
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
134
|
-
{guidance: true, description: 'Mark the current page with isSelected
|
|
135
|
-
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short
|
|
134
|
+
{guidance: true, description: 'Mark the current page with isSelected: it sets aria-current="page", so the current destination is announced rather than carried by color alone.'},
|
|
135
|
+
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short; they are the accessible name in both expanded and icon-only modes.'},
|
|
136
136
|
{guidance: true, description: 'While the nav is collapsed, an item with children shows them in a submenu flyout. On a device that can hover, pointing at the item opens it after a short delay and moving away closes it; a flyout opened by clicking stays open until it is dismissed. On touch, it opens on tap. Do not put an action in there that has no other route to it.'},
|
|
137
137
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
138
138
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
@@ -153,8 +153,8 @@ export const docsZh = {
|
|
|
153
153
|
bestPractices: [
|
|
154
154
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
155
155
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
156
|
-
{guidance: true, description: 'Mark the current page with isSelected
|
|
157
|
-
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short
|
|
156
|
+
{guidance: true, description: 'Mark the current page with isSelected: it sets aria-current="page", so the current destination is announced rather than carried by color alone.'},
|
|
157
|
+
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short; they are the accessible name in both expanded and icon-only modes.'},
|
|
158
158
|
{guidance: true, description: 'While the nav is collapsed, an item with children shows them in a submenu flyout. On a device that can hover, pointing at the item opens it after a short delay and moving away closes it; a flyout opened by clicking stays open until it is dismissed. On touch, it opens on tap. Do not put an action in there that has no other route to it.'},
|
|
159
159
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
160
160
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
@@ -175,8 +175,8 @@ export const docsDense = {
|
|
|
175
175
|
bestPractices: [
|
|
176
176
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
177
177
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
178
|
-
{guidance: true, description: 'Mark the current page with isSelected
|
|
179
|
-
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short
|
|
178
|
+
{guidance: true, description: 'Mark the current page with isSelected: it sets aria-current="page", so the current destination is announced rather than carried by color alone.'},
|
|
179
|
+
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short; they are the accessible name in both expanded and icon-only modes.'},
|
|
180
180
|
{guidance: true, description: 'While the nav is collapsed, an item with children shows them in a submenu flyout. On a device that can hover, pointing at the item opens it after a short delay and moving away closes it; a flyout opened by clicking stays open until it is dismissed. On touch, it opens on tap. Do not put an action in there that has no other route to it.'},
|
|
181
181
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
182
182
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
package/src/SideNav/SideNav.tsx
CHANGED
|
@@ -32,7 +32,7 @@ import type {BaseProps} from '../BaseProps';
|
|
|
32
32
|
import * as stylex from '@stylexjs/stylex';
|
|
33
33
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
34
34
|
import {spacingVars} from '../theme/tokens.stylex';
|
|
35
|
-
import {mergeProps
|
|
35
|
+
import {mergeProps} from '../utils';
|
|
36
36
|
import {
|
|
37
37
|
SideNavCollapseContext,
|
|
38
38
|
type SideNavCollapseState,
|
|
@@ -49,6 +49,7 @@ import {themeProps} from '../utils/themeProps';
|
|
|
49
49
|
import {SizeProvider} from '../SizeContext/SizeContext';
|
|
50
50
|
import {useTranslator} from '../i18n';
|
|
51
51
|
|
|
52
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
52
53
|
// =============================================================================
|
|
53
54
|
// Constants
|
|
54
55
|
// =============================================================================
|
|
@@ -349,6 +350,7 @@ export function SideNav({
|
|
|
349
350
|
useState(defaultIsCollapsed);
|
|
350
351
|
const collapsed = isControlled ? controlledCollapsed : uncontrolledCollapsed;
|
|
351
352
|
const navRef = useRef<HTMLElement>(null);
|
|
353
|
+
const mergedNavRef = useMergedRefs(ref, navRef);
|
|
352
354
|
const collapseStateRef = useRef<SideNavCollapseState>({
|
|
353
355
|
isCollapsed: collapsed,
|
|
354
356
|
toggle: () => {},
|
|
@@ -517,7 +519,7 @@ export function SideNav({
|
|
|
517
519
|
|
|
518
520
|
const navElement = (
|
|
519
521
|
<nav
|
|
520
|
-
ref={
|
|
522
|
+
ref={mergedNavRef}
|
|
521
523
|
role="navigation"
|
|
522
524
|
aria-label={t('@astryx.sideNav.label')}
|
|
523
525
|
data-testid={testId}
|
|
@@ -40,7 +40,8 @@ import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
|
40
40
|
import {useSideNavCollapse} from './SideNavCollapseContext';
|
|
41
41
|
import {useLinkComponent} from '../Link/useLinkComponent';
|
|
42
42
|
import type {LinkComponentType} from '../Link/types';
|
|
43
|
-
import {mergeProps
|
|
43
|
+
import {mergeProps} from '../utils';
|
|
44
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
44
45
|
import type {BaseProps} from '../BaseProps';
|
|
45
46
|
import {useMenuHover} from '../hooks/useMenuHover';
|
|
46
47
|
import {NavHeadingCloseContext} from '../NavMenu/NavMenuContext';
|
|
@@ -399,11 +400,18 @@ export function SideNavHeading({
|
|
|
399
400
|
// setTriggerEl belongs on the chevron button, not this root: it is the
|
|
400
401
|
// focus-restore target and a <div> cannot take focus. triggerRef stays here
|
|
401
402
|
// because the panel anchors to the whole heading.
|
|
402
|
-
const setRef =
|
|
403
|
+
const setRef = useMergedRefs<HTMLDivElement>(
|
|
403
404
|
rootRef,
|
|
404
405
|
ref,
|
|
405
406
|
menu ? popover.triggerRef : undefined,
|
|
406
407
|
);
|
|
408
|
+
const collapsedSetRef = useMergedRefs<HTMLElement>(
|
|
409
|
+
collapsedItemRef,
|
|
410
|
+
ref,
|
|
411
|
+
// Collapsed, this button is the trigger, so it takes both roles.
|
|
412
|
+
menu ? popover.triggerRef : undefined,
|
|
413
|
+
menu ? setTriggerEl : undefined,
|
|
414
|
+
);
|
|
407
415
|
|
|
408
416
|
// In collapsed mode: hide if no icon, show icon-only if has icon
|
|
409
417
|
if (isCollapsed && !icon) {
|
|
@@ -412,14 +420,6 @@ export function SideNavHeading({
|
|
|
412
420
|
if (isCollapsed && icon) {
|
|
413
421
|
const collapsedIcon = <span {...stylex.props(styles.icon)}>{icon}</span>;
|
|
414
422
|
|
|
415
|
-
const collapsedSetRef = mergeRefs<HTMLElement>(
|
|
416
|
-
collapsedItemRef,
|
|
417
|
-
ref,
|
|
418
|
-
// Collapsed, this button is the trigger, so it takes both roles.
|
|
419
|
-
menu ? popover.triggerRef : undefined,
|
|
420
|
-
menu ? setTriggerEl : undefined,
|
|
421
|
-
);
|
|
422
|
-
|
|
423
423
|
let collapsedElement: ReactNode;
|
|
424
424
|
|
|
425
425
|
if (headingHref) {
|
|
@@ -47,7 +47,7 @@ import {useLinkComponent} from '../Link/useLinkComponent';
|
|
|
47
47
|
import type {LinkComponentType} from '../Link/types';
|
|
48
48
|
import {usePopover} from '../Popover/usePopover';
|
|
49
49
|
import {useMenuHover} from '../hooks/useMenuHover';
|
|
50
|
-
import {mergeProps
|
|
50
|
+
import {mergeProps} from '../utils';
|
|
51
51
|
import type {BaseProps} from '../BaseProps';
|
|
52
52
|
import {Tooltip} from '../Tooltip';
|
|
53
53
|
import {navItemStyles, type NavItemSize} from '../NavItem/navItemStyles.stylex';
|
|
@@ -61,6 +61,7 @@ import {useAppShellMobile} from '../AppShell/AppShellMobileContext';
|
|
|
61
61
|
import {themeProps} from '../utils/themeProps';
|
|
62
62
|
import {useTranslator} from '../i18n';
|
|
63
63
|
|
|
64
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
64
65
|
// =============================================================================
|
|
65
66
|
// Styles
|
|
66
67
|
// =============================================================================
|
|
@@ -408,6 +409,7 @@ export function SideNavItem({
|
|
|
408
409
|
hasCloseButton: false,
|
|
409
410
|
dialogLabel: t('@astryx.sideNavItem.submenuLabel', {label}),
|
|
410
411
|
});
|
|
412
|
+
const mergedTriggerRef = useMergedRefs(ref, popover.triggerRef);
|
|
411
413
|
|
|
412
414
|
// Collapse state for items with children
|
|
413
415
|
const itemCollapsibleConfig = useMemo(
|
|
@@ -524,7 +526,7 @@ export function SideNavItem({
|
|
|
524
526
|
return (
|
|
525
527
|
<div {...stylex.props(styles.root, xstyle)}>
|
|
526
528
|
<button
|
|
527
|
-
ref={
|
|
529
|
+
ref={mergedTriggerRef}
|
|
528
530
|
type="button"
|
|
529
531
|
{...rest}
|
|
530
532
|
{...hoverTriggerProps}
|
package/src/Slider/Slider.tsx
CHANGED
|
@@ -40,13 +40,14 @@ import {Tooltip} from '../Tooltip/Tooltip';
|
|
|
40
40
|
import {useTooltip} from '../Tooltip';
|
|
41
41
|
import {VisuallyHidden} from '../VisuallyHidden';
|
|
42
42
|
import type {InputStatus} from '../Field/types';
|
|
43
|
-
import {mergeProps,
|
|
43
|
+
import {mergeProps, rtlStyles} from '../utils';
|
|
44
44
|
import {focusOutlineStyles} from '../utils/focusOutline.stylex';
|
|
45
45
|
import {isRtlElement} from '../hooks/isRtlElement';
|
|
46
46
|
import type {BaseProps} from '../BaseProps';
|
|
47
47
|
import type {SizeValue} from '../utils/types';
|
|
48
48
|
import {themeProps} from '../utils/themeProps';
|
|
49
49
|
|
|
50
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
50
51
|
// =============================================================================
|
|
51
52
|
// Types
|
|
52
53
|
// =============================================================================
|
|
@@ -964,7 +965,7 @@ export function Slider({ref, ...props}: SliderProps) {
|
|
|
964
965
|
/>
|
|
965
966
|
))}
|
|
966
967
|
<div
|
|
967
|
-
ref={
|
|
968
|
+
ref={useMergedRefs(ref, trackRef, disabledMessageTooltip.ref)}
|
|
968
969
|
{...(isRange
|
|
969
970
|
? {role: 'group', 'aria-labelledby': labelID}
|
|
970
971
|
: undefined)}
|
|
@@ -9,10 +9,29 @@
|
|
|
9
9
|
* SYNC: When Spinner.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {describe, it, expect} from 'vitest';
|
|
12
|
+
import {describe, it, expect, vi, afterEach} from 'vitest';
|
|
13
13
|
import {render, screen} from '@testing-library/react';
|
|
14
14
|
import {Spinner} from './Spinner';
|
|
15
15
|
|
|
16
|
+
/** sm/md/lg/xl, as Spinner.tsx defines them. */
|
|
17
|
+
const SIZES = {
|
|
18
|
+
sm: {diameter: 10, border: 2},
|
|
19
|
+
md: {diameter: 14, border: 3},
|
|
20
|
+
lg: {diameter: 18, border: 3},
|
|
21
|
+
xl: {diameter: 28, border: 4},
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
const ring = (testId = 'spinner') =>
|
|
25
|
+
screen.getByTestId(testId).querySelector('svg') as SVGSVGElement;
|
|
26
|
+
|
|
27
|
+
const circles = (testId = 'spinner') => {
|
|
28
|
+
const [track, arc] = [...ring(testId).querySelectorAll('circle')];
|
|
29
|
+
return {track, arc};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const dashOf = (el: SVGCircleElement) =>
|
|
33
|
+
(el.getAttribute('stroke-dasharray') ?? '').split(/[\s,]+/).map(Number);
|
|
34
|
+
|
|
16
35
|
describe('Spinner', () => {
|
|
17
36
|
it('renders with default props', () => {
|
|
18
37
|
render(<Spinner data-testid="spinner" />);
|
|
@@ -122,3 +141,130 @@ describe('Spinner', () => {
|
|
|
122
141
|
expect(spinner.tagName.toLowerCase()).toBe('span');
|
|
123
142
|
});
|
|
124
143
|
});
|
|
144
|
+
|
|
145
|
+
describe('Spinner ring', () => {
|
|
146
|
+
afterEach(() => {
|
|
147
|
+
vi.restoreAllMocks();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('draws the ring in SVG', () => {
|
|
151
|
+
render(<Spinner data-testid="spinner" />);
|
|
152
|
+
const svg = ring();
|
|
153
|
+
expect(svg).not.toBeNull();
|
|
154
|
+
expect(svg.querySelectorAll('circle')).toHaveLength(2);
|
|
155
|
+
expect(screen.getByTestId('spinner').querySelector('canvas')).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it.each(Object.entries(SIZES))(
|
|
159
|
+
'sizes the %s ring from its size token',
|
|
160
|
+
(size, {diameter, border}) => {
|
|
161
|
+
render(
|
|
162
|
+
<Spinner size={size as keyof typeof SIZES} data-testid="spinner" />,
|
|
163
|
+
);
|
|
164
|
+
const frame = diameter + border * 2;
|
|
165
|
+
const svg = ring();
|
|
166
|
+
expect(svg.getAttribute('viewBox')).toBe(`0 0 ${frame} ${frame}`);
|
|
167
|
+
for (const c of [circles().track, circles().arc]) {
|
|
168
|
+
expect(Number(c.getAttribute('cx'))).toBe(frame / 2);
|
|
169
|
+
expect(Number(c.getAttribute('cy'))).toBe(frame / 2);
|
|
170
|
+
expect(Number(c.getAttribute('r'))).toBe(diameter / 2);
|
|
171
|
+
expect(Number(c.getAttribute('stroke-width'))).toBe(border);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// The canvas ring swept 135deg, not the 270deg its SPREAD comment claimed.
|
|
177
|
+
it.each(Object.entries(SIZES))(
|
|
178
|
+
'sweeps 135 degrees at %s',
|
|
179
|
+
(size, {diameter}) => {
|
|
180
|
+
render(
|
|
181
|
+
<Spinner size={size as keyof typeof SIZES} data-testid="spinner" />,
|
|
182
|
+
);
|
|
183
|
+
const {track, arc} = circles();
|
|
184
|
+
const [on, off] = dashOf(arc);
|
|
185
|
+
expect(on + off).toBeCloseTo(Math.PI * diameter, 6);
|
|
186
|
+
expect((on / (on + off)) * 360).toBeCloseTo(135, 6);
|
|
187
|
+
expect(track.getAttribute('stroke-dasharray')).toBeNull();
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
it('starts the arc at twelve o’clock', () => {
|
|
192
|
+
render(<Spinner data-testid="spinner" />);
|
|
193
|
+
const frame = SIZES.md.diameter + SIZES.md.border * 2;
|
|
194
|
+
expect(circles().arc.getAttribute('transform')).toBe(
|
|
195
|
+
`rotate(-90 ${frame / 2} ${frame / 2})`,
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// The whole point of the SVG ring: the colours come off the cascade, so
|
|
200
|
+
// nothing has to resolve them in JS. A read reaching the paint path again
|
|
201
|
+
// fails here.
|
|
202
|
+
it.each(['default', 'subtle', 'onMedia', 'inherit'] as const)(
|
|
203
|
+
'reads no computed style to paint the %s shade',
|
|
204
|
+
shade => {
|
|
205
|
+
const spy = vi.spyOn(window, 'getComputedStyle');
|
|
206
|
+
render(<Spinner shade={shade} data-testid="spinner" />);
|
|
207
|
+
const spinner = screen.getByTestId('spinner');
|
|
208
|
+
const onSpinner = spy.mock.calls.filter(
|
|
209
|
+
([el]) => el instanceof Element && spinner.contains(el),
|
|
210
|
+
);
|
|
211
|
+
expect(onSpinner).toEqual([]);
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
it('schedules no frame where the Web Animations API is absent', () => {
|
|
216
|
+
expect(SVGElement.prototype).not.toHaveProperty('getAnimations');
|
|
217
|
+
const raf = vi.spyOn(window, 'requestAnimationFrame');
|
|
218
|
+
render(<Spinner data-testid="spinner" />);
|
|
219
|
+
expect(ring()).not.toBeNull();
|
|
220
|
+
expect(raf).not.toHaveBeenCalled();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('pins every ring to the timeline origin in a single frame', () => {
|
|
224
|
+
const animations: {startTime: number | null}[] = [];
|
|
225
|
+
const getAnimations = vi.fn(function (this: SVGElement) {
|
|
226
|
+
const a = {startTime: null as number | null};
|
|
227
|
+
animations.push(a);
|
|
228
|
+
return [a];
|
|
229
|
+
});
|
|
230
|
+
Object.defineProperty(SVGElement.prototype, 'getAnimations', {
|
|
231
|
+
value: getAnimations,
|
|
232
|
+
configurable: true,
|
|
233
|
+
writable: true,
|
|
234
|
+
});
|
|
235
|
+
const frames: FrameRequestCallback[] = [];
|
|
236
|
+
const raf = vi
|
|
237
|
+
.spyOn(window, 'requestAnimationFrame')
|
|
238
|
+
.mockImplementation(cb => {
|
|
239
|
+
frames.push(cb);
|
|
240
|
+
return frames.length;
|
|
241
|
+
});
|
|
242
|
+
const computed = vi.spyOn(window, 'getComputedStyle');
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const {container} = render(
|
|
246
|
+
<>
|
|
247
|
+
{Array.from({length: 5}, (_, i) => (
|
|
248
|
+
<Spinner key={i} />
|
|
249
|
+
))}
|
|
250
|
+
</>,
|
|
251
|
+
);
|
|
252
|
+
// One frame for five spinners: the reads are batched, so no mount
|
|
253
|
+
// re-forces what the previous mount invalidated.
|
|
254
|
+
expect(raf).toHaveBeenCalledTimes(1);
|
|
255
|
+
frames.forEach(cb => cb(0));
|
|
256
|
+
expect(animations).toHaveLength(5);
|
|
257
|
+
expect(animations.every(a => a.startTime === 0)).toBe(true);
|
|
258
|
+
// The pin runs on the branch the other shade cases cannot reach, so the
|
|
259
|
+
// no-read assertion is made here too.
|
|
260
|
+
expect(
|
|
261
|
+
computed.mock.calls.filter(
|
|
262
|
+
([el]) => el instanceof Element && container.contains(el),
|
|
263
|
+
),
|
|
264
|
+
).toEqual([]);
|
|
265
|
+
} finally {
|
|
266
|
+
// @ts-expect-error — removing the stub restores jsdom's real surface
|
|
267
|
+
delete SVGElement.prototype.getAnimations;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|