@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d
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
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
|
|
13
|
-
import {render, screen, fireEvent, act} from '@testing-library/react';
|
|
13
|
+
import {render, screen, fireEvent, act, waitFor} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {Tokenizer} from './Tokenizer';
|
|
16
16
|
import type {SearchSource, SearchableItem} from '../Typeahead/types';
|
|
@@ -763,4 +763,108 @@ describe('Tokenizer', () => {
|
|
|
763
763
|
expect(screen.getByText('Create "NewTag"')).toBeInTheDocument();
|
|
764
764
|
});
|
|
765
765
|
});
|
|
766
|
+
|
|
767
|
+
describe('disabledMessage', () => {
|
|
768
|
+
const h = {hidden: true} as const;
|
|
769
|
+
const isOpen = (el: Element) => el.matches(':popover-open');
|
|
770
|
+
|
|
771
|
+
function renderTokenizer(props?: {onChange?: () => void}) {
|
|
772
|
+
return render(
|
|
773
|
+
<Tokenizer
|
|
774
|
+
label="Members"
|
|
775
|
+
searchSource={userSource}
|
|
776
|
+
value={[]}
|
|
777
|
+
onChange={props?.onChange ?? (() => {})}
|
|
778
|
+
isDisabled
|
|
779
|
+
disabledMessage="You need edit access to change members"
|
|
780
|
+
/>,
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
785
|
+
renderTokenizer();
|
|
786
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
787
|
+
expect(tooltip).toHaveTextContent(
|
|
788
|
+
'You need edit access to change members',
|
|
789
|
+
);
|
|
790
|
+
const wrapper = screen.getByRole('group', {name: 'Members'});
|
|
791
|
+
fireEvent.mouseEnter(wrapper);
|
|
792
|
+
await waitFor(() => expect(isOpen(tooltip)).toBe(true));
|
|
793
|
+
fireEvent.mouseLeave(wrapper);
|
|
794
|
+
await waitFor(() => expect(isOpen(tooltip)).toBe(false));
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
798
|
+
const user = userEvent.setup();
|
|
799
|
+
renderTokenizer();
|
|
800
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
801
|
+
await user.tab();
|
|
802
|
+
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
803
|
+
await waitFor(() => expect(isOpen(tooltip)).toBe(true));
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
it('does not render a tooltip when not disabled', () => {
|
|
807
|
+
render(
|
|
808
|
+
<Tokenizer
|
|
809
|
+
label="Members"
|
|
810
|
+
searchSource={userSource}
|
|
811
|
+
value={[]}
|
|
812
|
+
onChange={() => {}}
|
|
813
|
+
disabledMessage="You need edit access to change members"
|
|
814
|
+
/>,
|
|
815
|
+
);
|
|
816
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
820
|
+
render(
|
|
821
|
+
<Tokenizer
|
|
822
|
+
label="Members"
|
|
823
|
+
searchSource={userSource}
|
|
824
|
+
value={[]}
|
|
825
|
+
onChange={() => {}}
|
|
826
|
+
isDisabled
|
|
827
|
+
/>,
|
|
828
|
+
);
|
|
829
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
833
|
+
renderTokenizer();
|
|
834
|
+
const input = screen.getByRole('combobox');
|
|
835
|
+
expect(input).not.toBeDisabled();
|
|
836
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it('links the reason tooltip via aria-describedby', () => {
|
|
840
|
+
renderTokenizer();
|
|
841
|
+
const input = screen.getByRole('combobox');
|
|
842
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
843
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
it('blocks input while focusable-disabled', async () => {
|
|
847
|
+
const user = userEvent.setup();
|
|
848
|
+
const onChange = vi.fn();
|
|
849
|
+
renderTokenizer({onChange});
|
|
850
|
+
const input = screen.getByRole('combobox');
|
|
851
|
+
input.focus();
|
|
852
|
+
await user.keyboard('Ali');
|
|
853
|
+
expect((input as HTMLInputElement).value).toBe('');
|
|
854
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
it('keeps the input natively disabled when disabled without a reason', () => {
|
|
858
|
+
render(
|
|
859
|
+
<Tokenizer
|
|
860
|
+
label="Members"
|
|
861
|
+
searchSource={userSource}
|
|
862
|
+
value={[]}
|
|
863
|
+
onChange={() => {}}
|
|
864
|
+
isDisabled
|
|
865
|
+
/>,
|
|
866
|
+
);
|
|
867
|
+
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
868
|
+
});
|
|
869
|
+
});
|
|
766
870
|
});
|
|
@@ -42,6 +42,7 @@ import {Token} from '../Token';
|
|
|
42
42
|
import {renderIconSlot, type IconType} from '../Icon';
|
|
43
43
|
import {OverflowList} from '../OverflowList';
|
|
44
44
|
import {useLayer} from '../Layer/useLayer';
|
|
45
|
+
import {useTooltip} from '../Tooltip';
|
|
45
46
|
import {
|
|
46
47
|
colorVars,
|
|
47
48
|
spacingVars,
|
|
@@ -145,6 +146,17 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<
|
|
|
145
146
|
emptySearchResultsText?: string;
|
|
146
147
|
/** Whether the input is disabled. @default false */
|
|
147
148
|
isDisabled?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Explains why the tokenizer is disabled. When set together with
|
|
151
|
+
* `isDisabled`, the tokenizer shows a tooltip with this text on hover and
|
|
152
|
+
* keyboard focus, and the input stays focusable (via `aria-disabled`) so the
|
|
153
|
+
* reason is discoverable by keyboard and assistive technology. Input stays
|
|
154
|
+
* blocked.
|
|
155
|
+
*
|
|
156
|
+
* Use this instead of wrapping a disabled tokenizer in `Tooltip` — disabled
|
|
157
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
158
|
+
*/
|
|
159
|
+
disabledMessage?: string;
|
|
148
160
|
/** Show clear button (clears all tokens). @default false */
|
|
149
161
|
hasClear?: boolean;
|
|
150
162
|
/**
|
|
@@ -365,6 +377,7 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
365
377
|
maxMenuItems,
|
|
366
378
|
emptySearchResultsText,
|
|
367
379
|
isDisabled = false,
|
|
380
|
+
disabledMessage,
|
|
368
381
|
hasClear = false,
|
|
369
382
|
endContent,
|
|
370
383
|
hasAutoFocus,
|
|
@@ -390,6 +403,19 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
390
403
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
391
404
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
392
405
|
|
|
406
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
407
|
+
// tooltip listeners attach to the input wrapper and the typeahead input stays
|
|
408
|
+
// perceivable via aria-disabled instead of the disabled attribute. Input is
|
|
409
|
+
// blocked by the isDisabled guards in BaseTypeahead and handleWrapperClick.
|
|
410
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
411
|
+
const disabledMessageTooltip = useTooltip({
|
|
412
|
+
placement: 'above',
|
|
413
|
+
// The wrapper is not naturally focusable; focusin bubbles up from the
|
|
414
|
+
// input, so always attach focus listeners.
|
|
415
|
+
focusTrigger: 'always',
|
|
416
|
+
isEnabled: showsDisabledMessage,
|
|
417
|
+
});
|
|
418
|
+
|
|
393
419
|
useImperativeHandle(handleRef, () => ({
|
|
394
420
|
focus() {
|
|
395
421
|
inputRef.current?.focus();
|
|
@@ -620,6 +646,7 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
620
646
|
[
|
|
621
647
|
description ? descriptionId : null,
|
|
622
648
|
status?.message ? statusMessageId : null,
|
|
649
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
623
650
|
]
|
|
624
651
|
.filter(Boolean)
|
|
625
652
|
.join(' ') || undefined;
|
|
@@ -659,7 +686,13 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
659
686
|
|
|
660
687
|
const wrapperContent = (
|
|
661
688
|
<div
|
|
662
|
-
ref={
|
|
689
|
+
ref={el => {
|
|
690
|
+
wrapperRef.current = el;
|
|
691
|
+
// Anchor + hover/focus listeners for the disabled-message tooltip.
|
|
692
|
+
// Handlers are gated internally by isEnabled, so attaching
|
|
693
|
+
// unconditionally is safe.
|
|
694
|
+
disabledMessageTooltip.ref(el);
|
|
695
|
+
}}
|
|
663
696
|
role="group"
|
|
664
697
|
aria-label={label}
|
|
665
698
|
onClick={handleWrapperClick}
|
|
@@ -710,6 +743,7 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
710
743
|
maxMenuItems={maxMenuItems}
|
|
711
744
|
emptySearchResultsText={emptySearchResultsText}
|
|
712
745
|
isDisabled={isDisabled}
|
|
746
|
+
isFocusableDisabled={showsDisabledMessage}
|
|
713
747
|
hasAutoFocus={hasAutoFocus}
|
|
714
748
|
inputId={inputId}
|
|
715
749
|
ariaDescribedBy={ariaDescribedBy}
|
|
@@ -831,6 +865,8 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
831
865
|
className={className}
|
|
832
866
|
style={style}>
|
|
833
867
|
{tokenizerContent}
|
|
868
|
+
{showsDisabledMessage &&
|
|
869
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
834
870
|
</Field>
|
|
835
871
|
);
|
|
836
872
|
}
|
|
@@ -71,9 +71,12 @@ describe('Toolbar', () => {
|
|
|
71
71
|
expect(screen.getByTestId('center')).toBeInTheDocument();
|
|
72
72
|
expect(screen.getByTestId('end')).toBeInTheDocument();
|
|
73
73
|
|
|
74
|
-
// Three-slot layout produces 3 child divs
|
|
74
|
+
// Three-slot layout produces 3 child divs (plus the aria-hidden
|
|
75
|
+
// keyboard-hint popover, which is excluded here as an implementation detail)
|
|
75
76
|
const toolbar = screen.getByRole('toolbar');
|
|
76
|
-
expect(toolbar.
|
|
77
|
+
expect(toolbar.querySelectorAll(':scope > :not([popover])')).toHaveLength(
|
|
78
|
+
3,
|
|
79
|
+
);
|
|
77
80
|
});
|
|
78
81
|
|
|
79
82
|
it('renders two-slot layout without centerContent', () => {
|
|
@@ -87,9 +90,12 @@ describe('Toolbar', () => {
|
|
|
87
90
|
expect(screen.getByTestId('start')).toBeInTheDocument();
|
|
88
91
|
expect(screen.getByTestId('end')).toBeInTheDocument();
|
|
89
92
|
|
|
90
|
-
// Two-slot layout produces 2 child divs
|
|
93
|
+
// Two-slot layout produces 2 child divs (plus the aria-hidden
|
|
94
|
+
// keyboard-hint popover, excluded here)
|
|
91
95
|
const toolbar = screen.getByRole('toolbar');
|
|
92
|
-
expect(toolbar.
|
|
96
|
+
expect(toolbar.querySelectorAll(':scope > :not([popover])')).toHaveLength(
|
|
97
|
+
2,
|
|
98
|
+
);
|
|
93
99
|
});
|
|
94
100
|
|
|
95
101
|
it('renders start-only layout', () => {
|
|
@@ -101,7 +107,9 @@ describe('Toolbar', () => {
|
|
|
101
107
|
);
|
|
102
108
|
expect(screen.getByTestId('start')).toBeInTheDocument();
|
|
103
109
|
const toolbar = screen.getByRole('toolbar');
|
|
104
|
-
expect(toolbar.
|
|
110
|
+
expect(toolbar.querySelectorAll(':scope > :not([popover])')).toHaveLength(
|
|
111
|
+
1,
|
|
112
|
+
);
|
|
105
113
|
});
|
|
106
114
|
|
|
107
115
|
it('renders end-only layout', () => {
|
|
@@ -113,7 +121,9 @@ describe('Toolbar', () => {
|
|
|
113
121
|
);
|
|
114
122
|
expect(screen.getByTestId('end')).toBeInTheDocument();
|
|
115
123
|
const toolbar = screen.getByRole('toolbar');
|
|
116
|
-
expect(toolbar.
|
|
124
|
+
expect(toolbar.querySelectorAll(':scope > :not([popover])')).toHaveLength(
|
|
125
|
+
1,
|
|
126
|
+
);
|
|
117
127
|
});
|
|
118
128
|
|
|
119
129
|
it('sets aria-orientation to horizontal by default', () => {
|
|
@@ -256,4 +266,97 @@ describe('Toolbar', () => {
|
|
|
256
266
|
render(<Toolbar label="Actions" data-testid="my-toolbar" />);
|
|
257
267
|
expect(screen.getByTestId('my-toolbar')).toBe(screen.getByRole('toolbar'));
|
|
258
268
|
});
|
|
269
|
+
|
|
270
|
+
it('is a single tab stop — only one item is tabbable (navigation-3)', () => {
|
|
271
|
+
render(
|
|
272
|
+
<Toolbar
|
|
273
|
+
label="Actions"
|
|
274
|
+
startContent={
|
|
275
|
+
<>
|
|
276
|
+
<button type="button">Cut</button>
|
|
277
|
+
<button type="button">Copy</button>
|
|
278
|
+
<button type="button">Paste</button>
|
|
279
|
+
</>
|
|
280
|
+
}
|
|
281
|
+
/>,
|
|
282
|
+
);
|
|
283
|
+
const buttons = screen.getAllByRole('button');
|
|
284
|
+
const tabbable = buttons.filter(b => b.getAttribute('tabindex') === '0');
|
|
285
|
+
expect(tabbable).toHaveLength(1);
|
|
286
|
+
expect(buttons[0]).toHaveAttribute('tabindex', '0');
|
|
287
|
+
expect(buttons[1]).toHaveAttribute('tabindex', '-1');
|
|
288
|
+
expect(buttons[2]).toHaveAttribute('tabindex', '-1');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('does not steal caret keys from a text input mid-line (navigation-4)', async () => {
|
|
292
|
+
const user = userEvent.setup();
|
|
293
|
+
render(
|
|
294
|
+
<Toolbar
|
|
295
|
+
label="Search"
|
|
296
|
+
startContent={
|
|
297
|
+
<>
|
|
298
|
+
<input type="text" aria-label="Query" defaultValue="hello" />
|
|
299
|
+
<button type="button">Go</button>
|
|
300
|
+
</>
|
|
301
|
+
}
|
|
302
|
+
/>,
|
|
303
|
+
);
|
|
304
|
+
const inputEl = screen.getByLabelText('Query');
|
|
305
|
+
if (!(inputEl instanceof HTMLInputElement)) {
|
|
306
|
+
throw new Error('expected an input');
|
|
307
|
+
}
|
|
308
|
+
inputEl.focus();
|
|
309
|
+
inputEl.setSelectionRange(1, 1); // caret mid-line
|
|
310
|
+
await user.keyboard('{ArrowRight}');
|
|
311
|
+
// Caret movement stays in the input; focus is not stolen by the toolbar.
|
|
312
|
+
expect(document.activeElement).toBe(inputEl);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('composes consumer onKeyDown with internal arrow navigation', async () => {
|
|
316
|
+
const user = userEvent.setup();
|
|
317
|
+
const onKeyDown = vi.fn();
|
|
318
|
+
|
|
319
|
+
render(
|
|
320
|
+
<Toolbar
|
|
321
|
+
label="Actions"
|
|
322
|
+
onKeyDown={onKeyDown}
|
|
323
|
+
startContent={
|
|
324
|
+
<>
|
|
325
|
+
<button type="button">Cut</button>
|
|
326
|
+
<button type="button">Copy</button>
|
|
327
|
+
</>
|
|
328
|
+
}
|
|
329
|
+
/>,
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
const buttons = screen.getAllByRole('button');
|
|
333
|
+
buttons[0].focus();
|
|
334
|
+
await user.keyboard('{ArrowRight}');
|
|
335
|
+
|
|
336
|
+
expect(onKeyDown).toHaveBeenCalled();
|
|
337
|
+
expect(buttons[1]).toHaveFocus();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('respects preventDefault from consumer onKeyDown', async () => {
|
|
341
|
+
const user = userEvent.setup();
|
|
342
|
+
|
|
343
|
+
render(
|
|
344
|
+
<Toolbar
|
|
345
|
+
label="Actions"
|
|
346
|
+
onKeyDown={e => e.preventDefault()}
|
|
347
|
+
startContent={
|
|
348
|
+
<>
|
|
349
|
+
<button type="button">Cut</button>
|
|
350
|
+
<button type="button">Copy</button>
|
|
351
|
+
</>
|
|
352
|
+
}
|
|
353
|
+
/>,
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const buttons = screen.getAllByRole('button');
|
|
357
|
+
buttons[0].focus();
|
|
358
|
+
await user.keyboard('{ArrowRight}');
|
|
359
|
+
|
|
360
|
+
expect(buttons[0]).toHaveFocus();
|
|
361
|
+
});
|
|
259
362
|
});
|
package/src/Toolbar/Toolbar.tsx
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Toolbar.tsx
|
|
7
|
-
* @input Uses Section, SizeContext, useListFocus, StyleX, spacingVars, sizeVars
|
|
7
|
+
* @input Uses Section, SizeContext, useListFocus, useKeyboardHint, StyleX, spacingVars, sizeVars
|
|
8
8
|
* @output Exports Toolbar component and ToolbarProps
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* - /packages/cli/templates/blocks/components/Toolbar/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type
|
|
19
|
+
import {useCallback, type ReactNode} from 'react';
|
|
20
20
|
import type {BaseProps} from '../BaseProps';
|
|
21
21
|
import type {SectionVariant} from '../Section/Section';
|
|
22
22
|
import type {SpacingStep} from '../utils/types';
|
|
@@ -26,6 +26,7 @@ import {spacingVars, sizeVars} from '../theme/tokens.stylex';
|
|
|
26
26
|
import {mergeProps} from '../utils';
|
|
27
27
|
import {Section} from '../Section/Section';
|
|
28
28
|
import {useListFocus} from '../hooks/useListFocus';
|
|
29
|
+
import {useKeyboardHint} from '../hooks/useKeyboardHint';
|
|
29
30
|
import {SizeProvider} from '../SizeContext/SizeContext';
|
|
30
31
|
import {edgeCompSlot} from '../Layout/edgeCompensation.stylex';
|
|
31
32
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -227,6 +228,12 @@ export function Toolbar({
|
|
|
227
228
|
className,
|
|
228
229
|
style,
|
|
229
230
|
ref,
|
|
231
|
+
onKeyDown: onKeyDownProp,
|
|
232
|
+
onFocus: onFocusProp,
|
|
233
|
+
onBlur: onBlurProp,
|
|
234
|
+
role: _role,
|
|
235
|
+
'aria-label': _ariaLabel,
|
|
236
|
+
'aria-orientation': _ariaOrientation,
|
|
230
237
|
...props
|
|
231
238
|
}: ToolbarProps) {
|
|
232
239
|
const hasCenterContent = centerContent != null;
|
|
@@ -235,11 +242,55 @@ export function Toolbar({
|
|
|
235
242
|
|
|
236
243
|
const gapVar = spacingVars[spacingStepToVar[gap]] as string;
|
|
237
244
|
|
|
238
|
-
const {listRef, handleKeyDown} = useListFocus<HTMLDivElement>({
|
|
239
|
-
itemSelector: 'button, input, [tabindex
|
|
245
|
+
const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
|
|
246
|
+
itemSelector: 'button, input, [tabindex]',
|
|
240
247
|
orientation,
|
|
248
|
+
hasRovingTabIndex: true,
|
|
249
|
+
hasCaretGuard: true,
|
|
241
250
|
});
|
|
242
251
|
|
|
252
|
+
const {
|
|
253
|
+
hintElement,
|
|
254
|
+
onKeyDown: onHintKeyDown,
|
|
255
|
+
onFocus: onHintFocus,
|
|
256
|
+
onBlur: onHintBlur,
|
|
257
|
+
} = useKeyboardHint({orientation});
|
|
258
|
+
|
|
259
|
+
const handleToolbarKeyDown = useCallback(
|
|
260
|
+
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
261
|
+
onKeyDownProp?.(e);
|
|
262
|
+
if (e.defaultPrevented) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
onHintKeyDown(e);
|
|
266
|
+
handleKeyDown(e);
|
|
267
|
+
},
|
|
268
|
+
[onKeyDownProp, onHintKeyDown, handleKeyDown],
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const handleToolbarFocus = useCallback(
|
|
272
|
+
(e: React.FocusEvent<HTMLDivElement>) => {
|
|
273
|
+
onFocusProp?.(e);
|
|
274
|
+
if (e.defaultPrevented) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
onHintFocus(e);
|
|
278
|
+
handleFocus(e);
|
|
279
|
+
},
|
|
280
|
+
[onFocusProp, onHintFocus, handleFocus],
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const handleToolbarBlur = useCallback(
|
|
284
|
+
(e: React.FocusEvent<HTMLDivElement>) => {
|
|
285
|
+
onBlurProp?.(e);
|
|
286
|
+
if (e.defaultPrevented) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
onHintBlur(e);
|
|
290
|
+
},
|
|
291
|
+
[onBlurProp, onHintBlur],
|
|
292
|
+
);
|
|
293
|
+
|
|
243
294
|
return (
|
|
244
295
|
<SizeProvider value={size}>
|
|
245
296
|
<Section
|
|
@@ -255,7 +306,9 @@ export function Toolbar({
|
|
|
255
306
|
role="toolbar"
|
|
256
307
|
aria-label={label}
|
|
257
308
|
aria-orientation={orientation}
|
|
258
|
-
onKeyDown={
|
|
309
|
+
onKeyDown={handleToolbarKeyDown}
|
|
310
|
+
onFocus={handleToolbarFocus}
|
|
311
|
+
onBlur={handleToolbarBlur}
|
|
259
312
|
{...mergeProps(
|
|
260
313
|
themeProps('toolbar', {size}),
|
|
261
314
|
stylex.props(
|
|
@@ -320,6 +373,7 @@ export function Toolbar({
|
|
|
320
373
|
)}
|
|
321
374
|
</>
|
|
322
375
|
)}
|
|
376
|
+
{hintElement}
|
|
323
377
|
</div>
|
|
324
378
|
</Section>
|
|
325
379
|
</SizeProvider>
|
|
@@ -153,4 +153,72 @@ describe('Tooltip', () => {
|
|
|
153
153
|
});
|
|
154
154
|
});
|
|
155
155
|
});
|
|
156
|
+
|
|
157
|
+
describe('WCAG 1.4.13 — content on hover or focus', () => {
|
|
158
|
+
it('dismisses on Escape while visible (dismissible)', async () => {
|
|
159
|
+
const onOpenChange = vi.fn();
|
|
160
|
+
render(
|
|
161
|
+
<Tooltip content="Dismiss me" onOpenChange={onOpenChange} delay={0}>
|
|
162
|
+
<button type="button">Trigger</button>
|
|
163
|
+
</Tooltip>,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const trigger = screen.getByRole('button', {name: 'Trigger'});
|
|
167
|
+
fireEvent.mouseEnter(trigger);
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
fireEvent.keyDown(document, {key: 'Escape'});
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('ignores Escape during IME composition', async () => {
|
|
179
|
+
const onOpenChange = vi.fn();
|
|
180
|
+
render(
|
|
181
|
+
<Tooltip content="Stay" onOpenChange={onOpenChange} delay={0}>
|
|
182
|
+
<button type="button">Trigger</button>
|
|
183
|
+
</Tooltip>,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const trigger = screen.getByRole('button', {name: 'Trigger'});
|
|
187
|
+
fireEvent.mouseEnter(trigger);
|
|
188
|
+
await waitFor(() => {
|
|
189
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
190
|
+
});
|
|
191
|
+
onOpenChange.mockClear();
|
|
192
|
+
|
|
193
|
+
fireEvent.keyDown(document, {key: 'Escape', isComposing: true});
|
|
194
|
+
// Give any (incorrect) async hide a chance to run.
|
|
195
|
+
await new Promise(r => setTimeout(r, 20));
|
|
196
|
+
expect(onOpenChange).not.toHaveBeenCalledWith(false);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('stays open when the pointer moves onto the tooltip surface (hoverable)', async () => {
|
|
200
|
+
const onOpenChange = vi.fn();
|
|
201
|
+
render(
|
|
202
|
+
<Tooltip content="Hover me" onOpenChange={onOpenChange} delay={0}>
|
|
203
|
+
<button type="button">Trigger</button>
|
|
204
|
+
</Tooltip>,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const trigger = screen.getByRole('button', {name: 'Trigger'});
|
|
208
|
+
fireEvent.mouseEnter(trigger);
|
|
209
|
+
await waitFor(() => {
|
|
210
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
211
|
+
});
|
|
212
|
+
onOpenChange.mockClear();
|
|
213
|
+
|
|
214
|
+
// Pointer leaves the trigger but enters the tooltip surface before the
|
|
215
|
+
// hover-bridge grace period elapses — the tooltip must not hide.
|
|
216
|
+
fireEvent.mouseLeave(trigger);
|
|
217
|
+
const layer = screen.getByRole('tooltip', {hidden: true});
|
|
218
|
+
fireEvent.mouseEnter(layer);
|
|
219
|
+
|
|
220
|
+
await new Promise(r => setTimeout(r, 150));
|
|
221
|
+
expect(onOpenChange).not.toHaveBeenCalledWith(false);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
156
224
|
});
|
|
@@ -37,6 +37,13 @@ import {
|
|
|
37
37
|
typeScaleVars,
|
|
38
38
|
} from '../theme/tokens.stylex';
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Grace period (ms) before hiding on pointer-leave when no explicit `hideDelay`
|
|
42
|
+
* is set, so the pointer can travel across the small gap from the trigger onto
|
|
43
|
+
* the tooltip surface without the tooltip disappearing (WCAG 1.4.13 hoverable).
|
|
44
|
+
*/
|
|
45
|
+
const HOVER_BRIDGE_DELAY = 100;
|
|
46
|
+
|
|
40
47
|
const styles = stylex.create({
|
|
41
48
|
// Base container styles - inverted colors for high contrast
|
|
42
49
|
container: {
|
|
@@ -291,21 +298,29 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
|
|
|
291
298
|
}, delay);
|
|
292
299
|
}, [isEnabled, isOpen, clearTimeouts, layer, delay]);
|
|
293
300
|
|
|
294
|
-
// Schedule hide with delay (suppressed when isOpen is true)
|
|
301
|
+
// Schedule hide with delay (suppressed when isOpen is true).
|
|
302
|
+
// A small hover bridge (when hideDelay is 0) lets the pointer travel from the
|
|
303
|
+
// trigger onto the tooltip surface without the tooltip vanishing — required
|
|
304
|
+
// for WCAG 1.4.13 (Content on Hover or Focus: hoverable).
|
|
295
305
|
const scheduleHide = useCallback(() => {
|
|
296
306
|
if (isOpen === true) {
|
|
297
307
|
return;
|
|
298
308
|
}
|
|
299
309
|
clearTimeouts();
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
layer.hide();
|
|
303
|
-
}, hideDelay);
|
|
304
|
-
} else {
|
|
310
|
+
const effectiveHideDelay = hideDelay > 0 ? hideDelay : HOVER_BRIDGE_DELAY;
|
|
311
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
305
312
|
layer.hide();
|
|
306
|
-
}
|
|
313
|
+
}, effectiveHideDelay);
|
|
307
314
|
}, [isOpen, clearTimeouts, layer, hideDelay]);
|
|
308
315
|
|
|
316
|
+
// Cancel a pending hide (e.g. the pointer entered the tooltip surface).
|
|
317
|
+
const cancelHide = useCallback(() => {
|
|
318
|
+
if (hideTimeoutRef.current) {
|
|
319
|
+
clearTimeout(hideTimeoutRef.current);
|
|
320
|
+
hideTimeoutRef.current = null;
|
|
321
|
+
}
|
|
322
|
+
}, []);
|
|
323
|
+
|
|
309
324
|
// Event handlers
|
|
310
325
|
const handleMouseEnter = useCallback(() => {
|
|
311
326
|
// Suppress tooltips on touch devices — hover is simulated and eats a tap
|
|
@@ -420,6 +435,32 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
|
|
|
420
435
|
}
|
|
421
436
|
}, [isOpen, clearTimeouts, layer]);
|
|
422
437
|
|
|
438
|
+
// Dismiss on Escape (WCAG 1.4.13 — dismissible). Uncontrolled tooltips only;
|
|
439
|
+
// a controlled tooltip's visibility is owned by the consumer. The listener is
|
|
440
|
+
// mounted for the lifetime of an uncontrolled tooltip rather than gated on
|
|
441
|
+
// `layer.isOpen` (React state, which can lag a frame behind the DOM) —
|
|
442
|
+
// `layer.hide()` self-guards and no-ops when the layer is already closed.
|
|
443
|
+
// Guarded against IME composition-cancel.
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
if (isOpen !== undefined) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
449
|
+
if (e.key !== 'Escape') {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (e.isComposing || e.keyCode === 229) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
clearTimeouts();
|
|
456
|
+
layer.hide();
|
|
457
|
+
};
|
|
458
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
459
|
+
return () => {
|
|
460
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
461
|
+
};
|
|
462
|
+
}, [isOpen, layer, clearTimeouts]);
|
|
463
|
+
|
|
423
464
|
// Render function that wraps layer.render with tooltip styling
|
|
424
465
|
const renderTooltip = useCallback(
|
|
425
466
|
(children: ReactNode, props?: ContextRenderProps): ReactNode => {
|
|
@@ -430,6 +471,12 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
|
|
|
430
471
|
role: 'tooltip',
|
|
431
472
|
xstyle: [popoverXstyle, layerAnimations[renderPlacement]],
|
|
432
473
|
className: themeProps('tooltip').className,
|
|
474
|
+
// Keep the tooltip open while the pointer is over the surface itself
|
|
475
|
+
// (WCAG 1.4.13 hoverable). These sit on the layer container — the
|
|
476
|
+
// element the user actually hovers — not the inner content div, since
|
|
477
|
+
// mouseenter/leave do not bubble.
|
|
478
|
+
onMouseEnter: cancelHide,
|
|
479
|
+
onMouseLeave: scheduleHide,
|
|
433
480
|
};
|
|
434
481
|
|
|
435
482
|
return layer.render(
|
|
@@ -437,7 +484,7 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
|
|
|
437
484
|
renderProps,
|
|
438
485
|
);
|
|
439
486
|
},
|
|
440
|
-
[layer, placement, alignment, popoverXstyle],
|
|
487
|
+
[layer, placement, alignment, popoverXstyle, cancelHide, scheduleHide],
|
|
441
488
|
);
|
|
442
489
|
|
|
443
490
|
return {
|