@astryxdesign/core 0.1.2 → 0.1.3
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 +165 -0
- package/README.md +12 -12
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +11 -5
- 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.map +1 -1
- package/dist/Button/Button.js +2 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +0 -1
- 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/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +0 -1
- 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 +8 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -3
- 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/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +0 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +9 -5
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +51 -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 +167 -99
- 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/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 +32 -6
- package/dist/InputGroup/InputGroup.d.ts +4 -3
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +16 -6
- 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/groupStyles.d.ts +1 -1
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +3 -3
- 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 +33 -1
- 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 +10 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +64 -9
- 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 +16 -1
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +18 -7
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -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 +27 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +141 -42
- 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 +15 -1
- package/dist/Popover/Popover.d.ts +18 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +5 -2
- package/dist/Popover/usePopover.d.ts +38 -3
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +24 -9
- 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 +24 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +122 -43
- 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/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +2 -0
- 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 +36 -9
- 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 +87 -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 +11 -8
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +10 -1
- 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/index.d.ts +2 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -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/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 +66 -0
- package/dist/Table/types.d.ts.map +1 -1
- 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 +147 -76
- 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 +131 -3
- package/dist/Tokenizer/Tokenizer.d.ts +12 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +26 -4
- 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 +56 -9
- package/dist/TopNav/TopNavHeading.d.ts +7 -1
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +8 -0
- 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 +13 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +69 -6
- package/dist/Typeahead/Typeahead.d.ts +26 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +65 -29
- 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 +18 -0
- package/dist/astryx.umd.js +55 -55
- package/dist/astryx.umd.js.map +4 -4
- 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/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/docs.mjs +2 -2
- package/package.json +12 -8
- package/src/Avatar/Avatar.test.tsx +37 -0
- package/src/Avatar/Avatar.tsx +10 -5
- package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
- package/src/Button/Button.tsx +3 -16
- package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
- package/src/ButtonGroup/ButtonGroup.tsx +0 -1
- 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/ChatComposerInput.test.tsx +26 -7
- package/src/Chat/ChatComposerInput.tsx +0 -1
- package/src/Chat/ChatDictationButton.doc.mjs +1 -1
- 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 +22 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -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 +192 -26
- package/src/CheckboxList/CheckboxList.tsx +56 -3
- package/src/CheckboxList/CheckboxListContext.tsx +7 -0
- package/src/CheckboxList/CheckboxListItem.tsx +0 -3
- package/src/Citation/Citation.doc.mjs +10 -0
- package/src/Citation/Citation.test.tsx +121 -0
- package/src/Citation/Citation.tsx +15 -3
- package/src/CodeBlock/CodeBlock.test.tsx +115 -0
- package/src/CodeBlock/CodeBlock.tsx +63 -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 +286 -56
- package/src/DateInput/DateInput.test.tsx +248 -2
- package/src/DateInput/DateInput.tsx +196 -100
- 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/Field/Field.doc.mjs +59 -18
- 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 +17 -4
- package/src/Grid/Grid.test.tsx +43 -30
- package/src/Grid/Grid.tsx +38 -3
- package/src/Icon/Icon.doc.mjs +3 -3
- package/src/InputGroup/InputGroup.doc.mjs +111 -23
- package/src/InputGroup/InputGroup.test.tsx +257 -60
- package/src/InputGroup/InputGroup.tsx +23 -5
- package/src/InputGroup/InputGroupContext.ts +9 -4
- package/src/InputGroup/groupStyles.ts +7 -1
- package/src/InputGroup/index.ts +1 -0
- package/src/Kbd/Kbd.test.tsx +22 -4
- package/src/Kbd/Kbd.tsx +35 -3
- package/src/Layer/layerAnimations.stylex.ts +20 -4
- package/src/Layer/useLayer.doc.mjs +5 -0
- package/src/Layer/useLayer.test.tsx +111 -0
- package/src/Layer/useLayer.tsx +90 -9
- 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 -0
- 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 +50 -0
- package/src/Link/Link.tsx +39 -4
- package/src/Markdown/Markdown.test.tsx +11 -0
- package/src/Markdown/Markdown.tsx +7 -0
- package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
- package/src/MoreMenu/MoreMenu.tsx +0 -9
- package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
- package/src/MultiSelector/MultiSelector.test.tsx +375 -8
- package/src/MultiSelector/MultiSelector.tsx +194 -48
- 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/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +117 -5
- package/src/Pagination/Pagination.tsx +21 -1
- package/src/Popover/Popover.doc.mjs +26 -0
- package/src/Popover/Popover.test.tsx +121 -0
- package/src/Popover/Popover.tsx +23 -2
- package/src/Popover/usePopover.doc.mjs +9 -3
- package/src/Popover/usePopover.tsx +70 -12
- 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 +34 -26
- 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/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 +1 -1
- package/src/Selector/Selector.doc.mjs +26 -0
- package/src/Selector/Selector.test.tsx +387 -15
- package/src/Selector/Selector.tsx +175 -67
- package/src/Selector/hooks.ts +23 -0
- package/src/SideNav/SideNav.test.tsx +48 -0
- package/src/SideNav/SideNavHeading.tsx +8 -2
- package/src/SideNav/SideNavItem.tsx +2 -4
- package/src/Skeleton/Skeleton.test.tsx +25 -0
- package/src/Skeleton/Skeleton.tsx +11 -1
- package/src/Slider/Slider.doc.mjs +15 -0
- package/src/Slider/Slider.test.tsx +155 -2
- package/src/Slider/Slider.tsx +53 -5
- 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 +6 -0
- package/src/TabList/TabList.test.tsx +251 -0
- package/src/TabList/TabList.tsx +103 -12
- package/src/TabList/TabListContext.ts +7 -3
- package/src/TabList/TabMenu.tsx +6 -3
- package/src/Table/BaseTable.tsx +21 -17
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.test.tsx +10 -0
- package/src/Table/Table.tsx +8 -0
- package/src/Table/TableCell.tsx +25 -23
- package/src/Table/TableHeaderCell.tsx +28 -29
- package/src/Table/index.ts +3 -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/tableContextMenu.test.tsx +157 -0
- package/src/Table/tableContextMenu.tsx +142 -0
- package/src/Table/types.ts +74 -0
- package/src/Table/useTableColumnResize.doc.mjs +3 -3
- package/src/Table/useTableStickyColumns.doc.mjs +2 -2
- package/src/Text/Text.doc.mjs +18 -18
- 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/Thumbnail/Thumbnail.doc.mjs +4 -4
- package/src/TimeInput/TimeInput.doc.mjs +340 -60
- package/src/TimeInput/TimeInput.test.tsx +255 -7
- package/src/TimeInput/TimeInput.tsx +176 -70
- package/src/Timestamp/Timestamp.doc.mjs +19 -19
- package/src/Toast/Toast.tsx +14 -0
- package/src/Toast/ToastViewport.test.tsx +214 -0
- package/src/Toast/ToastViewport.tsx +167 -21
- package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
- package/src/Tokenizer/Tokenizer.test.tsx +105 -1
- package/src/Tokenizer/Tokenizer.tsx +37 -1
- package/src/Toolbar/Toolbar.test.tsx +109 -6
- package/src/Toolbar/Toolbar.tsx +59 -5
- package/src/Tooltip/Tooltip.test.tsx +68 -0
- package/src/Tooltip/useTooltip.tsx +55 -8
- package/src/TopNav/TopNav.test.tsx +50 -0
- package/src/TopNav/TopNavHeading.tsx +17 -1
- 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 +93 -9
- package/src/Typeahead/Typeahead.doc.mjs +141 -30
- package/src/Typeahead/Typeahead.test.tsx +331 -1
- package/src/Typeahead/Typeahead.tsx +105 -33
- 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/docs-script.test.mjs +50 -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/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 +10 -5
- 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
|
@@ -9,12 +9,39 @@
|
|
|
9
9
|
* SYNC: When SegmentedControl components change, 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 {SegmentedControl} from './SegmentedControl';
|
|
16
16
|
import {SegmentedControlItem} from './SegmentedControlItem';
|
|
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('SegmentedControl', () => {
|
|
19
46
|
it('renders a radiogroup with radio buttons', () => {
|
|
20
47
|
render(
|
|
@@ -56,10 +83,7 @@ describe('SegmentedControl', () => {
|
|
|
56
83
|
const handleChange = vi.fn();
|
|
57
84
|
|
|
58
85
|
render(
|
|
59
|
-
<SegmentedControl
|
|
60
|
-
value="grid"
|
|
61
|
-
onChange={handleChange}
|
|
62
|
-
label="View mode">
|
|
86
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
63
87
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
64
88
|
<SegmentedControlItem value="list" label="List" />
|
|
65
89
|
</SegmentedControl>,
|
|
@@ -74,10 +98,7 @@ describe('SegmentedControl', () => {
|
|
|
74
98
|
const handleChange = vi.fn();
|
|
75
99
|
|
|
76
100
|
render(
|
|
77
|
-
<SegmentedControl
|
|
78
|
-
value="grid"
|
|
79
|
-
onChange={handleChange}
|
|
80
|
-
label="View mode">
|
|
101
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
81
102
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
82
103
|
<SegmentedControlItem value="list" label="List" />
|
|
83
104
|
</SegmentedControl>,
|
|
@@ -196,6 +217,45 @@ describe('SegmentedControl', () => {
|
|
|
196
217
|
'-1',
|
|
197
218
|
);
|
|
198
219
|
});
|
|
220
|
+
|
|
221
|
+
it('keeps a tab stop when the value matches no item (tab-stop repair)', () => {
|
|
222
|
+
render(
|
|
223
|
+
<SegmentedControl
|
|
224
|
+
value="nonexistent"
|
|
225
|
+
onChange={() => {}}
|
|
226
|
+
label="View mode">
|
|
227
|
+
<SegmentedControlItem value="grid" label="Grid" />
|
|
228
|
+
<SegmentedControlItem value="list" label="List" />
|
|
229
|
+
<SegmentedControlItem value="table" label="Table" />
|
|
230
|
+
</SegmentedControl>,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// No item is selected, but the group must remain Tab-reachable: the first
|
|
234
|
+
// enabled radio is promoted to tabIndex=0.
|
|
235
|
+
expect(screen.getByRole('radio', {name: 'Grid'})).toHaveAttribute(
|
|
236
|
+
'tabIndex',
|
|
237
|
+
'0',
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('promotes the first enabled item when the value matches no item and the first is disabled', () => {
|
|
242
|
+
render(
|
|
243
|
+
<SegmentedControl
|
|
244
|
+
value="nonexistent"
|
|
245
|
+
onChange={() => {}}
|
|
246
|
+
label="View mode">
|
|
247
|
+
<SegmentedControlItem value="grid" label="Grid" isDisabled />
|
|
248
|
+
<SegmentedControlItem value="list" label="List" />
|
|
249
|
+
<SegmentedControlItem value="table" label="Table" />
|
|
250
|
+
</SegmentedControl>,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// The disabled first item is skipped; the first ENABLED radio is tabbable.
|
|
254
|
+
expect(screen.getByRole('radio', {name: 'List'})).toHaveAttribute(
|
|
255
|
+
'tabIndex',
|
|
256
|
+
'0',
|
|
257
|
+
);
|
|
258
|
+
});
|
|
199
259
|
});
|
|
200
260
|
|
|
201
261
|
describe('SegmentedControl keyboard navigation', () => {
|
|
@@ -204,10 +264,7 @@ describe('SegmentedControl keyboard navigation', () => {
|
|
|
204
264
|
const handleChange = vi.fn();
|
|
205
265
|
|
|
206
266
|
render(
|
|
207
|
-
<SegmentedControl
|
|
208
|
-
value="grid"
|
|
209
|
-
onChange={handleChange}
|
|
210
|
-
label="View mode">
|
|
267
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
211
268
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
212
269
|
<SegmentedControlItem value="list" label="List" />
|
|
213
270
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -226,10 +283,7 @@ describe('SegmentedControl keyboard navigation', () => {
|
|
|
226
283
|
const handleChange = vi.fn();
|
|
227
284
|
|
|
228
285
|
render(
|
|
229
|
-
<SegmentedControl
|
|
230
|
-
value="list"
|
|
231
|
-
onChange={handleChange}
|
|
232
|
-
label="View mode">
|
|
286
|
+
<SegmentedControl value="list" onChange={handleChange} label="View mode">
|
|
233
287
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
234
288
|
<SegmentedControlItem value="list" label="List" />
|
|
235
289
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -248,10 +302,7 @@ describe('SegmentedControl keyboard navigation', () => {
|
|
|
248
302
|
const handleChange = vi.fn();
|
|
249
303
|
|
|
250
304
|
render(
|
|
251
|
-
<SegmentedControl
|
|
252
|
-
value="table"
|
|
253
|
-
onChange={handleChange}
|
|
254
|
-
label="View mode">
|
|
305
|
+
<SegmentedControl value="table" onChange={handleChange} label="View mode">
|
|
255
306
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
256
307
|
<SegmentedControlItem value="list" label="List" />
|
|
257
308
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -270,10 +321,7 @@ describe('SegmentedControl keyboard navigation', () => {
|
|
|
270
321
|
const handleChange = vi.fn();
|
|
271
322
|
|
|
272
323
|
render(
|
|
273
|
-
<SegmentedControl
|
|
274
|
-
value="grid"
|
|
275
|
-
onChange={handleChange}
|
|
276
|
-
label="View mode">
|
|
324
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
277
325
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
278
326
|
<SegmentedControlItem value="list" label="List" />
|
|
279
327
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -292,10 +340,7 @@ describe('SegmentedControl keyboard navigation', () => {
|
|
|
292
340
|
const handleChange = vi.fn();
|
|
293
341
|
|
|
294
342
|
render(
|
|
295
|
-
<SegmentedControl
|
|
296
|
-
value="table"
|
|
297
|
-
onChange={handleChange}
|
|
298
|
-
label="View mode">
|
|
343
|
+
<SegmentedControl value="table" onChange={handleChange} label="View mode">
|
|
299
344
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
300
345
|
<SegmentedControlItem value="list" label="List" />
|
|
301
346
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -314,10 +359,7 @@ describe('SegmentedControl keyboard navigation', () => {
|
|
|
314
359
|
const handleChange = vi.fn();
|
|
315
360
|
|
|
316
361
|
render(
|
|
317
|
-
<SegmentedControl
|
|
318
|
-
value="grid"
|
|
319
|
-
onChange={handleChange}
|
|
320
|
-
label="View mode">
|
|
362
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
321
363
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
322
364
|
<SegmentedControlItem value="list" label="List" />
|
|
323
365
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -351,6 +393,41 @@ describe('SegmentedControl disabled state', () => {
|
|
|
351
393
|
);
|
|
352
394
|
});
|
|
353
395
|
|
|
396
|
+
it('removes the tab stop from the selected item when the group is disabled (navigation-13)', () => {
|
|
397
|
+
render(
|
|
398
|
+
<SegmentedControl
|
|
399
|
+
value="grid"
|
|
400
|
+
onChange={() => {}}
|
|
401
|
+
label="View mode"
|
|
402
|
+
isDisabled>
|
|
403
|
+
<SegmentedControlItem value="grid" label="Grid" />
|
|
404
|
+
<SegmentedControlItem value="list" label="List" />
|
|
405
|
+
</SegmentedControl>,
|
|
406
|
+
);
|
|
407
|
+
// Selected segment must not be a focusable-but-dead tab stop when disabled.
|
|
408
|
+
const selected = screen.getByRole('radio', {name: 'Grid'});
|
|
409
|
+
expect(selected).toHaveAttribute('tabIndex', '-1');
|
|
410
|
+
expect(selected).toHaveAttribute('aria-disabled', 'true');
|
|
411
|
+
// No enabled segment is tabbable either.
|
|
412
|
+
expect(screen.getByRole('radio', {name: 'List'})).toHaveAttribute(
|
|
413
|
+
'tabIndex',
|
|
414
|
+
'-1',
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('removes the tab stop from an individually disabled selected item', () => {
|
|
419
|
+
render(
|
|
420
|
+
<SegmentedControl value="grid" onChange={() => {}} label="View mode">
|
|
421
|
+
<SegmentedControlItem value="grid" label="Grid" isDisabled />
|
|
422
|
+
<SegmentedControlItem value="list" label="List" />
|
|
423
|
+
</SegmentedControl>,
|
|
424
|
+
);
|
|
425
|
+
expect(screen.getByRole('radio', {name: 'Grid'})).toHaveAttribute(
|
|
426
|
+
'tabIndex',
|
|
427
|
+
'-1',
|
|
428
|
+
);
|
|
429
|
+
});
|
|
430
|
+
|
|
354
431
|
it('does not call onChange when group is disabled', async () => {
|
|
355
432
|
const user = userEvent.setup({pointerEventsCheck: 0});
|
|
356
433
|
const handleChange = vi.fn();
|
|
@@ -375,10 +452,7 @@ describe('SegmentedControl disabled state', () => {
|
|
|
375
452
|
const handleChange = vi.fn();
|
|
376
453
|
|
|
377
454
|
render(
|
|
378
|
-
<SegmentedControl
|
|
379
|
-
value="grid"
|
|
380
|
-
onChange={handleChange}
|
|
381
|
-
label="View mode">
|
|
455
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
382
456
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
383
457
|
<SegmentedControlItem value="list" label="List" isDisabled />
|
|
384
458
|
</SegmentedControl>,
|
|
@@ -398,10 +472,7 @@ describe('SegmentedControl disabled state', () => {
|
|
|
398
472
|
const handleChange = vi.fn();
|
|
399
473
|
|
|
400
474
|
render(
|
|
401
|
-
<SegmentedControl
|
|
402
|
-
value="grid"
|
|
403
|
-
onChange={handleChange}
|
|
404
|
-
label="View mode">
|
|
475
|
+
<SegmentedControl value="grid" onChange={handleChange} label="View mode">
|
|
405
476
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
406
477
|
<SegmentedControlItem value="list" label="List" isDisabled />
|
|
407
478
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -415,4 +486,105 @@ describe('SegmentedControl disabled state', () => {
|
|
|
415
486
|
expect(handleChange).toHaveBeenCalledWith('table');
|
|
416
487
|
expect(screen.getByRole('radio', {name: 'Table'})).toHaveFocus();
|
|
417
488
|
});
|
|
489
|
+
|
|
490
|
+
describe('disabledMessage', () => {
|
|
491
|
+
const h = {hidden: true} as const;
|
|
492
|
+
|
|
493
|
+
function renderControl(props?: {onChange?: (v: string) => void}) {
|
|
494
|
+
return render(
|
|
495
|
+
<SegmentedControl
|
|
496
|
+
value="grid"
|
|
497
|
+
onChange={props?.onChange ?? (() => {})}
|
|
498
|
+
label="View mode"
|
|
499
|
+
isDisabled
|
|
500
|
+
disabledMessage="Choose a project to switch views">
|
|
501
|
+
<SegmentedControlItem value="grid" label="Grid" />
|
|
502
|
+
<SegmentedControlItem value="list" label="List" />
|
|
503
|
+
</SegmentedControl>,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
it('shows the reason tooltip on hover when the control is disabled with a reason', async () => {
|
|
508
|
+
renderControl();
|
|
509
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
510
|
+
expect(tooltip).toHaveTextContent('Choose a project to switch views');
|
|
511
|
+
const group = screen.getByRole('radiogroup');
|
|
512
|
+
fireEvent.mouseEnter(group);
|
|
513
|
+
await waitFor(() => expect(tooltip).toHaveAttribute('popover-open'));
|
|
514
|
+
fireEvent.mouseLeave(group);
|
|
515
|
+
await waitFor(() => expect(tooltip).not.toHaveAttribute('popover-open'));
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
519
|
+
const user = userEvent.setup();
|
|
520
|
+
renderControl();
|
|
521
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
522
|
+
await user.tab();
|
|
523
|
+
await waitFor(() => expect(tooltip).toHaveAttribute('popover-open'));
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('does not render a tooltip when not disabled', () => {
|
|
527
|
+
render(
|
|
528
|
+
<SegmentedControl
|
|
529
|
+
value="grid"
|
|
530
|
+
onChange={() => {}}
|
|
531
|
+
label="View mode"
|
|
532
|
+
disabledMessage="Choose a project to switch views">
|
|
533
|
+
<SegmentedControlItem value="grid" label="Grid" />
|
|
534
|
+
</SegmentedControl>,
|
|
535
|
+
);
|
|
536
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
540
|
+
render(
|
|
541
|
+
<SegmentedControl
|
|
542
|
+
value="grid"
|
|
543
|
+
onChange={() => {}}
|
|
544
|
+
label="View mode"
|
|
545
|
+
isDisabled>
|
|
546
|
+
<SegmentedControlItem value="grid" label="Grid" />
|
|
547
|
+
</SegmentedControl>,
|
|
548
|
+
);
|
|
549
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('keeps the selected segment focusable when a reason is provided', () => {
|
|
553
|
+
renderControl();
|
|
554
|
+
const selected = screen.getByRole('radio', {name: 'Grid', hidden: true});
|
|
555
|
+
expect(selected).toHaveAttribute('aria-disabled', 'true');
|
|
556
|
+
expect(selected).toHaveAttribute('tabindex', '0');
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('links the reason tooltip from the group via aria-describedby', () => {
|
|
560
|
+
renderControl();
|
|
561
|
+
const group = screen.getByRole('radiogroup');
|
|
562
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
563
|
+
expect(group.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('blocks selection while focusable-disabled', () => {
|
|
567
|
+
const onChange = vi.fn();
|
|
568
|
+
renderControl({onChange});
|
|
569
|
+
const list = screen.getByRole('radio', {name: 'List', hidden: true});
|
|
570
|
+
fireEvent.click(list);
|
|
571
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('drops all segments from the tab order when disabled without a reason', () => {
|
|
575
|
+
render(
|
|
576
|
+
<SegmentedControl
|
|
577
|
+
value="grid"
|
|
578
|
+
onChange={() => {}}
|
|
579
|
+
label="View mode"
|
|
580
|
+
isDisabled>
|
|
581
|
+
<SegmentedControlItem value="grid" label="Grid" />
|
|
582
|
+
<SegmentedControlItem value="list" label="List" />
|
|
583
|
+
</SegmentedControl>,
|
|
584
|
+
);
|
|
585
|
+
for (const radio of screen.getAllByRole('radio', h)) {
|
|
586
|
+
expect(radio).toHaveAttribute('tabindex', '-1');
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
});
|
|
418
590
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file SegmentedControl.tsx
|
|
7
|
-
* @input Uses React, StyleX, SegmentedControlContext
|
|
7
|
+
* @input Uses React, StyleX, SegmentedControlContext, useListFocus, useKeyboardHint
|
|
8
8
|
* @output Exports SegmentedControl component and SegmentedControlProps type
|
|
9
9
|
* @position Container wrapper; provides context to SegmentedControlItem children
|
|
10
10
|
*
|
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
* - /packages/cli/templates/blocks/components/SegmentedControl/ (showcase blocks)
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import React, {useMemo,
|
|
18
|
+
import React, {useMemo, useCallback, type ReactNode} from 'react';
|
|
19
19
|
import * as stylex from '@stylexjs/stylex';
|
|
20
20
|
import {colorVars, spacingVars, radiusVars} from '../theme/tokens.stylex';
|
|
21
21
|
import {SegmentedControlContext} from './SegmentedControlContext';
|
|
22
|
+
import {useListFocus} from '../hooks/useListFocus';
|
|
23
|
+
import {useKeyboardHint} from '../hooks/useKeyboardHint';
|
|
24
|
+
import {useTooltip} from '../Tooltip';
|
|
22
25
|
import type {
|
|
23
26
|
SegmentedControlSize,
|
|
24
27
|
SegmentedControlLayout,
|
|
@@ -62,6 +65,17 @@ export interface SegmentedControlProps extends Omit<
|
|
|
62
65
|
* @default false
|
|
63
66
|
*/
|
|
64
67
|
isDisabled?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Explains why the control is disabled. Applies to the whole-group disabled
|
|
70
|
+
* state (`isDisabled`), not individual segments. When set together with
|
|
71
|
+
* `isDisabled`, the control shows a tooltip with this text on hover and
|
|
72
|
+
* keyboard focus, and stays focusable (via `aria-disabled`) so the reason is
|
|
73
|
+
* discoverable by keyboard and assistive technology. Selection stays blocked.
|
|
74
|
+
*
|
|
75
|
+
* Use this instead of wrapping a disabled control in `Tooltip` — disabled
|
|
76
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
77
|
+
*/
|
|
78
|
+
disabledMessage?: string;
|
|
65
79
|
/**
|
|
66
80
|
* SegmentedControlItem children.
|
|
67
81
|
*/
|
|
@@ -89,6 +103,12 @@ const styles = stylex.create({
|
|
|
89
103
|
opacity: 0.5,
|
|
90
104
|
pointerEvents: 'none',
|
|
91
105
|
},
|
|
106
|
+
// Disabled *with* a disabledMessage: keep the dimmed look but leave pointer
|
|
107
|
+
// events on so the group can receive hover and surface the reason tooltip.
|
|
108
|
+
// Selection is still blocked by the isDisabled guards.
|
|
109
|
+
disabledWithMessage: {
|
|
110
|
+
opacity: 0.5,
|
|
111
|
+
},
|
|
92
112
|
});
|
|
93
113
|
|
|
94
114
|
const sizeStyles = stylex.create({
|
|
@@ -127,98 +147,126 @@ export function SegmentedControl({
|
|
|
127
147
|
size: sizeProp,
|
|
128
148
|
layout = 'hug',
|
|
129
149
|
isDisabled = false,
|
|
150
|
+
disabledMessage,
|
|
130
151
|
children,
|
|
131
152
|
xstyle,
|
|
132
153
|
className,
|
|
133
154
|
style,
|
|
134
155
|
}: SegmentedControlProps) {
|
|
135
156
|
const size = useSize(sizeProp, 'md');
|
|
136
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
137
157
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
// Disabled-reason tooltip. Applies to the whole-group disabled state. Disabled
|
|
159
|
+
// controls swallow pointer events, so the tooltip listeners attach to the
|
|
160
|
+
// radiogroup container (and the container keeps pointer events on in this
|
|
161
|
+
// mode) while the radios stay perceivable via aria-disabled. Selection is
|
|
162
|
+
// blocked by the isDisabled guards in the click/focus handlers.
|
|
163
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
164
|
+
const disabledMessageTooltip = useTooltip({
|
|
165
|
+
placement: 'above',
|
|
166
|
+
// The radiogroup container is not naturally focusable; focusin bubbles up
|
|
167
|
+
// from the radios, so always attach focus listeners.
|
|
168
|
+
focusTrigger: 'always',
|
|
169
|
+
isEnabled: showsDisabledMessage,
|
|
170
|
+
});
|
|
143
171
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
172
|
+
// Roving tabindex + arrow/Home/End navigation is owned by the shared
|
|
173
|
+
// useListFocus primitive: it stamps a single tab stop (tabIndex 0/-1) across
|
|
174
|
+
// the radios, skips disabled ones, wraps at the ends, handles Home/End, and
|
|
175
|
+
// repairs the tab stop on mount and whenever items mount/disable — replacing
|
|
176
|
+
// the component's former inline keyboard handler and tab-stop repair effect.
|
|
177
|
+
const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
|
|
178
|
+
itemSelector: '[role="radio"]:not([aria-disabled="true"])',
|
|
179
|
+
hasRovingTabIndex: true,
|
|
180
|
+
wrap: true,
|
|
181
|
+
orientation: 'horizontal',
|
|
182
|
+
});
|
|
148
183
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
const hint = useKeyboardHint({
|
|
185
|
+
orientation: 'horizontal',
|
|
186
|
+
isEnabled: !isDisabled,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const handleContainerKeyDown = useCallback(
|
|
190
|
+
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
191
|
+
hint.onKeyDown(e);
|
|
192
|
+
handleKeyDown(e);
|
|
193
|
+
},
|
|
194
|
+
[hint, handleKeyDown],
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Selection-follows-focus (APG radiogroup): useListFocus only *moves* focus,
|
|
198
|
+
// so whenever it lands focus on a new radio (arrow/Home/End, or a click) we
|
|
199
|
+
// select that radio's value. Reading the focused element's data-value here
|
|
200
|
+
// keeps selection in lockstep with focus without duplicating the navigation
|
|
201
|
+
// logic. Disabled radios are ignored (they should never become the value),
|
|
202
|
+
// and the already-selected value is skipped so an initial Tab-in (or a click
|
|
203
|
+
// on the current segment) is a no-op, matching click behavior.
|
|
204
|
+
const handleContainerFocus = useCallback(
|
|
205
|
+
(e: React.FocusEvent) => {
|
|
206
|
+
hint.onFocus(e);
|
|
207
|
+
handleFocus(e);
|
|
208
|
+
if (isDisabled) {
|
|
155
209
|
return;
|
|
156
210
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
item => item === document.activeElement,
|
|
211
|
+
const focused = (e.target as HTMLElement | null)?.closest<HTMLElement>(
|
|
212
|
+
'[role="radio"][data-value]',
|
|
160
213
|
);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
switch (e.key) {
|
|
164
|
-
case 'ArrowRight':
|
|
165
|
-
nextIndex =
|
|
166
|
-
currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;
|
|
167
|
-
break;
|
|
168
|
-
case 'ArrowLeft':
|
|
169
|
-
nextIndex =
|
|
170
|
-
currentIndex === -1
|
|
171
|
-
? items.length - 1
|
|
172
|
-
: (currentIndex - 1 + items.length) % items.length;
|
|
173
|
-
break;
|
|
174
|
-
case 'Home':
|
|
175
|
-
nextIndex = 0;
|
|
176
|
-
break;
|
|
177
|
-
case 'End':
|
|
178
|
-
nextIndex = items.length - 1;
|
|
179
|
-
break;
|
|
180
|
-
default:
|
|
181
|
-
return;
|
|
214
|
+
if (!focused || focused.getAttribute('aria-disabled') === 'true') {
|
|
215
|
+
return;
|
|
182
216
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const nextItem = items[nextIndex];
|
|
186
|
-
nextItem.focus();
|
|
187
|
-
const nextValue = nextItem.dataset.value;
|
|
188
|
-
if (nextValue != null) {
|
|
217
|
+
const nextValue = focused.dataset.value;
|
|
218
|
+
if (nextValue != null && nextValue !== value) {
|
|
189
219
|
onChange(nextValue);
|
|
190
220
|
}
|
|
191
221
|
},
|
|
192
|
-
[isDisabled, onChange],
|
|
222
|
+
[hint, handleFocus, isDisabled, onChange, value],
|
|
193
223
|
);
|
|
194
224
|
|
|
195
225
|
const contextValue = useMemo(
|
|
196
|
-
() => ({
|
|
197
|
-
|
|
226
|
+
() => ({
|
|
227
|
+
value,
|
|
228
|
+
onChange,
|
|
229
|
+
size,
|
|
230
|
+
layout,
|
|
231
|
+
isDisabled,
|
|
232
|
+
hasDisabledMessage: showsDisabledMessage,
|
|
233
|
+
}),
|
|
234
|
+
[value, onChange, size, layout, isDisabled, showsDisabledMessage],
|
|
198
235
|
);
|
|
199
236
|
|
|
200
237
|
return (
|
|
201
238
|
<SegmentedControlContext value={contextValue}>
|
|
202
239
|
<div
|
|
203
|
-
ref={mergeRefs(ref,
|
|
240
|
+
ref={mergeRefs(ref, listRef, disabledMessageTooltip.ref)}
|
|
204
241
|
role="radiogroup"
|
|
205
242
|
aria-label={label}
|
|
206
243
|
aria-disabled={isDisabled || undefined}
|
|
207
|
-
|
|
244
|
+
aria-describedby={
|
|
245
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : undefined
|
|
246
|
+
}
|
|
247
|
+
onKeyDown={handleContainerKeyDown}
|
|
248
|
+
onFocus={handleContainerFocus}
|
|
249
|
+
onBlur={hint.onBlur}
|
|
208
250
|
{...mergeProps(
|
|
209
251
|
themeProps('segmented-control', {size}),
|
|
210
252
|
stylex.props(
|
|
211
253
|
styles.container,
|
|
212
254
|
sizeStyles[size],
|
|
213
255
|
layout === 'fill' && styles.fill,
|
|
214
|
-
isDisabled &&
|
|
256
|
+
isDisabled &&
|
|
257
|
+
(showsDisabledMessage
|
|
258
|
+
? styles.disabledWithMessage
|
|
259
|
+
: styles.disabled),
|
|
215
260
|
xstyle,
|
|
216
261
|
),
|
|
217
262
|
className,
|
|
218
263
|
style,
|
|
219
264
|
)}>
|
|
220
265
|
{children}
|
|
266
|
+
{hint.hintElement}
|
|
221
267
|
</div>
|
|
268
|
+
{showsDisabledMessage &&
|
|
269
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
222
270
|
</SegmentedControlContext>
|
|
223
271
|
);
|
|
224
272
|
}
|
|
@@ -22,6 +22,13 @@ export interface SegmentedControlContextValue {
|
|
|
22
22
|
size: SegmentedControlSize;
|
|
23
23
|
layout: SegmentedControlLayout;
|
|
24
24
|
isDisabled: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* True when the whole control is disabled *and* a `disabledMessage` is set.
|
|
27
|
+
* In that mode the selected segment stays focusable (via `aria-disabled`
|
|
28
|
+
* rather than being dropped from the tab order) so the group's disabled-reason
|
|
29
|
+
* tooltip is keyboard-discoverable; selection is still blocked.
|
|
30
|
+
*/
|
|
31
|
+
hasDisabledMessage?: boolean;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
export const SegmentedControlContext =
|
|
@@ -173,6 +173,12 @@ export function SegmentedControlItem({
|
|
|
173
173
|
|
|
174
174
|
const isSelected = ctx.value === value;
|
|
175
175
|
const isItemDisabled = isDisabled || ctx.isDisabled;
|
|
176
|
+
// When the whole group is disabled with a disabledMessage, keep the selected
|
|
177
|
+
// segment focusable so the group's reason tooltip is keyboard-discoverable.
|
|
178
|
+
// Per-item disabling (`isDisabled` on the item) always drops out of the tab
|
|
179
|
+
// order. Activation stays blocked by the isItemDisabled guard in handleClick.
|
|
180
|
+
const keepsSelectedFocusable =
|
|
181
|
+
isSelected && (ctx.hasDisabledMessage ?? false) && !isDisabled;
|
|
176
182
|
const size: SegmentedControlSize = ctx.size;
|
|
177
183
|
const isFill = ctx.layout === 'fill';
|
|
178
184
|
|
|
@@ -195,7 +201,14 @@ export function SegmentedControlItem({
|
|
|
195
201
|
aria-disabled={isItemDisabled || undefined}
|
|
196
202
|
aria-label={isLabelHidden ? label : undefined}
|
|
197
203
|
data-value={value}
|
|
198
|
-
|
|
204
|
+
// Disabled items (including when the whole group is disabled) are not tab
|
|
205
|
+
// stops — otherwise the selected segment stays keyboard-focusable but is
|
|
206
|
+
// silently dead (arrows and activation are no-ops) (navigation-13). The
|
|
207
|
+
// exception is a whole-group disabledMessage, where the selected segment
|
|
208
|
+
// stays focusable so the reason tooltip is keyboard-discoverable.
|
|
209
|
+
tabIndex={
|
|
210
|
+
(isSelected && !isItemDisabled) || keepsSelectedFocusable ? 0 : -1
|
|
211
|
+
}
|
|
199
212
|
onClick={handleClick}
|
|
200
213
|
{...mergeProps(
|
|
201
214
|
themeProps('segmented-control-item', {
|
|
@@ -46,7 +46,7 @@ export const docs = {
|
|
|
46
46
|
props: {gap: 1},
|
|
47
47
|
children: [
|
|
48
48
|
{__element: 'XDSHeading', props: {level: 3}, children: 'Pro plan'},
|
|
49
|
-
{__element: 'XDSText', props: {type: 'body'}, children: '$29/month
|
|
49
|
+
{__element: 'XDSText', props: {type: 'body'}, children: '$29/month, unlimited projects and priority support.'},
|
|
50
50
|
],
|
|
51
51
|
},
|
|
52
52
|
},
|