@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
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Selector.tsx
|
|
7
|
-
* @input Uses React, StyleX, usePopover, Icon
|
|
7
|
+
* @input Uses React, StyleX, usePopover, useTooltip, Icon, InputGroupContext
|
|
8
8
|
* @output Exports Selector component
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update:
|
|
12
12
|
* - /packages/core/src/Selector/Selector.doc.mjs
|
|
13
|
+
* - /packages/core/src/Selector/Selector.test.tsx
|
|
13
14
|
* - /packages/core/src/Selector/index.ts
|
|
15
|
+
* - /apps/storybook/stories/InputGroup.stories.tsx
|
|
14
16
|
* - /packages/cli/templates/blocks/components/Selector/ (showcase blocks)
|
|
15
17
|
*/
|
|
16
18
|
|
|
@@ -27,6 +29,7 @@ import React, {
|
|
|
27
29
|
} from 'react';
|
|
28
30
|
import * as stylex from '@stylexjs/stylex';
|
|
29
31
|
import {usePopover} from '../Popover/usePopover';
|
|
32
|
+
import {useTooltip} from '../Tooltip';
|
|
30
33
|
import {Icon, renderIconSlot, type IconType} from '../Icon';
|
|
31
34
|
import type {IconName} from '../Icon';
|
|
32
35
|
import {
|
|
@@ -61,11 +64,14 @@ import {
|
|
|
61
64
|
} from './utils';
|
|
62
65
|
import {useCombobox, useSelectedItemOffset} from './hooks';
|
|
63
66
|
import {SelectorOption} from './SelectorOption';
|
|
64
|
-
import {mergeProps} from '../utils';
|
|
67
|
+
import {getInputARIA, mergeProps} from '../utils';
|
|
65
68
|
import {useSize} from '../SizeContext/SizeContext';
|
|
66
69
|
import type {BaseProps} from '../BaseProps';
|
|
67
70
|
import type {SizeValue} from '../utils/types';
|
|
68
71
|
import {themeProps} from '../utils/themeProps';
|
|
72
|
+
import {groupStyles} from '../InputGroup/groupStyles';
|
|
73
|
+
import {useInputGroup} from '../InputGroup/InputGroupContext';
|
|
74
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
69
75
|
|
|
70
76
|
const styles = stylex.create({
|
|
71
77
|
// Trigger container — the enhanced click target wrapping the combobox button and clear button as siblings
|
|
@@ -369,6 +375,28 @@ interface SelectorPropsBase<
|
|
|
369
375
|
*/
|
|
370
376
|
isDisabled?: boolean;
|
|
371
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Explains why the selector is disabled. When set together with
|
|
380
|
+
* `isDisabled`, the selector shows a tooltip with this text on hover and
|
|
381
|
+
* keyboard focus, and the trigger stays focusable (via `aria-disabled`)
|
|
382
|
+
* so the reason is discoverable by keyboard and assistive technology.
|
|
383
|
+
* Activation stays blocked.
|
|
384
|
+
*
|
|
385
|
+
* Use this instead of wrapping a disabled selector in `Tooltip` — disabled
|
|
386
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```
|
|
390
|
+
* <Selector
|
|
391
|
+
* label="Owner"
|
|
392
|
+
* options={owners}
|
|
393
|
+
* isDisabled
|
|
394
|
+
* disabledMessage="You need the Editor role to change this"
|
|
395
|
+
* />
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
disabledMessage?: string;
|
|
399
|
+
|
|
372
400
|
/**
|
|
373
401
|
* The options to display in the selector.
|
|
374
402
|
* Can be strings, objects, dividers, or sections.
|
|
@@ -474,34 +502,30 @@ type SelectorPropsNonClearable<
|
|
|
474
502
|
changeAction?: (value: string) => void | Promise<void>;
|
|
475
503
|
};
|
|
476
504
|
|
|
477
|
-
type SelectorPropsClearable<
|
|
478
|
-
T
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
T
|
|
494
|
-
> = SelectorPropsNonClearable<T> | SelectorPropsClearable<T>;
|
|
505
|
+
type SelectorPropsClearable<T extends SelectorOptionType = SelectorOptionType> =
|
|
506
|
+
SelectorPropsBase<T> & {
|
|
507
|
+
/**
|
|
508
|
+
* Whether to show a clear button when a value is selected.
|
|
509
|
+
* When clicked, resets the value to `null` and returns focus to the trigger.
|
|
510
|
+
*
|
|
511
|
+
* When enabled, `value` and `onChange` widen to include `null`.
|
|
512
|
+
*/
|
|
513
|
+
hasClear: true;
|
|
514
|
+
value: string | null;
|
|
515
|
+
onChange?: (value: string | null) => void;
|
|
516
|
+
changeAction?: (value: string | null) => void | Promise<void>;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
export type SelectorProps<T extends SelectorOptionType = SelectorOptionType> =
|
|
520
|
+
| SelectorPropsNonClearable<T>
|
|
521
|
+
| SelectorPropsClearable<T>;
|
|
495
522
|
|
|
496
523
|
/**
|
|
497
524
|
* Default option renderer
|
|
498
525
|
*/
|
|
499
526
|
function DefaultOption({option}: {option: SelectorOptionData}) {
|
|
500
527
|
return (
|
|
501
|
-
<SelectorOption
|
|
502
|
-
icon={option.icon}
|
|
503
|
-
label={option.label ?? option.value}
|
|
504
|
-
/>
|
|
528
|
+
<SelectorOption icon={option.icon} label={option.label ?? option.value} />
|
|
505
529
|
);
|
|
506
530
|
}
|
|
507
531
|
|
|
@@ -529,6 +553,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
529
553
|
isOptional = false,
|
|
530
554
|
isRequired = false,
|
|
531
555
|
isDisabled = false,
|
|
556
|
+
disabledMessage,
|
|
532
557
|
options,
|
|
533
558
|
value,
|
|
534
559
|
onChange,
|
|
@@ -561,9 +586,11 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
561
586
|
const listboxId = useId();
|
|
562
587
|
const descriptionId = useId();
|
|
563
588
|
const statusMessageId = useId();
|
|
589
|
+
const inputLabelId = useId();
|
|
564
590
|
const searchId = useId();
|
|
565
591
|
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
566
592
|
const searchRef = useRef<HTMLInputElement>(null);
|
|
593
|
+
const inputGroup = useInputGroup();
|
|
567
594
|
|
|
568
595
|
const [searchQuery, setSearchQuery] = useState('');
|
|
569
596
|
|
|
@@ -571,14 +598,29 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
571
598
|
const [optimisticValue, setOptimisticValue] = useOptimistic(normalizedValue);
|
|
572
599
|
const isBusy = isLoading || optimisticValue !== normalizedValue;
|
|
573
600
|
|
|
574
|
-
//
|
|
575
|
-
|
|
601
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
602
|
+
// tooltip listeners attach to the trigger container (which already exists)
|
|
603
|
+
// and the trigger button stays perceivable via aria-disabled instead of the
|
|
604
|
+
// disabled attribute. Activation is blocked by the isDisabled guards in
|
|
605
|
+
// useCombobox (onTriggerClick / onKeyDown).
|
|
606
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
607
|
+
const disabledMessageTooltip = useTooltip({
|
|
608
|
+
placement: 'above',
|
|
609
|
+
// The container div is not naturally focusable; focusin bubbles up from
|
|
610
|
+
// the trigger button, so always attach focus listeners.
|
|
611
|
+
focusTrigger: 'always',
|
|
612
|
+
isEnabled: showsDisabledMessage,
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
|
|
616
|
+
inputLabelId,
|
|
576
617
|
[
|
|
577
618
|
description ? descriptionId : null,
|
|
578
619
|
status?.message ? statusMessageId : null,
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
620
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
621
|
+
],
|
|
622
|
+
inputGroup,
|
|
623
|
+
);
|
|
582
624
|
|
|
583
625
|
// Flatten options for keyboard navigation
|
|
584
626
|
const selectableItems = useMemo(
|
|
@@ -622,6 +664,9 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
622
664
|
hasLightDismiss: true,
|
|
623
665
|
hasCloseButton: false,
|
|
624
666
|
hasAutoFocus: false,
|
|
667
|
+
// The popup's own role="listbox" is the exposed semantics; the trigger
|
|
668
|
+
// keeps DOM focus, so wrapping it in a modal dialog would misrepresent it.
|
|
669
|
+
role: 'none',
|
|
625
670
|
});
|
|
626
671
|
|
|
627
672
|
// Open dropdown on mount when isDefaultOpen is true
|
|
@@ -653,6 +698,18 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
653
698
|
? {marginBlockStart: `-${selectedItemOffset}px`}
|
|
654
699
|
: undefined;
|
|
655
700
|
|
|
701
|
+
// Clear the current value. Shared by the clear button and the keyboard
|
|
702
|
+
// Delete/Backspace path so clearing is reachable without a mouse.
|
|
703
|
+
const clearValue = useCallback(() => {
|
|
704
|
+
onChange?.(null);
|
|
705
|
+
if (changeAction) {
|
|
706
|
+
startTransition(async () => {
|
|
707
|
+
setOptimisticValue(undefined);
|
|
708
|
+
await changeAction(null);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}, [onChange, changeAction, startTransition, setOptimisticValue]);
|
|
712
|
+
|
|
656
713
|
// Selector behavior (keyboard nav, typeahead, selection)
|
|
657
714
|
const {
|
|
658
715
|
highlightedIndex,
|
|
@@ -689,22 +746,30 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
689
746
|
},
|
|
690
747
|
[onChange, changeAction, startTransition, setOptimisticValue],
|
|
691
748
|
),
|
|
749
|
+
onClear: hasClear ? clearValue : undefined,
|
|
692
750
|
listboxId,
|
|
693
751
|
});
|
|
694
752
|
|
|
753
|
+
// Keep the highlighted option visible during keyboard navigation. The
|
|
754
|
+
// listbox is a fixed-height scroll container, so without this the virtual
|
|
755
|
+
// cursor walks off-screen once navigation passes the visible window. Mirrors
|
|
756
|
+
// CommandPaletteItem's scrollIntoView({block: 'nearest'}) behavior.
|
|
757
|
+
useEffect(() => {
|
|
758
|
+
if (!popover.isOpen || highlightedIndex < 0) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
document
|
|
762
|
+
.getElementById(getItemId(highlightedIndex))
|
|
763
|
+
?.scrollIntoView?.({block: 'nearest'});
|
|
764
|
+
}, [popover.isOpen, highlightedIndex, getItemId]);
|
|
765
|
+
|
|
695
766
|
// Handle clear button click
|
|
696
767
|
const handleClear = useCallback(
|
|
697
768
|
(e: React.MouseEvent) => {
|
|
698
769
|
e.stopPropagation(); // Don't open dropdown
|
|
699
|
-
|
|
700
|
-
if (changeAction) {
|
|
701
|
-
startTransition(async () => {
|
|
702
|
-
setOptimisticValue(undefined);
|
|
703
|
-
await changeAction(null);
|
|
704
|
-
});
|
|
705
|
-
}
|
|
770
|
+
clearValue();
|
|
706
771
|
},
|
|
707
|
-
[
|
|
772
|
+
[clearValue],
|
|
708
773
|
);
|
|
709
774
|
|
|
710
775
|
// Render search input
|
|
@@ -717,16 +782,30 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
717
782
|
<input
|
|
718
783
|
ref={searchRef}
|
|
719
784
|
id={searchId}
|
|
720
|
-
|
|
785
|
+
// When hasSearch is set, focus moves into this input on open, so it —
|
|
786
|
+
// not the trigger — must be the combobox that reports the highlighted
|
|
787
|
+
// option via aria-activedescendant (comboboxes-4). A bare searchbox
|
|
788
|
+
// left the highlight silent to screen readers.
|
|
789
|
+
role="combobox"
|
|
790
|
+
aria-expanded={popover.isOpen}
|
|
721
791
|
aria-controls={listboxId}
|
|
792
|
+
aria-autocomplete="list"
|
|
793
|
+
aria-activedescendant={
|
|
794
|
+
popover.isOpen && highlightedIndex >= 0
|
|
795
|
+
? getItemId(highlightedIndex)
|
|
796
|
+
: undefined
|
|
797
|
+
}
|
|
722
798
|
aria-label="Search options"
|
|
723
799
|
type="text"
|
|
724
800
|
value={searchQuery}
|
|
725
801
|
onChange={e => setSearchQuery(e.target.value)}
|
|
726
802
|
onKeyDown={e => {
|
|
803
|
+
// Arrow keys navigate options; Enter selects; Escape/Tab close.
|
|
804
|
+
// Home/End are left to the input for caret movement.
|
|
727
805
|
if (
|
|
728
806
|
e.key === 'ArrowDown' ||
|
|
729
807
|
e.key === 'ArrowUp' ||
|
|
808
|
+
e.key === 'Enter' ||
|
|
730
809
|
e.key === 'Escape' ||
|
|
731
810
|
e.key === 'Tab'
|
|
732
811
|
) {
|
|
@@ -745,6 +824,9 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
745
824
|
searchQuery,
|
|
746
825
|
searchPlaceholder,
|
|
747
826
|
onKeyDown,
|
|
827
|
+
popover.isOpen,
|
|
828
|
+
highlightedIndex,
|
|
829
|
+
getItemId,
|
|
748
830
|
]);
|
|
749
831
|
|
|
750
832
|
// Render an individual item
|
|
@@ -812,9 +894,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
812
894
|
const option = options[i];
|
|
813
895
|
|
|
814
896
|
if (isDivider(option)) {
|
|
815
|
-
elements.push(
|
|
816
|
-
<Divider key={`divider-${i}`} xstyle={styles.divider} />,
|
|
817
|
-
);
|
|
897
|
+
elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
|
|
818
898
|
} else if (isSection(option)) {
|
|
819
899
|
const sectionItems: ReactNode[] = [];
|
|
820
900
|
for (const opt of option.options) {
|
|
@@ -844,30 +924,15 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
844
924
|
return elements;
|
|
845
925
|
}, [options, renderItem, hasSearch, searchQuery, filteredItems]);
|
|
846
926
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
label={label}
|
|
850
|
-
isLabelHidden={isLabelHidden}
|
|
851
|
-
description={description}
|
|
852
|
-
inputID={triggerId}
|
|
853
|
-
descriptionID={description ? descriptionId : undefined}
|
|
854
|
-
isOptional={isOptional}
|
|
855
|
-
isRequired={isRequired}
|
|
856
|
-
isDisabled={isDisabled}
|
|
857
|
-
status={
|
|
858
|
-
status
|
|
859
|
-
? {
|
|
860
|
-
type: status.type,
|
|
861
|
-
message: status.message,
|
|
862
|
-
messageID: status.message ? statusMessageId : undefined,
|
|
863
|
-
}
|
|
864
|
-
: undefined
|
|
865
|
-
}
|
|
866
|
-
labelTooltip={labelTooltip}
|
|
867
|
-
width={width}>
|
|
927
|
+
const selectorContent = (
|
|
928
|
+
<>
|
|
868
929
|
<div
|
|
869
930
|
ref={el => {
|
|
870
931
|
popover.triggerRef(el);
|
|
932
|
+
// Anchor + hover/focus listeners for the disabled-message tooltip.
|
|
933
|
+
// Handlers are gated internally by isEnabled, and anchor names
|
|
934
|
+
// compose, so attaching unconditionally is safe.
|
|
935
|
+
disabledMessageTooltip.ref(el);
|
|
871
936
|
}}
|
|
872
937
|
onClick={onTriggerClick}
|
|
873
938
|
data-testid={testId}
|
|
@@ -881,6 +946,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
881
946
|
!selectedItem && styles.triggerPlaceholder,
|
|
882
947
|
status && inputStatusBorderStyles[status.type],
|
|
883
948
|
status && inputStatusHoverShadowStyles[status.type],
|
|
949
|
+
inputGroup && groupStyles.inGroup,
|
|
884
950
|
xstyle,
|
|
885
951
|
),
|
|
886
952
|
className,
|
|
@@ -888,27 +954,38 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
888
954
|
)}>
|
|
889
955
|
{startIcon &&
|
|
890
956
|
renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
|
|
957
|
+
{inputGroup && (
|
|
958
|
+
<VisuallyHidden id={inputLabelId}>{label}</VisuallyHidden>
|
|
959
|
+
)}
|
|
891
960
|
<button
|
|
892
961
|
ref={triggerRef}
|
|
893
962
|
id={triggerId}
|
|
894
963
|
type="button"
|
|
895
|
-
|
|
964
|
+
// In hasSearch mode the popup's search input is the combobox (it owns
|
|
965
|
+
// focus + aria-activedescendant, comboboxes-4), so the trigger is a
|
|
966
|
+
// plain button that opens the listbox — not a second combobox.
|
|
967
|
+
role={hasSearch ? undefined : 'combobox'}
|
|
896
968
|
{...rest}
|
|
897
969
|
aria-haspopup="listbox"
|
|
898
970
|
aria-expanded={popover.isOpen}
|
|
899
971
|
aria-controls={listboxId}
|
|
900
972
|
aria-activedescendant={
|
|
901
|
-
popover.isOpen && highlightedIndex >= 0
|
|
973
|
+
!hasSearch && popover.isOpen && highlightedIndex >= 0
|
|
902
974
|
? getItemId(highlightedIndex)
|
|
903
975
|
: undefined
|
|
904
976
|
}
|
|
905
977
|
aria-describedby={ariaDescribedBy}
|
|
978
|
+
aria-labelledby={ariaLabelledBy}
|
|
906
979
|
aria-required={isRequired ? 'true' : undefined}
|
|
907
980
|
aria-invalid={status?.type === 'error' ? 'true' : undefined}
|
|
908
981
|
aria-busy={isBusy || undefined}
|
|
909
|
-
|
|
982
|
+
// With a disabledMessage the trigger keeps focusability via
|
|
983
|
+
// aria-disabled so the reason is focus-discoverable; activation is
|
|
984
|
+
// still blocked by the isDisabled guards in useCombobox.
|
|
985
|
+
disabled={isDisabled && !showsDisabledMessage}
|
|
986
|
+
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
910
987
|
onKeyDown={onKeyDown}
|
|
911
|
-
tabIndex={-1}
|
|
988
|
+
tabIndex={isDisabled && !showsDisabledMessage ? -1 : 0}
|
|
912
989
|
{...stylex.props(styles.trigger)}>
|
|
913
990
|
<span {...stylex.props(styles.triggerLabel)}>
|
|
914
991
|
{selectedItem?.label ?? placeholder}
|
|
@@ -918,7 +995,6 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
918
995
|
{hasClear && value != null && !isDisabled && (
|
|
919
996
|
<button
|
|
920
997
|
type="button"
|
|
921
|
-
tabIndex={-1}
|
|
922
998
|
onClick={handleClear}
|
|
923
999
|
aria-label={`Clear ${label}`}
|
|
924
1000
|
{...stylex.props(styles.clearButton)}>
|
|
@@ -976,6 +1052,38 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
976
1052
|
style: popoverOffsetStyle,
|
|
977
1053
|
},
|
|
978
1054
|
)}
|
|
1055
|
+
|
|
1056
|
+
{showsDisabledMessage &&
|
|
1057
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
1058
|
+
</>
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
if (inputGroup) {
|
|
1062
|
+
return selectorContent;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return (
|
|
1066
|
+
<Field
|
|
1067
|
+
label={label}
|
|
1068
|
+
isLabelHidden={isLabelHidden}
|
|
1069
|
+
description={description}
|
|
1070
|
+
inputID={triggerId}
|
|
1071
|
+
descriptionID={description ? descriptionId : undefined}
|
|
1072
|
+
isOptional={isOptional}
|
|
1073
|
+
isRequired={isRequired}
|
|
1074
|
+
isDisabled={isDisabled}
|
|
1075
|
+
status={
|
|
1076
|
+
status
|
|
1077
|
+
? {
|
|
1078
|
+
type: status.type,
|
|
1079
|
+
message: status.message,
|
|
1080
|
+
messageID: status.message ? statusMessageId : undefined,
|
|
1081
|
+
}
|
|
1082
|
+
: undefined
|
|
1083
|
+
}
|
|
1084
|
+
labelTooltip={labelTooltip}
|
|
1085
|
+
width={width}>
|
|
1086
|
+
{selectorContent}
|
|
979
1087
|
</Field>
|
|
980
1088
|
);
|
|
981
1089
|
}
|
package/src/Selector/hooks.ts
CHANGED
|
@@ -138,6 +138,13 @@ interface UseComboboxOptions {
|
|
|
138
138
|
onOpen: () => void;
|
|
139
139
|
onClose: () => void;
|
|
140
140
|
onSelect?: (value: string) => void;
|
|
141
|
+
/**
|
|
142
|
+
* Clear the current value. When provided, pressing Delete or Backspace on the
|
|
143
|
+
* closed trigger clears the selection — a keyboard equivalent of the clear
|
|
144
|
+
* button, so clearing is not mouse-only. No-op when the popup is open (arrow
|
|
145
|
+
* navigation owns those keys there) or when there is no value.
|
|
146
|
+
*/
|
|
147
|
+
onClear?: () => void;
|
|
141
148
|
listboxId: string;
|
|
142
149
|
}
|
|
143
150
|
|
|
@@ -163,6 +170,7 @@ export function useCombobox({
|
|
|
163
170
|
onOpen,
|
|
164
171
|
onClose,
|
|
165
172
|
onSelect,
|
|
173
|
+
onClear,
|
|
166
174
|
listboxId,
|
|
167
175
|
}: UseComboboxOptions): UseComboboxResult {
|
|
168
176
|
const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
|
|
@@ -310,6 +318,19 @@ export function useCombobox({
|
|
|
310
318
|
}
|
|
311
319
|
break;
|
|
312
320
|
|
|
321
|
+
case 'Delete':
|
|
322
|
+
case 'Backspace':
|
|
323
|
+
// Keyboard equivalent of the clear button (comboboxes-2): clear the
|
|
324
|
+
// value from the closed trigger so clearing is not mouse-only. When
|
|
325
|
+
// hasSearch is active these keys must edit the search text instead,
|
|
326
|
+
// and when the popup is open arrow navigation owns interaction, so
|
|
327
|
+
// only handle the closed non-search case with a clearable value.
|
|
328
|
+
if (!hasSearch && !isOpen && onClear != null && value != null) {
|
|
329
|
+
e.preventDefault();
|
|
330
|
+
onClear();
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
|
|
313
334
|
default:
|
|
314
335
|
if (!hasSearch && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
|
|
315
336
|
const newTypeahead = typeahead + e.key.toLowerCase();
|
|
@@ -349,6 +370,8 @@ export function useCombobox({
|
|
|
349
370
|
getEnabledIndices,
|
|
350
371
|
typeahead,
|
|
351
372
|
hasSearch,
|
|
373
|
+
onClear,
|
|
374
|
+
value,
|
|
352
375
|
],
|
|
353
376
|
);
|
|
354
377
|
|
|
@@ -234,6 +234,38 @@ describe('SideNavHeading', () => {
|
|
|
234
234
|
expect(links[1]).toHaveAttribute('href', '/product');
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
it('gives every link an accessible name with superheadingHref, headingHref, and menu', () => {
|
|
238
|
+
render(
|
|
239
|
+
<SideNavHeading
|
|
240
|
+
icon={<span>Icon</span>}
|
|
241
|
+
superheading="Suite Name"
|
|
242
|
+
superheadingHref="/suite"
|
|
243
|
+
heading="Product Name"
|
|
244
|
+
headingHref="/product"
|
|
245
|
+
menu={<div>Analytics</div>}
|
|
246
|
+
/>,
|
|
247
|
+
);
|
|
248
|
+
// The icon link to /product previously rendered with no text and no
|
|
249
|
+
// aria-label, producing an empty accessible name (axe rule: link-name).
|
|
250
|
+
// Every link pointing at /product must now expose "Product Name".
|
|
251
|
+
const productLinks = screen
|
|
252
|
+
.getAllByRole('link', {name: 'Product Name'})
|
|
253
|
+
.filter(link => link.getAttribute('href') === '/product');
|
|
254
|
+
expect(productLinks.length).toBeGreaterThan(0);
|
|
255
|
+
for (const link of productLinks) {
|
|
256
|
+
expect(link).toHaveAccessibleName('Product Name');
|
|
257
|
+
}
|
|
258
|
+
// The independent superheading link is unaffected.
|
|
259
|
+
expect(screen.getByRole('link', {name: 'Suite Name'})).toHaveAttribute(
|
|
260
|
+
'href',
|
|
261
|
+
'/suite',
|
|
262
|
+
);
|
|
263
|
+
// No link should be missing an accessible name.
|
|
264
|
+
for (const link of screen.getAllByRole('link')) {
|
|
265
|
+
expect(link).toHaveAccessibleName();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
237
269
|
it('shows chevron when menu is provided', () => {
|
|
238
270
|
render(<SideNavHeading heading="My App" menu={<div>Menu content</div>} />);
|
|
239
271
|
// The chevron SVG should be rendered
|
|
@@ -577,6 +609,22 @@ describe('SideNavItem', () => {
|
|
|
577
609
|
expect(link).toHaveAttribute('aria-current', 'page');
|
|
578
610
|
});
|
|
579
611
|
|
|
612
|
+
it('places aria-current on the link, not the wrapper, for split-action items', () => {
|
|
613
|
+
// A collapsible item (has children) WITH a primary href renders the
|
|
614
|
+
// split-action path: the link and the expand toggle are siblings inside a
|
|
615
|
+
// wrapper div. aria-current="page" must sit on the focusable link so it is
|
|
616
|
+
// announced as the current page (navigation-8).
|
|
617
|
+
render(
|
|
618
|
+
<SideNavItem label="Reports" href="/reports" isSelected>
|
|
619
|
+
<SideNavItem label="Weekly" href="/reports/weekly" />
|
|
620
|
+
</SideNavItem>,
|
|
621
|
+
);
|
|
622
|
+
const link = screen.getByRole('link', {name: /Reports/});
|
|
623
|
+
expect(link).toHaveAttribute('aria-current', 'page');
|
|
624
|
+
// The wrapper div must NOT carry aria-current.
|
|
625
|
+
expect(link.closest('[aria-current="page"]')).toBe(link);
|
|
626
|
+
});
|
|
627
|
+
|
|
580
628
|
it('renders custom component when as and href are provided', () => {
|
|
581
629
|
render(<SideNavItem label="Dashboard" href="/dashboard" as={CustomLink} />);
|
|
582
630
|
const link = screen.getByRole('link');
|
|
@@ -652,7 +652,10 @@ export function SideNavHeading({
|
|
|
652
652
|
)}>
|
|
653
653
|
{icon &&
|
|
654
654
|
(headingHref ? (
|
|
655
|
-
<LinkComponent
|
|
655
|
+
<LinkComponent
|
|
656
|
+
href={headingHref}
|
|
657
|
+
aria-label={heading}
|
|
658
|
+
{...stylex.props(styles.icon)}>
|
|
656
659
|
{icon}
|
|
657
660
|
</LinkComponent>
|
|
658
661
|
) : (
|
|
@@ -709,7 +712,10 @@ export function SideNavHeading({
|
|
|
709
712
|
{...props}>
|
|
710
713
|
{icon &&
|
|
711
714
|
(headingHref ? (
|
|
712
|
-
<LinkComponent
|
|
715
|
+
<LinkComponent
|
|
716
|
+
href={headingHref}
|
|
717
|
+
aria-label={heading}
|
|
718
|
+
{...stylex.props(styles.icon)}>
|
|
713
719
|
{icon}
|
|
714
720
|
</LinkComponent>
|
|
715
721
|
) : (
|
|
@@ -612,16 +612,14 @@ export function SideNavItem({
|
|
|
612
612
|
|
|
613
613
|
if (hasIndependentToggle) {
|
|
614
614
|
itemElement = (
|
|
615
|
-
<div
|
|
616
|
-
aria-current={isSelected ? ('page' as const) : undefined}
|
|
617
|
-
data-testid={testId}
|
|
618
|
-
{...navItemStyleProps}>
|
|
615
|
+
<div data-testid={testId} {...navItemStyleProps}>
|
|
619
616
|
<NavItemElement
|
|
620
617
|
ref={ref}
|
|
621
618
|
href={href}
|
|
622
619
|
as={as}
|
|
623
620
|
isDisabled={isDisabled}
|
|
624
621
|
onClick={handleClick}
|
|
622
|
+
aria-current={isSelected ? ('page' as const) : undefined}
|
|
625
623
|
{...stylex.props(styles.splitAction)}>
|
|
626
624
|
{itemContent}
|
|
627
625
|
</NavItemElement>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
import {render, screen} from '@testing-library/react';
|
|
5
|
+
import {Skeleton} from './Skeleton';
|
|
6
|
+
|
|
7
|
+
describe('Skeleton', () => {
|
|
8
|
+
it('renders a placeholder element', () => {
|
|
9
|
+
render(<Skeleton width={200} height={20} data-testid="sk" />);
|
|
10
|
+
expect(screen.getByTestId('sk')).toBeInTheDocument();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('is hidden from assistive tech by default (complex-20)', () => {
|
|
14
|
+
render(<Skeleton width={200} height={20} data-testid="sk" />);
|
|
15
|
+
expect(screen.getByTestId('sk')).toHaveAttribute('aria-hidden', 'true');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('allows the aria-hidden default to be overridden', () => {
|
|
19
|
+
render(
|
|
20
|
+
<Skeleton width={200} height={20} data-testid="sk" aria-hidden={false} />,
|
|
21
|
+
);
|
|
22
|
+
// Consumer opt-out: not hidden when explicitly set false.
|
|
23
|
+
expect(screen.getByTestId('sk')).toHaveAttribute('aria-hidden', 'false');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -62,7 +62,12 @@ const styles = stylex.create({
|
|
|
62
62
|
animationDirection: 'alternate',
|
|
63
63
|
animationDuration: durationVars['--duration-medium-max'],
|
|
64
64
|
animationIterationCount: 'infinite',
|
|
65
|
-
|
|
65
|
+
// Disable the pulse under reduced-motion; the static placeholder still
|
|
66
|
+
// reads as loading (complex-20).
|
|
67
|
+
animationName: {
|
|
68
|
+
default: skeletonFade,
|
|
69
|
+
'@media (prefers-reduced-motion: reduce)': 'none',
|
|
70
|
+
},
|
|
66
71
|
animationTimingFunction: 'steps(10, end)',
|
|
67
72
|
},
|
|
68
73
|
});
|
|
@@ -180,6 +185,11 @@ export function Skeleton({
|
|
|
180
185
|
return (
|
|
181
186
|
<div
|
|
182
187
|
ref={ref}
|
|
188
|
+
// Purely decorative loading placeholder — hide from assistive tech so it
|
|
189
|
+
// isn't announced as empty content. The surrounding region should convey
|
|
190
|
+
// the loading/busy state (e.g. aria-busy) (complex-20). Consumers can
|
|
191
|
+
// override via props if a specific skeleton must be exposed.
|
|
192
|
+
aria-hidden="true"
|
|
183
193
|
data-testid={testId}
|
|
184
194
|
{...mergeProps(
|
|
185
195
|
themeProps('skeleton'),
|