@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
|
@@ -28,7 +28,7 @@ import {useId, useMemo, useState, type ReactNode} from 'react';
|
|
|
28
28
|
import type {BaseProps} from '../BaseProps';
|
|
29
29
|
import {resolveSize, type AvatarSize} from '../Avatar';
|
|
30
30
|
import * as stylex from '@stylexjs/stylex';
|
|
31
|
-
import {mergeProps
|
|
31
|
+
import {mergeProps} from '../utils';
|
|
32
32
|
import {composeEventHandlers} from '../utils/composeEventHandlers';
|
|
33
33
|
import {AvatarGroupContext} from './AvatarGroupContext';
|
|
34
34
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -37,6 +37,7 @@ import {useListFocus} from '../hooks/useListFocus';
|
|
|
37
37
|
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
38
38
|
import {VisuallyHidden} from '../VisuallyHidden';
|
|
39
39
|
|
|
40
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
40
41
|
const OVERLAP_RATIO = 0.25;
|
|
41
42
|
|
|
42
43
|
export interface AvatarGroupProps extends BaseProps<HTMLDivElement> {
|
|
@@ -143,7 +144,7 @@ export function AvatarGroup({
|
|
|
143
144
|
<AvatarGroupContext value={contextValue}>
|
|
144
145
|
<div
|
|
145
146
|
{...props}
|
|
146
|
-
ref={
|
|
147
|
+
ref={useMergedRefs(ref, listRef)}
|
|
147
148
|
role="group"
|
|
148
149
|
aria-label={ariaLabel}
|
|
149
150
|
aria-describedby={describedBy}
|
|
@@ -17,6 +17,7 @@ export const docs = {
|
|
|
17
17
|
{guidance: true, description: 'Keep titles short and scannable: "Payment failed" not "There was a problem processing your most recent payment."'},
|
|
18
18
|
{guidance: false, description: 'Use Banner for short-lived messages that disappear on their own; use Toast instead.'},
|
|
19
19
|
{guidance: false, description: 'Stack multiple banners with the same status; combine related messages into one banner.'},
|
|
20
|
+
{guidance: true, description: 'Set collapsible={false} when the user needs the content to act on the message, like the list of fields that failed validation. Keep the default toggle when the detail is long enough to bury the banner\u2019s own message.'},
|
|
20
21
|
{guidance: true, description: 'Error and warning banners render as role="alert"; info and success render as role="status". Mount an alert banner in response to an event rather than on first paint, so assistive tech has a change to report.'},
|
|
21
22
|
{guidance: false, description: 'Rely on the status color or icon alone to carry meaning; say which status it is in the title text, because the icon is decorative to a screen reader.'},
|
|
22
23
|
],
|
|
@@ -26,7 +27,7 @@ export const docs = {
|
|
|
26
27
|
{name: 'Description', required: false, description: 'Additional detail below the title.'},
|
|
27
28
|
{name: 'Action button', required: false, description: 'A button for the user to act on the message, like "Review" or "Retry".'},
|
|
28
29
|
{name: 'Dismiss button', required: false, description: 'Lets the user close the banner. Enabled by setting isDismissable.'},
|
|
29
|
-
{name: '
|
|
30
|
+
{name: 'Content', required: false, description: 'Extra detail below the banner header, like a list of errors. Sits behind an expand/collapse toggle by default; set collapsible={false} to keep it visible.'},
|
|
30
31
|
],
|
|
31
32
|
},
|
|
32
33
|
|
|
@@ -94,14 +95,14 @@ export const docs = {
|
|
|
94
95
|
name: 'children',
|
|
95
96
|
type: 'ReactNode',
|
|
96
97
|
description:
|
|
97
|
-
'Content rendered in the card-background area below the colored header.',
|
|
98
|
+
'Content rendered in the card-background area below the colored header. Sits behind an expand/collapse toggle unless `collapsible={false}`.',
|
|
98
99
|
},
|
|
99
100
|
{
|
|
100
|
-
name: '
|
|
101
|
-
type: 'boolean',
|
|
101
|
+
name: 'collapsible',
|
|
102
|
+
type: 'boolean | {defaultIsOpen?: boolean; isOpen?: boolean; onOpenChange?: (isOpen: boolean) => void}',
|
|
102
103
|
description:
|
|
103
|
-
'Whether the content area (children)
|
|
104
|
-
default: '
|
|
104
|
+
'Whether the content area (children) sits behind an expand/collapse toggle in the header. On by default, starting collapsed. `false` opts out: children are always visible with no toggle. `{defaultIsOpen: true}` starts open; `{isOpen, onOpenChange}` is controlled. Takes the same CollapsibleConfig as Collapsible.',
|
|
105
|
+
default: 'true',
|
|
105
106
|
},
|
|
106
107
|
{
|
|
107
108
|
name: 'xstyle',
|
|
@@ -154,7 +155,7 @@ export const docsZh = {
|
|
|
154
155
|
{name: 'Description', required: false, description: 'Additional detail below the title.'},
|
|
155
156
|
{name: 'Action button', required: false, description: 'A button for the user to act on the message, like "Review" or "Retry".'},
|
|
156
157
|
{name: 'Dismiss button', required: false, description: 'Lets the user close the banner. Enabled by setting isDismissable.'},
|
|
157
|
-
{name: '
|
|
158
|
+
{name: 'Content', required: false, description: 'Extra detail below the banner header, like a list of errors. Sits behind an expand/collapse toggle by default; set collapsible={false} to keep it visible.'},
|
|
158
159
|
],
|
|
159
160
|
},
|
|
160
161
|
props: [
|
|
@@ -167,8 +168,8 @@ export const docsZh = {
|
|
|
167
168
|
{name: 'endContent', type: 'ReactNode', description: '渲染在头部区域末端对齐的操作内容,通常是按钮或链接。头部过窄时会整体换行到文本下方,自成一行。'},
|
|
168
169
|
{name: 'container', type: "'card' | 'section'", description: '视觉变体:card 带圆角;section 无圆角全宽,适用于页面级场景。', default: "'card'"},
|
|
169
170
|
{name: 'elevation', type: "'none' | 'low' | 'med' | 'high'", description: '静止阴影深度。用于悬浮于内容之上的浮动横幅;none 为默认内联横幅。', default: "'none'"},
|
|
170
|
-
{name: 'children', type: 'ReactNode', description: '
|
|
171
|
-
{name: '
|
|
171
|
+
{name: 'children', type: 'ReactNode', description: '渲染在彩色头部下方卡片背景区域的内容。默认位于展开/折叠开关之后,除非设置 collapsible={false}。'},
|
|
172
|
+
{name: 'collapsible', type: 'boolean | {defaultIsOpen?: boolean; isOpen?: boolean; onOpenChange?: (isOpen: boolean) => void}', description: '内容区域(children)是否位于头部的展开/折叠开关之后。默认开启,且初始为折叠状态。false 表示关闭:内容始终可见且没有开关。{defaultIsOpen: true} 表示初始展开;{isOpen, onOpenChange} 为受控模式。与 Collapsible 使用同一套 CollapsibleConfig。', default: 'true'},
|
|
172
173
|
{
|
|
173
174
|
name: 'xstyle',
|
|
174
175
|
type: 'StyleXStyles',
|
|
@@ -230,7 +231,7 @@ export const docsDense = {
|
|
|
230
231
|
{name: 'Description', required: false, description: 'Detail below title.'},
|
|
231
232
|
{name: 'Action button', required: false, description: 'CTA like Review or Retry.'},
|
|
232
233
|
{name: 'Dismiss button', required: false, description: 'Close button via isDismissable.'},
|
|
233
|
-
{name: '
|
|
234
|
+
{name: 'Content', required: false, description: 'Detail area; behind a toggle unless collapsible={false}.'},
|
|
234
235
|
],
|
|
235
236
|
},
|
|
236
237
|
propDescriptions: {
|
|
@@ -243,7 +244,8 @@ export const docsDense = {
|
|
|
243
244
|
endContent: 'end-aligned action in header, typically button/link; wraps to its own row when the header is too narrow',
|
|
244
245
|
container: 'card=border-radius; section=full-width no radius for page-level',
|
|
245
246
|
elevation: 'resting shadow depth: none|low|med|high; raise for a floating banner',
|
|
246
|
-
children: 'content in card-bg area below colored header',
|
|
247
|
+
children: 'content in card-bg area below colored header; behind a toggle unless collapsible={false}',
|
|
248
|
+
collapsible: 'children behind a header toggle; default true=collapsed. false=always visible, no toggle. {defaultIsOpen:true}=starts open, {isOpen,onOpenChange}=controlled',
|
|
247
249
|
xstyle: 'StyleX layout customization via stylex.create()',
|
|
248
250
|
},
|
|
249
251
|
};
|
|
@@ -184,33 +184,84 @@ describe('Banner', () => {
|
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
// =========================================================================
|
|
187
|
-
//
|
|
187
|
+
// Content area — collapsible by default, `collapsible={false}` opts out
|
|
188
188
|
// =========================================================================
|
|
189
189
|
|
|
190
|
-
it('hides children by default
|
|
190
|
+
it('hides children behind a toggle by default', () => {
|
|
191
191
|
render(
|
|
192
192
|
<Banner status="info" title="Collapsible">
|
|
193
193
|
<div data-testid="child-content">Extra content</div>
|
|
194
194
|
</Banner>,
|
|
195
195
|
);
|
|
196
|
+
// The historical default, unchanged: chevron present, content collapsed.
|
|
196
197
|
expect(screen.queryByTestId('child-content')).not.toBeInTheDocument();
|
|
198
|
+
expect(screen.getByRole('button', {name: 'Expand'})).toBeInTheDocument();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('treats an explicit collapsible={true} as the default', () => {
|
|
202
|
+
render(
|
|
203
|
+
<Banner status="info" title="Explicit" collapsible>
|
|
204
|
+
<div data-testid="child-content">Extra content</div>
|
|
205
|
+
</Banner>,
|
|
206
|
+
);
|
|
207
|
+
expect(screen.queryByTestId('child-content')).not.toBeInTheDocument();
|
|
208
|
+
expect(screen.getByRole('button', {name: 'Expand'})).toBeInTheDocument();
|
|
197
209
|
});
|
|
198
210
|
|
|
199
|
-
it('shows children
|
|
211
|
+
it('shows children with no toggle for collapsible={false}', () => {
|
|
200
212
|
render(
|
|
201
|
-
<Banner status="info" title="
|
|
213
|
+
<Banner status="info" title="Opted out" collapsible={false}>
|
|
202
214
|
<div data-testid="child-content">Extra content</div>
|
|
203
215
|
</Banner>,
|
|
204
216
|
);
|
|
205
217
|
expect(screen.getByTestId('child-content')).toBeInTheDocument();
|
|
218
|
+
expect(
|
|
219
|
+
screen.queryByRole('button', {name: 'Expand'}),
|
|
220
|
+
).not.toBeInTheDocument();
|
|
221
|
+
expect(
|
|
222
|
+
screen.queryByRole('button', {name: 'Collapse'}),
|
|
223
|
+
).not.toBeInTheDocument();
|
|
206
224
|
});
|
|
207
225
|
|
|
208
|
-
it('
|
|
226
|
+
it('leaves non-collapsible content out of the disclosure wiring', () => {
|
|
209
227
|
render(
|
|
210
|
-
<Banner
|
|
211
|
-
|
|
228
|
+
<Banner
|
|
229
|
+
status="info"
|
|
230
|
+
title="Plain content"
|
|
231
|
+
isDismissable
|
|
232
|
+
collapsible={false}>
|
|
233
|
+
<div data-testid="child-content">Extra content</div>
|
|
234
|
+
</Banner>,
|
|
235
|
+
);
|
|
236
|
+
// No toggle exists, so nothing should carry disclosure state, and the
|
|
237
|
+
// region needs no id for a button to point at.
|
|
238
|
+
const dismiss = screen.getByRole('button', {name: 'Dismiss'});
|
|
239
|
+
expect(dismiss).not.toHaveAttribute('aria-expanded');
|
|
240
|
+
expect(dismiss).not.toHaveAttribute('aria-controls');
|
|
241
|
+
expect(
|
|
242
|
+
screen.getByTestId('child-content').parentElement,
|
|
243
|
+
).not.toHaveAttribute('id');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('starts open for collapsible={{defaultIsOpen: true}}', () => {
|
|
247
|
+
render(
|
|
248
|
+
<Banner status="info" title="Open" collapsible={{defaultIsOpen: true}}>
|
|
249
|
+
<div data-testid="child-content">Content</div>
|
|
250
|
+
</Banner>,
|
|
251
|
+
);
|
|
252
|
+
expect(screen.getByTestId('child-content')).toBeInTheDocument();
|
|
253
|
+
expect(screen.getByRole('button', {name: 'Collapse'})).toBeInTheDocument();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('treats a null collapsible as the default', () => {
|
|
257
|
+
render(
|
|
258
|
+
// @ts-expect-error null is outside the prop's type, but JS callers and a
|
|
259
|
+
// value widened to `| null` still reach this.
|
|
260
|
+
<Banner status="info" title="Nullish" collapsible={null}>
|
|
261
|
+
<div data-testid="child-content">Content</div>
|
|
212
262
|
</Banner>,
|
|
213
263
|
);
|
|
264
|
+
expect(screen.queryByTestId('child-content')).not.toBeInTheDocument();
|
|
214
265
|
expect(screen.getByRole('button', {name: 'Expand'})).toBeInTheDocument();
|
|
215
266
|
});
|
|
216
267
|
|
|
@@ -247,13 +298,38 @@ describe('Banner', () => {
|
|
|
247
298
|
expect(screen.getByRole('button', {name: 'Expand'})).toBeInTheDocument();
|
|
248
299
|
});
|
|
249
300
|
|
|
250
|
-
it('
|
|
301
|
+
it('reports open-state changes through onOpenChange', async () => {
|
|
302
|
+
const user = userEvent.setup();
|
|
303
|
+
const onOpenChange = vi.fn();
|
|
251
304
|
render(
|
|
252
|
-
<Banner status="info" title="
|
|
253
|
-
<div>
|
|
305
|
+
<Banner status="info" title="Notify" collapsible={{onOpenChange}}>
|
|
306
|
+
<div data-testid="child-content">Extra content</div>
|
|
254
307
|
</Banner>,
|
|
255
308
|
);
|
|
256
|
-
|
|
309
|
+
|
|
310
|
+
await user.click(screen.getByRole('button', {name: 'Expand'}));
|
|
311
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
312
|
+
await user.click(screen.getByRole('button', {name: 'Collapse'}));
|
|
313
|
+
expect(onOpenChange).toHaveBeenLastCalledWith(false);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('defers to the consumer when collapsible is controlled', async () => {
|
|
317
|
+
const user = userEvent.setup();
|
|
318
|
+
const onOpenChange = vi.fn();
|
|
319
|
+
render(
|
|
320
|
+
<Banner
|
|
321
|
+
status="info"
|
|
322
|
+
title="Controlled"
|
|
323
|
+
collapsible={{isOpen: false, onOpenChange}}>
|
|
324
|
+
<div data-testid="child-content">Extra content</div>
|
|
325
|
+
</Banner>,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
// A controlled banner must not move on its own: the click reports, the
|
|
329
|
+
// content stays hidden until the consumer re-renders with isOpen.
|
|
330
|
+
await user.click(screen.getByRole('button', {name: 'Expand'}));
|
|
331
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
332
|
+
expect(screen.queryByTestId('child-content')).not.toBeInTheDocument();
|
|
257
333
|
});
|
|
258
334
|
|
|
259
335
|
it('renders expand button to the left of dismiss button', () => {
|
|
@@ -273,7 +349,10 @@ describe('Banner', () => {
|
|
|
273
349
|
|
|
274
350
|
it('links the expand toggle to its content region via aria-controls', () => {
|
|
275
351
|
render(
|
|
276
|
-
<Banner
|
|
352
|
+
<Banner
|
|
353
|
+
status="info"
|
|
354
|
+
title="Controls Test"
|
|
355
|
+
collapsible={{defaultIsOpen: true}}>
|
|
277
356
|
<div data-testid="region-content">Region content</div>
|
|
278
357
|
</Banner>,
|
|
279
358
|
);
|
|
@@ -509,9 +588,17 @@ describe('Banner', () => {
|
|
|
509
588
|
<p>Detail</p>
|
|
510
589
|
</Banner>,
|
|
511
590
|
);
|
|
512
|
-
expect(
|
|
513
|
-
|
|
514
|
-
|
|
591
|
+
expect(screen.getByRole('button', {name: 'Expand'})).toBeInTheDocument();
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('renders no content area for children that render nothing', () => {
|
|
595
|
+
const {container} = render(
|
|
596
|
+
<Banner status="info" title="Heads up" collapsible={false}>
|
|
597
|
+
{false}
|
|
598
|
+
</Banner>,
|
|
599
|
+
);
|
|
600
|
+
// Header only — an empty slot must not draw the card-background area.
|
|
601
|
+
expect(container.firstElementChild?.children).toHaveLength(1);
|
|
515
602
|
});
|
|
516
603
|
|
|
517
604
|
it('renders no description node for a description that renders nothing', () => {
|
package/src/Banner/Banner.tsx
CHANGED
|
@@ -4,20 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Banner.tsx
|
|
7
|
-
* @input Uses React useState/useRef/useId, Button, Icon (with registry string names), StyleX
|
|
7
|
+
* @input Uses React useState/useRef/useId, useCollapsible, Button, Icon (with registry string names), StyleX
|
|
8
8
|
* @output Exports Banner component, BannerProps, BannerStatus, BannerContainer types
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by Banner.test.tsx
|
|
10
10
|
*
|
|
11
11
|
* Visual structure:
|
|
12
12
|
* - Root container: layout-only wrapper (flex column), no visual styling, no theme target
|
|
13
13
|
* - Header area (themeProps 'banner'): colored status background with icon, title, description, actions, dismiss
|
|
14
|
-
* - Content area (themeProps 'banner-content'):
|
|
14
|
+
* - Content area (themeProps 'banner-content'): card background for additional content (children)
|
|
15
15
|
* - Status icon (themeProps 'banner-icon'): the target rides on the default
|
|
16
16
|
* <Icon> itself — the element that paints — so 'status:X' overrides reach
|
|
17
17
|
* the glyph (#4166); for a custom `icon` node it stays on the layout wrapper
|
|
18
18
|
* - No left border accent — color is expressed through the full header background
|
|
19
19
|
* - Each visual area owns its own border-radius (no overflow:clip on the container)
|
|
20
|
-
* -
|
|
20
|
+
* - Children are collapsible by default: a toggle appears in the header end
|
|
21
|
+
* area and the content starts closed. `collapsible={false}` opts out and
|
|
22
|
+
* pins the content open with no toggle. The whole collapse axis (enabled /
|
|
23
|
+
* default / controlled) lives on that one prop and is driven by the shared
|
|
24
|
+
* `useCollapsible` hook rather than local state.
|
|
21
25
|
*
|
|
22
26
|
* A status added through `BannerStatusMap` augmentation has no entry in the
|
|
23
27
|
* status lookups, so it renders with no status fill, no default glyph and the
|
|
@@ -55,6 +59,8 @@ import {
|
|
|
55
59
|
easeVars,
|
|
56
60
|
shadowVars,
|
|
57
61
|
} from '../theme/tokens.stylex';
|
|
62
|
+
import {useCollapsible} from '../Collapsible/useCollapsible';
|
|
63
|
+
import type {CollapsibleConfig} from '../Collapsible/useCollapsible';
|
|
58
64
|
import {composeEventHandlers, isRenderable, mergeProps} from '../utils';
|
|
59
65
|
import type {Elevation} from '../utils/types';
|
|
60
66
|
import {edgeCompSlot} from '../Layout/edgeCompensation.stylex';
|
|
@@ -140,15 +146,29 @@ export interface BannerProps extends BaseProps<HTMLDivElement> {
|
|
|
140
146
|
*/
|
|
141
147
|
elevation?: Elevation;
|
|
142
148
|
/**
|
|
143
|
-
* Whether the content area (children)
|
|
144
|
-
*
|
|
145
|
-
*
|
|
149
|
+
* Whether the content area (children) sits behind an expand/collapse toggle
|
|
150
|
+
* in the header. On by default, so a banner with children behaves as it
|
|
151
|
+
* always has.
|
|
152
|
+
*
|
|
153
|
+
* - omitted / `true` — collapsible, starts collapsed
|
|
154
|
+
* - `{defaultIsOpen: true}` — collapsible, starts open
|
|
155
|
+
* - `{isOpen, onOpenChange}` — controlled by the consumer
|
|
156
|
+
* - `false` — not collapsible: children are always visible, with no toggle,
|
|
157
|
+
* no `aria-expanded`, and a content region that stays mounted
|
|
158
|
+
*
|
|
159
|
+
* Takes the shared `CollapsibleConfig` so a banner's disclosure is
|
|
160
|
+
* configured exactly like `Collapsible`'s, rather than through a
|
|
161
|
+
* Banner-only vocabulary. Banner's default differs from the hook's on one
|
|
162
|
+
* point — it starts closed, not open — because a banner's message lives in
|
|
163
|
+
* its header and the content is supplementary.
|
|
164
|
+
*
|
|
165
|
+
* @default true
|
|
146
166
|
*/
|
|
147
|
-
|
|
167
|
+
collapsible?: boolean | CollapsibleConfig;
|
|
148
168
|
/**
|
|
149
|
-
* Extra content rendered below the header in a
|
|
169
|
+
* Extra content rendered below the header in a card-background area.
|
|
150
170
|
* Use for rich content like lists, links, or detailed information.
|
|
151
|
-
*
|
|
171
|
+
* Collapsed behind a toggle unless `collapsible={false}`.
|
|
152
172
|
*/
|
|
153
173
|
children?: ReactNode;
|
|
154
174
|
}
|
|
@@ -367,11 +387,12 @@ const elevationStyles = stylex.create({
|
|
|
367
387
|
*
|
|
368
388
|
* Two-part visual structure:
|
|
369
389
|
* - Header: colored status background with icon, title, description, and actions
|
|
370
|
-
* - Content (optional):
|
|
390
|
+
* - Content (optional): card background area for additional rich content
|
|
371
391
|
*
|
|
372
|
-
* When children are provided, a
|
|
373
|
-
*
|
|
374
|
-
*
|
|
392
|
+
* When children are provided, a chevron toggle appears in the header end area
|
|
393
|
+
* (to the left of the dismiss button if present) and the content starts
|
|
394
|
+
* collapsed. Pass `collapsible={false}` for content that is always visible,
|
|
395
|
+
* or a config object to change the initial state or control it.
|
|
375
396
|
*
|
|
376
397
|
* Manages its own dismissed state internally — the banner hides on dismiss
|
|
377
398
|
* even if `onDismiss` is not provided, so product teams don't need to wire
|
|
@@ -402,9 +423,14 @@ const elevationStyles = stylex.create({
|
|
|
402
423
|
* <Banner
|
|
403
424
|
* status="warning"
|
|
404
425
|
* title="Configuration changes"
|
|
405
|
-
*
|
|
426
|
+
* collapsible={{defaultIsOpen: true}}>
|
|
406
427
|
* <p>Details here...</p>
|
|
407
428
|
* </Banner>
|
|
429
|
+
* <Banner status="error" title="3 fields need attention" collapsible={false}>
|
|
430
|
+
* <ul>
|
|
431
|
+
* <li>Email address is invalid</li>
|
|
432
|
+
* </ul>
|
|
433
|
+
* </Banner>
|
|
408
434
|
* ```
|
|
409
435
|
*/
|
|
410
436
|
export function Banner({
|
|
@@ -417,7 +443,7 @@ export function Banner({
|
|
|
417
443
|
endContent,
|
|
418
444
|
container = 'card',
|
|
419
445
|
elevation = 'none',
|
|
420
|
-
|
|
446
|
+
collapsible = true,
|
|
421
447
|
children,
|
|
422
448
|
xstyle,
|
|
423
449
|
className,
|
|
@@ -429,7 +455,26 @@ export function Banner({
|
|
|
429
455
|
}: BannerProps) {
|
|
430
456
|
const t = useTranslator();
|
|
431
457
|
const [isDismissed, setIsDismissed] = useState(false);
|
|
432
|
-
|
|
458
|
+
// The disclosure state machine is the shared one — Banner owns no collapse
|
|
459
|
+
// state of its own. `collapsible={false}` disables it, and that is what
|
|
460
|
+
// makes the content permanently visible.
|
|
461
|
+
//
|
|
462
|
+
// The one place Banner departs from the hook's defaults: `useCollapsible`
|
|
463
|
+
// opens by default, a banner starts closed. Its header already carries the
|
|
464
|
+
// message, so the content is supplementary — and this is the behaviour
|
|
465
|
+
// Banner has always had.
|
|
466
|
+
const collapsibleConfig =
|
|
467
|
+
collapsible != null && typeof collapsible === 'object' ? collapsible : {};
|
|
468
|
+
const {
|
|
469
|
+
isEnabled: isCollapsible,
|
|
470
|
+
isOpen: isExpanded,
|
|
471
|
+
toggle: handleToggleExpand,
|
|
472
|
+
} = useCollapsible({
|
|
473
|
+
isCollapsible: collapsible !== false && {
|
|
474
|
+
...collapsibleConfig,
|
|
475
|
+
defaultIsOpen: collapsibleConfig.defaultIsOpen ?? false,
|
|
476
|
+
},
|
|
477
|
+
});
|
|
433
478
|
// The element focus came from before it entered the banner. Dismissing
|
|
434
479
|
// unmounts the whole banner, dismiss button included, so without a handoff
|
|
435
480
|
// the browser drops focus to <body> and a keyboard user loses their place.
|
|
@@ -438,7 +483,8 @@ export function Banner({
|
|
|
438
483
|
// Links the expand/collapse toggle to the content region it shows/hides so
|
|
439
484
|
// assistive tech can move from the button to its controlled content
|
|
440
485
|
// (disclosure pattern). The region is conditionally rendered, so aria-controls
|
|
441
|
-
// below is set only while it's mounted to avoid a dangling reference.
|
|
486
|
+
// below is set only while it's mounted to avoid a dangling reference. A
|
|
487
|
+
// non-collapsible banner has no toggle, so neither end of the link is used.
|
|
442
488
|
const contentId = useId();
|
|
443
489
|
const defaultIconName = defaultIconNames[status];
|
|
444
490
|
const role = statusRole[status] ?? FALLBACK_ROLE;
|
|
@@ -482,18 +528,19 @@ export function Banner({
|
|
|
482
528
|
}
|
|
483
529
|
};
|
|
484
530
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
531
|
+
// The toggle exists only for a collapsible banner that actually has content
|
|
532
|
+
// to disclose.
|
|
533
|
+
const hasToggle = isCollapsible && hasChildren;
|
|
489
534
|
// Show the end area if there are actions, dismiss, or a collapsible toggle
|
|
490
|
-
const showEndArea = isRenderable(endContent) || isDismissable ||
|
|
535
|
+
const showEndArea = isRenderable(endContent) || isDismissable || hasToggle;
|
|
491
536
|
// Center items vertically when there's only a title (no description)
|
|
492
537
|
// and the banner has action buttons
|
|
493
538
|
const hasActions = isRenderable(endContent) || isDismissable;
|
|
494
539
|
const isSingleLine = !isRenderable(description) && hasActions;
|
|
495
540
|
|
|
496
|
-
|
|
541
|
+
// Non-collapsible children are always shown; collapsible ones follow the
|
|
542
|
+
// hook's open state.
|
|
543
|
+
const showContent = hasChildren && (!isCollapsible || isExpanded);
|
|
497
544
|
const isCard = container === 'card';
|
|
498
545
|
|
|
499
546
|
return (
|
|
@@ -575,7 +622,7 @@ export function Banner({
|
|
|
575
622
|
edgeCompSlot.inset(spacingVars['--spacing-2']),
|
|
576
623
|
)}>
|
|
577
624
|
{endContent}
|
|
578
|
-
{
|
|
625
|
+
{hasToggle && (
|
|
579
626
|
<Button
|
|
580
627
|
variant="ghost"
|
|
581
628
|
size="sm"
|
|
@@ -623,7 +670,7 @@ export function Banner({
|
|
|
623
670
|
{/* Content area: collapsible card background — theme target ('banner-content') */}
|
|
624
671
|
{showContent && (
|
|
625
672
|
<div
|
|
626
|
-
id={contentId}
|
|
673
|
+
id={hasToggle ? contentId : undefined}
|
|
627
674
|
{...mergeProps(
|
|
628
675
|
themeProps('banner-content', {container, status}),
|
|
629
676
|
stylex.props(styles.contentArea, isCard && styles.contentAreaCard),
|
|
@@ -334,6 +334,106 @@ describe('BottomSheet', () => {
|
|
|
334
334
|
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
335
335
|
});
|
|
336
336
|
|
|
337
|
+
it('ignores Escape while an IME composition is active', () => {
|
|
338
|
+
const onOpenChange = vi.fn();
|
|
339
|
+
render(
|
|
340
|
+
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
341
|
+
<input aria-label="Search" />
|
|
342
|
+
</BottomSheet>,
|
|
343
|
+
);
|
|
344
|
+
const dialog = screen.getByRole('dialog');
|
|
345
|
+
|
|
346
|
+
fireEvent.keyDown(dialog, {key: 'Escape', isComposing: true});
|
|
347
|
+
fireEvent.keyDown(dialog, {key: 'Escape', keyCode: 229});
|
|
348
|
+
|
|
349
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('claims the composing Escape so no native close request follows', () => {
|
|
353
|
+
const onOpenChange = vi.fn();
|
|
354
|
+
render(
|
|
355
|
+
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
356
|
+
<input aria-label="Search" />
|
|
357
|
+
</BottomSheet>,
|
|
358
|
+
);
|
|
359
|
+
const dialog = screen.getByRole('dialog');
|
|
360
|
+
|
|
361
|
+
// An unclaimed Escape lets the browser raise its own close request, which
|
|
362
|
+
// arrives as `cancel` and dismisses the sheet on the same keypress. The
|
|
363
|
+
// guard has to swallow the composing Escape, not merely skip dismissal.
|
|
364
|
+
const wasNotClaimed = fireEvent.keyDown(dialog, {
|
|
365
|
+
key: 'Escape',
|
|
366
|
+
isComposing: true,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
expect(wasNotClaimed).toBe(false);
|
|
370
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('keeps a form sheet open when Escape cancels a field composition', () => {
|
|
374
|
+
const onOpenChange = vi.fn();
|
|
375
|
+
render(
|
|
376
|
+
<BottomSheet
|
|
377
|
+
isOpen
|
|
378
|
+
purpose="form"
|
|
379
|
+
onOpenChange={onOpenChange}
|
|
380
|
+
label="Edit profile">
|
|
381
|
+
<input aria-label="Name" />
|
|
382
|
+
</BottomSheet>,
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
// The reported path: the keydown starts at the composing field and bubbles
|
|
386
|
+
// to the sheet's <dialog>, so the guard has to survive the trip.
|
|
387
|
+
fireEvent.keyDown(screen.getByRole('textbox', {name: 'Name'}), {
|
|
388
|
+
key: 'Escape',
|
|
389
|
+
isComposing: true,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('still dismisses on the Escape after a composition is cancelled', () => {
|
|
396
|
+
const onOpenChange = vi.fn();
|
|
397
|
+
render(
|
|
398
|
+
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
399
|
+
<input aria-label="Search" />
|
|
400
|
+
</BottomSheet>,
|
|
401
|
+
);
|
|
402
|
+
const dialog = screen.getByRole('dialog');
|
|
403
|
+
|
|
404
|
+
fireEvent.keyDown(dialog, {key: 'Escape', isComposing: true});
|
|
405
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
406
|
+
|
|
407
|
+
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
408
|
+
|
|
409
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('leaves the IME the composition keys that are not Escape', () => {
|
|
413
|
+
const onOpenChange = vi.fn();
|
|
414
|
+
render(
|
|
415
|
+
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
416
|
+
<input aria-label="Search" />
|
|
417
|
+
</BottomSheet>,
|
|
418
|
+
);
|
|
419
|
+
const dialog = screen.getByRole('dialog');
|
|
420
|
+
|
|
421
|
+
// Enter commits a candidate and the arrows walk the candidate window. The
|
|
422
|
+
// sheet must claim neither, or the IME loses keys it owns.
|
|
423
|
+
const enterUnclaimed = fireEvent.keyDown(dialog, {
|
|
424
|
+
key: 'Enter',
|
|
425
|
+
isComposing: true,
|
|
426
|
+
});
|
|
427
|
+
const arrowUnclaimed = fireEvent.keyDown(dialog, {
|
|
428
|
+
key: 'ArrowDown',
|
|
429
|
+
isComposing: true,
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
expect(enterUnclaimed).toBe(true);
|
|
433
|
+
expect(arrowUnclaimed).toBe(true);
|
|
434
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
435
|
+
});
|
|
436
|
+
|
|
337
437
|
it('requests close when the scrim (dialog element itself) is clicked', () => {
|
|
338
438
|
const onOpenChange = vi.fn();
|
|
339
439
|
render(
|
|
@@ -474,6 +574,29 @@ describe('BottomSheet', () => {
|
|
|
474
574
|
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
475
575
|
});
|
|
476
576
|
|
|
577
|
+
it('ignores Escape while an IME composition is active', () => {
|
|
578
|
+
const onOpenChange = vi.fn();
|
|
579
|
+
render(
|
|
580
|
+
<BottomSheet
|
|
581
|
+
isOpen
|
|
582
|
+
hasScrim={false}
|
|
583
|
+
onOpenChange={onOpenChange}
|
|
584
|
+
label="Filters">
|
|
585
|
+
<input aria-label="Search" />
|
|
586
|
+
</BottomSheet>,
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
// Escape is this sheet's only keyboard route out — there is no native
|
|
590
|
+
// close request behind it — so the guard is all that stands between a
|
|
591
|
+
// composing CJK user and a dismissed sheet.
|
|
592
|
+
fireEvent.keyDown(screen.getByRole('dialog'), {
|
|
593
|
+
key: 'Escape',
|
|
594
|
+
isComposing: true,
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
598
|
+
});
|
|
599
|
+
|
|
477
600
|
it('still dismisses on a downward swipe past the threshold', () => {
|
|
478
601
|
const onOpenChange = vi.fn();
|
|
479
602
|
render(
|