@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2
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 +105 -0
- package/README.md +131 -13
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +11 -5
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
- package/dist/Banner/Banner.d.ts +7 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +9 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
- package/dist/Button/Button.d.ts +12 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +53 -17
- package/dist/Calendar/Calendar.d.ts +5 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +82 -43
- package/dist/Calendar/hooks/index.d.ts +0 -2
- package/dist/Calendar/hooks/index.d.ts.map +1 -1
- package/dist/Calendar/hooks/index.js +1 -2
- package/dist/Calendar/index.d.ts +3 -3
- package/dist/Calendar/index.d.ts.map +1 -1
- package/dist/Calendar/index.js +1 -1
- package/dist/Calendar/styles.d.ts +3 -9
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +3 -11
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +17 -3
- package/dist/Center/Center.d.ts +11 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +9 -3
- package/dist/Chat/ChatMessageList.d.ts +14 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +2 -0
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +4 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +51 -9
- package/dist/CheckboxList/CheckboxList.d.ts +13 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +39 -9
- package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +3 -3
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +49 -37
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -5
- package/dist/ContextMenu/ContextMenu.d.ts +4 -5
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +93 -11
- package/dist/DateInput/DateInput.d.ts +23 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +59 -8
- package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +42 -9
- package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +78 -15
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +12 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +42 -14
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +7 -1
- package/dist/Field/Field.d.ts +19 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -0
- package/dist/Field/FieldLabel.d.ts +21 -2
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +41 -26
- package/dist/FileInput/FileInput.d.ts +22 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +56 -10
- package/dist/Grid/Grid.d.ts +11 -1
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +8 -0
- package/dist/HoverCard/HoverCard.d.ts +2 -2
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +18 -6
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +7 -3
- package/dist/InputGroup/InputGroup.d.ts +2 -2
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +14 -5
- package/dist/InputGroup/InputGroupContext.d.ts +4 -0
- package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
- package/dist/InputGroup/InputGroupContext.js +2 -2
- package/dist/InputGroup/index.d.ts +1 -0
- package/dist/InputGroup/index.d.ts.map +1 -1
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +34 -2
- package/dist/Layer/anchorName.d.ts +20 -0
- package/dist/Layer/anchorName.d.ts.map +1 -0
- package/dist/Layer/anchorName.js +36 -0
- package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +8 -4
- package/dist/Layer/useLayer.d.ts +29 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +55 -11
- package/dist/Layout/Layout.d.ts +4 -11
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +10 -11
- package/dist/Layout/LayoutFooter.d.ts +2 -2
- package/dist/Layout/LayoutFooter.d.ts.map +1 -1
- package/dist/Layout/LayoutHeader.d.ts +2 -2
- package/dist/Layout/LayoutHeader.d.ts.map +1 -1
- package/dist/Layout/LayoutPanel.d.ts +2 -2
- package/dist/Layout/LayoutPanel.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.d.ts +51 -0
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +63 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +4 -2
- package/dist/Link/Link.d.ts +17 -2
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +20 -9
- package/dist/Link/LinkProvider.d.ts +1 -0
- package/dist/Link/LinkProvider.d.ts.map +1 -1
- package/dist/Link/LinkProvider.js +1 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +18 -3
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +13 -0
- package/dist/MoreMenu/MoreMenu.d.ts +1 -7
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +0 -2
- package/dist/MultiSelector/MultiSelector.d.ts +25 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +103 -22
- package/dist/MultiSelector/hooks.d.ts +12 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +14 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +34 -3
- package/dist/NumberInput/NumberInput.d.ts +23 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +62 -12
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +46 -28
- package/dist/Popover/usePopover.d.ts +25 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +18 -6
- package/dist/PowerSearch/PowerSearch.d.ts +11 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +6 -1
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +3 -5
- package/dist/RadioList/RadioList.d.ts +20 -2
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +105 -9
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +78 -44
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
- package/dist/Selector/Selector.d.ts +22 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +84 -23
- package/dist/Selector/hooks.d.ts +8 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +14 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +8 -2
- package/dist/Slider/Slider.d.ts +21 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +38 -11
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Stack/Stack.d.ts +38 -23
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +25 -1
- package/dist/Stack/StackItem.d.ts +12 -23
- package/dist/Stack/StackItem.d.ts.map +1 -1
- package/dist/Stack/StackItem.js +8 -1
- package/dist/Switch/Switch.d.ts +23 -2
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -11
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +6 -0
- package/dist/TabList/TabList.d.ts +16 -4
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +86 -10
- package/dist/TabList/TabListContext.d.ts +5 -0
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +5 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +38 -16
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +36 -7
- package/dist/Table/TableCell.d.ts +8 -1
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +10 -10
- package/dist/Table/TableHeaderCell.d.ts +8 -1
- package/dist/Table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Table/TableHeaderCell.js +16 -16
- package/dist/Table/TableRow.d.ts +6 -1
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +15 -7
- package/dist/Table/index.d.ts +4 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +3 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +8 -3
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
- package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
- package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
- package/dist/Table/plugins/stickyColumns/index.js +3 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
- package/dist/Table/tableContextMenu.d.ts +33 -0
- package/dist/Table/tableContextMenu.d.ts.map +1 -0
- package/dist/Table/tableContextMenu.js +133 -0
- package/dist/Table/types.d.ts +161 -5
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +1 -1
- package/dist/Text/text.stylex.d.ts +2 -2
- package/dist/Text/text.stylex.js +2 -2
- package/dist/TextArea/TextArea.d.ts +22 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +41 -9
- package/dist/TextInput/TextInput.d.ts +23 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +55 -10
- package/dist/TimeInput/TimeInput.d.ts +23 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +53 -10
- package/dist/Timestamp/Timestamp.d.ts +1 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +28 -3
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +14 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +132 -3
- package/dist/ToggleButton/ToggleButton.d.ts +7 -9
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +34 -57
- package/dist/Tokenizer/Tokenizer.d.ts +12 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +27 -5
- package/dist/Toolbar/Toolbar.d.ts +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +51 -7
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +57 -9
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +72 -2
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +51 -12
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
- package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
- package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +67 -6
- package/dist/Typeahead/Typeahead.d.ts +24 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +29 -5
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
- package/dist/VisuallyHidden/index.d.ts +11 -0
- package/dist/VisuallyHidden/index.d.ts.map +1 -0
- package/dist/VisuallyHidden/index.js +13 -0
- package/dist/astryx.css +40 -1
- package/dist/astryx.umd.js +147 -0
- package/dist/astryx.umd.js.map +7 -0
- package/dist/hooks/focusableSelector.d.ts +22 -0
- package/dist/hooks/focusableSelector.d.ts.map +1 -0
- package/dist/hooks/focusableSelector.js +24 -0
- package/dist/hooks/index.d.ts +12 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +7 -2
- package/dist/hooks/useAnnounce.d.ts +40 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -0
- package/dist/hooks/useAnnounce.js +142 -0
- package/dist/hooks/useClickableContainer.d.ts +16 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +16 -4
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +4 -4
- package/dist/hooks/useFocusTrap.d.ts +18 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +87 -3
- package/dist/hooks/useGridFocus.d.ts +88 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +261 -62
- package/dist/hooks/useKeyboardHint.d.ts +82 -0
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
- package/dist/hooks/useKeyboardHint.js +220 -0
- package/dist/hooks/useListFocus.d.ts +78 -7
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +305 -52
- package/dist/hooks/useLongPress.d.ts +21 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -0
- package/dist/hooks/useLongPress.js +89 -0
- package/dist/hooks/useTreeFocus.d.ts +157 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -0
- package/dist/hooks/useTreeFocus.js +337 -0
- package/dist/hooks/useTypeahead.d.ts +63 -0
- package/dist/hooks/useTypeahead.d.ts.map +1 -0
- package/dist/hooks/useTypeahead.js +117 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/generateThemeRules.js +1 -1
- package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
- package/dist/theme/syntax/SyntaxTheme.js +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
- package/dist/theme/syntax/presets.d.ts +1 -1
- package/dist/theme/syntax/presets.d.ts.map +1 -1
- package/dist/theme/syntax/presets.js +1 -1
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.js +1 -1
- package/dist/theme/types.d.ts +1 -1
- package/dist/utils/dateTypes.d.ts +12 -0
- package/dist/utils/dateTypes.d.ts.map +1 -1
- package/dist/utils/dateTypes.js +41 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/inputAria.d.ts +29 -0
- package/dist/utils/inputAria.d.ts.map +1 -0
- package/dist/utils/inputAria.js +30 -0
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -2
- package/package.json +15 -5
- package/src/Avatar/Avatar.test.tsx +37 -0
- package/src/Avatar/Avatar.tsx +10 -5
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
- package/src/Banner/Banner.doc.mjs +8 -0
- package/src/Banner/Banner.test.tsx +16 -7
- package/src/Banner/Banner.tsx +9 -2
- package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
- package/src/Button/Button.doc.mjs +6 -0
- package/src/Button/Button.test.tsx +105 -11
- package/src/Button/Button.tsx +76 -21
- package/src/Calendar/Calendar.doc.mjs +4 -4
- package/src/Calendar/Calendar.test.tsx +168 -30
- package/src/Calendar/Calendar.tsx +121 -66
- package/src/Calendar/hooks/index.ts +0 -6
- package/src/Calendar/index.ts +1 -3
- package/src/Calendar/styles.ts +4 -9
- package/src/Carousel/Carousel.doc.mjs +7 -1
- package/src/Carousel/Carousel.test.tsx +20 -0
- package/src/Carousel/Carousel.tsx +16 -1
- package/src/Center/Center.doc.mjs +12 -2
- package/src/Center/Center.tsx +28 -3
- package/src/Chat/ChatComposer.doc.mjs +3 -0
- package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
- package/src/Chat/ChatMessageList.doc.mjs +8 -0
- package/src/Chat/ChatMessageList.test.tsx +18 -0
- package/src/Chat/ChatMessageList.tsx +16 -0
- package/src/Chat/useTriggerMenu.tsx +3 -0
- package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
- package/src/CheckboxInput/CheckboxInput.tsx +78 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
- package/src/CheckboxList/CheckboxList.test.tsx +169 -16
- package/src/CheckboxList/CheckboxList.tsx +56 -3
- package/src/CheckboxList/CheckboxListContext.tsx +7 -0
- package/src/Citation/Citation.doc.mjs +10 -0
- package/src/Citation/Citation.test.tsx +117 -0
- package/src/Citation/Citation.tsx +9 -1
- package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
- package/src/CodeBlock/CodeBlock.test.tsx +115 -0
- package/src/CodeBlock/CodeBlock.tsx +61 -33
- package/src/CommandPalette/CommandPalette.test.tsx +47 -1
- package/src/CommandPalette/CommandPalette.tsx +9 -21
- package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
- package/src/ContextMenu/ContextMenu.test.tsx +155 -1
- package/src/ContextMenu/ContextMenu.tsx +113 -16
- package/src/DateInput/DateInput.doc.mjs +281 -56
- package/src/DateInput/DateInput.test.tsx +188 -2
- package/src/DateInput/DateInput.tsx +89 -10
- package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
- package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
- package/src/DateRangeInput/DateRangeInput.tsx +63 -5
- package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
- package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
- package/src/DateTimeInput/DateTimeInput.tsx +125 -17
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.tsx +12 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
- package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
- package/src/DropdownMenu/DropdownMenu.tsx +52 -18
- package/src/EmptyState/EmptyState.test.tsx +4 -2
- package/src/EmptyState/EmptyState.tsx +6 -2
- package/src/Field/Field.test.tsx +32 -0
- package/src/Field/Field.tsx +23 -5
- package/src/Field/FieldLabel.tsx +55 -17
- package/src/FileInput/FileInput.doc.mjs +17 -0
- package/src/FileInput/FileInput.test.tsx +283 -49
- package/src/FileInput/FileInput.tsx +76 -4
- package/src/Grid/Grid.doc.mjs +14 -4
- package/src/Grid/Grid.tsx +20 -0
- package/src/HoverCard/HoverCard.doc.mjs +3 -0
- package/src/HoverCard/HoverCard.test.tsx +189 -2
- package/src/HoverCard/HoverCard.tsx +20 -16
- package/src/HoverCard/useHoverCard.tsx +13 -10
- package/src/Icon/Icon.doc.mjs +8 -0
- package/src/InputGroup/InputGroup.doc.mjs +2 -2
- package/src/InputGroup/InputGroup.test.tsx +98 -60
- package/src/InputGroup/InputGroup.tsx +21 -4
- package/src/InputGroup/InputGroupContext.ts +9 -4
- package/src/InputGroup/index.ts +1 -0
- package/src/Kbd/Kbd.test.tsx +22 -4
- package/src/Kbd/Kbd.tsx +37 -12
- package/src/Layer/anchorName.test.ts +146 -0
- package/src/Layer/anchorName.ts +46 -0
- package/src/Layer/layerAnimations.stylex.ts +20 -4
- package/src/Layer/useLayer.doc.mjs +12 -2
- package/src/Layer/useLayer.tsx +100 -15
- package/src/Layout/Layout.doc.mjs +1 -1
- package/src/Layout/Layout.tsx +8 -16
- package/src/Layout/LayoutFooter.doc.mjs +4 -4
- package/src/Layout/LayoutFooter.tsx +3 -3
- package/src/Layout/LayoutHeader.doc.mjs +4 -4
- package/src/Layout/LayoutHeader.tsx +3 -3
- package/src/Layout/LayoutPanel.tsx +3 -3
- package/src/Layout/padding.stylex.ts +52 -0
- package/src/Lightbox/Lightbox.doc.mjs +9 -2
- package/src/Lightbox/Lightbox.test.tsx +53 -5
- package/src/Lightbox/Lightbox.tsx +9 -4
- package/src/Link/Link.doc.mjs +14 -0
- package/src/Link/Link.test.tsx +52 -2
- package/src/Link/Link.tsx +42 -7
- package/src/Link/LinkProvider.tsx +1 -0
- package/src/List/List.doc.mjs +7 -0
- package/src/Markdown/Markdown.doc.mjs +2 -0
- package/src/Markdown/Markdown.test.tsx +28 -26
- package/src/Markdown/Markdown.tsx +21 -6
- package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
- package/src/MobileNav/MobileNav.doc.mjs +8 -8
- package/src/MobileNav/MobileNav.tsx +13 -0
- package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
- package/src/MoreMenu/MoreMenu.tsx +0 -9
- package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
- package/src/MultiSelector/MultiSelector.test.tsx +375 -8
- package/src/MultiSelector/MultiSelector.tsx +145 -21
- package/src/MultiSelector/hooks.ts +27 -0
- package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
- package/src/NavMenu/NavHeadingMenu.tsx +50 -3
- package/src/NumberInput/NumberInput.doc.mjs +17 -0
- package/src/NumberInput/NumberInput.test.tsx +242 -40
- package/src/NumberInput/NumberInput.tsx +89 -11
- package/src/OverflowList/OverflowList.doc.mjs +17 -0
- package/src/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +250 -14
- package/src/Pagination/Pagination.tsx +54 -29
- package/src/Popover/usePopover.doc.mjs +7 -3
- package/src/Popover/usePopover.tsx +49 -6
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +97 -1
- package/src/PowerSearch/PowerSearch.tsx +16 -3
- package/src/ProgressBar/ProgressBar.test.tsx +45 -27
- package/src/ProgressBar/ProgressBar.tsx +3 -4
- package/src/RadioList/RadioList.doc.mjs +9 -0
- package/src/RadioList/RadioList.test.tsx +252 -16
- package/src/RadioList/RadioList.tsx +166 -10
- package/src/RadioList/RadioListItem.tsx +14 -2
- package/src/Resizable/Resizable.doc.mjs +1 -1
- package/src/Section/Section.doc.mjs +11 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
- package/src/SegmentedControl/SegmentedControl.tsx +102 -54
- package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
- package/src/Selector/Selector.doc.mjs +25 -0
- package/src/Selector/Selector.test.tsx +322 -15
- package/src/Selector/Selector.tsx +126 -40
- package/src/Selector/hooks.ts +23 -0
- package/src/SideNav/SideNav.test.tsx +16 -0
- package/src/SideNav/SideNavItem.tsx +2 -4
- package/src/Skeleton/Skeleton.doc.mjs +11 -1
- package/src/Skeleton/Skeleton.test.tsx +25 -0
- package/src/Skeleton/Skeleton.tsx +11 -1
- package/src/Slider/Slider.doc.mjs +19 -0
- package/src/Slider/Slider.test.tsx +158 -18
- package/src/Slider/Slider.tsx +54 -6
- package/src/Spinner/Spinner.tsx +7 -1
- package/src/Stack/Stack.doc.mjs +77 -0
- package/src/Stack/Stack.test.tsx +55 -5
- package/src/Stack/Stack.tsx +73 -24
- package/src/Stack/StackItem.test.tsx +23 -0
- package/src/Stack/StackItem.tsx +20 -23
- package/src/Switch/Switch.doc.mjs +15 -0
- package/src/Switch/Switch.test.tsx +162 -27
- package/src/Switch/Switch.tsx +61 -21
- package/src/TabList/Tab.tsx +6 -0
- package/src/TabList/TabList.doc.mjs +12 -0
- package/src/TabList/TabList.test.tsx +208 -0
- package/src/TabList/TabList.tsx +102 -12
- package/src/TabList/TabListContext.ts +7 -3
- package/src/TabList/TabMenu.tsx +6 -3
- package/src/Table/BaseTable.tsx +72 -41
- package/src/Table/Table.doc.mjs +14 -0
- package/src/Table/Table.test.tsx +39 -30
- package/src/Table/Table.tsx +27 -1
- package/src/Table/TableCell.tsx +25 -23
- package/src/Table/TableHeaderCell.tsx +28 -29
- package/src/Table/TableRow.tsx +37 -7
- package/src/Table/index.ts +6 -2
- package/src/Table/plugins/pagination/paginateData.ts +10 -3
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
- package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
- package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
- package/src/Table/plugins/stickyColumns/index.ts +4 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
- package/src/Table/tableContextMenu.test.tsx +157 -0
- package/src/Table/tableContextMenu.tsx +142 -0
- package/src/Table/types.ts +175 -4
- package/src/Table/useBaseTablePlugins.ts +1 -0
- package/src/Table/useTableColumnResize.doc.mjs +96 -0
- package/src/Table/useTableStickyColumns.doc.mjs +59 -0
- package/src/Text/Heading.doc.mjs +15 -2
- package/src/Text/Text.doc.mjs +7 -1
- package/src/Text/text.stylex.ts +2 -2
- package/src/TextArea/TextArea.doc.mjs +17 -0
- package/src/TextArea/TextArea.test.tsx +167 -35
- package/src/TextArea/TextArea.tsx +62 -15
- package/src/TextInput/TextInput.doc.mjs +17 -0
- package/src/TextInput/TextInput.test.tsx +168 -34
- package/src/TextInput/TextInput.tsx +77 -9
- package/src/TimeInput/TimeInput.doc.mjs +335 -60
- package/src/TimeInput/TimeInput.test.tsx +144 -7
- package/src/TimeInput/TimeInput.tsx +74 -5
- package/src/Timestamp/Timestamp.doc.mjs +9 -0
- package/src/Timestamp/Timestamp.test.tsx +44 -18
- package/src/Timestamp/Timestamp.tsx +30 -10
- package/src/Toast/Toast.tsx +14 -0
- package/src/Toast/ToastViewport.test.tsx +204 -0
- package/src/Toast/ToastViewport.tsx +167 -21
- package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
- package/src/ToggleButton/ToggleButton.test.tsx +39 -18
- package/src/ToggleButton/ToggleButton.tsx +46 -83
- package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
- package/src/Tokenizer/Tokenizer.test.tsx +105 -1
- package/src/Tokenizer/Tokenizer.tsx +41 -2
- package/src/Toolbar/Toolbar.doc.mjs +65 -5
- package/src/Toolbar/Toolbar.test.tsx +109 -6
- package/src/Toolbar/Toolbar.tsx +59 -5
- package/src/Tooltip/Tooltip.test.tsx +81 -0
- package/src/Tooltip/useTooltip.tsx +57 -11
- package/src/TreeList/TreeList.test.tsx +295 -6
- package/src/TreeList/TreeList.tsx +73 -1
- package/src/TreeList/TreeListItem.tsx +51 -5
- package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
- package/src/Typeahead/BaseTypeahead.tsx +86 -9
- package/src/Typeahead/Typeahead.doc.mjs +136 -30
- package/src/Typeahead/Typeahead.test.tsx +331 -1
- package/src/Typeahead/Typeahead.tsx +52 -1
- package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
- package/src/VisuallyHidden/index.ts +15 -0
- package/src/hooks/focusableSelector.ts +25 -0
- package/src/hooks/index.ts +28 -2
- package/src/hooks/useAnnounce.test.tsx +101 -0
- package/src/hooks/useAnnounce.ts +168 -0
- package/src/hooks/useClickableContainer.ts +16 -4
- package/src/hooks/useEntryAnimation.ts +16 -4
- package/src/hooks/useFocusTrap.test.tsx +132 -0
- package/src/hooks/useFocusTrap.ts +98 -4
- package/src/hooks/useGridFocus.doc.mjs +40 -2
- package/src/hooks/useGridFocus.test.tsx +132 -0
- package/src/hooks/useGridFocus.ts +343 -67
- package/src/hooks/useImageMode.test.ts +133 -0
- package/src/hooks/useKeyboardHint.doc.mjs +101 -0
- package/src/hooks/useKeyboardHint.test.tsx +70 -0
- package/src/hooks/useKeyboardHint.tsx +332 -0
- package/src/hooks/useListFocus.doc.mjs +50 -10
- package/src/hooks/useListFocus.test.tsx +375 -0
- package/src/hooks/useListFocus.ts +395 -57
- package/src/hooks/useLongPress.test.tsx +134 -0
- package/src/hooks/useLongPress.ts +119 -0
- package/src/hooks/useTreeFocus.doc.mjs +140 -0
- package/src/hooks/useTreeFocus.test.tsx +214 -0
- package/src/hooks/useTreeFocus.ts +559 -0
- package/src/hooks/useTypeahead.test.tsx +109 -0
- package/src/hooks/useTypeahead.ts +183 -0
- package/src/index.ts +1 -0
- package/src/theme/derivedVarRegistry.test.ts +4 -0
- package/src/theme/generateThemeRules.test.ts +7 -8
- package/src/theme/generateThemeRules.ts +1 -1
- package/src/theme/syntax/SyntaxTheme.tsx +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
- package/src/theme/syntax/presets.ts +5 -2
- package/src/theme/syntax/tokens.ts +1 -1
- package/src/theme/types.ts +1 -1
- package/src/utils/dateTypes.test.ts +40 -0
- package/src/utils/dateTypes.ts +40 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/inputAria.test.ts +39 -0
- package/src/utils/inputAria.ts +58 -0
- package/src/utils/timeParser.test.ts +10 -0
- package/src/utils/timeParser.ts +5 -2
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
- package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
- package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import {useState} from 'react';
|
|
13
13
|
import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
|
|
14
|
-
import {render, screen, act} from '@testing-library/react';
|
|
14
|
+
import {render, screen, act, fireEvent, waitFor} from '@testing-library/react';
|
|
15
15
|
import userEvent from '@testing-library/user-event';
|
|
16
16
|
import {PowerSearch} from './PowerSearch';
|
|
17
17
|
import type {PowerSearchConfig, PowerSearchFilter} from './types';
|
|
@@ -199,4 +199,100 @@ describe('PowerSearch', () => {
|
|
|
199
199
|
expect(pasteResults).toEqual(typeResults);
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
|
+
|
|
203
|
+
describe('disabledMessage', () => {
|
|
204
|
+
const h = {hidden: true} as const;
|
|
205
|
+
const isOpen = (el: Element) => el.matches(':popover-open');
|
|
206
|
+
|
|
207
|
+
function renderSearch(props?: {onChange?: () => void}) {
|
|
208
|
+
return render(
|
|
209
|
+
<PowerSearch
|
|
210
|
+
config={config}
|
|
211
|
+
filters={[]}
|
|
212
|
+
onChange={props?.onChange ?? (() => {})}
|
|
213
|
+
isDisabled
|
|
214
|
+
disabledMessage="You need edit access to search"
|
|
215
|
+
/>,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
220
|
+
renderSearch();
|
|
221
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
222
|
+
expect(tooltip).toHaveTextContent('You need edit access to search');
|
|
223
|
+
const wrapper = screen.getByRole('group');
|
|
224
|
+
fireEvent.mouseEnter(wrapper);
|
|
225
|
+
await waitFor(() => expect(isOpen(tooltip)).toBe(true));
|
|
226
|
+
fireEvent.mouseLeave(wrapper);
|
|
227
|
+
await waitFor(() => expect(isOpen(tooltip)).toBe(false));
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
231
|
+
const user = userEvent.setup();
|
|
232
|
+
renderSearch();
|
|
233
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
234
|
+
await user.tab();
|
|
235
|
+
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
236
|
+
await waitFor(() => expect(isOpen(tooltip)).toBe(true));
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('does not render a tooltip when not disabled', () => {
|
|
240
|
+
render(
|
|
241
|
+
<PowerSearch
|
|
242
|
+
config={config}
|
|
243
|
+
filters={[]}
|
|
244
|
+
onChange={() => {}}
|
|
245
|
+
disabledMessage="You need edit access to search"
|
|
246
|
+
/>,
|
|
247
|
+
);
|
|
248
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
252
|
+
render(
|
|
253
|
+
<PowerSearch
|
|
254
|
+
config={config}
|
|
255
|
+
filters={[]}
|
|
256
|
+
onChange={() => {}}
|
|
257
|
+
isDisabled
|
|
258
|
+
/>,
|
|
259
|
+
);
|
|
260
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
264
|
+
renderSearch();
|
|
265
|
+
const input = screen.getByRole('combobox');
|
|
266
|
+
expect(input).not.toBeDisabled();
|
|
267
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('links the reason tooltip via aria-describedby', () => {
|
|
271
|
+
renderSearch();
|
|
272
|
+
const input = screen.getByRole('combobox');
|
|
273
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
274
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('blocks input while focusable-disabled', async () => {
|
|
278
|
+
const user = userEvent.setup();
|
|
279
|
+
renderSearch();
|
|
280
|
+
const input = screen.getByRole('combobox');
|
|
281
|
+
input.focus();
|
|
282
|
+
await user.keyboard('open');
|
|
283
|
+
expect((input as HTMLInputElement).value).toBe('');
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('keeps the input natively disabled when disabled without a reason', () => {
|
|
287
|
+
render(
|
|
288
|
+
<PowerSearch
|
|
289
|
+
config={config}
|
|
290
|
+
filters={[]}
|
|
291
|
+
onChange={() => {}}
|
|
292
|
+
isDisabled
|
|
293
|
+
/>,
|
|
294
|
+
);
|
|
295
|
+
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
202
298
|
});
|
|
@@ -358,6 +358,16 @@ export interface PowerSearchProps extends Omit<
|
|
|
358
358
|
isReadOnly?: boolean;
|
|
359
359
|
/** Whether the input is disabled. @default false */
|
|
360
360
|
isDisabled?: boolean;
|
|
361
|
+
/**
|
|
362
|
+
* Explains why the search is disabled. When set together with `isDisabled`,
|
|
363
|
+
* the search shows a tooltip with this text on hover and keyboard focus, and
|
|
364
|
+
* the input stays focusable (via `aria-disabled`) so the reason is
|
|
365
|
+
* discoverable by keyboard and assistive technology. Input stays blocked.
|
|
366
|
+
*
|
|
367
|
+
* Use this instead of wrapping a disabled PowerSearch in `Tooltip` — disabled
|
|
368
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
369
|
+
*/
|
|
370
|
+
disabledMessage?: string;
|
|
361
371
|
/**
|
|
362
372
|
* Icon to display at the start of the input.
|
|
363
373
|
* Accepts a ReactNode (e.g. `<Icon icon={SearchIcon} />`) or an SVG icon component directly.
|
|
@@ -508,6 +518,7 @@ export function PowerSearch({
|
|
|
508
518
|
hasClear = true,
|
|
509
519
|
isReadOnly = false,
|
|
510
520
|
isDisabled = false,
|
|
521
|
+
disabledMessage,
|
|
511
522
|
startIcon,
|
|
512
523
|
onFocus,
|
|
513
524
|
onBlur,
|
|
@@ -546,6 +557,9 @@ export function PowerSearch({
|
|
|
546
557
|
hasLightDismiss: true,
|
|
547
558
|
hasCloseButton: false,
|
|
548
559
|
hasAutoFocus: false,
|
|
560
|
+
// The popup's own listbox/menu content is the exposed semantics; focus
|
|
561
|
+
// stays on the tokenizer input, so a modal dialog wrapper is incorrect.
|
|
562
|
+
role: 'none',
|
|
549
563
|
});
|
|
550
564
|
|
|
551
565
|
// Wrapper that manages layer visibility and tokenizer focus alongside state
|
|
@@ -795,9 +809,7 @@ export function PowerSearch({
|
|
|
795
809
|
filter.value.value[0].photo
|
|
796
810
|
) {
|
|
797
811
|
const entity = filter.value.value[0];
|
|
798
|
-
tokenIcon =
|
|
799
|
-
<Avatar src={entity.photo} name={entity.label} size={16} />
|
|
800
|
-
);
|
|
812
|
+
tokenIcon = <Avatar src={entity.photo} name={entity.label} size={16} />;
|
|
801
813
|
}
|
|
802
814
|
|
|
803
815
|
return (
|
|
@@ -982,6 +994,7 @@ export function PowerSearch({
|
|
|
982
994
|
startIcon={startIcon}
|
|
983
995
|
endContent={combinedEndContent}
|
|
984
996
|
isDisabled={isDisabled}
|
|
997
|
+
disabledMessage={disabledMessage}
|
|
985
998
|
size={size}
|
|
986
999
|
tokenOverflowBehavior={tokenOverflowBehavior}
|
|
987
1000
|
hasEntriesOnFocus
|
|
@@ -7,11 +7,19 @@ import {ProgressBar} from './ProgressBar';
|
|
|
7
7
|
describe('ProgressBar', () => {
|
|
8
8
|
it('renders with default props', () => {
|
|
9
9
|
render(<ProgressBar value={50} label="Progress" />);
|
|
10
|
-
const
|
|
11
|
-
expect(
|
|
12
|
-
expect(
|
|
13
|
-
expect(
|
|
14
|
-
expect(
|
|
10
|
+
const progressbar = screen.getByRole('progressbar');
|
|
11
|
+
expect(progressbar).toBeInTheDocument();
|
|
12
|
+
expect(progressbar).toHaveAttribute('aria-valuenow', '50');
|
|
13
|
+
expect(progressbar).toHaveAttribute('aria-valuemin', '0');
|
|
14
|
+
expect(progressbar).toHaveAttribute('aria-valuemax', '100');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('uses role="progressbar" (not "meter") for determinate progress', () => {
|
|
18
|
+
// A determinate ProgressBar conveys task completion, so it must be a
|
|
19
|
+
// progressbar (announced on update), not a meter (a static gauge).
|
|
20
|
+
render(<ProgressBar value={50} label="Progress" />);
|
|
21
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
|
22
|
+
expect(screen.queryByRole('meter')).not.toBeInTheDocument();
|
|
15
23
|
});
|
|
16
24
|
|
|
17
25
|
it('renders visible label by default', () => {
|
|
@@ -23,8 +31,8 @@ describe('ProgressBar', () => {
|
|
|
23
31
|
render(<ProgressBar value={50} label="Hidden label" isLabelHidden />);
|
|
24
32
|
const label = screen.getByText('Hidden label');
|
|
25
33
|
expect(label).toBeInTheDocument();
|
|
26
|
-
const
|
|
27
|
-
expect(
|
|
34
|
+
const progressbar = screen.getByRole('progressbar');
|
|
35
|
+
expect(progressbar).toHaveAttribute('aria-labelledby');
|
|
28
36
|
});
|
|
29
37
|
|
|
30
38
|
it('shows value label when hasValueLabel is true', () => {
|
|
@@ -43,33 +51,33 @@ describe('ProgressBar', () => {
|
|
|
43
51
|
/>,
|
|
44
52
|
);
|
|
45
53
|
expect(screen.getByText('3 GB / 5 GB')).toBeInTheDocument();
|
|
46
|
-
const
|
|
47
|
-
expect(
|
|
54
|
+
const progressbar = screen.getByRole('progressbar');
|
|
55
|
+
expect(progressbar).toHaveAttribute('aria-valuetext', '3 GB / 5 GB');
|
|
48
56
|
});
|
|
49
57
|
|
|
50
58
|
it('sets aria-valuetext from formatValueLabel', () => {
|
|
51
59
|
render(<ProgressBar value={50} label="Progress" />);
|
|
52
|
-
const
|
|
53
|
-
expect(
|
|
60
|
+
const progressbar = screen.getByRole('progressbar');
|
|
61
|
+
expect(progressbar).toHaveAttribute('aria-valuetext', '50%');
|
|
54
62
|
});
|
|
55
63
|
|
|
56
64
|
it('respects custom max', () => {
|
|
57
65
|
render(<ProgressBar value={3} max={10} label="Steps" />);
|
|
58
|
-
const
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
66
|
+
const progressbar = screen.getByRole('progressbar');
|
|
67
|
+
expect(progressbar).toHaveAttribute('aria-valuenow', '3');
|
|
68
|
+
expect(progressbar).toHaveAttribute('aria-valuemax', '10');
|
|
61
69
|
});
|
|
62
70
|
|
|
63
71
|
it('clamps value to [0, max]', () => {
|
|
64
72
|
const {rerender} = render(
|
|
65
73
|
<ProgressBar value={150} max={100} label="Over" />,
|
|
66
74
|
);
|
|
67
|
-
let
|
|
68
|
-
expect(
|
|
75
|
+
let progressbar = screen.getByRole('progressbar');
|
|
76
|
+
expect(progressbar).toHaveAttribute('aria-valuenow', '100');
|
|
69
77
|
|
|
70
78
|
rerender(<ProgressBar value={-10} max={100} label="Under" />);
|
|
71
|
-
|
|
72
|
-
expect(
|
|
79
|
+
progressbar = screen.getByRole('progressbar');
|
|
80
|
+
expect(progressbar).toHaveAttribute('aria-valuenow', '0');
|
|
73
81
|
});
|
|
74
82
|
|
|
75
83
|
it('forwards ref to outer container', () => {
|
|
@@ -79,9 +87,7 @@ describe('ProgressBar', () => {
|
|
|
79
87
|
});
|
|
80
88
|
|
|
81
89
|
it('passes data-testid', () => {
|
|
82
|
-
render(
|
|
83
|
-
<ProgressBar value={50} label="Test" data-testid="my-progress" />,
|
|
84
|
-
);
|
|
90
|
+
render(<ProgressBar value={50} label="Test" data-testid="my-progress" />);
|
|
85
91
|
expect(screen.getByTestId('my-progress')).toBeInTheDocument();
|
|
86
92
|
});
|
|
87
93
|
|
|
@@ -97,14 +103,14 @@ describe('ProgressBar', () => {
|
|
|
97
103
|
const {unmount} = render(
|
|
98
104
|
<ProgressBar value={50} label={variant} variant={variant} />,
|
|
99
105
|
);
|
|
100
|
-
expect(screen.getByRole('
|
|
106
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
|
101
107
|
unmount();
|
|
102
108
|
}
|
|
103
109
|
});
|
|
104
110
|
|
|
105
111
|
it('renders at fixed 8px track height', () => {
|
|
106
112
|
render(<ProgressBar value={50} label="Progress" />);
|
|
107
|
-
expect(screen.getByRole('
|
|
113
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
|
108
114
|
});
|
|
109
115
|
|
|
110
116
|
it('shows value label with hidden label', () => {
|
|
@@ -115,11 +121,23 @@ describe('ProgressBar', () => {
|
|
|
115
121
|
expect(screen.getByText('Hidden')).toBeInTheDocument();
|
|
116
122
|
});
|
|
117
123
|
|
|
124
|
+
it('renders no visible value label when isLabelHidden without hasValueLabel', () => {
|
|
125
|
+
// Mirrors the intended "accessible label only" composition: the text
|
|
126
|
+
// label is kept for assistive tech (visually hidden) while no extra
|
|
127
|
+
// visible value label is surfaced.
|
|
128
|
+
render(<ProgressBar value={42} label="Context usage" isLabelHidden />);
|
|
129
|
+
expect(screen.queryByText('42%')).not.toBeInTheDocument();
|
|
130
|
+
const label = screen.getByText('Context usage');
|
|
131
|
+
expect(label).toBeInTheDocument();
|
|
132
|
+
const progressbar = screen.getByRole('progressbar');
|
|
133
|
+
expect(progressbar).toHaveAttribute('aria-labelledby', label.id);
|
|
134
|
+
});
|
|
135
|
+
|
|
118
136
|
it('handles zero max gracefully', () => {
|
|
119
137
|
render(<ProgressBar value={0} max={0} label="Empty" />);
|
|
120
|
-
const
|
|
121
|
-
expect(
|
|
122
|
-
expect(
|
|
138
|
+
const progressbar = screen.getByRole('progressbar');
|
|
139
|
+
expect(progressbar).toHaveAttribute('aria-valuenow', '0');
|
|
140
|
+
expect(progressbar).toHaveAttribute('aria-valuemax', '0');
|
|
123
141
|
});
|
|
124
142
|
|
|
125
143
|
// Disabled state
|
|
@@ -128,7 +146,7 @@ describe('ProgressBar', () => {
|
|
|
128
146
|
render(
|
|
129
147
|
<ProgressBar value={50} label="Canceled" isDisabled hasValueLabel />,
|
|
130
148
|
);
|
|
131
|
-
expect(screen.getByRole('
|
|
149
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
|
132
150
|
expect(screen.getByText('50%')).toBeInTheDocument();
|
|
133
151
|
});
|
|
134
152
|
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import {mergeProps} from '../utils';
|
|
32
32
|
import type {BaseProps} from '../BaseProps';
|
|
33
33
|
import {themeProps} from '../utils/themeProps';
|
|
34
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Extensible variant map for ProgressBar.
|
|
@@ -313,14 +314,12 @@ export function ProgressBar({
|
|
|
313
314
|
)}
|
|
314
315
|
</div>
|
|
315
316
|
) : (
|
|
316
|
-
<
|
|
317
|
-
{label}
|
|
318
|
-
</span>
|
|
317
|
+
<VisuallyHidden id={labelId}>{label}</VisuallyHidden>
|
|
319
318
|
)}
|
|
320
319
|
|
|
321
320
|
{/* Progress track */}
|
|
322
321
|
<div
|
|
323
|
-
role=
|
|
322
|
+
role="progressbar"
|
|
324
323
|
aria-valuenow={isIndeterminate ? undefined : clampedValue}
|
|
325
324
|
aria-valuemin={isIndeterminate ? undefined : 0}
|
|
326
325
|
aria-valuemax={isIndeterminate ? undefined : max}
|
|
@@ -74,6 +74,12 @@ export const docs = {
|
|
|
74
74
|
description: 'Whether all radio items are disabled.',
|
|
75
75
|
default: 'false',
|
|
76
76
|
},
|
|
77
|
+
{
|
|
78
|
+
name: 'disabledMessage',
|
|
79
|
+
type: 'string',
|
|
80
|
+
description:
|
|
81
|
+
'Explains why the group is disabled. Applies to the whole-group disabled state (isDisabled), not per item. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the radios focusable via aria-disabled (selection stays blocked). Use this instead of wrapping a disabled RadioList in Tooltip — disabled controls swallow the hover events an external Tooltip needs.',
|
|
82
|
+
},
|
|
77
83
|
{
|
|
78
84
|
name: 'isRequired',
|
|
79
85
|
type: 'boolean',
|
|
@@ -121,6 +127,7 @@ export const docs = {
|
|
|
121
127
|
{ guidance: false, description: 'Use when multiple selections are needed; use CheckboxList instead.' },
|
|
122
128
|
{ guidance: false, description: 'Use for long lists; use Selector for better discoverability.' },
|
|
123
129
|
{ guidance: false, description: 'Use horizontal layout with more than 4 options; it wraps awkwardly.' },
|
|
130
|
+
{ guidance: false, description: 'Wrap a disabled RadioList in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
|
|
124
131
|
],
|
|
125
132
|
anatomy: [
|
|
126
133
|
{name: 'Header', required: false, description: 'Optional heading above the radio list.'},
|
|
@@ -142,6 +149,7 @@ export const docsZh = {
|
|
|
142
149
|
{ guidance: false, description: 'Use when multiple selections are needed; use CheckboxList instead.' },
|
|
143
150
|
{ guidance: false, description: 'Use for long lists; use Selector for better discoverability.' },
|
|
144
151
|
{ guidance: false, description: 'Use horizontal layout with more than 4 options; it wraps awkwardly.' },
|
|
152
|
+
{ guidance: false, description: 'Wrap a disabled RadioList in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
|
|
145
153
|
],
|
|
146
154
|
anatomy: [
|
|
147
155
|
{name: 'Header', required: false, description: 'Optional heading above the radio list.'},
|
|
@@ -165,6 +173,7 @@ export const docsDense = {
|
|
|
165
173
|
{ guidance: false, description: 'Use when multiple selections are needed; use CheckboxList instead.' },
|
|
166
174
|
{ guidance: false, description: 'Use for long lists; use Selector for better discoverability.' },
|
|
167
175
|
{ guidance: false, description: 'Use horizontal layout with more than 4 options; it wraps awkwardly.' },
|
|
176
|
+
{ guidance: false, description: 'Wrap a disabled RadioList in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
|
|
168
177
|
],
|
|
169
178
|
anatomy: [
|
|
170
179
|
{name: 'Header', required: false, description: 'Optional heading above the radio list.'},
|
|
@@ -9,12 +9,39 @@
|
|
|
9
9
|
* SYNC: When RadioList.tsx or RadioListItem.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {describe, it, expect, vi} from 'vitest';
|
|
13
|
-
import {render, screen} from '@testing-library/react';
|
|
12
|
+
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
13
|
+
import {render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {RadioList} from './RadioList';
|
|
16
16
|
import {RadioListItem} from './RadioListItem';
|
|
17
17
|
|
|
18
|
+
// Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
|
|
19
|
+
// reflects its open state via a `popover-open` attribute the tests can assert.
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
22
|
+
this.setAttribute('popover-open', '');
|
|
23
|
+
const event = new Event('toggle', {bubbles: false});
|
|
24
|
+
Object.defineProperty(event, 'newState', {value: 'open'});
|
|
25
|
+
this.dispatchEvent(event);
|
|
26
|
+
});
|
|
27
|
+
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
28
|
+
this.removeAttribute('popover-open');
|
|
29
|
+
const event = new Event('toggle', {bubbles: false});
|
|
30
|
+
Object.defineProperty(event, 'newState', {value: 'closed'});
|
|
31
|
+
this.dispatchEvent(event);
|
|
32
|
+
});
|
|
33
|
+
const originalMatches = HTMLElement.prototype.matches;
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
(HTMLElement.prototype as any).matches = function (
|
|
36
|
+
selector: string,
|
|
37
|
+
): boolean {
|
|
38
|
+
if (selector === ':popover-open') {
|
|
39
|
+
return this.hasAttribute('popover-open');
|
|
40
|
+
}
|
|
41
|
+
return originalMatches.call(this, selector);
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
18
45
|
describe('RadioList', () => {
|
|
19
46
|
it('renders with label', () => {
|
|
20
47
|
render(
|
|
@@ -104,11 +131,7 @@ describe('RadioList', () => {
|
|
|
104
131
|
const user = userEvent.setup();
|
|
105
132
|
const handleChange = vi.fn();
|
|
106
133
|
render(
|
|
107
|
-
<RadioList
|
|
108
|
-
label="Preference"
|
|
109
|
-
value=""
|
|
110
|
-
onChange={handleChange}
|
|
111
|
-
isDisabled>
|
|
134
|
+
<RadioList label="Preference" value="" onChange={handleChange} isDisabled>
|
|
112
135
|
<RadioListItem label="Option A" value="a" />
|
|
113
136
|
</RadioList>,
|
|
114
137
|
);
|
|
@@ -257,11 +280,7 @@ describe('RadioList', () => {
|
|
|
257
280
|
it('supports data-testid on RadioListItem', () => {
|
|
258
281
|
render(
|
|
259
282
|
<RadioList label="Preference" value="" onChange={() => {}}>
|
|
260
|
-
<RadioListItem
|
|
261
|
-
label="Option A"
|
|
262
|
-
value="a"
|
|
263
|
-
data-testid="my-radio-item"
|
|
264
|
-
/>
|
|
283
|
+
<RadioListItem label="Option A" value="a" data-testid="my-radio-item" />
|
|
265
284
|
</RadioList>,
|
|
266
285
|
);
|
|
267
286
|
expect(screen.getByTestId('my-radio-item')).toBeInTheDocument();
|
|
@@ -279,11 +298,32 @@ describe('RadioList', () => {
|
|
|
279
298
|
);
|
|
280
299
|
const label = screen.getByText('Hidden label');
|
|
281
300
|
expect(label).toBeInTheDocument();
|
|
282
|
-
// The radiogroup
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
'Hidden label',
|
|
301
|
+
// The radiogroup is named by the label element via aria-labelledby (not a
|
|
302
|
+
// duplicated aria-label), so its accessible name is still "Hidden label".
|
|
303
|
+
expect(
|
|
304
|
+
screen.getByRole('radiogroup', {name: 'Hidden label'}),
|
|
305
|
+
).toBeInTheDocument();
|
|
306
|
+
expect(screen.getByRole('radiogroup')).toHaveAttribute('aria-labelledby');
|
|
307
|
+
expect(screen.getByRole('radiogroup')).not.toHaveAttribute('aria-label');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('renders the group label as a span, not a label element (forms-14)', () => {
|
|
311
|
+
render(
|
|
312
|
+
<RadioList label="Plan" value="" onChange={() => {}}>
|
|
313
|
+
<RadioListItem label="Free" value="free" />
|
|
314
|
+
<RadioListItem label="Pro" value="pro" />
|
|
315
|
+
</RadioList>,
|
|
286
316
|
);
|
|
317
|
+
// A radiogroup's accessible name must not come from a literal `<label>`
|
|
318
|
+
// element — a `<label>` names a single control and can't be associated
|
|
319
|
+
// with a group. It is rendered as a `<span>` and referenced via
|
|
320
|
+
// aria-labelledby (with no orphaned htmlFor).
|
|
321
|
+
const labelEl = screen.getByText('Plan');
|
|
322
|
+
expect(labelEl.tagName).toBe('SPAN');
|
|
323
|
+
expect(labelEl.closest('label')).toBeNull();
|
|
324
|
+
expect(labelEl).not.toHaveAttribute('for');
|
|
325
|
+
const group = screen.getByRole('radiogroup', {name: 'Plan'});
|
|
326
|
+
expect(group.getAttribute('aria-labelledby')).toBe(labelEl.id);
|
|
287
327
|
});
|
|
288
328
|
|
|
289
329
|
it('renders description on items', () => {
|
|
@@ -340,4 +380,200 @@ describe('RadioList', () => {
|
|
|
340
380
|
'true',
|
|
341
381
|
);
|
|
342
382
|
});
|
|
383
|
+
|
|
384
|
+
describe('focus management (no-selection tab stop)', () => {
|
|
385
|
+
it('keeps focus on the selected radio when a value is selected', () => {
|
|
386
|
+
render(
|
|
387
|
+
<RadioList label="Preference" value="b" onChange={() => {}}>
|
|
388
|
+
<RadioListItem label="Option A" value="a" />
|
|
389
|
+
<RadioListItem label="Option B" value="b" />
|
|
390
|
+
<RadioListItem label="Option C" value="c" />
|
|
391
|
+
</RadioList>,
|
|
392
|
+
);
|
|
393
|
+
const selected = screen.getByLabelText('Option B');
|
|
394
|
+
// A selected value provides a deterministic native tab stop; focusing it
|
|
395
|
+
// must not be redirected elsewhere.
|
|
396
|
+
selected.focus();
|
|
397
|
+
expect(selected).toHaveFocus();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('redirects to the first radio when focus enters an unselected group forward', () => {
|
|
401
|
+
render(
|
|
402
|
+
<>
|
|
403
|
+
<button type="button">before</button>
|
|
404
|
+
<RadioList label="Preference" value="" onChange={() => {}}>
|
|
405
|
+
<RadioListItem label="Option A" value="a" />
|
|
406
|
+
<RadioListItem label="Option B" value="b" />
|
|
407
|
+
<RadioListItem label="Option C" value="c" />
|
|
408
|
+
</RadioList>
|
|
409
|
+
</>,
|
|
410
|
+
);
|
|
411
|
+
const radios = screen.getAllByRole('radio');
|
|
412
|
+
const outside = screen.getByText('before');
|
|
413
|
+
outside.focus();
|
|
414
|
+
// Forward entry: the browser lands on a leading radio; the group keeps the
|
|
415
|
+
// first radio as the deterministic tab stop.
|
|
416
|
+
radios[0].focus();
|
|
417
|
+
expect(radios[0]).toHaveFocus();
|
|
418
|
+
|
|
419
|
+
// Landing on a middle radio from outside is normalized to the first.
|
|
420
|
+
outside.focus();
|
|
421
|
+
radios[1].focus();
|
|
422
|
+
expect(radios[0]).toHaveFocus();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('redirects to the last radio when focus enters an unselected group backward', () => {
|
|
426
|
+
render(
|
|
427
|
+
<>
|
|
428
|
+
<RadioList label="Preference" value="" onChange={() => {}}>
|
|
429
|
+
<RadioListItem label="Option A" value="a" />
|
|
430
|
+
<RadioListItem label="Option B" value="b" />
|
|
431
|
+
<RadioListItem label="Option C" value="c" />
|
|
432
|
+
</RadioList>
|
|
433
|
+
<button type="button">after</button>
|
|
434
|
+
</>,
|
|
435
|
+
);
|
|
436
|
+
const radios = screen.getAllByRole('radio');
|
|
437
|
+
const outside = screen.getByText('after');
|
|
438
|
+
outside.focus();
|
|
439
|
+
// Backward (Shift+Tab) entry: the browser focuses the last radio; the
|
|
440
|
+
// group keeps it as the deterministic tab stop rather than jumping away.
|
|
441
|
+
radios[radios.length - 1].focus();
|
|
442
|
+
expect(radios[radios.length - 1]).toHaveFocus();
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('does not hijack focus moving between radios within the group', () => {
|
|
446
|
+
render(
|
|
447
|
+
<RadioList label="Preference" value="" onChange={() => {}}>
|
|
448
|
+
<RadioListItem label="Option A" value="a" />
|
|
449
|
+
<RadioListItem label="Option B" value="b" />
|
|
450
|
+
<RadioListItem label="Option C" value="c" />
|
|
451
|
+
</RadioList>,
|
|
452
|
+
);
|
|
453
|
+
const radios = screen.getAllByRole('radio');
|
|
454
|
+
// Enter the group from outside, then move to a middle radio as arrow-key
|
|
455
|
+
// navigation would. Intra-group movement must not be redirected back.
|
|
456
|
+
radios[0].focus();
|
|
457
|
+
radios[1].focus();
|
|
458
|
+
expect(radios[1]).toHaveFocus();
|
|
459
|
+
radios[2].focus();
|
|
460
|
+
expect(radios[2]).toHaveFocus();
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('skips disabled radios when choosing the deterministic tab stop', () => {
|
|
464
|
+
render(
|
|
465
|
+
<>
|
|
466
|
+
<button type="button">before</button>
|
|
467
|
+
<RadioList label="Preference" value="" onChange={() => {}}>
|
|
468
|
+
<RadioListItem label="Option A" value="a" isDisabled />
|
|
469
|
+
<RadioListItem label="Option B" value="b" />
|
|
470
|
+
<RadioListItem label="Option C" value="c" />
|
|
471
|
+
<RadioListItem label="Option D" value="d" />
|
|
472
|
+
</RadioList>
|
|
473
|
+
</>,
|
|
474
|
+
);
|
|
475
|
+
const outside = screen.getByText('before');
|
|
476
|
+
const optionB = screen.getByLabelText('Option B');
|
|
477
|
+
const optionC = screen.getByLabelText('Option C');
|
|
478
|
+
// A middle enabled radio (C) entered from outside is normalized to the
|
|
479
|
+
// first *enabled* radio (B) — the disabled Option A is skipped.
|
|
480
|
+
outside.focus();
|
|
481
|
+
optionC.focus();
|
|
482
|
+
expect(optionB).toHaveFocus();
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
describe('disabledMessage', () => {
|
|
487
|
+
const h = {hidden: true} as const;
|
|
488
|
+
|
|
489
|
+
function renderGroup(props?: {onChange?: (v: string) => void}) {
|
|
490
|
+
return render(
|
|
491
|
+
<RadioList
|
|
492
|
+
label="Plan"
|
|
493
|
+
value="free"
|
|
494
|
+
onChange={props?.onChange ?? (() => {})}
|
|
495
|
+
isDisabled
|
|
496
|
+
disabledMessage="Upgrade your account to change plans">
|
|
497
|
+
<RadioListItem label="Free" value="free" />
|
|
498
|
+
<RadioListItem label="Pro" value="pro" />
|
|
499
|
+
</RadioList>,
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
it('shows the reason tooltip on hover when the group is disabled with a reason', async () => {
|
|
504
|
+
renderGroup();
|
|
505
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
506
|
+
expect(tooltip).toHaveTextContent('Upgrade your account to change plans');
|
|
507
|
+
const group = screen.getByRole('radiogroup');
|
|
508
|
+
fireEvent.mouseEnter(group);
|
|
509
|
+
await waitFor(() => expect(tooltip).toHaveAttribute('popover-open'));
|
|
510
|
+
fireEvent.mouseLeave(group);
|
|
511
|
+
await waitFor(() => expect(tooltip).not.toHaveAttribute('popover-open'));
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
515
|
+
const user = userEvent.setup();
|
|
516
|
+
renderGroup();
|
|
517
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
518
|
+
await user.tab();
|
|
519
|
+
await waitFor(() => expect(tooltip).toHaveAttribute('popover-open'));
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it('does not render a tooltip when not disabled', () => {
|
|
523
|
+
render(
|
|
524
|
+
<RadioList
|
|
525
|
+
label="Plan"
|
|
526
|
+
value="free"
|
|
527
|
+
onChange={() => {}}
|
|
528
|
+
disabledMessage="Upgrade your account to change plans">
|
|
529
|
+
<RadioListItem label="Free" value="free" />
|
|
530
|
+
</RadioList>,
|
|
531
|
+
);
|
|
532
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
536
|
+
render(
|
|
537
|
+
<RadioList label="Plan" value="free" onChange={() => {}} isDisabled>
|
|
538
|
+
<RadioListItem label="Free" value="free" />
|
|
539
|
+
</RadioList>,
|
|
540
|
+
);
|
|
541
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('keeps radios focusable via aria-disabled when a reason is provided', () => {
|
|
545
|
+
renderGroup();
|
|
546
|
+
for (const radio of screen.getAllByRole('radio', h)) {
|
|
547
|
+
expect(radio).not.toBeDisabled();
|
|
548
|
+
expect(radio).toHaveAttribute('aria-disabled', 'true');
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('links the reason tooltip from the group via aria-describedby', () => {
|
|
553
|
+
renderGroup();
|
|
554
|
+
const group = screen.getByRole('radiogroup');
|
|
555
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
556
|
+
expect(group.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('blocks selection while focusable-disabled', () => {
|
|
560
|
+
const onChange = vi.fn();
|
|
561
|
+
renderGroup({onChange});
|
|
562
|
+
const pro = screen.getByRole('radio', {name: 'Pro', hidden: true});
|
|
563
|
+
fireEvent.click(pro);
|
|
564
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('keeps radios natively disabled when disabled without a reason', () => {
|
|
568
|
+
render(
|
|
569
|
+
<RadioList label="Plan" value="free" onChange={() => {}} isDisabled>
|
|
570
|
+
<RadioListItem label="Free" value="free" />
|
|
571
|
+
<RadioListItem label="Pro" value="pro" />
|
|
572
|
+
</RadioList>,
|
|
573
|
+
);
|
|
574
|
+
for (const radio of screen.getAllByRole('radio', h)) {
|
|
575
|
+
expect(radio).toBeDisabled();
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
});
|
|
343
579
|
});
|