@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +165 -0
- package/README.md +12 -12
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +11 -5
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +2 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +0 -1
- package/dist/Calendar/Calendar.d.ts +5 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +82 -43
- package/dist/Calendar/hooks/index.d.ts +0 -2
- package/dist/Calendar/hooks/index.d.ts.map +1 -1
- package/dist/Calendar/hooks/index.js +1 -2
- package/dist/Calendar/index.d.ts +3 -3
- package/dist/Calendar/index.d.ts.map +1 -1
- package/dist/Calendar/index.js +1 -1
- package/dist/Calendar/styles.d.ts +3 -9
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +3 -11
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +17 -3
- package/dist/Center/Center.d.ts +11 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +9 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +0 -1
- package/dist/Chat/ChatMessageList.d.ts +14 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +2 -0
- package/dist/Chat/useTriggerMenu.d.ts +8 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +51 -9
- package/dist/CheckboxList/CheckboxList.d.ts +13 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +39 -9
- package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +0 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +9 -5
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +51 -37
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -5
- package/dist/ContextMenu/ContextMenu.d.ts +4 -5
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +93 -11
- package/dist/DateInput/DateInput.d.ts +23 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +167 -99
- package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +42 -9
- package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +78 -15
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +12 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +42 -14
- package/dist/Field/Field.d.ts +19 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -0
- package/dist/Field/FieldLabel.d.ts +21 -2
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +41 -26
- package/dist/FileInput/FileInput.d.ts +22 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +56 -10
- package/dist/Grid/Grid.d.ts +11 -1
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +32 -6
- package/dist/InputGroup/InputGroup.d.ts +4 -3
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +16 -6
- package/dist/InputGroup/InputGroupContext.d.ts +4 -0
- package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
- package/dist/InputGroup/InputGroupContext.js +2 -2
- package/dist/InputGroup/groupStyles.d.ts +1 -1
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +3 -3
- package/dist/InputGroup/index.d.ts +1 -0
- package/dist/InputGroup/index.d.ts.map +1 -1
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +33 -1
- package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +8 -4
- package/dist/Layer/useLayer.d.ts +10 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +64 -9
- package/dist/Layout/Layout.d.ts +4 -11
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +10 -11
- package/dist/Layout/LayoutFooter.d.ts +2 -2
- package/dist/Layout/LayoutFooter.d.ts.map +1 -1
- package/dist/Layout/LayoutHeader.d.ts +2 -2
- package/dist/Layout/LayoutHeader.d.ts.map +1 -1
- package/dist/Layout/LayoutPanel.d.ts +2 -2
- package/dist/Layout/LayoutPanel.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.d.ts +51 -0
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +63 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +4 -2
- package/dist/Link/Link.d.ts +16 -1
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +18 -7
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MoreMenu/MoreMenu.d.ts +1 -7
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +0 -2
- package/dist/MultiSelector/MultiSelector.d.ts +27 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +141 -42
- package/dist/MultiSelector/hooks.d.ts +12 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +14 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +34 -3
- package/dist/NumberInput/NumberInput.d.ts +23 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +62 -12
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +15 -1
- package/dist/Popover/Popover.d.ts +18 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +5 -2
- package/dist/Popover/usePopover.d.ts +38 -3
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +24 -9
- package/dist/PowerSearch/PowerSearch.d.ts +11 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +6 -1
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +3 -5
- package/dist/RadioList/RadioList.d.ts +20 -2
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +105 -9
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +78 -44
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
- package/dist/Selector/Selector.d.ts +24 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +122 -43
- package/dist/Selector/hooks.d.ts +8 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +14 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +2 -0
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +8 -2
- package/dist/Slider/Slider.d.ts +21 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +36 -9
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Stack/Stack.d.ts +38 -23
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +25 -1
- package/dist/Stack/StackItem.d.ts +12 -23
- package/dist/Stack/StackItem.d.ts.map +1 -1
- package/dist/Stack/StackItem.js +8 -1
- package/dist/Switch/Switch.d.ts +23 -2
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -11
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +6 -0
- package/dist/TabList/TabList.d.ts +16 -4
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +87 -10
- package/dist/TabList/TabListContext.d.ts +5 -0
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +5 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +11 -8
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +10 -1
- package/dist/Table/TableCell.d.ts +8 -1
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +10 -10
- package/dist/Table/TableHeaderCell.d.ts +8 -1
- package/dist/Table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Table/TableHeaderCell.js +16 -16
- package/dist/Table/index.d.ts +2 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +8 -3
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
- package/dist/Table/tableContextMenu.d.ts +33 -0
- package/dist/Table/tableContextMenu.d.ts.map +1 -0
- package/dist/Table/tableContextMenu.js +133 -0
- package/dist/Table/types.d.ts +66 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +22 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +41 -9
- package/dist/TextInput/TextInput.d.ts +23 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +55 -10
- package/dist/TimeInput/TimeInput.d.ts +23 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +147 -76
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +14 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +131 -3
- package/dist/Tokenizer/Tokenizer.d.ts +12 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +26 -4
- package/dist/Toolbar/Toolbar.d.ts +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +51 -7
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +56 -9
- package/dist/TopNav/TopNavHeading.d.ts +7 -1
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +8 -0
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +72 -2
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +51 -12
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
- package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
- package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +69 -6
- package/dist/Typeahead/Typeahead.d.ts +26 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +65 -29
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
- package/dist/VisuallyHidden/index.d.ts +11 -0
- package/dist/VisuallyHidden/index.d.ts.map +1 -0
- package/dist/VisuallyHidden/index.js +13 -0
- package/dist/astryx.css +18 -0
- package/dist/astryx.umd.js +55 -55
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +22 -0
- package/dist/hooks/focusableSelector.d.ts.map +1 -0
- package/dist/hooks/focusableSelector.js +24 -0
- package/dist/hooks/index.d.ts +12 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +7 -2
- package/dist/hooks/useAnnounce.d.ts +40 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -0
- package/dist/hooks/useAnnounce.js +142 -0
- package/dist/hooks/useClickableContainer.d.ts +16 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +16 -4
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +4 -4
- package/dist/hooks/useFocusTrap.d.ts +18 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +87 -3
- package/dist/hooks/useGridFocus.d.ts +88 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +261 -62
- package/dist/hooks/useKeyboardHint.d.ts +82 -0
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
- package/dist/hooks/useKeyboardHint.js +220 -0
- package/dist/hooks/useListFocus.d.ts +78 -7
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +305 -52
- package/dist/hooks/useLongPress.d.ts +21 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -0
- package/dist/hooks/useLongPress.js +89 -0
- package/dist/hooks/useTreeFocus.d.ts +157 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -0
- package/dist/hooks/useTreeFocus.js +337 -0
- package/dist/hooks/useTypeahead.d.ts +63 -0
- package/dist/hooks/useTypeahead.d.ts.map +1 -0
- package/dist/hooks/useTypeahead.js +117 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/utils/dateTypes.d.ts +12 -0
- package/dist/utils/dateTypes.d.ts.map +1 -1
- package/dist/utils/dateTypes.js +41 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/inputAria.d.ts +29 -0
- package/dist/utils/inputAria.d.ts.map +1 -0
- package/dist/utils/inputAria.js +30 -0
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -2
- package/docs.mjs +2 -2
- package/package.json +12 -8
- package/src/Avatar/Avatar.test.tsx +37 -0
- package/src/Avatar/Avatar.tsx +10 -5
- package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
- package/src/Button/Button.tsx +3 -16
- package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
- package/src/ButtonGroup/ButtonGroup.tsx +0 -1
- package/src/Calendar/Calendar.doc.mjs +4 -4
- package/src/Calendar/Calendar.test.tsx +168 -30
- package/src/Calendar/Calendar.tsx +121 -66
- package/src/Calendar/hooks/index.ts +0 -6
- package/src/Calendar/index.ts +1 -3
- package/src/Calendar/styles.ts +4 -9
- package/src/Carousel/Carousel.doc.mjs +7 -1
- package/src/Carousel/Carousel.test.tsx +20 -0
- package/src/Carousel/Carousel.tsx +16 -1
- package/src/Center/Center.doc.mjs +12 -2
- package/src/Center/Center.tsx +28 -3
- package/src/Chat/ChatComposerInput.test.tsx +26 -7
- package/src/Chat/ChatComposerInput.tsx +0 -1
- package/src/Chat/ChatDictationButton.doc.mjs +1 -1
- package/src/Chat/ChatMessageList.doc.mjs +8 -0
- package/src/Chat/ChatMessageList.test.tsx +18 -0
- package/src/Chat/ChatMessageList.tsx +16 -0
- package/src/Chat/useTriggerMenu.tsx +22 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
- package/src/CheckboxInput/CheckboxInput.tsx +78 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
- package/src/CheckboxList/CheckboxList.test.tsx +192 -26
- package/src/CheckboxList/CheckboxList.tsx +56 -3
- package/src/CheckboxList/CheckboxListContext.tsx +7 -0
- package/src/CheckboxList/CheckboxListItem.tsx +0 -3
- package/src/Citation/Citation.doc.mjs +10 -0
- package/src/Citation/Citation.test.tsx +121 -0
- package/src/Citation/Citation.tsx +15 -3
- package/src/CodeBlock/CodeBlock.test.tsx +115 -0
- package/src/CodeBlock/CodeBlock.tsx +63 -33
- package/src/CommandPalette/CommandPalette.test.tsx +47 -1
- package/src/CommandPalette/CommandPalette.tsx +9 -21
- package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
- package/src/ContextMenu/ContextMenu.test.tsx +155 -1
- package/src/ContextMenu/ContextMenu.tsx +113 -16
- package/src/DateInput/DateInput.doc.mjs +286 -56
- package/src/DateInput/DateInput.test.tsx +248 -2
- package/src/DateInput/DateInput.tsx +196 -100
- package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
- package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
- package/src/DateRangeInput/DateRangeInput.tsx +63 -5
- package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
- package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
- package/src/DateTimeInput/DateTimeInput.tsx +125 -17
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.tsx +12 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
- package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
- package/src/DropdownMenu/DropdownMenu.tsx +52 -18
- package/src/Field/Field.doc.mjs +59 -18
- package/src/Field/Field.test.tsx +32 -0
- package/src/Field/Field.tsx +23 -5
- package/src/Field/FieldLabel.tsx +55 -17
- package/src/FileInput/FileInput.doc.mjs +17 -0
- package/src/FileInput/FileInput.test.tsx +283 -49
- package/src/FileInput/FileInput.tsx +76 -4
- package/src/Grid/Grid.doc.mjs +17 -4
- package/src/Grid/Grid.test.tsx +43 -30
- package/src/Grid/Grid.tsx +38 -3
- package/src/Icon/Icon.doc.mjs +3 -3
- package/src/InputGroup/InputGroup.doc.mjs +111 -23
- package/src/InputGroup/InputGroup.test.tsx +257 -60
- package/src/InputGroup/InputGroup.tsx +23 -5
- package/src/InputGroup/InputGroupContext.ts +9 -4
- package/src/InputGroup/groupStyles.ts +7 -1
- package/src/InputGroup/index.ts +1 -0
- package/src/Kbd/Kbd.test.tsx +22 -4
- package/src/Kbd/Kbd.tsx +35 -3
- package/src/Layer/layerAnimations.stylex.ts +20 -4
- package/src/Layer/useLayer.doc.mjs +5 -0
- package/src/Layer/useLayer.test.tsx +111 -0
- package/src/Layer/useLayer.tsx +90 -9
- package/src/Layout/Layout.tsx +8 -16
- package/src/Layout/LayoutFooter.doc.mjs +4 -4
- package/src/Layout/LayoutFooter.tsx +3 -3
- package/src/Layout/LayoutHeader.doc.mjs +4 -4
- package/src/Layout/LayoutHeader.tsx +3 -3
- package/src/Layout/LayoutPanel.tsx +3 -3
- package/src/Layout/padding.stylex.ts +52 -0
- package/src/Lightbox/Lightbox.doc.mjs +9 -0
- package/src/Lightbox/Lightbox.test.tsx +53 -5
- package/src/Lightbox/Lightbox.tsx +9 -4
- package/src/Link/Link.doc.mjs +14 -0
- package/src/Link/Link.test.tsx +50 -0
- package/src/Link/Link.tsx +39 -4
- package/src/Markdown/Markdown.test.tsx +11 -0
- package/src/Markdown/Markdown.tsx +7 -0
- package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
- package/src/MoreMenu/MoreMenu.tsx +0 -9
- package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
- package/src/MultiSelector/MultiSelector.test.tsx +375 -8
- package/src/MultiSelector/MultiSelector.tsx +194 -48
- package/src/MultiSelector/hooks.ts +27 -0
- package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
- package/src/NavMenu/NavHeadingMenu.tsx +50 -3
- package/src/NumberInput/NumberInput.doc.mjs +17 -0
- package/src/NumberInput/NumberInput.test.tsx +242 -40
- package/src/NumberInput/NumberInput.tsx +89 -11
- package/src/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +117 -5
- package/src/Pagination/Pagination.tsx +21 -1
- package/src/Popover/Popover.doc.mjs +26 -0
- package/src/Popover/Popover.test.tsx +121 -0
- package/src/Popover/Popover.tsx +23 -2
- package/src/Popover/usePopover.doc.mjs +9 -3
- package/src/Popover/usePopover.tsx +70 -12
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +97 -1
- package/src/PowerSearch/PowerSearch.tsx +16 -3
- package/src/ProgressBar/ProgressBar.test.tsx +34 -26
- package/src/ProgressBar/ProgressBar.tsx +3 -4
- package/src/RadioList/RadioList.doc.mjs +9 -0
- package/src/RadioList/RadioList.test.tsx +252 -16
- package/src/RadioList/RadioList.tsx +166 -10
- package/src/RadioList/RadioListItem.tsx +14 -2
- package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
- package/src/SegmentedControl/SegmentedControl.tsx +102 -54
- package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +26 -0
- package/src/Selector/Selector.test.tsx +387 -15
- package/src/Selector/Selector.tsx +175 -67
- package/src/Selector/hooks.ts +23 -0
- package/src/SideNav/SideNav.test.tsx +48 -0
- package/src/SideNav/SideNavHeading.tsx +8 -2
- package/src/SideNav/SideNavItem.tsx +2 -4
- package/src/Skeleton/Skeleton.test.tsx +25 -0
- package/src/Skeleton/Skeleton.tsx +11 -1
- package/src/Slider/Slider.doc.mjs +15 -0
- package/src/Slider/Slider.test.tsx +155 -2
- package/src/Slider/Slider.tsx +53 -5
- package/src/Spinner/Spinner.tsx +7 -1
- package/src/Stack/Stack.doc.mjs +77 -0
- package/src/Stack/Stack.test.tsx +55 -5
- package/src/Stack/Stack.tsx +73 -24
- package/src/Stack/StackItem.test.tsx +23 -0
- package/src/Stack/StackItem.tsx +20 -23
- package/src/Switch/Switch.doc.mjs +15 -0
- package/src/Switch/Switch.test.tsx +162 -27
- package/src/Switch/Switch.tsx +61 -21
- package/src/TabList/Tab.tsx +6 -0
- package/src/TabList/TabList.doc.mjs +6 -0
- package/src/TabList/TabList.test.tsx +251 -0
- package/src/TabList/TabList.tsx +103 -12
- package/src/TabList/TabListContext.ts +7 -3
- package/src/TabList/TabMenu.tsx +6 -3
- package/src/Table/BaseTable.tsx +21 -17
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.test.tsx +10 -0
- package/src/Table/Table.tsx +8 -0
- package/src/Table/TableCell.tsx +25 -23
- package/src/Table/TableHeaderCell.tsx +28 -29
- package/src/Table/index.ts +3 -2
- package/src/Table/plugins/pagination/paginateData.ts +10 -3
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
- package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
- package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
- package/src/Table/tableContextMenu.test.tsx +157 -0
- package/src/Table/tableContextMenu.tsx +142 -0
- package/src/Table/types.ts +74 -0
- package/src/Table/useTableColumnResize.doc.mjs +3 -3
- package/src/Table/useTableStickyColumns.doc.mjs +2 -2
- package/src/Text/Text.doc.mjs +18 -18
- package/src/TextArea/TextArea.doc.mjs +17 -0
- package/src/TextArea/TextArea.test.tsx +167 -35
- package/src/TextArea/TextArea.tsx +62 -15
- package/src/TextInput/TextInput.doc.mjs +17 -0
- package/src/TextInput/TextInput.test.tsx +168 -34
- package/src/TextInput/TextInput.tsx +77 -9
- package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
- package/src/TimeInput/TimeInput.doc.mjs +340 -60
- package/src/TimeInput/TimeInput.test.tsx +255 -7
- package/src/TimeInput/TimeInput.tsx +176 -70
- package/src/Timestamp/Timestamp.doc.mjs +19 -19
- package/src/Toast/Toast.tsx +14 -0
- package/src/Toast/ToastViewport.test.tsx +214 -0
- package/src/Toast/ToastViewport.tsx +167 -21
- package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
- package/src/Tokenizer/Tokenizer.test.tsx +105 -1
- package/src/Tokenizer/Tokenizer.tsx +37 -1
- package/src/Toolbar/Toolbar.test.tsx +109 -6
- package/src/Toolbar/Toolbar.tsx +59 -5
- package/src/Tooltip/Tooltip.test.tsx +68 -0
- package/src/Tooltip/useTooltip.tsx +55 -8
- package/src/TopNav/TopNav.test.tsx +50 -0
- package/src/TopNav/TopNavHeading.tsx +17 -1
- package/src/TreeList/TreeList.test.tsx +295 -6
- package/src/TreeList/TreeList.tsx +73 -1
- package/src/TreeList/TreeListItem.tsx +51 -5
- package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
- package/src/Typeahead/BaseTypeahead.tsx +93 -9
- package/src/Typeahead/Typeahead.doc.mjs +141 -30
- package/src/Typeahead/Typeahead.test.tsx +331 -1
- package/src/Typeahead/Typeahead.tsx +105 -33
- package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
- package/src/VisuallyHidden/index.ts +15 -0
- package/src/docs-script.test.mjs +50 -0
- package/src/hooks/focusableSelector.ts +25 -0
- package/src/hooks/index.ts +28 -2
- package/src/hooks/useAnnounce.test.tsx +101 -0
- package/src/hooks/useAnnounce.ts +168 -0
- package/src/hooks/useClickableContainer.ts +16 -4
- package/src/hooks/useEntryAnimation.ts +16 -4
- package/src/hooks/useFocusTrap.test.tsx +132 -0
- package/src/hooks/useFocusTrap.ts +98 -4
- package/src/hooks/useGridFocus.doc.mjs +40 -2
- package/src/hooks/useGridFocus.test.tsx +132 -0
- package/src/hooks/useGridFocus.ts +343 -67
- package/src/hooks/useKeyboardHint.doc.mjs +101 -0
- package/src/hooks/useKeyboardHint.test.tsx +70 -0
- package/src/hooks/useKeyboardHint.tsx +332 -0
- package/src/hooks/useListFocus.doc.mjs +50 -10
- package/src/hooks/useListFocus.test.tsx +375 -0
- package/src/hooks/useListFocus.ts +395 -57
- package/src/hooks/useLongPress.test.tsx +134 -0
- package/src/hooks/useLongPress.ts +119 -0
- package/src/hooks/useTreeFocus.doc.mjs +140 -0
- package/src/hooks/useTreeFocus.test.tsx +214 -0
- package/src/hooks/useTreeFocus.ts +559 -0
- package/src/hooks/useTypeahead.test.tsx +109 -0
- package/src/hooks/useTypeahead.ts +183 -0
- package/src/index.ts +1 -0
- package/src/theme/derivedVarRegistry.test.ts +10 -5
- package/src/utils/dateTypes.test.ts +40 -0
- package/src/utils/dateTypes.ts +40 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/inputAria.test.ts +39 -0
- package/src/utils/inputAria.ts +58 -0
- package/src/utils/timeParser.test.ts +10 -0
- package/src/utils/timeParser.ts +5 -2
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
- package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
- package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
|
@@ -62,6 +62,12 @@ export const docs = {
|
|
|
62
62
|
type: 'boolean',
|
|
63
63
|
description: 'Whether the input is disabled.',
|
|
64
64
|
},
|
|
65
|
+
{
|
|
66
|
+
name: 'disabledMessage',
|
|
67
|
+
type: 'string',
|
|
68
|
+
description:
|
|
69
|
+
'Explains why the input is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the input focusable via aria-disabled (the field becomes read-only). Use this instead of wrapping a disabled NumberInput in Tooltip.',
|
|
70
|
+
},
|
|
65
71
|
{
|
|
66
72
|
name: 'placeholder',
|
|
67
73
|
type: 'string',
|
|
@@ -166,6 +172,7 @@ export const docs = {
|
|
|
166
172
|
{ guidance: true, description: 'Show units (e.g. "%" or "GB") so users know what the number represents.' },
|
|
167
173
|
{ guidance: false, description: 'Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.' },
|
|
168
174
|
{ guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
|
|
175
|
+
{ guidance: false, description: "Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead." },
|
|
169
176
|
],
|
|
170
177
|
anatomy: [
|
|
171
178
|
{name: 'Label', required: true, description: 'The label for the number input.'},
|
|
@@ -236,6 +243,12 @@ export const docsZh = {
|
|
|
236
243
|
type: 'boolean',
|
|
237
244
|
description: '输入框是否禁用。',
|
|
238
245
|
},
|
|
246
|
+
{
|
|
247
|
+
name: 'disabledMessage',
|
|
248
|
+
type: 'string',
|
|
249
|
+
description:
|
|
250
|
+
'说明输入框被禁用的原因。与 isDisabled 一起使用时,在悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持输入框可聚焦(字段变为只读)。请使用此属性,而不是用 Tooltip 包裹已禁用的 NumberInput。',
|
|
251
|
+
},
|
|
239
252
|
{
|
|
240
253
|
name: 'placeholder',
|
|
241
254
|
type: 'string',
|
|
@@ -339,6 +352,7 @@ export const docsZh = {
|
|
|
339
352
|
{ guidance: true, description: 'Show units (e.g. "%" or "GB") so users know what the number represents.' },
|
|
340
353
|
{ guidance: false, description: 'Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.' },
|
|
341
354
|
{ guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
|
|
355
|
+
{ guidance: false, description: "Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead." },
|
|
342
356
|
],
|
|
343
357
|
anatomy: [
|
|
344
358
|
{name: 'Label', required: true, description: 'The label for the number input.'},
|
|
@@ -361,6 +375,7 @@ export const docsDense = {
|
|
|
361
375
|
{ guidance: true, description: 'Show units (e.g. "%" or "GB") so users know what the number represents.' },
|
|
362
376
|
{ guidance: false, description: 'Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.' },
|
|
363
377
|
{ guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
|
|
378
|
+
{ guidance: false, description: "Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead." },
|
|
364
379
|
],
|
|
365
380
|
anatomy: [
|
|
366
381
|
{name: 'Label', required: true, description: 'The label for the number input.'},
|
|
@@ -380,6 +395,8 @@ export const docsDense = {
|
|
|
380
395
|
isOptional: 'Field optional (mutually exclusive w/ isRequired).',
|
|
381
396
|
isRequired: 'Field required (mutually exclusive w/ isOptional).',
|
|
382
397
|
isDisabled: 'Input disabled.',
|
|
398
|
+
disabledMessage:
|
|
399
|
+
'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps input focusable via aria-disabled (field becomes read-only). Use instead of wrapping a disabled NumberInput in Tooltip.',
|
|
383
400
|
placeholder: 'Placeholder text.',
|
|
384
401
|
labelTooltip: 'Tooltip text in info icon at label end.',
|
|
385
402
|
startIcon: 'Icon at input start.',
|
|
@@ -9,17 +9,46 @@
|
|
|
9
9
|
* SYNC: When NumberInput.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {describe, it, expect, vi} from 'vitest';
|
|
13
|
-
import {render, screen, fireEvent} from '@testing-library/react';
|
|
12
|
+
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
13
|
+
import {render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {HashtagIcon} from '@heroicons/react/24/outline';
|
|
16
16
|
import {NumberInput} from './NumberInput';
|
|
17
17
|
|
|
18
|
+
// Mock showPopover/hidePopover since jsdom does not implement them. Used by the
|
|
19
|
+
// disabledMessage tooltip.
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
22
|
+
this.setAttribute('popover-open', '');
|
|
23
|
+
const event = new Event('toggle', {bubbles: false});
|
|
24
|
+
Object.defineProperty(event, 'newState', {value: 'open'});
|
|
25
|
+
this.dispatchEvent(event);
|
|
26
|
+
});
|
|
27
|
+
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
28
|
+
this.removeAttribute('popover-open');
|
|
29
|
+
const event = new Event('toggle', {bubbles: false});
|
|
30
|
+
Object.defineProperty(event, 'newState', {value: 'closed'});
|
|
31
|
+
this.dispatchEvent(event);
|
|
32
|
+
});
|
|
33
|
+
const originalMatches = HTMLElement.prototype.matches;
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
(HTMLElement.prototype as any).matches = function (
|
|
36
|
+
selector: string,
|
|
37
|
+
): boolean {
|
|
38
|
+
if (selector === ':popover-open') {
|
|
39
|
+
return this.hasAttribute('popover-open');
|
|
40
|
+
}
|
|
41
|
+
return originalMatches.call(this, selector);
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// jsdom popover content is in the DOM but may not be "visible" in the
|
|
46
|
+
// accessibility tree. Use hidden: true to find it.
|
|
47
|
+
const h = {hidden: true} as const;
|
|
48
|
+
|
|
18
49
|
describe('NumberInput', () => {
|
|
19
50
|
it('renders with label', () => {
|
|
20
|
-
render(
|
|
21
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
22
|
-
);
|
|
51
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
23
52
|
expect(screen.getByLabelText('Quantity')).toBeInTheDocument();
|
|
24
53
|
});
|
|
25
54
|
|
|
@@ -41,9 +70,7 @@ describe('NumberInput', () => {
|
|
|
41
70
|
});
|
|
42
71
|
|
|
43
72
|
it('displays null for null value', () => {
|
|
44
|
-
render(
|
|
45
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
46
|
-
);
|
|
73
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
47
74
|
expect(screen.getByRole('spinbutton')).toHaveValue(null);
|
|
48
75
|
});
|
|
49
76
|
|
|
@@ -103,9 +130,7 @@ describe('NumberInput', () => {
|
|
|
103
130
|
});
|
|
104
131
|
|
|
105
132
|
it('does not set aria-required when isRequired is false', () => {
|
|
106
|
-
render(
|
|
107
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
108
|
-
);
|
|
133
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
109
134
|
expect(screen.getByRole('spinbutton')).not.toHaveAttribute('aria-required');
|
|
110
135
|
});
|
|
111
136
|
|
|
@@ -139,9 +164,7 @@ describe('NumberInput', () => {
|
|
|
139
164
|
});
|
|
140
165
|
|
|
141
166
|
it('is not disabled by default', () => {
|
|
142
|
-
render(
|
|
143
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
144
|
-
);
|
|
167
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
145
168
|
expect(screen.getByRole('spinbutton')).not.toBeDisabled();
|
|
146
169
|
});
|
|
147
170
|
|
|
@@ -176,12 +199,7 @@ describe('NumberInput', () => {
|
|
|
176
199
|
|
|
177
200
|
it('sets max attribute', () => {
|
|
178
201
|
render(
|
|
179
|
-
<NumberInput
|
|
180
|
-
label="Age"
|
|
181
|
-
value={null}
|
|
182
|
-
onChange={() => {}}
|
|
183
|
-
max={120}
|
|
184
|
-
/>,
|
|
202
|
+
<NumberInput label="Age" value={null} onChange={() => {}} max={120} />,
|
|
185
203
|
);
|
|
186
204
|
expect(screen.getByRole('spinbutton')).toHaveAttribute('max', '120');
|
|
187
205
|
});
|
|
@@ -204,11 +222,7 @@ describe('NumberInput', () => {
|
|
|
204
222
|
const user = userEvent.setup();
|
|
205
223
|
const handleChange = vi.fn();
|
|
206
224
|
render(
|
|
207
|
-
<NumberInput
|
|
208
|
-
label="Quantity"
|
|
209
|
-
value={null}
|
|
210
|
-
onChange={handleChange}
|
|
211
|
-
/>,
|
|
225
|
+
<NumberInput label="Quantity" value={null} onChange={handleChange} />,
|
|
212
226
|
);
|
|
213
227
|
|
|
214
228
|
const input = screen.getByRole('spinbutton');
|
|
@@ -497,6 +511,68 @@ describe('NumberInput', () => {
|
|
|
497
511
|
});
|
|
498
512
|
});
|
|
499
513
|
|
|
514
|
+
describe('invalid typed input feedback (WCAG 3.3.1)', () => {
|
|
515
|
+
it('sets aria-invalid="true" when typed input is unparseable', async () => {
|
|
516
|
+
const user = userEvent.setup();
|
|
517
|
+
render(
|
|
518
|
+
<NumberInput
|
|
519
|
+
label="Count"
|
|
520
|
+
value={null}
|
|
521
|
+
onChange={() => {}}
|
|
522
|
+
isIntegerOnly
|
|
523
|
+
/>,
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
const input = screen.getByRole('spinbutton');
|
|
527
|
+
await user.click(input);
|
|
528
|
+
// "3.5" is invalid when isIntegerOnly is set
|
|
529
|
+
await user.type(input, '3.5');
|
|
530
|
+
|
|
531
|
+
expect(input).toHaveAttribute('aria-invalid', 'true');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('does not set aria-invalid when typed input is valid', async () => {
|
|
535
|
+
const user = userEvent.setup();
|
|
536
|
+
render(<NumberInput label="Count" value={null} onChange={() => {}} />);
|
|
537
|
+
|
|
538
|
+
const input = screen.getByRole('spinbutton');
|
|
539
|
+
await user.click(input);
|
|
540
|
+
await user.type(input, '42');
|
|
541
|
+
|
|
542
|
+
expect(input).not.toHaveAttribute('aria-invalid');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('announces an alert message when typed input is invalid', async () => {
|
|
546
|
+
const user = userEvent.setup();
|
|
547
|
+
render(
|
|
548
|
+
<NumberInput
|
|
549
|
+
label="Count"
|
|
550
|
+
value={null}
|
|
551
|
+
onChange={() => {}}
|
|
552
|
+
isIntegerOnly
|
|
553
|
+
/>,
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
const input = screen.getByRole('spinbutton');
|
|
557
|
+
await user.click(input);
|
|
558
|
+
await user.type(input, '3.5');
|
|
559
|
+
|
|
560
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Invalid number');
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('does not announce an alert message when input is valid', async () => {
|
|
564
|
+
const user = userEvent.setup();
|
|
565
|
+
render(<NumberInput label="Count" value={null} onChange={() => {}} />);
|
|
566
|
+
|
|
567
|
+
const input = screen.getByRole('spinbutton');
|
|
568
|
+
await user.click(input);
|
|
569
|
+
await user.type(input, '42');
|
|
570
|
+
|
|
571
|
+
expect(screen.getByRole('alert')).toHaveTextContent('');
|
|
572
|
+
expect(screen.queryByText('Invalid number')).not.toBeInTheDocument();
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
500
576
|
it('renders tooltip info icon when labelTooltip is provided', () => {
|
|
501
577
|
render(
|
|
502
578
|
<NumberInput
|
|
@@ -510,9 +586,7 @@ describe('NumberInput', () => {
|
|
|
510
586
|
});
|
|
511
587
|
|
|
512
588
|
it('does not render tooltip icon when labelTooltip is not provided', () => {
|
|
513
|
-
render(
|
|
514
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
515
|
-
);
|
|
589
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
516
590
|
expect(document.querySelector('svg')).not.toBeInTheDocument();
|
|
517
591
|
});
|
|
518
592
|
|
|
@@ -530,9 +604,7 @@ describe('NumberInput', () => {
|
|
|
530
604
|
});
|
|
531
605
|
|
|
532
606
|
it('does not focus when hasAutoFocus is false', () => {
|
|
533
|
-
render(
|
|
534
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
535
|
-
);
|
|
607
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
536
608
|
expect(screen.getByRole('spinbutton')).not.toHaveFocus();
|
|
537
609
|
});
|
|
538
610
|
});
|
|
@@ -554,9 +626,7 @@ describe('NumberInput', () => {
|
|
|
554
626
|
});
|
|
555
627
|
|
|
556
628
|
it('does not set name attribute when htmlName is not provided', () => {
|
|
557
|
-
render(
|
|
558
|
-
<NumberInput label="Quantity" value={null} onChange={() => {}} />,
|
|
559
|
-
);
|
|
629
|
+
render(<NumberInput label="Quantity" value={null} onChange={() => {}} />);
|
|
560
630
|
expect(screen.getByRole('spinbutton')).not.toHaveAttribute('name');
|
|
561
631
|
});
|
|
562
632
|
});
|
|
@@ -590,12 +660,7 @@ describe('NumberInput', () => {
|
|
|
590
660
|
|
|
591
661
|
it('does not show clear button when value is null', () => {
|
|
592
662
|
render(
|
|
593
|
-
<NumberInput
|
|
594
|
-
label="Qty"
|
|
595
|
-
value={null}
|
|
596
|
-
onChange={() => {}}
|
|
597
|
-
hasClear
|
|
598
|
-
/>,
|
|
663
|
+
<NumberInput label="Qty" value={null} onChange={() => {}} hasClear />,
|
|
599
664
|
);
|
|
600
665
|
expect(
|
|
601
666
|
screen.queryByRole('button', {name: 'Clear Qty'}),
|
|
@@ -664,4 +729,141 @@ describe('NumberInput', () => {
|
|
|
664
729
|
expect(input).toHaveFocus();
|
|
665
730
|
});
|
|
666
731
|
});
|
|
732
|
+
|
|
733
|
+
describe('disabledMessage', () => {
|
|
734
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
735
|
+
render(
|
|
736
|
+
<NumberInput
|
|
737
|
+
label="Quantity"
|
|
738
|
+
value={5}
|
|
739
|
+
onChange={() => {}}
|
|
740
|
+
isDisabled
|
|
741
|
+
disabledMessage="You need the Editor role"
|
|
742
|
+
/>,
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
const input = screen.getByRole('spinbutton');
|
|
746
|
+
const container = input.parentElement as HTMLElement;
|
|
747
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
748
|
+
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
749
|
+
|
|
750
|
+
fireEvent.mouseEnter(container);
|
|
751
|
+
await waitFor(() => {
|
|
752
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
fireEvent.mouseLeave(container);
|
|
756
|
+
await waitFor(() => {
|
|
757
|
+
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
762
|
+
const user = userEvent.setup();
|
|
763
|
+
render(
|
|
764
|
+
<NumberInput
|
|
765
|
+
label="Quantity"
|
|
766
|
+
value={5}
|
|
767
|
+
onChange={() => {}}
|
|
768
|
+
isDisabled
|
|
769
|
+
disabledMessage="You need the Editor role"
|
|
770
|
+
/>,
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
774
|
+
await user.tab();
|
|
775
|
+
expect(screen.getByRole('spinbutton')).toHaveFocus();
|
|
776
|
+
await waitFor(() => {
|
|
777
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('does not render a tooltip when not disabled', () => {
|
|
782
|
+
render(
|
|
783
|
+
<NumberInput
|
|
784
|
+
label="Quantity"
|
|
785
|
+
value={5}
|
|
786
|
+
onChange={() => {}}
|
|
787
|
+
disabledMessage="You need the Editor role"
|
|
788
|
+
/>,
|
|
789
|
+
);
|
|
790
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
794
|
+
render(
|
|
795
|
+
<NumberInput
|
|
796
|
+
label="Quantity"
|
|
797
|
+
value={5}
|
|
798
|
+
onChange={() => {}}
|
|
799
|
+
isDisabled
|
|
800
|
+
/>,
|
|
801
|
+
);
|
|
802
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
806
|
+
render(
|
|
807
|
+
<NumberInput
|
|
808
|
+
label="Quantity"
|
|
809
|
+
value={5}
|
|
810
|
+
onChange={() => {}}
|
|
811
|
+
isDisabled
|
|
812
|
+
disabledMessage="You need the Editor role"
|
|
813
|
+
/>,
|
|
814
|
+
);
|
|
815
|
+
const input = screen.getByRole('spinbutton');
|
|
816
|
+
expect(input).not.toBeDisabled();
|
|
817
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
818
|
+
expect(input).toHaveAttribute('readonly');
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
it('links the reason tooltip from the input via aria-describedby', () => {
|
|
822
|
+
render(
|
|
823
|
+
<NumberInput
|
|
824
|
+
label="Quantity"
|
|
825
|
+
value={5}
|
|
826
|
+
onChange={() => {}}
|
|
827
|
+
isDisabled
|
|
828
|
+
disabledMessage="You need the Editor role"
|
|
829
|
+
/>,
|
|
830
|
+
);
|
|
831
|
+
const input = screen.getByRole('spinbutton');
|
|
832
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
833
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
it('blocks value changes while focusable-disabled', async () => {
|
|
837
|
+
const user = userEvent.setup();
|
|
838
|
+
const onChange = vi.fn();
|
|
839
|
+
render(
|
|
840
|
+
<NumberInput
|
|
841
|
+
label="Quantity"
|
|
842
|
+
value={5}
|
|
843
|
+
onChange={onChange}
|
|
844
|
+
isDisabled
|
|
845
|
+
disabledMessage="You need the Editor role"
|
|
846
|
+
/>,
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
const input = screen.getByRole('spinbutton');
|
|
850
|
+
await user.click(input);
|
|
851
|
+
await user.keyboard('9');
|
|
852
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
it('remains natively disabled when disabled without a reason', () => {
|
|
856
|
+
render(
|
|
857
|
+
<NumberInput
|
|
858
|
+
label="Quantity"
|
|
859
|
+
value={5}
|
|
860
|
+
onChange={() => {}}
|
|
861
|
+
isDisabled
|
|
862
|
+
/>,
|
|
863
|
+
);
|
|
864
|
+
const input = screen.getByRole('spinbutton');
|
|
865
|
+
expect(input).toBeDisabled();
|
|
866
|
+
expect(input).not.toHaveAttribute('aria-disabled');
|
|
867
|
+
});
|
|
868
|
+
});
|
|
667
869
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file NumberInput.tsx
|
|
7
|
-
* @input Uses React, useId, useState, useMemo, useCallback, Field, Icon
|
|
7
|
+
* @input Uses React, useId, useState, useMemo, useCallback, Field, Icon, InputGroupContext
|
|
8
8
|
* @output Exports NumberInput component, NumberInputProps
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by NumberInput.test.tsx
|
|
10
10
|
*
|
|
@@ -46,6 +46,9 @@ import {
|
|
|
46
46
|
inputStatusFocusWithinStyles,
|
|
47
47
|
} from '../Field';
|
|
48
48
|
import {Icon, renderIconSlot, type IconType} from '../Icon';
|
|
49
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
50
|
+
import {useTooltip} from '../Tooltip';
|
|
51
|
+
import {getInputARIA} from '../utils';
|
|
49
52
|
import {useSize} from '../SizeContext/SizeContext';
|
|
50
53
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
51
54
|
import {useInputGroup} from '../InputGroup/InputGroupContext';
|
|
@@ -167,6 +170,27 @@ interface NumberInputPropsBase extends Omit<
|
|
|
167
170
|
* @default false
|
|
168
171
|
*/
|
|
169
172
|
isDisabled?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Explains why the input is disabled. When set together with `isDisabled`,
|
|
175
|
+
* the input shows a tooltip with this text on hover and keyboard focus, and
|
|
176
|
+
* stays focusable (via `aria-disabled`) so the reason is discoverable by
|
|
177
|
+
* keyboard and assistive technology. The field cannot be edited (it becomes
|
|
178
|
+
* read-only) while disabled.
|
|
179
|
+
*
|
|
180
|
+
* Use this instead of wrapping a disabled input in `Tooltip` — disabled
|
|
181
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```
|
|
185
|
+
* <NumberInput
|
|
186
|
+
* label="Quantity"
|
|
187
|
+
* value={quantity}
|
|
188
|
+
* isDisabled
|
|
189
|
+
* disabledMessage="Editing is locked while the order is processing"
|
|
190
|
+
* />
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
disabledMessage?: string;
|
|
170
194
|
/**
|
|
171
195
|
* Icon to display at the start of the input.
|
|
172
196
|
* Accepts a ReactNode (e.g. `<Icon icon={SearchIcon} />`) or an SVG icon component directly.
|
|
@@ -346,6 +370,7 @@ export function NumberInput({
|
|
|
346
370
|
isOptional = false,
|
|
347
371
|
isRequired = false,
|
|
348
372
|
isDisabled = false,
|
|
373
|
+
disabledMessage,
|
|
349
374
|
startIcon,
|
|
350
375
|
labelIcon,
|
|
351
376
|
status,
|
|
@@ -376,6 +401,7 @@ export function NumberInput({
|
|
|
376
401
|
}: NumberInputProps) {
|
|
377
402
|
const size = useSize(sizeProp, 'md');
|
|
378
403
|
const id = useId();
|
|
404
|
+
const inputLabelID = useId();
|
|
379
405
|
const descriptionID = useId();
|
|
380
406
|
const statusMessageID = useId();
|
|
381
407
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
@@ -385,6 +411,20 @@ export function NumberInput({
|
|
|
385
411
|
// Pending input while user is typing (null = show formatted value)
|
|
386
412
|
const [pendingInput, setPendingInput] = useState<string | null>(null);
|
|
387
413
|
|
|
414
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
415
|
+
// tooltip listeners attach to the input container (which already exists) and
|
|
416
|
+
// the input stays perceivable via aria-disabled instead of the native
|
|
417
|
+
// disabled attribute. The field is made read-only so it can't be typed into,
|
|
418
|
+
// and value mutation is blocked by the isDisabled guard in the handlers.
|
|
419
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
420
|
+
const disabledMessageTooltip = useTooltip({
|
|
421
|
+
placement: 'above',
|
|
422
|
+
// The container div is not naturally focusable; focusin bubbles up from
|
|
423
|
+
// the input, so always attach focus listeners.
|
|
424
|
+
focusTrigger: 'always',
|
|
425
|
+
isEnabled: showsDisabledMessage,
|
|
426
|
+
});
|
|
427
|
+
|
|
388
428
|
const statusIconMap: Record<InputStatusType, IconName> = {
|
|
389
429
|
warning: 'warning',
|
|
390
430
|
error: 'error',
|
|
@@ -400,13 +440,15 @@ export function NumberInput({
|
|
|
400
440
|
success: 'success',
|
|
401
441
|
};
|
|
402
442
|
|
|
403
|
-
const ariaDescribedBy =
|
|
443
|
+
const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
|
|
444
|
+
inputLabelID,
|
|
404
445
|
[
|
|
405
446
|
description ? descriptionID : null,
|
|
406
447
|
status?.message ? statusMessageID : null,
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
448
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
449
|
+
],
|
|
450
|
+
inputGroup,
|
|
451
|
+
);
|
|
410
452
|
|
|
411
453
|
// Display value: pending input if typing, otherwise the raw value
|
|
412
454
|
// Note: With type="number", we can't use formatted display values
|
|
@@ -431,6 +473,12 @@ export function NumberInput({
|
|
|
431
473
|
// Handle input text change - update immediately if valid
|
|
432
474
|
const handleInputChange = useCallback(
|
|
433
475
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
476
|
+
// Value can't change while showing a disabled message (the field is
|
|
477
|
+
// read-only and non-native-disabled), but guard the handler too so the
|
|
478
|
+
// pending value and onChange never fire.
|
|
479
|
+
if (isDisabled) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
434
482
|
const newValue = e.target.value;
|
|
435
483
|
setPendingInput(newValue);
|
|
436
484
|
|
|
@@ -440,7 +488,7 @@ export function NumberInput({
|
|
|
440
488
|
onChange(parsed);
|
|
441
489
|
}
|
|
442
490
|
},
|
|
443
|
-
[value, onChange, min, max, isIntegerOnly],
|
|
491
|
+
[value, onChange, min, max, isIntegerOnly, isDisabled],
|
|
444
492
|
);
|
|
445
493
|
|
|
446
494
|
// Handle focus
|
|
@@ -522,7 +570,13 @@ export function NumberInput({
|
|
|
522
570
|
|
|
523
571
|
const inputWrapper = (
|
|
524
572
|
<div
|
|
525
|
-
ref={
|
|
573
|
+
ref={el => {
|
|
574
|
+
containerRef.current = el;
|
|
575
|
+
// Anchor + hover/focus listeners for the disabled-message tooltip.
|
|
576
|
+
// Handlers are gated internally by isEnabled, and anchor names
|
|
577
|
+
// compose, so attaching unconditionally is safe.
|
|
578
|
+
disabledMessageTooltip.ref(el);
|
|
579
|
+
}}
|
|
526
580
|
onClick={handleWrapperClick}
|
|
527
581
|
onMouseUp={handleWrapperMouseUp}
|
|
528
582
|
{...mergeProps(
|
|
@@ -542,6 +596,7 @@ export function NumberInput({
|
|
|
542
596
|
style,
|
|
543
597
|
)}>
|
|
544
598
|
{startIcon && renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
|
|
599
|
+
{inputGroup && <VisuallyHidden id={inputLabelID}>{label}</VisuallyHidden>}
|
|
545
600
|
<input
|
|
546
601
|
{...rest}
|
|
547
602
|
ref={mergeRefs(ref, inputRef)}
|
|
@@ -555,7 +610,12 @@ export function NumberInput({
|
|
|
555
610
|
onBlur={handleBlur}
|
|
556
611
|
onKeyDown={handleKeyDown}
|
|
557
612
|
placeholder={placeholder}
|
|
558
|
-
disabled
|
|
613
|
+
// With a disabledMessage the input keeps focusability via aria-disabled
|
|
614
|
+
// so the reason is focus-discoverable; readOnly + the handler guards
|
|
615
|
+
// keep the value from changing.
|
|
616
|
+
disabled={isDisabled && !showsDisabledMessage}
|
|
617
|
+
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
618
|
+
readOnly={showsDisabledMessage || undefined}
|
|
559
619
|
autoFocus={hasAutoFocus}
|
|
560
620
|
data-autofocus={hasAutoFocus || undefined}
|
|
561
621
|
min={min ?? undefined}
|
|
@@ -563,8 +623,10 @@ export function NumberInput({
|
|
|
563
623
|
step={step ?? undefined}
|
|
564
624
|
aria-describedby={ariaDescribedBy}
|
|
565
625
|
aria-required={isRequired === true ? 'true' : undefined}
|
|
566
|
-
aria-invalid={
|
|
567
|
-
|
|
626
|
+
aria-invalid={
|
|
627
|
+
status?.type === 'error' || !isInputValid ? 'true' : undefined
|
|
628
|
+
}
|
|
629
|
+
aria-labelledby={ariaLabelledBy}
|
|
568
630
|
{...stylex.props(
|
|
569
631
|
styles.input,
|
|
570
632
|
isDisabled && styles.inputDisabled,
|
|
@@ -572,6 +634,14 @@ export function NumberInput({
|
|
|
572
634
|
)}
|
|
573
635
|
/>
|
|
574
636
|
{units && <span {...stylex.props(styles.units)}>{units}</span>}
|
|
637
|
+
{/*
|
|
638
|
+
Live region announcing invalid typed input to assistive technology.
|
|
639
|
+
The value silently reverts on blur, so without this a screen-reader
|
|
640
|
+
user would get no feedback that their entry was rejected (WCAG 3.3.1).
|
|
641
|
+
*/}
|
|
642
|
+
<VisuallyHidden as="div" role="alert" aria-live="assertive">
|
|
643
|
+
{!isInputValid ? 'Invalid number' : ''}
|
|
644
|
+
</VisuallyHidden>
|
|
575
645
|
{hasClear && value != null && !isDisabled && (
|
|
576
646
|
<button
|
|
577
647
|
type="button"
|
|
@@ -592,7 +662,13 @@ export function NumberInput({
|
|
|
592
662
|
);
|
|
593
663
|
|
|
594
664
|
if (inputGroup) {
|
|
595
|
-
return
|
|
665
|
+
return (
|
|
666
|
+
<>
|
|
667
|
+
{inputWrapper}
|
|
668
|
+
{showsDisabledMessage &&
|
|
669
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
670
|
+
</>
|
|
671
|
+
);
|
|
596
672
|
}
|
|
597
673
|
|
|
598
674
|
return (
|
|
@@ -618,6 +694,8 @@ export function NumberInput({
|
|
|
618
694
|
labelTooltip={labelTooltip}
|
|
619
695
|
width={width}>
|
|
620
696
|
{inputWrapper}
|
|
697
|
+
{showsDisabledMessage &&
|
|
698
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
621
699
|
</Field>
|
|
622
700
|
);
|
|
623
701
|
}
|
|
@@ -47,7 +47,7 @@ export const docs = {
|
|
|
47
47
|
{
|
|
48
48
|
name: 'pageSize',
|
|
49
49
|
type: 'number',
|
|
50
|
-
description: 'Number of items per page.',
|
|
50
|
+
description: 'Number of items per page. Coerced to a positive integer; non-finite values fall back to the default.',
|
|
51
51
|
default: '10',
|
|
52
52
|
},
|
|
53
53
|
{
|
|
@@ -175,7 +175,7 @@ export const docsZh = {
|
|
|
175
175
|
{
|
|
176
176
|
name: 'pageSize',
|
|
177
177
|
type: 'number',
|
|
178
|
-
description: '
|
|
178
|
+
description: '每页项目数。强制转换为正整数;非有限值回退到默认值。',
|
|
179
179
|
default: '10',
|
|
180
180
|
},
|
|
181
181
|
{
|
|
@@ -277,7 +277,7 @@ export const docsDense = {
|
|
|
277
277
|
totalItems: 'Total items. Calculates page count. Precedence over totalPages.',
|
|
278
278
|
totalPages: 'Total pages. Use when page count known but not item count.',
|
|
279
279
|
hasMore: 'More pages exist after current. For cursor-based pagination.',
|
|
280
|
-
pageSize: 'Items per page
|
|
280
|
+
pageSize: 'Items per page; coerced to positive integer, non-finite falls back to default',
|
|
281
281
|
pageSizeOptions: 'Page size options. Shows selector dropdown when provided.',
|
|
282
282
|
onPageSizeChange: 'Called on page size change. Auto resets to page 1.',
|
|
283
283
|
variant: 'Display between prev/next buttons.',
|