@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,7 +9,15 @@
|
|
|
9
9
|
* SYNC: When Typeahead components change, update tests to match
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
describe,
|
|
14
|
+
it,
|
|
15
|
+
expect,
|
|
16
|
+
vi,
|
|
17
|
+
beforeAll,
|
|
18
|
+
afterAll,
|
|
19
|
+
beforeEach,
|
|
20
|
+
} from 'vitest';
|
|
13
21
|
import {render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
14
22
|
import userEvent from '@testing-library/user-event';
|
|
15
23
|
import {Typeahead} from './Typeahead';
|
|
@@ -122,6 +130,47 @@ describe('BaseTypeahead', () => {
|
|
|
122
130
|
});
|
|
123
131
|
});
|
|
124
132
|
|
|
133
|
+
it('announces the result count to a live region (comboboxes-6)', async () => {
|
|
134
|
+
render(
|
|
135
|
+
<BaseTypeahead
|
|
136
|
+
searchSource={fruitSource}
|
|
137
|
+
value={null}
|
|
138
|
+
onChange={() => {}}
|
|
139
|
+
debounceMs={0}
|
|
140
|
+
/>,
|
|
141
|
+
);
|
|
142
|
+
const input = screen.getByRole('combobox');
|
|
143
|
+
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
144
|
+
|
|
145
|
+
await waitFor(() => {
|
|
146
|
+
const region = document.querySelector(
|
|
147
|
+
'[data-astryx-live-region="polite"]',
|
|
148
|
+
);
|
|
149
|
+
expect(region?.textContent).toMatch(/\d+ results?/);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('announces "no results found" when the search is empty (comboboxes-6)', async () => {
|
|
154
|
+
render(
|
|
155
|
+
<BaseTypeahead
|
|
156
|
+
searchSource={fruitSource}
|
|
157
|
+
value={null}
|
|
158
|
+
onChange={() => {}}
|
|
159
|
+
debounceMs={0}
|
|
160
|
+
emptySearchResultsText="No results found"
|
|
161
|
+
/>,
|
|
162
|
+
);
|
|
163
|
+
const input = screen.getByRole('combobox');
|
|
164
|
+
fireEvent.change(input, {target: {value: 'zzzzz'}});
|
|
165
|
+
|
|
166
|
+
await waitFor(() => {
|
|
167
|
+
const region = document.querySelector(
|
|
168
|
+
'[data-astryx-live-region="polite"]',
|
|
169
|
+
);
|
|
170
|
+
expect(region).toHaveTextContent('No results found');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
125
174
|
it('disables input when isDisabled', () => {
|
|
126
175
|
render(
|
|
127
176
|
<BaseTypeahead
|
|
@@ -180,6 +229,90 @@ describe('BaseTypeahead', () => {
|
|
|
180
229
|
});
|
|
181
230
|
});
|
|
182
231
|
|
|
232
|
+
describe('BaseTypeahead focus-out', () => {
|
|
233
|
+
it('closes the dropdown when focus leaves the input', async () => {
|
|
234
|
+
render(
|
|
235
|
+
<>
|
|
236
|
+
<BaseTypeahead
|
|
237
|
+
searchSource={fruitSource}
|
|
238
|
+
value={null}
|
|
239
|
+
onChange={() => {}}
|
|
240
|
+
debounceMs={0}
|
|
241
|
+
/>
|
|
242
|
+
<button type="button">Outside</button>
|
|
243
|
+
</>,
|
|
244
|
+
);
|
|
245
|
+
const input = screen.getByRole('combobox');
|
|
246
|
+
fireEvent.change(input, {target: {value: 'App'}});
|
|
247
|
+
|
|
248
|
+
await waitFor(() => {
|
|
249
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Focus moves to an element outside the field/dropdown → menu closes.
|
|
253
|
+
const outside = screen.getByRole('button', {name: 'Outside'});
|
|
254
|
+
fireEvent.blur(input, {relatedTarget: outside});
|
|
255
|
+
|
|
256
|
+
await waitFor(() => {
|
|
257
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('keeps the dropdown open when focus moves into the anchor wrapper', async () => {
|
|
262
|
+
const anchor = document.createElement('div');
|
|
263
|
+
document.body.appendChild(anchor);
|
|
264
|
+
const anchorRef = {current: anchor};
|
|
265
|
+
render(
|
|
266
|
+
<BaseTypeahead
|
|
267
|
+
searchSource={fruitSource}
|
|
268
|
+
value={null}
|
|
269
|
+
onChange={() => {}}
|
|
270
|
+
anchorRef={anchorRef}
|
|
271
|
+
debounceMs={0}
|
|
272
|
+
/>,
|
|
273
|
+
);
|
|
274
|
+
const input = screen.getByRole('combobox');
|
|
275
|
+
// The input lives inside the wrapper we hand to anchorRef.
|
|
276
|
+
anchor.appendChild(input.closest('div') ?? input);
|
|
277
|
+
fireEvent.change(input, {target: {value: 'App'}});
|
|
278
|
+
|
|
279
|
+
await waitFor(() => {
|
|
280
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// A sibling control inside the field (e.g. a clear button) receives focus.
|
|
284
|
+
const sibling = document.createElement('button');
|
|
285
|
+
anchor.appendChild(sibling);
|
|
286
|
+
fireEvent.blur(input, {relatedTarget: sibling});
|
|
287
|
+
|
|
288
|
+
// Menu stays open because focus is still within the field.
|
|
289
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
290
|
+
document.body.removeChild(anchor);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('does not close when a dropdown option receives focus', async () => {
|
|
294
|
+
render(
|
|
295
|
+
<BaseTypeahead
|
|
296
|
+
searchSource={fruitSource}
|
|
297
|
+
value={null}
|
|
298
|
+
onChange={() => {}}
|
|
299
|
+
debounceMs={0}
|
|
300
|
+
/>,
|
|
301
|
+
);
|
|
302
|
+
const input = screen.getByRole('combobox');
|
|
303
|
+
fireEvent.change(input, {target: {value: 'App'}});
|
|
304
|
+
|
|
305
|
+
await waitFor(() => {
|
|
306
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const option = screen.getByRole('option', {hidden: true});
|
|
310
|
+
fireEvent.blur(input, {relatedTarget: option});
|
|
311
|
+
|
|
312
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
183
316
|
describe('Typeahead', () => {
|
|
184
317
|
it('renders with label', () => {
|
|
185
318
|
render(
|
|
@@ -573,4 +706,201 @@ describe('BaseTypeahead paste behavior', () => {
|
|
|
573
706
|
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
574
707
|
});
|
|
575
708
|
});
|
|
709
|
+
|
|
710
|
+
it('scrolls the highlighted option into view during arrow navigation', async () => {
|
|
711
|
+
const scrollIntoView = vi.fn();
|
|
712
|
+
Object.defineProperty(HTMLElement.prototype, 'scrollIntoView', {
|
|
713
|
+
configurable: true,
|
|
714
|
+
value: scrollIntoView,
|
|
715
|
+
});
|
|
716
|
+
try {
|
|
717
|
+
const user = userEvent.setup();
|
|
718
|
+
render(
|
|
719
|
+
<BaseTypeahead
|
|
720
|
+
searchSource={fruitSource}
|
|
721
|
+
value={null}
|
|
722
|
+
onChange={() => {}}
|
|
723
|
+
debounceMs={0}
|
|
724
|
+
/>,
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
const input = screen.getByRole('combobox');
|
|
728
|
+
await user.click(input);
|
|
729
|
+
await user.paste('e'); // matches multiple fruits, opens listbox
|
|
730
|
+
await waitFor(() => {
|
|
731
|
+
expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
scrollIntoView.mockClear();
|
|
735
|
+
await user.keyboard('{ArrowDown}');
|
|
736
|
+
await user.keyboard('{ArrowDown}');
|
|
737
|
+
|
|
738
|
+
expect(scrollIntoView).toHaveBeenCalledWith({block: 'nearest'});
|
|
739
|
+
} finally {
|
|
740
|
+
delete (HTMLElement.prototype as unknown as {scrollIntoView?: unknown})
|
|
741
|
+
.scrollIntoView;
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
describe('Typeahead disabledMessage', () => {
|
|
747
|
+
// jsdom does not implement the Popover API used by the tooltip, so mock
|
|
748
|
+
// showPopover/hidePopover to toggle a `popover-open` attribute the tests
|
|
749
|
+
// can assert on.
|
|
750
|
+
beforeEach(() => {
|
|
751
|
+
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
752
|
+
this.setAttribute('popover-open', '');
|
|
753
|
+
});
|
|
754
|
+
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
755
|
+
this.removeAttribute('popover-open');
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// jsdom popover content is in the DOM but not "visible" in the
|
|
760
|
+
// accessibility tree; use hidden: true to find it.
|
|
761
|
+
const h = {hidden: true} as const;
|
|
762
|
+
|
|
763
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
764
|
+
render(
|
|
765
|
+
<Typeahead
|
|
766
|
+
label="Assignee"
|
|
767
|
+
searchSource={fruitSource}
|
|
768
|
+
value={null}
|
|
769
|
+
onChange={() => {}}
|
|
770
|
+
isDisabled
|
|
771
|
+
disabledMessage="You need the Editor role"
|
|
772
|
+
/>,
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
const container = screen.getByRole('combobox').parentElement as HTMLElement;
|
|
776
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
777
|
+
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
778
|
+
|
|
779
|
+
fireEvent.mouseEnter(container);
|
|
780
|
+
await waitFor(() => {
|
|
781
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
fireEvent.mouseLeave(container);
|
|
785
|
+
await waitFor(() => {
|
|
786
|
+
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
791
|
+
const user = userEvent.setup();
|
|
792
|
+
render(
|
|
793
|
+
<Typeahead
|
|
794
|
+
label="Assignee"
|
|
795
|
+
searchSource={fruitSource}
|
|
796
|
+
value={null}
|
|
797
|
+
onChange={() => {}}
|
|
798
|
+
isDisabled
|
|
799
|
+
disabledMessage="You need the Editor role"
|
|
800
|
+
/>,
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
804
|
+
await user.tab();
|
|
805
|
+
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
806
|
+
await waitFor(() => {
|
|
807
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it('does not render a tooltip when not disabled', () => {
|
|
812
|
+
render(
|
|
813
|
+
<Typeahead
|
|
814
|
+
label="Assignee"
|
|
815
|
+
searchSource={fruitSource}
|
|
816
|
+
value={null}
|
|
817
|
+
onChange={() => {}}
|
|
818
|
+
disabledMessage="You need the Editor role"
|
|
819
|
+
/>,
|
|
820
|
+
);
|
|
821
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
825
|
+
render(
|
|
826
|
+
<Typeahead
|
|
827
|
+
label="Assignee"
|
|
828
|
+
searchSource={fruitSource}
|
|
829
|
+
value={null}
|
|
830
|
+
onChange={() => {}}
|
|
831
|
+
isDisabled
|
|
832
|
+
/>,
|
|
833
|
+
);
|
|
834
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
838
|
+
render(
|
|
839
|
+
<Typeahead
|
|
840
|
+
label="Assignee"
|
|
841
|
+
searchSource={fruitSource}
|
|
842
|
+
value={null}
|
|
843
|
+
onChange={() => {}}
|
|
844
|
+
isDisabled
|
|
845
|
+
disabledMessage="You need the Editor role"
|
|
846
|
+
/>,
|
|
847
|
+
);
|
|
848
|
+
const input = screen.getByRole('combobox');
|
|
849
|
+
expect(input).not.toBeDisabled();
|
|
850
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
851
|
+
expect(input).toHaveAttribute('readonly');
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
it('links the reason tooltip from the input via aria-describedby', () => {
|
|
855
|
+
render(
|
|
856
|
+
<Typeahead
|
|
857
|
+
label="Assignee"
|
|
858
|
+
searchSource={fruitSource}
|
|
859
|
+
value={null}
|
|
860
|
+
onChange={() => {}}
|
|
861
|
+
isDisabled
|
|
862
|
+
disabledMessage="You need the Editor role"
|
|
863
|
+
/>,
|
|
864
|
+
);
|
|
865
|
+
const input = screen.getByRole('combobox');
|
|
866
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
867
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it('blocks typing and selection while focusable-disabled', async () => {
|
|
871
|
+
const user = userEvent.setup();
|
|
872
|
+
const onChange = vi.fn();
|
|
873
|
+
render(
|
|
874
|
+
<Typeahead
|
|
875
|
+
label="Assignee"
|
|
876
|
+
searchSource={fruitSource}
|
|
877
|
+
value={null}
|
|
878
|
+
onChange={onChange}
|
|
879
|
+
isDisabled
|
|
880
|
+
disabledMessage="You need the Editor role"
|
|
881
|
+
/>,
|
|
882
|
+
);
|
|
883
|
+
|
|
884
|
+
const input = screen.getByRole('combobox');
|
|
885
|
+
await user.click(input);
|
|
886
|
+
await user.type(input, 'App');
|
|
887
|
+
expect(input).toHaveValue('');
|
|
888
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
889
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it('remains natively disabled when disabled without a reason', () => {
|
|
893
|
+
render(
|
|
894
|
+
<Typeahead
|
|
895
|
+
label="Assignee"
|
|
896
|
+
searchSource={fruitSource}
|
|
897
|
+
value={null}
|
|
898
|
+
onChange={() => {}}
|
|
899
|
+
isDisabled
|
|
900
|
+
/>,
|
|
901
|
+
);
|
|
902
|
+
const input = screen.getByRole('combobox');
|
|
903
|
+
expect(input).toBeDisabled();
|
|
904
|
+
expect(input).not.toHaveAttribute('aria-disabled');
|
|
905
|
+
});
|
|
576
906
|
});
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Typeahead.tsx
|
|
7
|
-
* @input Uses React, BaseTypeahead, Field, Token
|
|
7
|
+
* @input Uses React, BaseTypeahead, Field, Token, InputGroupContext
|
|
8
8
|
* @output Exports Typeahead styled typeahead component
|
|
9
|
-
* @position Styled wrapper; composes BaseTypeahead with Field
|
|
9
|
+
* @position Styled wrapper; composes BaseTypeahead with Field or InputGroup
|
|
10
10
|
*
|
|
11
11
|
* Owns the input wrapper (border, padding, status styles), selected value
|
|
12
12
|
* token with spacing compensation, and edit mode behavior. Delegates
|
|
@@ -38,9 +38,13 @@ import {
|
|
|
38
38
|
inputStatusFocusWithinStyles,
|
|
39
39
|
} from '../Field';
|
|
40
40
|
import {Token} from '../Token';
|
|
41
|
+
import {useTooltip} from '../Tooltip';
|
|
41
42
|
import {renderIconSlot, type IconType} from '../Icon';
|
|
43
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
42
44
|
import {spacingVars, sizeVars} from '../theme/tokens.stylex';
|
|
43
|
-
import {
|
|
45
|
+
import {groupStyles} from '../InputGroup/groupStyles';
|
|
46
|
+
import {useInputGroup} from '../InputGroup/InputGroupContext';
|
|
47
|
+
import {getInputARIA, mergeProps, mergeRefs} from '../utils';
|
|
44
48
|
import type {BaseProps} from '../BaseProps';
|
|
45
49
|
import type {SizeValue} from '../utils/types';
|
|
46
50
|
import type {SearchableItem, SearchSource} from './types';
|
|
@@ -101,6 +105,29 @@ export interface TypeaheadProps<T extends SearchableItem> extends Omit<
|
|
|
101
105
|
emptySearchResultsText?: string;
|
|
102
106
|
/** Whether the input is disabled. @default false */
|
|
103
107
|
isDisabled?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Explains why the input is disabled. When set together with `isDisabled`,
|
|
110
|
+
* the input shows a tooltip with this text on hover and keyboard focus, and
|
|
111
|
+
* the field stays focusable (via `aria-disabled`) so the reason is
|
|
112
|
+
* discoverable by keyboard and assistive technology. Editing and selection
|
|
113
|
+
* stay blocked.
|
|
114
|
+
*
|
|
115
|
+
* Use this instead of wrapping a disabled input in `Tooltip` — disabled
|
|
116
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```
|
|
120
|
+
* <Typeahead
|
|
121
|
+
* label="Assignee"
|
|
122
|
+
* searchSource={userSource}
|
|
123
|
+
* value={assignee}
|
|
124
|
+
* onChange={setAssignee}
|
|
125
|
+
* isDisabled
|
|
126
|
+
* disabledMessage="You need the Editor role to change this"
|
|
127
|
+
* />
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
disabledMessage?: string;
|
|
104
131
|
/** Show clear button. @default true */
|
|
105
132
|
hasClear?: boolean;
|
|
106
133
|
/** Auto-focus on mount. @default false */
|
|
@@ -209,6 +236,7 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
209
236
|
maxMenuItems,
|
|
210
237
|
emptySearchResultsText,
|
|
211
238
|
isDisabled = false,
|
|
239
|
+
disabledMessage,
|
|
212
240
|
hasClear = true,
|
|
213
241
|
hasAutoFocus,
|
|
214
242
|
size: sizeProp,
|
|
@@ -223,13 +251,30 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
223
251
|
}: TypeaheadProps<T>) {
|
|
224
252
|
const size = useSize(sizeProp, 'md');
|
|
225
253
|
const inputId = useId();
|
|
254
|
+
const inputLabelId = useId();
|
|
226
255
|
const descriptionId = useId();
|
|
227
256
|
const statusMessageId = useId();
|
|
257
|
+
const inputGroup = useInputGroup();
|
|
228
258
|
|
|
229
259
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
230
260
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
231
261
|
const tokenRef = useRef<HTMLElement>(null);
|
|
232
262
|
|
|
263
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
264
|
+
// tooltip listeners attach to the input wrapper (which already exists) and
|
|
265
|
+
// the input stays perceivable via aria-disabled + readOnly instead of the
|
|
266
|
+
// disabled attribute. Editing and selection stay blocked by the isDisabled
|
|
267
|
+
// guards (handleWrapperClick, handleEnterEditMode, and BaseTypeahead's own
|
|
268
|
+
// focus/change guards).
|
|
269
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
270
|
+
const disabledMessageTooltip = useTooltip({
|
|
271
|
+
placement: 'above',
|
|
272
|
+
// The wrapper div is not naturally focusable; focusin bubbles up from the
|
|
273
|
+
// input, so always attach focus listeners.
|
|
274
|
+
focusTrigger: 'always',
|
|
275
|
+
isEnabled: showsDisabledMessage,
|
|
276
|
+
});
|
|
277
|
+
|
|
233
278
|
// Edit mode: when the user clicks the token to edit the selected value
|
|
234
279
|
const [isEditing, setIsEditing] = useState(false);
|
|
235
280
|
const [editingValue, setEditingValue] = useState<T | null>(null);
|
|
@@ -335,43 +380,26 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
335
380
|
}
|
|
336
381
|
}, [isDisabled, showToken, handleEnterEditMode]);
|
|
337
382
|
|
|
338
|
-
const ariaDescribedBy =
|
|
383
|
+
const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
|
|
384
|
+
inputLabelId,
|
|
339
385
|
[
|
|
340
386
|
description ? descriptionId : null,
|
|
341
387
|
status?.message ? statusMessageId : null,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
388
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
389
|
+
],
|
|
390
|
+
inputGroup,
|
|
391
|
+
);
|
|
345
392
|
|
|
346
393
|
const sizeStyle = wrapperSizeStyles[size];
|
|
347
394
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
ref={ref}
|
|
351
|
-
label={label}
|
|
352
|
-
isLabelHidden={isLabelHidden}
|
|
353
|
-
description={description}
|
|
354
|
-
inputID={inputId}
|
|
355
|
-
descriptionID={description ? descriptionId : undefined}
|
|
356
|
-
isOptional={isOptional}
|
|
357
|
-
isRequired={isRequired}
|
|
358
|
-
isDisabled={isDisabled}
|
|
359
|
-
status={
|
|
360
|
-
status
|
|
361
|
-
? {
|
|
362
|
-
type: status.type,
|
|
363
|
-
message: status.message,
|
|
364
|
-
messageID: status.message ? statusMessageId : undefined,
|
|
365
|
-
}
|
|
366
|
-
: undefined
|
|
367
|
-
}
|
|
368
|
-
labelTooltip={labelTooltip}
|
|
369
|
-
width={width}
|
|
370
|
-
xstyle={xstyle}
|
|
371
|
-
className={className}
|
|
372
|
-
style={style}>
|
|
395
|
+
const typeaheadContent = (
|
|
396
|
+
<>
|
|
373
397
|
<div
|
|
374
|
-
ref={
|
|
398
|
+
ref={mergeRefs(
|
|
399
|
+
wrapperRef,
|
|
400
|
+
disabledMessageTooltip.ref,
|
|
401
|
+
inputGroup ? ref : undefined,
|
|
402
|
+
)}
|
|
375
403
|
data-testid={testId}
|
|
376
404
|
onClick={handleWrapperClick}
|
|
377
405
|
onBlur={handleBlur}
|
|
@@ -385,10 +413,17 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
385
413
|
status && inputStatusHoverShadowStyles[status.type],
|
|
386
414
|
status && inputStatusFocusWithinStyles[status.type],
|
|
387
415
|
isDisabled && inputWrapperStyles.disabled,
|
|
416
|
+
inputGroup && groupStyles.inGroup,
|
|
417
|
+
inputGroup && xstyle,
|
|
388
418
|
),
|
|
419
|
+
inputGroup ? className : undefined,
|
|
420
|
+
inputGroup ? style : undefined,
|
|
389
421
|
)}>
|
|
390
422
|
{startIcon &&
|
|
391
423
|
renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
|
|
424
|
+
{inputGroup && (
|
|
425
|
+
<VisuallyHidden id={inputLabelId}>{label}</VisuallyHidden>
|
|
426
|
+
)}
|
|
392
427
|
{showToken && (
|
|
393
428
|
<Token
|
|
394
429
|
ref={tokenRef}
|
|
@@ -411,8 +446,10 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
411
446
|
emptySearchResultsText={emptySearchResultsText}
|
|
412
447
|
isDisabled={isDisabled}
|
|
413
448
|
hasAutoFocus={hasAutoFocus}
|
|
449
|
+
isFocusableDisabled={showsDisabledMessage}
|
|
414
450
|
inputId={inputId}
|
|
415
451
|
ariaDescribedBy={ariaDescribedBy}
|
|
452
|
+
ariaLabelledBy={ariaLabelledBy}
|
|
416
453
|
onChangeQuery={onChangeQuery}
|
|
417
454
|
onOpenChange={onOpenChange}
|
|
418
455
|
debounceMs={debounceMs}
|
|
@@ -432,6 +469,41 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
432
469
|
/>
|
|
433
470
|
)}
|
|
434
471
|
</div>
|
|
472
|
+
{showsDisabledMessage &&
|
|
473
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
474
|
+
</>
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
if (inputGroup) {
|
|
478
|
+
return typeaheadContent;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return (
|
|
482
|
+
<Field
|
|
483
|
+
ref={ref}
|
|
484
|
+
label={label}
|
|
485
|
+
isLabelHidden={isLabelHidden}
|
|
486
|
+
description={description}
|
|
487
|
+
inputID={inputId}
|
|
488
|
+
descriptionID={description ? descriptionId : undefined}
|
|
489
|
+
isOptional={isOptional}
|
|
490
|
+
isRequired={isRequired}
|
|
491
|
+
isDisabled={isDisabled}
|
|
492
|
+
status={
|
|
493
|
+
status
|
|
494
|
+
? {
|
|
495
|
+
type: status.type,
|
|
496
|
+
message: status.message,
|
|
497
|
+
messageID: status.message ? statusMessageId : undefined,
|
|
498
|
+
}
|
|
499
|
+
: undefined
|
|
500
|
+
}
|
|
501
|
+
labelTooltip={labelTooltip}
|
|
502
|
+
width={width}
|
|
503
|
+
xstyle={xstyle}
|
|
504
|
+
className={className}
|
|
505
|
+
style={style}>
|
|
506
|
+
{typeaheadContent}
|
|
435
507
|
</Field>
|
|
436
508
|
);
|
|
437
509
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/** @type {import('../docs-types').ComponentDoc} */
|
|
6
|
+
|
|
7
|
+
export const docs = {
|
|
8
|
+
name: 'VisuallyHidden',
|
|
9
|
+
displayName: 'VisuallyHidden',
|
|
10
|
+
category: 'Utility',
|
|
11
|
+
keywords: ["visually hidden","sr-only","screen reader","accessibility","a11y","aria-live","hidden label","assistive technology"],
|
|
12
|
+
props: [
|
|
13
|
+
{
|
|
14
|
+
name: 'children',
|
|
15
|
+
type: 'ReactNode',
|
|
16
|
+
description: 'Content exposed to assistive technology while hidden from sight.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'as',
|
|
20
|
+
type: 'ElementType',
|
|
21
|
+
description: 'HTML tag to render as. Use a block element for live regions.',
|
|
22
|
+
default: "'span'",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
usage: {
|
|
26
|
+
description: 'Renders content in the accessibility tree while hiding it visually. Use for accessible names on icon-only controls, aria-live announcement regions, and supplementary screen-reader context. Deliberately has no styling props; the whole point is to stay invisible.',
|
|
27
|
+
bestPractices: [
|
|
28
|
+
{ guidance: true, description: 'Use to give icon-only buttons and controls an accessible name that screen readers announce.' },
|
|
29
|
+
{ guidance: true, description: 'Render as a block element (as="div") with aria-live to announce dynamic updates like drag-and-drop or result counts.' },
|
|
30
|
+
{ guidance: false, description: 'Use it to hide content from everyone; it stays in the accessibility tree; use conditional rendering or `hidden` to remove content entirely.' },
|
|
31
|
+
{ guidance: false, description: 'Put interactive controls inside it; the content is not visible and cannot receive pointer input.' },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** @type {import('../docs-types').ComponentDoc} */
|
|
37
|
+
export const docsZh = {
|
|
38
|
+
name: 'VisuallyHidden',
|
|
39
|
+
displayName: 'VisuallyHidden',
|
|
40
|
+
props: [
|
|
41
|
+
{
|
|
42
|
+
name: 'children',
|
|
43
|
+
type: 'ReactNode',
|
|
44
|
+
description: '对辅助技术公开但对视觉隐藏的内容。',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'as',
|
|
48
|
+
type: 'ElementType',
|
|
49
|
+
description: '要渲染的 HTML 标签。实时区域请使用块级元素。',
|
|
50
|
+
default: "'span'",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
usage: {
|
|
54
|
+
description: 'Renders content in the accessibility tree while hiding it visually. Use for accessible names on icon-only controls, aria-live announcement regions, and supplementary screen-reader context.',
|
|
55
|
+
bestPractices: [
|
|
56
|
+
{ guidance: true, description: 'Use to give icon-only buttons and controls an accessible name that screen readers announce.' },
|
|
57
|
+
{ guidance: true, description: 'Render as a block element (as="div") with aria-live to announce dynamic updates like drag-and-drop or result counts.' },
|
|
58
|
+
{ guidance: false, description: 'Use it to hide content from everyone; it stays in the accessibility tree; use conditional rendering or `hidden` to remove content entirely.' },
|
|
59
|
+
{ guidance: false, description: 'Put interactive controls inside it; the content is not visible and cannot receive pointer input.' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** @type {import('../docs-types').TranslationDoc} */
|
|
65
|
+
export const docsDense = {
|
|
66
|
+
description: 'renders content in the a11y tree while hiding it visually (sr-only)',
|
|
67
|
+
usage: {
|
|
68
|
+
description: 'Renders content in the accessibility tree while hiding it visually. Use for accessible names on icon-only controls, aria-live announcement regions, and supplementary screen-reader context.',
|
|
69
|
+
bestPractices: [
|
|
70
|
+
{ guidance: true, description: 'Use to give icon-only buttons and controls an accessible name that screen readers announce.' },
|
|
71
|
+
{ guidance: true, description: 'Render as a block element (as="div") with aria-live to announce dynamic updates like drag-and-drop or result counts.' },
|
|
72
|
+
{ guidance: false, description: 'Use it to hide content from everyone; it stays in the accessibility tree; use conditional rendering or `hidden` to remove content entirely.' },
|
|
73
|
+
{ guidance: false, description: 'Put interactive controls inside it; the content is not visible and cannot receive pointer input.' },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
propDescriptions: {
|
|
77
|
+
children: 'content exposed to AT while visually hidden',
|
|
78
|
+
as: 'HTML tag to render as; block element for live regions',
|
|
79
|
+
},
|
|
80
|
+
};
|