@astryxdesign/core 0.1.3 → 0.1.4-canary.01e5b29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +20 -18
- package/dist/AlertDialog/useImperativeAlertDialog.js +3 -3
- package/dist/AppShell/AppShell.d.ts +1 -1
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +55 -53
- package/dist/AspectRatio/AspectRatio.js +5 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +27 -25
- package/dist/Avatar/AvatarStatusDot.js +5 -5
- package/dist/AvatarGroup/AvatarGroup.js +5 -5
- package/dist/AvatarGroup/AvatarGroupOverflow.js +5 -5
- package/dist/Badge/Badge.js +3 -3
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +32 -25
- package/dist/Blockquote/Blockquote.js +7 -7
- package/dist/Breadcrumbs/BreadcrumbItem.js +17 -17
- package/dist/Breadcrumbs/Breadcrumbs.js +7 -7
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +47 -26
- package/dist/ButtonGroup/ButtonGroup.js +7 -7
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +108 -64
- package/dist/Calendar/dayCellUtils.d.ts +67 -5
- package/dist/Calendar/dayCellUtils.d.ts.map +1 -1
- package/dist/Calendar/dayCellUtils.js +139 -18
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +2 -2
- package/dist/Card/Card.d.ts +4 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +18 -10
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +20 -19
- package/dist/Center/Center.js +3 -3
- package/dist/Chat/ChatComposer.js +23 -23
- package/dist/Chat/ChatComposerDrawer.js +14 -14
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +27 -15
- package/dist/Chat/ChatDictationButton.d.ts +1 -1
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +13 -11
- package/dist/Chat/ChatLayout.js +17 -17
- package/dist/Chat/ChatLayoutScrollButton.d.ts +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +12 -10
- package/dist/Chat/ChatMessage.js +12 -12
- package/dist/Chat/ChatMessageBubble.js +7 -7
- package/dist/Chat/ChatMessageList.js +14 -14
- package/dist/Chat/ChatMessageMetadata.d.ts +1 -1
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatMessageMetadata.js +14 -12
- package/dist/Chat/ChatPastedTextToken.js +13 -13
- package/dist/Chat/ChatSendButton.js +3 -3
- package/dist/Chat/ChatSystemMessage.d.ts +1 -1
- package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
- package/dist/Chat/ChatSystemMessage.js +17 -17
- package/dist/Chat/ChatTokenizedText.d.ts +1 -1
- package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
- package/dist/Chat/ChatTokenizedText.js +24 -15
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +81 -74
- package/dist/Chat/index.d.ts +1 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -15
- package/dist/CheckboxInput/CheckboxInput.d.ts +6 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +32 -24
- package/dist/CheckboxList/CheckboxList.js +9 -9
- package/dist/CheckboxList/CheckboxListItem.d.ts +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +8 -6
- package/dist/Citation/Citation.js +10 -10
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +32 -9
- package/dist/Code/Code.d.ts +15 -1
- package/dist/Code/Code.d.ts.map +1 -1
- package/dist/Code/Code.js +35 -5
- package/dist/Code/index.d.ts +1 -1
- package/dist/Code/index.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.d.ts +10 -1
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +88 -52
- package/dist/CodeBlock/index.d.ts +1 -1
- package/dist/CodeBlock/index.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.d.ts +9 -3
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +110 -22
- package/dist/Collapsible/CollapsibleGroup.d.ts +39 -7
- package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroup.js +73 -12
- package/dist/Collapsible/CollapsibleGroupContext.d.ts +31 -0
- package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroupContext.js +33 -3
- package/dist/Collapsible/index.d.ts +1 -0
- package/dist/Collapsible/index.d.ts.map +1 -1
- package/dist/Collapsible/useCollapsible.d.ts +4 -1
- package/dist/Collapsible/useCollapsible.d.ts.map +1 -1
- package/dist/Collapsible/useCollapsible.js +20 -6
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +36 -34
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.js +27 -7
- package/dist/CommandPalette/CommandPaletteFooter.js +16 -16
- package/dist/CommandPalette/CommandPaletteGroup.js +5 -5
- package/dist/CommandPalette/CommandPaletteInput.js +13 -13
- package/dist/CommandPalette/CommandPaletteItem.js +3 -3
- package/dist/CommandPalette/CommandPaletteList.js +3 -3
- package/dist/ContextMenu/ContextMenu.d.ts +9 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +16 -11
- package/dist/DateInput/DateInput.js +18 -18
- package/dist/DateRangeInput/DateRangeInput.js +23 -23
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -7
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +30 -26
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +9 -9
- package/dist/Dialog/DialogHeader.js +16 -16
- package/dist/Dialog/useImperativeDialog.js +3 -3
- package/dist/Divider/Divider.d.ts +0 -22
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +7 -7
- package/dist/DropdownMenu/DropdownMenu.js +10 -10
- package/dist/DropdownMenu/DropdownMenuItem.d.ts +2 -17
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +3 -3
- package/dist/DropdownMenu/renderDropdownItems.js +10 -10
- package/dist/EmptyState/EmptyState.js +9 -9
- package/dist/Field/Field.js +17 -17
- package/dist/Field/FieldLabel.js +15 -15
- package/dist/Field/InputClearButton.js +5 -5
- package/dist/FieldStatus/FieldStatus.js +3 -3
- package/dist/FileInput/FileInput.js +29 -29
- package/dist/FormLayout/FormLayout.js +5 -5
- package/dist/Grid/Grid.d.ts +4 -3
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +29 -12
- package/dist/Grid/GridSpan.js +3 -3
- package/dist/HStack/HStack.js +3 -3
- package/dist/Heading/Heading.js +10 -10
- package/dist/HoverCard/HoverCard.js +9 -9
- package/dist/HoverCard/useHoverCard.js +3 -3
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +16 -12
- package/dist/Icon/defaultIcons.js +116 -116
- package/dist/IconButton/IconButton.js +3 -3
- package/dist/InputGroup/InputGroup.js +11 -11
- package/dist/InputGroup/InputGroupText.js +3 -3
- package/dist/Item/Item.js +18 -18
- package/dist/Kbd/Kbd.js +5 -5
- package/dist/Layer/LayerProvider.js +7 -7
- package/dist/Layer/useLayer.js +5 -5
- package/dist/Layout/Layout.js +21 -21
- package/dist/Layout/LayoutContent.js +3 -3
- package/dist/Layout/LayoutFooter.js +5 -5
- package/dist/Layout/LayoutHeader.js +5 -5
- package/dist/Layout/LayoutPanel.js +3 -3
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +55 -31
- package/dist/Lightbox/useLightbox.js +3 -3
- package/dist/Link/Link.js +15 -15
- package/dist/Link/LinkProvider.js +3 -3
- package/dist/Link/useLinkify.js +3 -3
- package/dist/List/List.js +11 -11
- package/dist/List/ListItem.js +11 -11
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +102 -102
- package/dist/Markdown/parser.d.ts +7 -0
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +300 -6
- package/dist/MetadataList/MetadataList.js +10 -10
- package/dist/MetadataList/MetadataListItem.js +15 -15
- package/dist/MobileNav/MobileNav.js +15 -15
- package/dist/MobileNav/MobileNavToggle.js +5 -5
- package/dist/MoreMenu/MoreMenu.js +3 -3
- package/dist/MultiSelector/MultiSelector.d.ts +7 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +64 -55
- package/dist/NavIcon/NavIcon.js +3 -3
- package/dist/NavMenu/NavHeadingMenu.js +5 -5
- package/dist/NavMenu/NavHeadingMenuItem.js +8 -8
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +48 -32
- package/dist/Outline/Outline.js +15 -15
- package/dist/OverflowList/OverflowList.js +8 -8
- package/dist/Overlay/Overlay.js +3 -3
- package/dist/Overlay/OverlayScrim.js +5 -5
- package/dist/Overlay/useOverlay.js +3 -3
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +125 -72
- package/dist/Popover/Popover.js +16 -16
- package/dist/Popover/usePopover.js +7 -7
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +110 -92
- package/dist/PowerSearch/PowerSearchEditPopover.js +84 -84
- package/dist/PowerSearch/PowerSearchFilterEditor.js +3 -3
- package/dist/PowerSearch/PowerSearchToken.js +5 -5
- package/dist/PowerSearch/PowerSearchValueEditor.js +54 -54
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +25 -18
- package/dist/RadioList/RadioList.d.ts +7 -1
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +10 -8
- package/dist/RadioList/RadioListItem.d.ts +1 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +30 -22
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +28 -10
- package/dist/Section/Section.js +5 -5
- package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +32 -10
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +18 -12
- package/dist/SelectableCard/SelectableCard.js +5 -5
- package/dist/Selector/Selector.d.ts +6 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +54 -45
- package/dist/Selector/SelectorOption.js +3 -3
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +38 -34
- package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +8 -6
- package/dist/SideNav/SideNavHeading.js +87 -87
- package/dist/SideNav/SideNavItem.js +42 -42
- package/dist/SideNav/SideNavSection.d.ts +1 -1
- package/dist/SideNav/SideNavSection.d.ts.map +1 -1
- package/dist/SideNav/SideNavSection.js +18 -16
- package/dist/Skeleton/Skeleton.js +3 -3
- package/dist/Slider/Slider.d.ts +6 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +31 -22
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +10 -10
- package/dist/StatusDot/StatusDot.js +5 -5
- package/dist/Switch/Switch.d.ts +13 -4
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -30
- package/dist/TabList/Tab.js +18 -18
- package/dist/TabList/TabList.js +5 -5
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +57 -30
- package/dist/TabList/tab.markers.stylex.d.ts +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +51 -39
- package/dist/Table/Table.js +7 -7
- package/dist/Table/TableBody.d.ts +1 -1
- package/dist/Table/TableBody.d.ts.map +1 -1
- package/dist/Table/TableBody.js +9 -5
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +76 -7
- package/dist/Table/TableFooter.d.ts +1 -1
- package/dist/Table/TableFooter.d.ts.map +1 -1
- package/dist/Table/TableFooter.js +9 -5
- package/dist/Table/TableHeader.d.ts +1 -1
- package/dist/Table/TableHeader.d.ts.map +1 -1
- package/dist/Table/TableHeader.js +9 -5
- package/dist/Table/TableHeaderCell.js +3 -3
- package/dist/Table/TableRow.js +5 -5
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +14 -11
- package/dist/Table/plugins/filtering/useTableFiltering.js +71 -71
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +5 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +7 -7
- package/dist/Table/plugins/rowExpansion/index.d.ts +3 -0
- package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowExpansion/index.js +3 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +90 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +398 -0
- package/dist/Table/plugins/selection/useTableSelection.js +18 -18
- package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelectionState.js +11 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +17 -17
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +8 -4
- package/dist/Table/tableContextMenu.d.ts +5 -1
- package/dist/Table/tableContextMenu.d.ts.map +1 -1
- package/dist/Table/tableContextMenu.js +19 -12
- package/dist/Text/Text.d.ts +1 -1
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +14 -13
- package/dist/Text/text.stylex.d.ts +35 -0
- package/dist/Text/text.stylex.d.ts.map +1 -1
- package/dist/Text/text.stylex.js +52 -1
- package/dist/TextArea/TextArea.js +14 -14
- package/dist/TextInput/TextInput.js +15 -15
- package/dist/Thumbnail/Thumbnail.d.ts +3 -2
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +30 -29
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +26 -21
- package/dist/Timestamp/Timestamp.d.ts +1 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +37 -26
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +25 -17
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +23 -11
- package/dist/Toast/useToast.js +4 -4
- package/dist/ToggleButton/ToggleButton.js +12 -12
- package/dist/ToggleButton/ToggleButtonGroup.js +5 -5
- package/dist/Token/Token.js +21 -21
- package/dist/Tokenizer/Tokenizer.d.ts +6 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +38 -29
- package/dist/Toolbar/Toolbar.js +18 -18
- package/dist/Tooltip/Tooltip.js +11 -11
- package/dist/Tooltip/useTooltip.js +3 -3
- package/dist/TopNav/TopNav.js +32 -32
- package/dist/TopNav/TopNavHeading.js +67 -67
- package/dist/TopNav/TopNavItem.js +5 -5
- package/dist/TopNav/TopNavMegaMenu.js +30 -30
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +25 -14
- package/dist/TopNav/TopNavMegaMenuItem.js +16 -16
- package/dist/TopNav/TopNavMenu.js +32 -32
- package/dist/TreeList/TreeList.js +8 -8
- package/dist/TreeList/TreeListBranches.js +11 -11
- package/dist/TreeList/TreeListItem.js +30 -30
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +23 -20
- package/dist/Typeahead/Typeahead.js +12 -12
- package/dist/Typeahead/TypeaheadItem.js +10 -10
- package/dist/VStack/VStack.js +3 -3
- package/dist/astryx.css +23 -0
- package/dist/astryx.umd.js +60 -58
- package/dist/astryx.umd.js.map +4 -4
- package/dist/docs-types.d.ts +13 -1
- package/dist/docs-types.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.d.ts +1 -1
- package/dist/hooks/focusableSelector.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.js +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFocusTrap.d.ts +3 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +38 -1
- package/dist/hooks/useHotkeys.d.ts +51 -0
- package/dist/hooks/useHotkeys.d.ts.map +1 -0
- package/dist/hooks/useHotkeys.js +160 -0
- package/dist/hooks/useInteractiveRole.d.ts +6 -2
- package/dist/hooks/useInteractiveRole.d.ts.map +1 -1
- package/dist/hooks/useInteractiveRole.js +6 -2
- package/dist/hooks/useKeyboardHint.js +9 -9
- package/dist/hooks/useStreamingText.d.ts +3 -1
- package/dist/hooks/useStreamingText.d.ts.map +1 -1
- package/dist/hooks/useStreamingText.js +14 -5
- package/dist/theme/MediaTheme.js +3 -3
- package/dist/theme/Theme.js +7 -7
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +10 -3
- package/dist/theme/hct.d.ts +0 -15
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +9 -9
- package/dist/theme/syntax/SyntaxTheme.js +5 -5
- package/dist/theme/tokens.d.ts +10 -2
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +220 -4
- package/dist/utils/color.d.ts +42 -0
- package/dist/utils/color.d.ts.map +1 -0
- package/dist/utils/color.js +150 -0
- package/dist/utils/composeEventHandlers.d.ts +30 -0
- package/dist/utils/composeEventHandlers.d.ts.map +1 -0
- package/dist/utils/composeEventHandlers.js +40 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +8 -5
- package/package.json +3 -3
- package/src/AlertDialog/AlertDialog.tsx +2 -0
- package/src/AppShell/AppShell.tsx +2 -0
- package/src/AspectRatio/AspectRatio.doc.mjs +1 -1
- package/src/Avatar/Avatar.doc.mjs +13 -2
- package/src/Avatar/Avatar.test.tsx +32 -1
- package/src/Avatar/Avatar.tsx +12 -20
- package/src/Banner/Banner.doc.mjs +8 -0
- package/src/Banner/Banner.test.tsx +38 -0
- package/src/Banner/Banner.tsx +8 -1
- package/src/Button/Button.tsx +39 -6
- package/src/ButtonGroup/ButtonGroup.doc.mjs +1 -1
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +201 -17
- package/src/Calendar/Calendar.tsx +79 -26
- package/src/Calendar/dayCellUtils.test.ts +306 -1
- package/src/Calendar/dayCellUtils.ts +206 -13
- package/src/Calendar/styles.ts +12 -2
- package/src/Card/Card.doc.mjs +1 -1
- package/src/Card/Card.tsx +16 -6
- package/src/Carousel/Carousel.doc.mjs +12 -12
- package/src/Carousel/Carousel.test.tsx +13 -0
- package/src/Carousel/Carousel.tsx +1 -0
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.test.tsx +24 -0
- package/src/Chat/ChatComposerInput.tsx +17 -1
- package/src/Chat/ChatDictationButton.test.tsx +43 -0
- package/src/Chat/ChatDictationButton.tsx +4 -4
- package/src/Chat/ChatLayoutScrollButton.test.tsx +33 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
- package/src/Chat/ChatMessageMetadata.test.tsx +32 -0
- package/src/Chat/ChatMessageMetadata.tsx +4 -6
- package/src/Chat/ChatSystemMessage.test.tsx +26 -11
- package/src/Chat/ChatSystemMessage.tsx +7 -7
- package/src/Chat/ChatTokenizedText.test.tsx +29 -16
- package/src/Chat/ChatTokenizedText.tsx +15 -8
- package/src/Chat/ChatToolCalls.test.tsx +55 -0
- package/src/Chat/ChatToolCalls.tsx +10 -12
- package/src/Chat/index.ts +1 -4
- package/src/Chat/useChatNewMessages.test.tsx +4 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +12 -4
- package/src/CheckboxInput/CheckboxInput.test.tsx +103 -0
- package/src/CheckboxInput/CheckboxInput.tsx +13 -0
- package/src/CheckboxList/CheckboxList.doc.mjs +1 -1
- package/src/CheckboxList/CheckboxList.test.tsx +19 -0
- package/src/CheckboxList/CheckboxListItem.tsx +11 -2
- package/src/Citation/Citation.doc.mjs +5 -0
- package/src/ClickableCard/ClickableCard.tsx +45 -2
- package/src/Code/Code.test.tsx +54 -0
- package/src/Code/Code.tsx +48 -4
- package/src/Code/index.ts +1 -1
- package/src/CodeBlock/CodeBlock.doc.mjs +7 -2
- package/src/CodeBlock/CodeBlock.test.tsx +122 -3
- package/src/CodeBlock/CodeBlock.tsx +62 -4
- package/src/CodeBlock/index.ts +1 -1
- package/src/Collapsible/Collapsible.doc.mjs +8 -4
- package/src/Collapsible/Collapsible.test.tsx +405 -0
- package/src/Collapsible/Collapsible.tsx +86 -10
- package/src/Collapsible/CollapsibleGroup.doc.mjs +27 -3
- package/src/Collapsible/CollapsibleGroup.test.tsx +318 -0
- package/src/Collapsible/CollapsibleGroup.tsx +107 -9
- package/src/Collapsible/CollapsibleGroupContext.tsx +42 -2
- package/src/Collapsible/index.ts +4 -0
- package/src/Collapsible/useCollapsible.ts +20 -6
- package/src/CommandPalette/CommandPalette.tsx +5 -3
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +5 -0
- package/src/CommandPalette/CommandPaletteEmpty.tsx +15 -1
- package/src/CommandPalette/CommandPaletteGroup.doc.mjs +10 -0
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteList.doc.mjs +9 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +15 -0
- package/src/ContextMenu/ContextMenu.test.tsx +15 -0
- package/src/ContextMenu/ContextMenu.tsx +19 -1
- package/src/ContextMenu/ContextMenuItem.doc.mjs +6 -0
- package/src/DateInput/DateInput.doc.mjs +6 -1
- package/src/DateInput/DateInput.test.tsx +11 -20
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.test.tsx +31 -44
- package/src/DateRangeInput/DateRangeInput.tsx +2 -2
- package/src/DateTimeInput/DateTimeInput.doc.mjs +4 -3
- package/src/DateTimeInput/DateTimeInput.test.tsx +118 -16
- package/src/DateTimeInput/DateTimeInput.tsx +9 -8
- package/src/DateTimeInput/index.ts +1 -0
- package/src/Dialog/DialogHeader.doc.mjs +45 -0
- package/src/Divider/Divider.tsx +1 -23
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenu.test.tsx +5 -3
- package/src/DropdownMenu/DropdownMenuItem.tsx +5 -17
- package/src/EmptyState/EmptyState.doc.mjs +1 -1
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/FieldLabel.test.tsx +2 -2
- package/src/FieldStatus/FieldStatus.test.tsx +282 -0
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -3
- package/src/Grid/Grid.doc.mjs +3 -3
- package/src/Grid/Grid.test.tsx +43 -18
- package/src/Grid/Grid.tsx +26 -9
- package/src/Icon/Icon.test.tsx +41 -29
- package/src/Icon/Icon.tsx +6 -2
- package/src/InputGroup/InputGroup.tsx +2 -2
- package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +185 -0
- package/src/Item/Item.tsx +1 -1
- package/src/Layout/Layout.test.tsx +285 -0
- package/src/Layout/LayoutContent.doc.mjs +12 -0
- package/src/Layout/LayoutPanel.doc.mjs +40 -0
- package/src/Layout/LayoutSlots.test.tsx +364 -0
- package/src/Layout/__tests__/edgeCompensation.test.tsx +72 -0
- package/src/Lightbox/Lightbox.doc.mjs +5 -0
- package/src/Lightbox/Lightbox.test.tsx +123 -2
- package/src/Lightbox/Lightbox.tsx +29 -1
- package/src/Markdown/Markdown.test.tsx +23 -0
- package/src/Markdown/Markdown.tsx +9 -7
- package/src/Markdown/incremental.test.ts +36 -0
- package/src/Markdown/parser.perf.test.ts +34 -12
- package/src/Markdown/parser.test.ts +210 -0
- package/src/Markdown/parser.ts +301 -6
- package/src/MobileNav/MobileNav.doc.mjs +20 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +14 -1
- package/src/MultiSelector/MultiSelector.test.tsx +33 -0
- package/src/MultiSelector/MultiSelector.tsx +20 -0
- package/src/NavMenu/NavMenu.doc.mjs +15 -0
- package/src/NumberInput/NumberInput.test.tsx +48 -3
- package/src/NumberInput/NumberInput.tsx +32 -15
- package/src/Outline/parseOutlineFromMarkdown.test.ts +80 -0
- package/src/OverflowList/OverflowList.test.tsx +416 -0
- package/src/Overlay/Overlay.doc.mjs +6 -0
- package/src/Overlay/Overlay.test.tsx +356 -0
- package/src/Pagination/Pagination.test.tsx +162 -0
- package/src/Pagination/Pagination.tsx +80 -26
- package/src/Popover/Popover.test.tsx +65 -0
- package/src/PowerSearch/PowerSearch.doc.mjs +4 -4
- package/src/PowerSearch/PowerSearch.test.tsx +109 -1
- package/src/PowerSearch/PowerSearch.tsx +37 -16
- package/src/ProgressBar/ProgressBar.test.tsx +30 -0
- package/src/ProgressBar/ProgressBar.tsx +12 -5
- package/src/RadioList/RadioList.doc.mjs +7 -1
- package/src/RadioList/RadioList.test.tsx +65 -0
- package/src/RadioList/RadioList.tsx +10 -1
- package/src/RadioList/RadioListItem.tsx +13 -4
- package/src/Resizable/ResizeHandle.test.tsx +218 -0
- package/src/Resizable/ResizeHandle.tsx +22 -4
- package/src/Section/Section.test.tsx +4 -10
- package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
- package/src/SegmentedControl/SegmentedControl.test.tsx +192 -0
- package/src/SegmentedControl/SegmentedControl.tsx +27 -5
- package/src/SegmentedControl/SegmentedControlItem.tsx +9 -4
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +12 -1
- package/src/Selector/Selector.test.tsx +30 -0
- package/src/Selector/Selector.tsx +18 -0
- package/src/SideNav/SideNav.doc.mjs +7 -1
- package/src/SideNav/SideNav.test.tsx +66 -0
- package/src/SideNav/SideNav.tsx +6 -2
- package/src/SideNav/SideNavCollapseButton.tsx +4 -3
- package/src/SideNav/SideNavSection.tsx +6 -4
- package/src/SizeContext/SizeContext.test.tsx +226 -0
- package/src/Slider/Slider.doc.mjs +14 -2
- package/src/Slider/Slider.test.tsx +30 -0
- package/src/Slider/Slider.tsx +20 -0
- package/src/Spinner/Spinner.tsx +1 -0
- package/src/Switch/Switch.doc.mjs +21 -9
- package/src/Switch/Switch.test.tsx +137 -0
- package/src/Switch/Switch.tsx +32 -7
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +185 -1
- package/src/TabList/TabMenu.tsx +45 -9
- package/src/Table/BaseTable.tsx +16 -3
- package/src/Table/Table.test.tsx +87 -0
- package/src/Table/TableBody.tsx +15 -2
- package/src/Table/TableCell.tsx +87 -8
- package/src/Table/TableFooter.tsx +15 -2
- package/src/Table/TableHeader.tsx +15 -2
- package/src/Table/index.ts +5 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +13 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +3 -1
- package/src/Table/plugins/pagination/paginateData.ts +5 -1
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +13 -0
- package/src/Table/plugins/rowExpansion/index.ts +7 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +155 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +590 -0
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +23 -0
- package/src/Table/plugins/selection/useTableSelectionState.tsx +11 -6
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +35 -1
- package/src/Table/plugins/sortable/useTableSortableState.tsx +8 -4
- package/src/Table/tableContextMenu.test.tsx +27 -3
- package/src/Table/tableContextMenu.tsx +20 -2
- package/src/Table/useTablePagination.doc.mjs +4 -1
- package/src/Table/useTableRowExpansion.doc.mjs +80 -0
- package/src/Table/useTableSortable.doc.mjs +4 -1
- package/src/Text/Text.doc.mjs +2 -2
- package/src/Text/Text.test.tsx +11 -0
- package/src/Text/Text.tsx +4 -2
- package/src/Text/text.stylex.ts +41 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +3 -3
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/TextInput/TextInput.test.tsx +3 -3
- package/src/Thumbnail/Thumbnail.test.tsx +36 -2
- package/src/Thumbnail/Thumbnail.tsx +4 -2
- package/src/TimeInput/TimeInput.doc.mjs +11 -6
- package/src/TimeInput/TimeInput.test.tsx +37 -0
- package/src/TimeInput/TimeInput.tsx +11 -1
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Timestamp/Timestamp.test.tsx +52 -0
- package/src/Timestamp/Timestamp.tsx +30 -16
- package/src/Toast/Toast.tsx +11 -3
- package/src/Toast/ToastViewport.test.tsx +64 -0
- package/src/Toast/ToastViewport.tsx +12 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +1 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +16 -4
- package/src/Tokenizer/Tokenizer.test.tsx +33 -0
- package/src/Tokenizer/Tokenizer.tsx +19 -0
- package/src/Toolbar/Toolbar.test.tsx +1 -1
- package/src/TopNav/TopNav.doc.mjs +1 -1
- package/src/TopNav/TopNav.test.tsx +18 -0
- package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +9 -2
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +19 -0
- package/src/Typeahead/BaseTypeahead.tsx +5 -2
- package/src/Typeahead/Typeahead.doc.mjs +7 -2
- package/src/Typeahead/Typeahead.test.tsx +45 -1
- package/src/__tests__/TestIcon.tsx +26 -0
- package/src/__tests__/fastRoleQueries.ts +66 -0
- package/src/docPropReferences.test.ts +178 -0
- package/src/docs-types.ts +14 -0
- package/src/hooks/focusableSelector.ts +1 -1
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useFocusTrap.doc.mjs +4 -2
- package/src/hooks/useFocusTrap.test.tsx +149 -1
- package/src/hooks/useFocusTrap.ts +50 -1
- package/src/hooks/useHotkeys.doc.mjs +52 -0
- package/src/hooks/useHotkeys.test.ts +245 -0
- package/src/hooks/useHotkeys.ts +203 -0
- package/src/hooks/useInteractiveRole.ts +12 -4
- package/src/hooks/useKeyboardHint.doc.mjs +6 -6
- package/src/hooks/useStreamingText.test.ts +54 -19
- package/src/hooks/useStreamingText.ts +16 -5
- package/src/theme/SyntaxTheme.doc.mjs +4 -4
- package/src/theme/defineTheme.ts +4 -13
- package/src/theme/derivedVarRegistry.test.ts +2 -2
- package/src/theme/expandColorScale.test.ts +29 -0
- package/src/theme/expandColorScale.ts +11 -6
- package/src/theme/hct.ts +9 -17
- package/src/theme/tokens.test.ts +182 -9
- package/src/theme/tokens.ts +239 -4
- package/src/theme/useTheme.test.tsx +20 -0
- package/src/utils/color.test.ts +133 -0
- package/src/utils/color.ts +148 -0
- package/src/utils/composeEventHandlers.test.ts +71 -0
- package/src/utils/composeEventHandlers.ts +44 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/mergeProps.test.ts +166 -0
- package/src/utils/sharedResizeObserver.test.ts +2 -2
- package/src/utils/timeParser.test.ts +13 -0
- package/src/utils/timeParser.ts +8 -5
|
@@ -62,11 +62,17 @@ export const docs = {
|
|
|
62
62
|
description: 'Whether the switch is disabled.',
|
|
63
63
|
default: 'false',
|
|
64
64
|
},
|
|
65
|
+
{
|
|
66
|
+
name: 'htmlName',
|
|
67
|
+
type: 'string',
|
|
68
|
+
description:
|
|
69
|
+
'The HTML name attribute for the underlying checkbox input, useful for form submissions (submits "on" when the switch is on).',
|
|
70
|
+
},
|
|
65
71
|
{
|
|
66
72
|
name: 'disabledMessage',
|
|
67
73
|
type: 'string',
|
|
68
74
|
description:
|
|
69
|
-
'Explains why the switch is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the switch focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled Switch in Tooltip
|
|
75
|
+
'Explains why the switch is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the switch focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled Switch in Tooltip. Disabled controls swallow the hover events an external Tooltip needs.',
|
|
70
76
|
},
|
|
71
77
|
{
|
|
72
78
|
name: 'isOptional',
|
|
@@ -118,10 +124,10 @@ export const docs = {
|
|
|
118
124
|
},
|
|
119
125
|
{
|
|
120
126
|
name: 'labelSpacing',
|
|
121
|
-
type: "'
|
|
127
|
+
type: "'hug' | 'spread'",
|
|
122
128
|
description:
|
|
123
|
-
'Spacing behavior between label and switch. "spread" pushes them to opposite ends of the container (full width).',
|
|
124
|
-
default: "'
|
|
129
|
+
'Spacing behavior between label and switch. "hug" places them next to each other; "spread" pushes them to opposite ends of the container (full width). "default" is a deprecated alias for "hug".',
|
|
130
|
+
default: "'hug'",
|
|
125
131
|
},
|
|
126
132
|
],
|
|
127
133
|
theming: {
|
|
@@ -203,11 +209,16 @@ export const docsZh = {
|
|
|
203
209
|
description: '开关是否被禁用。',
|
|
204
210
|
default: 'false',
|
|
205
211
|
},
|
|
212
|
+
{
|
|
213
|
+
name: 'htmlName',
|
|
214
|
+
type: 'string',
|
|
215
|
+
description: '底层复选框输入的 HTML name 属性,用于表单提交(开启时提交 "on")。',
|
|
216
|
+
},
|
|
206
217
|
{
|
|
207
218
|
name: 'disabledMessage',
|
|
208
219
|
type: 'string',
|
|
209
220
|
description:
|
|
210
|
-
'Explains why the switch is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the switch focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled Switch in Tooltip
|
|
221
|
+
'Explains why the switch is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the switch focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled Switch in Tooltip. Disabled controls swallow the hover events an external Tooltip needs.',
|
|
211
222
|
},
|
|
212
223
|
{
|
|
213
224
|
name: 'isOptional',
|
|
@@ -259,10 +270,10 @@ export const docsZh = {
|
|
|
259
270
|
},
|
|
260
271
|
{
|
|
261
272
|
name: 'labelSpacing',
|
|
262
|
-
type: "'
|
|
273
|
+
type: "'hug' | 'spread'",
|
|
263
274
|
description:
|
|
264
|
-
'标签和开关之间的间距行为。"spread" 将它们推到容器的两端(全宽)。',
|
|
265
|
-
default: "'
|
|
275
|
+
'标签和开关之间的间距行为。"hug" 将它们并排放置;"spread" 将它们推到容器的两端(全宽)。"default" 是 "hug" 的已弃用别名。',
|
|
276
|
+
default: "'hug'",
|
|
266
277
|
},
|
|
267
278
|
],
|
|
268
279
|
theming: {
|
|
@@ -309,6 +320,7 @@ export const docsDense = {
|
|
|
309
320
|
isLabelHidden: 'Visually hides label; still accessible to screen readers.',
|
|
310
321
|
description: 'Description text below label.',
|
|
311
322
|
isDisabled: 'Whether switch is disabled.',
|
|
323
|
+
htmlName: 'HTML name attr for the checkbox; submits "on" when on.',
|
|
312
324
|
isOptional: 'Whether field is optional; mutually exclusive w/ isRequired.',
|
|
313
325
|
isRequired: 'Whether switch is required; mutually exclusive w/ isOptional.',
|
|
314
326
|
status: 'Status indicator w/ type + message; colored message box, sets aria-invalid on error.',
|
|
@@ -317,6 +329,6 @@ export const docsDense = {
|
|
|
317
329
|
labelIcon: 'Icon before label text.',
|
|
318
330
|
labelTooltip: 'Tooltip text in info icon at label end.',
|
|
319
331
|
labelPosition: 'Which side label appears; "start" places before switch.',
|
|
320
|
-
labelSpacing: 'Spacing behavior; "spread" pushes to opposite ends (full width).',
|
|
332
|
+
labelSpacing: 'Spacing behavior; "hug" places next to each other, "spread" pushes to opposite ends (full width). "default" is deprecated alias for "hug".',
|
|
321
333
|
},
|
|
322
334
|
};
|
|
@@ -470,4 +470,141 @@ describe('Switch', () => {
|
|
|
470
470
|
expect(screen.getByRole('switch')).toBeDisabled();
|
|
471
471
|
});
|
|
472
472
|
});
|
|
473
|
+
describe('labelSpacing', () => {
|
|
474
|
+
const getField = (container: HTMLElement) =>
|
|
475
|
+
container.querySelector<HTMLElement>('.astryx-switch-field')!;
|
|
476
|
+
|
|
477
|
+
it('omits the data attribute for the default hug spacing', () => {
|
|
478
|
+
const {container} = render(
|
|
479
|
+
<Switch label="Notify" value={false} onChange={() => {}} />,
|
|
480
|
+
);
|
|
481
|
+
expect(getField(container)).not.toHaveAttribute('data-label-spacing');
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('reflects spread spacing as a data attribute and variant class', () => {
|
|
485
|
+
const {container} = render(
|
|
486
|
+
<Switch
|
|
487
|
+
label="Notify"
|
|
488
|
+
value={false}
|
|
489
|
+
onChange={() => {}}
|
|
490
|
+
labelSpacing="spread"
|
|
491
|
+
/>,
|
|
492
|
+
);
|
|
493
|
+
expect(getField(container)).toHaveAttribute(
|
|
494
|
+
'data-label-spacing',
|
|
495
|
+
'spread',
|
|
496
|
+
);
|
|
497
|
+
expect(getField(container).className).toContain('spread');
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('renders explicit hug the same as the default', () => {
|
|
501
|
+
const {container: implicit} = render(
|
|
502
|
+
<Switch label="Notify" value={false} onChange={() => {}} />,
|
|
503
|
+
);
|
|
504
|
+
const {container: explicit} = render(
|
|
505
|
+
<Switch
|
|
506
|
+
label="Notify"
|
|
507
|
+
value={false}
|
|
508
|
+
onChange={() => {}}
|
|
509
|
+
labelSpacing="hug"
|
|
510
|
+
/>,
|
|
511
|
+
);
|
|
512
|
+
expect(getField(explicit)).not.toHaveAttribute('data-label-spacing');
|
|
513
|
+
expect(getField(explicit).className).toBe(getField(implicit).className);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it("treats the deprecated 'default' value as an alias for hug", () => {
|
|
517
|
+
const {container: hug} = render(
|
|
518
|
+
<Switch
|
|
519
|
+
label="Notify"
|
|
520
|
+
value={false}
|
|
521
|
+
onChange={() => {}}
|
|
522
|
+
labelSpacing="hug"
|
|
523
|
+
/>,
|
|
524
|
+
);
|
|
525
|
+
const {container: deprecated} = render(
|
|
526
|
+
<Switch
|
|
527
|
+
label="Notify"
|
|
528
|
+
value={false}
|
|
529
|
+
onChange={() => {}}
|
|
530
|
+
labelSpacing="default"
|
|
531
|
+
/>,
|
|
532
|
+
);
|
|
533
|
+
expect(getField(deprecated)).not.toHaveAttribute('data-label-spacing');
|
|
534
|
+
expect(getField(deprecated).className).toBe(getField(hug).className);
|
|
535
|
+
// The switch row keeps the hug layout, not the spread justification.
|
|
536
|
+
expect(getField(deprecated).firstElementChild?.className).toBe(
|
|
537
|
+
getField(hug).firstElementChild?.className,
|
|
538
|
+
);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
describe('form participation', () => {
|
|
542
|
+
it('submits under htmlName when on', () => {
|
|
543
|
+
const {container} = render(
|
|
544
|
+
<form>
|
|
545
|
+
<Switch
|
|
546
|
+
label="Notify"
|
|
547
|
+
htmlName="notify"
|
|
548
|
+
value={true}
|
|
549
|
+
onChange={() => {}}
|
|
550
|
+
/>
|
|
551
|
+
</form>,
|
|
552
|
+
);
|
|
553
|
+
const data = new FormData(container.querySelector('form')!);
|
|
554
|
+
expect(data.get('notify')).toBe('on');
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('is excluded from form data when disabled, even with a disabledMessage', () => {
|
|
558
|
+
const {container} = render(
|
|
559
|
+
<form>
|
|
560
|
+
<Switch
|
|
561
|
+
label="Notify"
|
|
562
|
+
htmlName="notify"
|
|
563
|
+
value={true}
|
|
564
|
+
onChange={() => {}}
|
|
565
|
+
isDisabled
|
|
566
|
+
disabledMessage="Locked"
|
|
567
|
+
/>
|
|
568
|
+
</form>,
|
|
569
|
+
);
|
|
570
|
+
expect([
|
|
571
|
+
...new FormData(container.querySelector('form')!).keys(),
|
|
572
|
+
]).toEqual([]);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('submits nothing when off or when htmlName is omitted', () => {
|
|
576
|
+
const {container} = render(
|
|
577
|
+
<form>
|
|
578
|
+
<Switch
|
|
579
|
+
label="Off"
|
|
580
|
+
htmlName="off"
|
|
581
|
+
value={false}
|
|
582
|
+
onChange={() => {}}
|
|
583
|
+
/>
|
|
584
|
+
<Switch label="Unnamed" value={true} onChange={() => {}} />
|
|
585
|
+
</form>,
|
|
586
|
+
);
|
|
587
|
+
const data = new FormData(container.querySelector('form')!);
|
|
588
|
+
expect([...data.keys()]).toEqual([]);
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
describe('rest forwarding', () => {
|
|
593
|
+
it('forwards data-testid, id, and aria-* to the root element', () => {
|
|
594
|
+
const {container} = render(
|
|
595
|
+
<Switch
|
|
596
|
+
label="Notifications"
|
|
597
|
+
value={false}
|
|
598
|
+
onChange={() => {}}
|
|
599
|
+
data-testid="my-switch"
|
|
600
|
+
id="switch-1"
|
|
601
|
+
aria-label="Toggle notifications"
|
|
602
|
+
/>,
|
|
603
|
+
);
|
|
604
|
+
const root = container.querySelector('[data-testid="my-switch"]');
|
|
605
|
+
expect(root).not.toBeNull();
|
|
606
|
+
expect(root).toHaveAttribute('id', 'switch-1');
|
|
607
|
+
expect(root).toHaveAttribute('aria-label', 'Toggle notifications');
|
|
608
|
+
});
|
|
609
|
+
});
|
|
473
610
|
});
|
package/src/Switch/Switch.tsx
CHANGED
|
@@ -185,7 +185,11 @@ const styles = stylex.create({
|
|
|
185
185
|
|
|
186
186
|
export type SwitchLabelPosition = 'start' | 'end';
|
|
187
187
|
|
|
188
|
-
export type SwitchLabelSpacing =
|
|
188
|
+
export type SwitchLabelSpacing =
|
|
189
|
+
| 'hug'
|
|
190
|
+
| 'spread'
|
|
191
|
+
/** @deprecated Use `'hug'` instead. */
|
|
192
|
+
| 'default';
|
|
189
193
|
|
|
190
194
|
export interface SwitchProps extends Omit<BaseProps, 'onChange'> {
|
|
191
195
|
/** Ref forwarded to the root element */
|
|
@@ -228,6 +232,13 @@ export interface SwitchProps extends Omit<BaseProps, 'onChange'> {
|
|
|
228
232
|
* @default false
|
|
229
233
|
*/
|
|
230
234
|
isDisabled?: boolean;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The HTML name attribute for the underlying checkbox input.
|
|
238
|
+
* Useful for form submissions.
|
|
239
|
+
*/
|
|
240
|
+
htmlName?: string;
|
|
241
|
+
|
|
231
242
|
/**
|
|
232
243
|
* Explains why the switch is disabled. When set together with `isDisabled`,
|
|
233
244
|
* the switch shows a tooltip with this text on hover and keyboard focus, and
|
|
@@ -290,9 +301,11 @@ export interface SwitchProps extends Omit<BaseProps, 'onChange'> {
|
|
|
290
301
|
labelPosition?: SwitchLabelPosition;
|
|
291
302
|
/**
|
|
292
303
|
* Spacing behavior between label and switch.
|
|
293
|
-
* - '
|
|
304
|
+
* - 'hug': Label and switch are positioned next to each other
|
|
294
305
|
* - 'spread': Label and switch are pushed to opposite ends
|
|
295
|
-
*
|
|
306
|
+
*
|
|
307
|
+
* 'default' is a deprecated alias for 'hug'.
|
|
308
|
+
* @default 'hug'
|
|
296
309
|
*/
|
|
297
310
|
labelSpacing?: SwitchLabelSpacing;
|
|
298
311
|
/**
|
|
@@ -334,6 +347,7 @@ export function Switch({
|
|
|
334
347
|
isLoading = false,
|
|
335
348
|
value,
|
|
336
349
|
isDisabled = false,
|
|
350
|
+
htmlName,
|
|
337
351
|
disabledMessage,
|
|
338
352
|
isOptional = false,
|
|
339
353
|
isRequired = false,
|
|
@@ -342,13 +356,14 @@ export function Switch({
|
|
|
342
356
|
labelIcon,
|
|
343
357
|
labelTooltip,
|
|
344
358
|
labelPosition = 'end',
|
|
345
|
-
labelSpacing = '
|
|
359
|
+
labelSpacing = 'hug',
|
|
346
360
|
status,
|
|
347
361
|
width,
|
|
348
362
|
xstyle,
|
|
349
363
|
className,
|
|
350
364
|
style,
|
|
351
365
|
ref,
|
|
366
|
+
...rest
|
|
352
367
|
}: SwitchProps) {
|
|
353
368
|
const id = useId();
|
|
354
369
|
const descriptionID = useId();
|
|
@@ -360,6 +375,10 @@ export function Switch({
|
|
|
360
375
|
|
|
361
376
|
const isOn = optimisticValue === true;
|
|
362
377
|
|
|
378
|
+
// 'default' is a deprecated alias for 'hug' (#2889).
|
|
379
|
+
const resolvedLabelSpacing: SwitchLabelSpacing =
|
|
380
|
+
labelSpacing === 'default' ? 'hug' : labelSpacing;
|
|
381
|
+
|
|
363
382
|
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
364
383
|
// tooltip listeners attach to the switch row (which already exists) and the
|
|
365
384
|
// native checkbox stays perceivable via aria-disabled instead of the disabled
|
|
@@ -395,6 +414,10 @@ export function Switch({
|
|
|
395
414
|
id={id}
|
|
396
415
|
type="checkbox"
|
|
397
416
|
role="switch"
|
|
417
|
+
// Withhold the name while disabled: with a disabledMessage the
|
|
418
|
+
// input stays focusable (not natively disabled), and a disabled
|
|
419
|
+
// control must not submit.
|
|
420
|
+
name={isDisabled ? undefined : htmlName}
|
|
398
421
|
checked={isOn}
|
|
399
422
|
// With a disabledMessage the switch keeps focusability via aria-disabled
|
|
400
423
|
// so the reason is focus-discoverable; toggling is still blocked by the
|
|
@@ -475,12 +498,14 @@ export function Switch({
|
|
|
475
498
|
{...mergeProps(
|
|
476
499
|
themeProps('switch-field', {
|
|
477
500
|
labelPosition: labelPosition !== 'end' ? labelPosition : undefined,
|
|
478
|
-
labelSpacing:
|
|
501
|
+
labelSpacing:
|
|
502
|
+
resolvedLabelSpacing !== 'hug' ? resolvedLabelSpacing : undefined,
|
|
479
503
|
}),
|
|
480
504
|
stylex.props(width != null && dynamicWidthStyles.width(width), xstyle),
|
|
481
505
|
className,
|
|
482
506
|
style,
|
|
483
|
-
)}
|
|
507
|
+
)}
|
|
508
|
+
{...rest}>
|
|
484
509
|
<div
|
|
485
510
|
ref={el => {
|
|
486
511
|
// Interaction (hover/focus) listeners for the disabled-message
|
|
@@ -493,7 +518,7 @@ export function Switch({
|
|
|
493
518
|
}}
|
|
494
519
|
{...stylex.props(
|
|
495
520
|
styles.container,
|
|
496
|
-
|
|
521
|
+
resolvedLabelSpacing === 'spread' && styles.containerSpread,
|
|
497
522
|
!isDisabled && switchScope,
|
|
498
523
|
)}>
|
|
499
524
|
{' '}
|
|
@@ -92,7 +92,7 @@ export const docs = {
|
|
|
92
92
|
bestPractices: [
|
|
93
93
|
{ guidance: true, description: 'Keep tab labels short and descriptive so users can quickly scan available sections.' },
|
|
94
94
|
{ guidance: true, description: 'Use TabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.' },
|
|
95
|
-
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don\
|
|
95
|
+
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don\'t overpower the tab row.' },
|
|
96
96
|
{ guidance: false, description: 'Use tabs for sequential steps or workflows; use a stepper or wizard pattern instead.' },
|
|
97
97
|
{ guidance: false, description: 'Place more than 6–8 visible tabs before the overflow menu; prioritize the most important categories.' },
|
|
98
98
|
{ guidance: false, description: 'Confuse TabList with SegmentedControl or ToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls: SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.' },
|
|
@@ -113,7 +113,7 @@ export const docsZh = {
|
|
|
113
113
|
bestPractices: [
|
|
114
114
|
{ guidance: true, description: 'Keep tab labels short and descriptive so users can quickly scan available sections.' },
|
|
115
115
|
{ guidance: true, description: 'Use TabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.' },
|
|
116
|
-
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don\
|
|
116
|
+
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don\'t overpower the tab row.' },
|
|
117
117
|
{ guidance: false, description: 'Use tabs for sequential steps or workflows; use a stepper or wizard pattern instead.' },
|
|
118
118
|
{ guidance: false, description: 'Place more than 6–8 visible tabs before the overflow menu; prioritize the most important categories.' },
|
|
119
119
|
{ guidance: false, description: 'Confuse TabList with SegmentedControl or ToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls: SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.' },
|
|
@@ -135,7 +135,7 @@ export const docsDense = {
|
|
|
135
135
|
bestPractices: [
|
|
136
136
|
{ guidance: true, description: 'Keep tab labels short and descriptive so users can quickly scan available sections.' },
|
|
137
137
|
{ guidance: true, description: 'Use TabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.' },
|
|
138
|
-
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don\
|
|
138
|
+
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don\'t overpower the tab row.' },
|
|
139
139
|
{ guidance: false, description: 'Use tabs for sequential steps or workflows; use a stepper or wizard pattern instead.' },
|
|
140
140
|
{ guidance: false, description: 'Place more than 6–8 visible tabs before the overflow menu; prioritize the most important categories.' },
|
|
141
141
|
{ guidance: false, description: 'Confuse TabList with SegmentedControl or ToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls: SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.' },
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
|
|
13
|
-
import {render, screen} from '@testing-library/react';
|
|
13
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {TabList} from './TabList';
|
|
16
16
|
import {Tab} from './Tab';
|
|
@@ -702,3 +702,187 @@ describe('TabMenu', () => {
|
|
|
702
702
|
expect(reportsItem).not.toHaveAttribute('aria-current');
|
|
703
703
|
});
|
|
704
704
|
});
|
|
705
|
+
|
|
706
|
+
describe('TabMenu keyboard navigation (roving tabindex)', () => {
|
|
707
|
+
const menuOptions = [
|
|
708
|
+
{value: 'analytics', label: 'Analytics'},
|
|
709
|
+
{value: 'reports', label: 'Reports'},
|
|
710
|
+
];
|
|
711
|
+
|
|
712
|
+
it('exposes the overflow menu as a single Tab stop (one item tabbable, rest -1)', async () => {
|
|
713
|
+
const user = userEvent.setup();
|
|
714
|
+
render(
|
|
715
|
+
<TabList value="home" onChange={() => {}}>
|
|
716
|
+
<Tab value="home" label="Home" />
|
|
717
|
+
<TabMenu label="More" options={menuOptions} />
|
|
718
|
+
</TabList>,
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
722
|
+
|
|
723
|
+
const analytics = screen.getByRole('menuitem', {
|
|
724
|
+
name: 'Analytics',
|
|
725
|
+
hidden: true,
|
|
726
|
+
});
|
|
727
|
+
const reports = screen.getByRole('menuitem', {
|
|
728
|
+
name: 'Reports',
|
|
729
|
+
hidden: true,
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// Exactly one menu item is in the Tab sequence; arrow keys reach the rest.
|
|
733
|
+
expect(analytics).toHaveAttribute('tabindex', '0');
|
|
734
|
+
expect(reports).toHaveAttribute('tabindex', '-1');
|
|
735
|
+
|
|
736
|
+
const tabbable = [analytics, reports].filter(
|
|
737
|
+
el => el.getAttribute('tabindex') === '0',
|
|
738
|
+
);
|
|
739
|
+
expect(tabbable).toHaveLength(1);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
it('moves focus between items with ArrowDown and ArrowUp', async () => {
|
|
743
|
+
const user = userEvent.setup();
|
|
744
|
+
render(
|
|
745
|
+
<TabList value="home" onChange={() => {}}>
|
|
746
|
+
<Tab value="home" label="Home" />
|
|
747
|
+
<TabMenu label="More" options={menuOptions} />
|
|
748
|
+
</TabList>,
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
752
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
753
|
+
const analytics = screen.getByRole('menuitem', {
|
|
754
|
+
name: 'Analytics',
|
|
755
|
+
hidden: true,
|
|
756
|
+
});
|
|
757
|
+
const reports = screen.getByRole('menuitem', {
|
|
758
|
+
name: 'Reports',
|
|
759
|
+
hidden: true,
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
fireEvent.keyDown(menu, {key: 'ArrowDown'});
|
|
763
|
+
expect(analytics).toHaveFocus();
|
|
764
|
+
|
|
765
|
+
fireEvent.keyDown(menu, {key: 'ArrowDown'});
|
|
766
|
+
expect(reports).toHaveFocus();
|
|
767
|
+
|
|
768
|
+
fireEvent.keyDown(menu, {key: 'ArrowUp'});
|
|
769
|
+
expect(analytics).toHaveFocus();
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
it('moves the roving tab stop with arrow navigation', async () => {
|
|
773
|
+
const user = userEvent.setup();
|
|
774
|
+
render(
|
|
775
|
+
<TabList value="home" onChange={() => {}}>
|
|
776
|
+
<Tab value="home" label="Home" />
|
|
777
|
+
<TabMenu label="More" options={menuOptions} />
|
|
778
|
+
</TabList>,
|
|
779
|
+
);
|
|
780
|
+
|
|
781
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
782
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
783
|
+
const analytics = screen.getByRole('menuitem', {
|
|
784
|
+
name: 'Analytics',
|
|
785
|
+
hidden: true,
|
|
786
|
+
});
|
|
787
|
+
const reports = screen.getByRole('menuitem', {
|
|
788
|
+
name: 'Reports',
|
|
789
|
+
hidden: true,
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
fireEvent.keyDown(menu, {key: 'ArrowDown'}); // focus analytics
|
|
793
|
+
fireEvent.keyDown(menu, {key: 'ArrowDown'}); // focus reports
|
|
794
|
+
|
|
795
|
+
// The tab stop follows focus, so it is still a single stop after moving.
|
|
796
|
+
expect(reports).toHaveAttribute('tabindex', '0');
|
|
797
|
+
expect(analytics).toHaveAttribute('tabindex', '-1');
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('jumps to first and last item with Home and End', async () => {
|
|
801
|
+
const user = userEvent.setup();
|
|
802
|
+
const options = [
|
|
803
|
+
{value: 'analytics', label: 'Analytics'},
|
|
804
|
+
{value: 'reports', label: 'Reports'},
|
|
805
|
+
{value: 'exports', label: 'Exports'},
|
|
806
|
+
];
|
|
807
|
+
render(
|
|
808
|
+
<TabList value="home" onChange={() => {}}>
|
|
809
|
+
<Tab value="home" label="Home" />
|
|
810
|
+
<TabMenu label="More" options={options} />
|
|
811
|
+
</TabList>,
|
|
812
|
+
);
|
|
813
|
+
|
|
814
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
815
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
816
|
+
const analytics = screen.getByRole('menuitem', {
|
|
817
|
+
name: 'Analytics',
|
|
818
|
+
hidden: true,
|
|
819
|
+
});
|
|
820
|
+
const exports = screen.getByRole('menuitem', {
|
|
821
|
+
name: 'Exports',
|
|
822
|
+
hidden: true,
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
fireEvent.keyDown(menu, {key: 'End'});
|
|
826
|
+
expect(exports).toHaveFocus();
|
|
827
|
+
|
|
828
|
+
fireEvent.keyDown(menu, {key: 'Home'});
|
|
829
|
+
expect(analytics).toHaveFocus();
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it('selects an item with Enter and calls onChange', async () => {
|
|
833
|
+
const user = userEvent.setup();
|
|
834
|
+
const handleChange = vi.fn();
|
|
835
|
+
render(
|
|
836
|
+
<TabList value="home" onChange={handleChange}>
|
|
837
|
+
<Tab value="home" label="Home" />
|
|
838
|
+
<TabMenu label="More" options={menuOptions} />
|
|
839
|
+
</TabList>,
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
843
|
+
const analytics = screen.getByRole('menuitem', {
|
|
844
|
+
name: 'Analytics',
|
|
845
|
+
hidden: true,
|
|
846
|
+
});
|
|
847
|
+
analytics.focus();
|
|
848
|
+
fireEvent.keyDown(analytics, {key: 'Enter'});
|
|
849
|
+
|
|
850
|
+
expect(handleChange).toHaveBeenCalledWith('analytics');
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
it('closes the menu when Tab is pressed inside it (APG menu-button)', async () => {
|
|
854
|
+
const user = userEvent.setup();
|
|
855
|
+
render(
|
|
856
|
+
<TabList value="home" onChange={() => {}}>
|
|
857
|
+
<Tab value="home" label="Home" />
|
|
858
|
+
<TabMenu label="More" options={menuOptions} />
|
|
859
|
+
</TabList>,
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
863
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
864
|
+
|
|
865
|
+
const hidePopover = vi.mocked(HTMLElement.prototype.hidePopover);
|
|
866
|
+
hidePopover.mockClear();
|
|
867
|
+
fireEvent.keyDown(menu, {key: 'Tab'});
|
|
868
|
+
expect(hidePopover).toHaveBeenCalled();
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it('closes the menu when Escape is pressed', async () => {
|
|
872
|
+
const user = userEvent.setup();
|
|
873
|
+
render(
|
|
874
|
+
<TabList value="home" onChange={() => {}}>
|
|
875
|
+
<Tab value="home" label="Home" />
|
|
876
|
+
<TabMenu label="More" options={menuOptions} />
|
|
877
|
+
</TabList>,
|
|
878
|
+
);
|
|
879
|
+
|
|
880
|
+
await user.click(screen.getByRole('button', {name: /More/}));
|
|
881
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
882
|
+
|
|
883
|
+
const hidePopover = vi.mocked(HTMLElement.prototype.hidePopover);
|
|
884
|
+
hidePopover.mockClear();
|
|
885
|
+
fireEvent.keyDown(menu, {key: 'Escape'});
|
|
886
|
+
expect(hidePopover).toHaveBeenCalled();
|
|
887
|
+
});
|
|
888
|
+
});
|
package/src/TabList/TabMenu.tsx
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* - /packages/cli/templates/blocks/components/TabList/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import React, {useCallback, useId, type ReactNode} from 'react';
|
|
19
|
+
import React, {useCallback, useId, useRef, type ReactNode} from 'react';
|
|
20
20
|
import * as stylex from '@stylexjs/stylex';
|
|
21
21
|
import {Icon} from '../Icon';
|
|
22
22
|
import {renderIconSlot, type IconType} from '../Icon';
|
|
@@ -262,6 +262,7 @@ export function TabMenu({
|
|
|
262
262
|
}: TabMenuProps) {
|
|
263
263
|
const tabListCtx = useTabListContext();
|
|
264
264
|
const menuId = useId();
|
|
265
|
+
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
265
266
|
|
|
266
267
|
const popover = usePopover({
|
|
267
268
|
hasLightDismiss: true,
|
|
@@ -270,20 +271,50 @@ export function TabMenu({
|
|
|
270
271
|
// The popup's own role="menu" is the exposed semantics; a modal dialog
|
|
271
272
|
// wrapper would announce an unnamed dialog around the menu.
|
|
272
273
|
role: 'none',
|
|
274
|
+
// Return focus to the trigger when the menu closes (Escape, Tab, select,
|
|
275
|
+
// or light dismiss) so keyboard focus is never dropped to <body>.
|
|
276
|
+
onHide: useCallback(() => {
|
|
277
|
+
buttonRef.current?.focus();
|
|
278
|
+
}, []),
|
|
273
279
|
});
|
|
274
280
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
281
|
+
// The overflow menu is a composite widget: a single roving tab stop with
|
|
282
|
+
// arrow-key navigation between items, per the APG menu pattern. The hook owns
|
|
283
|
+
// item tabindex — items render tabIndex={-1} and exactly one is promoted to 0.
|
|
284
|
+
const {
|
|
285
|
+
listRef,
|
|
286
|
+
handleKeyDown: handleListKeyDown,
|
|
287
|
+
handleFocus,
|
|
288
|
+
focusFirst,
|
|
289
|
+
} = useListFocus<HTMLDivElement>({
|
|
290
|
+
hasRovingTabIndex: true,
|
|
291
|
+
onEscape: () => popover.hide(),
|
|
292
|
+
});
|
|
279
293
|
|
|
280
294
|
const handleToggle = useCallback(() => {
|
|
281
295
|
if (popover.isOpen) {
|
|
282
296
|
popover.hide();
|
|
283
297
|
} else {
|
|
284
298
|
popover.show();
|
|
299
|
+
// Move focus into the menu on open so arrow navigation works and the
|
|
300
|
+
// roving tab stop lands on a real item (APG menu-button focus-on-open).
|
|
301
|
+
requestAnimationFrame(() => focusFirst());
|
|
285
302
|
}
|
|
286
|
-
}, [popover]);
|
|
303
|
+
}, [popover, focusFirst]);
|
|
304
|
+
|
|
305
|
+
const handleMenuKeyDown = useCallback(
|
|
306
|
+
(e: React.KeyboardEvent) => {
|
|
307
|
+
// APG menu-button: Tab leaves the menu rather than walking its items.
|
|
308
|
+
// Close and let onHide return focus to the trigger, from which the
|
|
309
|
+
// browser's default Tab continues to the next element.
|
|
310
|
+
if (e.key === 'Tab') {
|
|
311
|
+
popover.hide();
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
handleListKeyDown(e);
|
|
315
|
+
},
|
|
316
|
+
[handleListKeyDown, popover],
|
|
317
|
+
);
|
|
287
318
|
|
|
288
319
|
const selectedOption = options.find(o => o.value === tabListCtx.value);
|
|
289
320
|
const triggerLabel = selectedOption?.label ?? label;
|
|
@@ -299,7 +330,11 @@ export function TabMenu({
|
|
|
299
330
|
[tabListCtx, popover],
|
|
300
331
|
);
|
|
301
332
|
|
|
302
|
-
const setButtonRef = mergeRefs<HTMLButtonElement>(
|
|
333
|
+
const setButtonRef = mergeRefs<HTMLButtonElement>(
|
|
334
|
+
popover.triggerRef,
|
|
335
|
+
buttonRef,
|
|
336
|
+
ref,
|
|
337
|
+
);
|
|
303
338
|
|
|
304
339
|
return (
|
|
305
340
|
<>
|
|
@@ -357,7 +392,8 @@ export function TabMenu({
|
|
|
357
392
|
id={menuId}
|
|
358
393
|
role="menu"
|
|
359
394
|
aria-label={label}
|
|
360
|
-
onKeyDown={
|
|
395
|
+
onKeyDown={handleMenuKeyDown}
|
|
396
|
+
onFocus={handleFocus}
|
|
361
397
|
{...mergeProps(
|
|
362
398
|
themeProps('tab-menu-dropdown'),
|
|
363
399
|
stylex.props(styles.dropdown),
|
|
@@ -371,7 +407,7 @@ export function TabMenu({
|
|
|
371
407
|
<div
|
|
372
408
|
key={option.value}
|
|
373
409
|
role="menuitem"
|
|
374
|
-
tabIndex={
|
|
410
|
+
tabIndex={-1}
|
|
375
411
|
aria-current={isSelected ? 'true' : undefined}
|
|
376
412
|
onClick={() => handleSelect(option.value)}
|
|
377
413
|
onKeyDown={e => {
|