@astryxdesign/core 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +165 -0
- package/README.md +12 -12
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +11 -5
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +2 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +0 -1
- package/dist/Calendar/Calendar.d.ts +5 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +82 -43
- package/dist/Calendar/hooks/index.d.ts +0 -2
- package/dist/Calendar/hooks/index.d.ts.map +1 -1
- package/dist/Calendar/hooks/index.js +1 -2
- package/dist/Calendar/index.d.ts +3 -3
- package/dist/Calendar/index.d.ts.map +1 -1
- package/dist/Calendar/index.js +1 -1
- package/dist/Calendar/styles.d.ts +3 -9
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +3 -11
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +17 -3
- package/dist/Center/Center.d.ts +11 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +9 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +0 -1
- package/dist/Chat/ChatMessageList.d.ts +14 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +2 -0
- package/dist/Chat/useTriggerMenu.d.ts +8 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +51 -9
- package/dist/CheckboxList/CheckboxList.d.ts +13 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +39 -9
- package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +0 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +9 -5
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +51 -37
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -5
- package/dist/ContextMenu/ContextMenu.d.ts +4 -5
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +93 -11
- package/dist/DateInput/DateInput.d.ts +23 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +167 -99
- package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +42 -9
- package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +78 -15
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +12 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +42 -14
- package/dist/Field/Field.d.ts +19 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -0
- package/dist/Field/FieldLabel.d.ts +21 -2
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +41 -26
- package/dist/FileInput/FileInput.d.ts +22 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +56 -10
- package/dist/Grid/Grid.d.ts +11 -1
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +32 -6
- package/dist/InputGroup/InputGroup.d.ts +4 -3
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +16 -6
- package/dist/InputGroup/InputGroupContext.d.ts +4 -0
- package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
- package/dist/InputGroup/InputGroupContext.js +2 -2
- package/dist/InputGroup/groupStyles.d.ts +1 -1
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +3 -3
- package/dist/InputGroup/index.d.ts +1 -0
- package/dist/InputGroup/index.d.ts.map +1 -1
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +33 -1
- package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +8 -4
- package/dist/Layer/useLayer.d.ts +10 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +64 -9
- package/dist/Layout/Layout.d.ts +4 -11
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +10 -11
- package/dist/Layout/LayoutFooter.d.ts +2 -2
- package/dist/Layout/LayoutFooter.d.ts.map +1 -1
- package/dist/Layout/LayoutHeader.d.ts +2 -2
- package/dist/Layout/LayoutHeader.d.ts.map +1 -1
- package/dist/Layout/LayoutPanel.d.ts +2 -2
- package/dist/Layout/LayoutPanel.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.d.ts +51 -0
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +63 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +4 -2
- package/dist/Link/Link.d.ts +16 -1
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +18 -7
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MoreMenu/MoreMenu.d.ts +1 -7
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +0 -2
- package/dist/MultiSelector/MultiSelector.d.ts +27 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +141 -42
- package/dist/MultiSelector/hooks.d.ts +12 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +14 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +34 -3
- package/dist/NumberInput/NumberInput.d.ts +23 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +62 -12
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +15 -1
- package/dist/Popover/Popover.d.ts +18 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +5 -2
- package/dist/Popover/usePopover.d.ts +38 -3
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +24 -9
- package/dist/PowerSearch/PowerSearch.d.ts +11 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +6 -1
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +3 -5
- package/dist/RadioList/RadioList.d.ts +20 -2
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +105 -9
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +78 -44
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
- package/dist/Selector/Selector.d.ts +24 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +122 -43
- package/dist/Selector/hooks.d.ts +8 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +14 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +2 -0
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +8 -2
- package/dist/Slider/Slider.d.ts +21 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +36 -9
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Stack/Stack.d.ts +38 -23
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +25 -1
- package/dist/Stack/StackItem.d.ts +12 -23
- package/dist/Stack/StackItem.d.ts.map +1 -1
- package/dist/Stack/StackItem.js +8 -1
- package/dist/Switch/Switch.d.ts +23 -2
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -11
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +6 -0
- package/dist/TabList/TabList.d.ts +16 -4
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +87 -10
- package/dist/TabList/TabListContext.d.ts +5 -0
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +5 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +11 -8
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +10 -1
- package/dist/Table/TableCell.d.ts +8 -1
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +10 -10
- package/dist/Table/TableHeaderCell.d.ts +8 -1
- package/dist/Table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Table/TableHeaderCell.js +16 -16
- package/dist/Table/index.d.ts +2 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +8 -3
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
- package/dist/Table/tableContextMenu.d.ts +33 -0
- package/dist/Table/tableContextMenu.d.ts.map +1 -0
- package/dist/Table/tableContextMenu.js +133 -0
- package/dist/Table/types.d.ts +66 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +22 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +41 -9
- package/dist/TextInput/TextInput.d.ts +23 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +55 -10
- package/dist/TimeInput/TimeInput.d.ts +23 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +147 -76
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +14 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +131 -3
- package/dist/Tokenizer/Tokenizer.d.ts +12 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +26 -4
- package/dist/Toolbar/Toolbar.d.ts +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +51 -7
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +56 -9
- package/dist/TopNav/TopNavHeading.d.ts +7 -1
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +8 -0
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +72 -2
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +51 -12
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
- package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
- package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +69 -6
- package/dist/Typeahead/Typeahead.d.ts +26 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +65 -29
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
- package/dist/VisuallyHidden/index.d.ts +11 -0
- package/dist/VisuallyHidden/index.d.ts.map +1 -0
- package/dist/VisuallyHidden/index.js +13 -0
- package/dist/astryx.css +18 -0
- package/dist/astryx.umd.js +55 -55
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +22 -0
- package/dist/hooks/focusableSelector.d.ts.map +1 -0
- package/dist/hooks/focusableSelector.js +24 -0
- package/dist/hooks/index.d.ts +12 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +7 -2
- package/dist/hooks/useAnnounce.d.ts +40 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -0
- package/dist/hooks/useAnnounce.js +142 -0
- package/dist/hooks/useClickableContainer.d.ts +16 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +16 -4
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +4 -4
- package/dist/hooks/useFocusTrap.d.ts +18 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +87 -3
- package/dist/hooks/useGridFocus.d.ts +88 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +261 -62
- package/dist/hooks/useKeyboardHint.d.ts +82 -0
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
- package/dist/hooks/useKeyboardHint.js +220 -0
- package/dist/hooks/useListFocus.d.ts +78 -7
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +305 -52
- package/dist/hooks/useLongPress.d.ts +21 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -0
- package/dist/hooks/useLongPress.js +89 -0
- package/dist/hooks/useTreeFocus.d.ts +157 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -0
- package/dist/hooks/useTreeFocus.js +337 -0
- package/dist/hooks/useTypeahead.d.ts +63 -0
- package/dist/hooks/useTypeahead.d.ts.map +1 -0
- package/dist/hooks/useTypeahead.js +117 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/utils/dateTypes.d.ts +12 -0
- package/dist/utils/dateTypes.d.ts.map +1 -1
- package/dist/utils/dateTypes.js +41 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/inputAria.d.ts +29 -0
- package/dist/utils/inputAria.d.ts.map +1 -0
- package/dist/utils/inputAria.js +30 -0
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -2
- package/docs.mjs +2 -2
- package/package.json +12 -8
- package/src/Avatar/Avatar.test.tsx +37 -0
- package/src/Avatar/Avatar.tsx +10 -5
- package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
- package/src/Button/Button.tsx +3 -16
- package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
- package/src/ButtonGroup/ButtonGroup.tsx +0 -1
- package/src/Calendar/Calendar.doc.mjs +4 -4
- package/src/Calendar/Calendar.test.tsx +168 -30
- package/src/Calendar/Calendar.tsx +121 -66
- package/src/Calendar/hooks/index.ts +0 -6
- package/src/Calendar/index.ts +1 -3
- package/src/Calendar/styles.ts +4 -9
- package/src/Carousel/Carousel.doc.mjs +7 -1
- package/src/Carousel/Carousel.test.tsx +20 -0
- package/src/Carousel/Carousel.tsx +16 -1
- package/src/Center/Center.doc.mjs +12 -2
- package/src/Center/Center.tsx +28 -3
- package/src/Chat/ChatComposerInput.test.tsx +26 -7
- package/src/Chat/ChatComposerInput.tsx +0 -1
- package/src/Chat/ChatDictationButton.doc.mjs +1 -1
- package/src/Chat/ChatMessageList.doc.mjs +8 -0
- package/src/Chat/ChatMessageList.test.tsx +18 -0
- package/src/Chat/ChatMessageList.tsx +16 -0
- package/src/Chat/useTriggerMenu.tsx +22 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
- package/src/CheckboxInput/CheckboxInput.tsx +78 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
- package/src/CheckboxList/CheckboxList.test.tsx +192 -26
- package/src/CheckboxList/CheckboxList.tsx +56 -3
- package/src/CheckboxList/CheckboxListContext.tsx +7 -0
- package/src/CheckboxList/CheckboxListItem.tsx +0 -3
- package/src/Citation/Citation.doc.mjs +10 -0
- package/src/Citation/Citation.test.tsx +121 -0
- package/src/Citation/Citation.tsx +15 -3
- package/src/CodeBlock/CodeBlock.test.tsx +115 -0
- package/src/CodeBlock/CodeBlock.tsx +63 -33
- package/src/CommandPalette/CommandPalette.test.tsx +47 -1
- package/src/CommandPalette/CommandPalette.tsx +9 -21
- package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
- package/src/ContextMenu/ContextMenu.test.tsx +155 -1
- package/src/ContextMenu/ContextMenu.tsx +113 -16
- package/src/DateInput/DateInput.doc.mjs +286 -56
- package/src/DateInput/DateInput.test.tsx +248 -2
- package/src/DateInput/DateInput.tsx +196 -100
- package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
- package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
- package/src/DateRangeInput/DateRangeInput.tsx +63 -5
- package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
- package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
- package/src/DateTimeInput/DateTimeInput.tsx +125 -17
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.tsx +12 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
- package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
- package/src/DropdownMenu/DropdownMenu.tsx +52 -18
- package/src/Field/Field.doc.mjs +59 -18
- package/src/Field/Field.test.tsx +32 -0
- package/src/Field/Field.tsx +23 -5
- package/src/Field/FieldLabel.tsx +55 -17
- package/src/FileInput/FileInput.doc.mjs +17 -0
- package/src/FileInput/FileInput.test.tsx +283 -49
- package/src/FileInput/FileInput.tsx +76 -4
- package/src/Grid/Grid.doc.mjs +17 -4
- package/src/Grid/Grid.test.tsx +43 -30
- package/src/Grid/Grid.tsx +38 -3
- package/src/Icon/Icon.doc.mjs +3 -3
- package/src/InputGroup/InputGroup.doc.mjs +111 -23
- package/src/InputGroup/InputGroup.test.tsx +257 -60
- package/src/InputGroup/InputGroup.tsx +23 -5
- package/src/InputGroup/InputGroupContext.ts +9 -4
- package/src/InputGroup/groupStyles.ts +7 -1
- package/src/InputGroup/index.ts +1 -0
- package/src/Kbd/Kbd.test.tsx +22 -4
- package/src/Kbd/Kbd.tsx +35 -3
- package/src/Layer/layerAnimations.stylex.ts +20 -4
- package/src/Layer/useLayer.doc.mjs +5 -0
- package/src/Layer/useLayer.test.tsx +111 -0
- package/src/Layer/useLayer.tsx +90 -9
- package/src/Layout/Layout.tsx +8 -16
- package/src/Layout/LayoutFooter.doc.mjs +4 -4
- package/src/Layout/LayoutFooter.tsx +3 -3
- package/src/Layout/LayoutHeader.doc.mjs +4 -4
- package/src/Layout/LayoutHeader.tsx +3 -3
- package/src/Layout/LayoutPanel.tsx +3 -3
- package/src/Layout/padding.stylex.ts +52 -0
- package/src/Lightbox/Lightbox.doc.mjs +9 -0
- package/src/Lightbox/Lightbox.test.tsx +53 -5
- package/src/Lightbox/Lightbox.tsx +9 -4
- package/src/Link/Link.doc.mjs +14 -0
- package/src/Link/Link.test.tsx +50 -0
- package/src/Link/Link.tsx +39 -4
- package/src/Markdown/Markdown.test.tsx +11 -0
- package/src/Markdown/Markdown.tsx +7 -0
- package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
- package/src/MoreMenu/MoreMenu.tsx +0 -9
- package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
- package/src/MultiSelector/MultiSelector.test.tsx +375 -8
- package/src/MultiSelector/MultiSelector.tsx +194 -48
- package/src/MultiSelector/hooks.ts +27 -0
- package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
- package/src/NavMenu/NavHeadingMenu.tsx +50 -3
- package/src/NumberInput/NumberInput.doc.mjs +17 -0
- package/src/NumberInput/NumberInput.test.tsx +242 -40
- package/src/NumberInput/NumberInput.tsx +89 -11
- package/src/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +117 -5
- package/src/Pagination/Pagination.tsx +21 -1
- package/src/Popover/Popover.doc.mjs +26 -0
- package/src/Popover/Popover.test.tsx +121 -0
- package/src/Popover/Popover.tsx +23 -2
- package/src/Popover/usePopover.doc.mjs +9 -3
- package/src/Popover/usePopover.tsx +70 -12
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +97 -1
- package/src/PowerSearch/PowerSearch.tsx +16 -3
- package/src/ProgressBar/ProgressBar.test.tsx +34 -26
- package/src/ProgressBar/ProgressBar.tsx +3 -4
- package/src/RadioList/RadioList.doc.mjs +9 -0
- package/src/RadioList/RadioList.test.tsx +252 -16
- package/src/RadioList/RadioList.tsx +166 -10
- package/src/RadioList/RadioListItem.tsx +14 -2
- package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
- package/src/SegmentedControl/SegmentedControl.tsx +102 -54
- package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +26 -0
- package/src/Selector/Selector.test.tsx +387 -15
- package/src/Selector/Selector.tsx +175 -67
- package/src/Selector/hooks.ts +23 -0
- package/src/SideNav/SideNav.test.tsx +48 -0
- package/src/SideNav/SideNavHeading.tsx +8 -2
- package/src/SideNav/SideNavItem.tsx +2 -4
- package/src/Skeleton/Skeleton.test.tsx +25 -0
- package/src/Skeleton/Skeleton.tsx +11 -1
- package/src/Slider/Slider.doc.mjs +15 -0
- package/src/Slider/Slider.test.tsx +155 -2
- package/src/Slider/Slider.tsx +53 -5
- package/src/Spinner/Spinner.tsx +7 -1
- package/src/Stack/Stack.doc.mjs +77 -0
- package/src/Stack/Stack.test.tsx +55 -5
- package/src/Stack/Stack.tsx +73 -24
- package/src/Stack/StackItem.test.tsx +23 -0
- package/src/Stack/StackItem.tsx +20 -23
- package/src/Switch/Switch.doc.mjs +15 -0
- package/src/Switch/Switch.test.tsx +162 -27
- package/src/Switch/Switch.tsx +61 -21
- package/src/TabList/Tab.tsx +6 -0
- package/src/TabList/TabList.doc.mjs +6 -0
- package/src/TabList/TabList.test.tsx +251 -0
- package/src/TabList/TabList.tsx +103 -12
- package/src/TabList/TabListContext.ts +7 -3
- package/src/TabList/TabMenu.tsx +6 -3
- package/src/Table/BaseTable.tsx +21 -17
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.test.tsx +10 -0
- package/src/Table/Table.tsx +8 -0
- package/src/Table/TableCell.tsx +25 -23
- package/src/Table/TableHeaderCell.tsx +28 -29
- package/src/Table/index.ts +3 -2
- package/src/Table/plugins/pagination/paginateData.ts +10 -3
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
- package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
- package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
- package/src/Table/tableContextMenu.test.tsx +157 -0
- package/src/Table/tableContextMenu.tsx +142 -0
- package/src/Table/types.ts +74 -0
- package/src/Table/useTableColumnResize.doc.mjs +3 -3
- package/src/Table/useTableStickyColumns.doc.mjs +2 -2
- package/src/Text/Text.doc.mjs +18 -18
- package/src/TextArea/TextArea.doc.mjs +17 -0
- package/src/TextArea/TextArea.test.tsx +167 -35
- package/src/TextArea/TextArea.tsx +62 -15
- package/src/TextInput/TextInput.doc.mjs +17 -0
- package/src/TextInput/TextInput.test.tsx +168 -34
- package/src/TextInput/TextInput.tsx +77 -9
- package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
- package/src/TimeInput/TimeInput.doc.mjs +340 -60
- package/src/TimeInput/TimeInput.test.tsx +255 -7
- package/src/TimeInput/TimeInput.tsx +176 -70
- package/src/Timestamp/Timestamp.doc.mjs +19 -19
- package/src/Toast/Toast.tsx +14 -0
- package/src/Toast/ToastViewport.test.tsx +214 -0
- package/src/Toast/ToastViewport.tsx +167 -21
- package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
- package/src/Tokenizer/Tokenizer.test.tsx +105 -1
- package/src/Tokenizer/Tokenizer.tsx +37 -1
- package/src/Toolbar/Toolbar.test.tsx +109 -6
- package/src/Toolbar/Toolbar.tsx +59 -5
- package/src/Tooltip/Tooltip.test.tsx +68 -0
- package/src/Tooltip/useTooltip.tsx +55 -8
- package/src/TopNav/TopNav.test.tsx +50 -0
- package/src/TopNav/TopNavHeading.tsx +17 -1
- package/src/TreeList/TreeList.test.tsx +295 -6
- package/src/TreeList/TreeList.tsx +73 -1
- package/src/TreeList/TreeListItem.tsx +51 -5
- package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
- package/src/Typeahead/BaseTypeahead.tsx +93 -9
- package/src/Typeahead/Typeahead.doc.mjs +141 -30
- package/src/Typeahead/Typeahead.test.tsx +331 -1
- package/src/Typeahead/Typeahead.tsx +105 -33
- package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
- package/src/VisuallyHidden/index.ts +15 -0
- package/src/docs-script.test.mjs +50 -0
- package/src/hooks/focusableSelector.ts +25 -0
- package/src/hooks/index.ts +28 -2
- package/src/hooks/useAnnounce.test.tsx +101 -0
- package/src/hooks/useAnnounce.ts +168 -0
- package/src/hooks/useClickableContainer.ts +16 -4
- package/src/hooks/useEntryAnimation.ts +16 -4
- package/src/hooks/useFocusTrap.test.tsx +132 -0
- package/src/hooks/useFocusTrap.ts +98 -4
- package/src/hooks/useGridFocus.doc.mjs +40 -2
- package/src/hooks/useGridFocus.test.tsx +132 -0
- package/src/hooks/useGridFocus.ts +343 -67
- package/src/hooks/useKeyboardHint.doc.mjs +101 -0
- package/src/hooks/useKeyboardHint.test.tsx +70 -0
- package/src/hooks/useKeyboardHint.tsx +332 -0
- package/src/hooks/useListFocus.doc.mjs +50 -10
- package/src/hooks/useListFocus.test.tsx +375 -0
- package/src/hooks/useListFocus.ts +395 -57
- package/src/hooks/useLongPress.test.tsx +134 -0
- package/src/hooks/useLongPress.ts +119 -0
- package/src/hooks/useTreeFocus.doc.mjs +140 -0
- package/src/hooks/useTreeFocus.test.tsx +214 -0
- package/src/hooks/useTreeFocus.ts +559 -0
- package/src/hooks/useTypeahead.test.tsx +109 -0
- package/src/hooks/useTypeahead.ts +183 -0
- package/src/index.ts +1 -0
- package/src/theme/derivedVarRegistry.test.ts +10 -5
- package/src/utils/dateTypes.test.ts +40 -0
- package/src/utils/dateTypes.ts +40 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/inputAria.test.ts +39 -0
- package/src/utils/inputAria.ts +58 -0
- package/src/utils/timeParser.test.ts +10 -0
- package/src/utils/timeParser.ts +5 -2
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
- package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
- package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
* SYNC: When DateInput.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
|
+
import userEvent from '@testing-library/user-event';
|
|
14
15
|
import {DateInput} from './DateInput';
|
|
16
|
+
import {InputGroup} from '../InputGroup';
|
|
17
|
+
import {InputGroupText} from '../InputGroup/InputGroupText';
|
|
15
18
|
|
|
16
19
|
describe('DateInput', () => {
|
|
17
20
|
it('renders with label', () => {
|
|
@@ -110,6 +113,43 @@ describe('DateInput', () => {
|
|
|
110
113
|
expect(onChange).not.toHaveBeenCalled();
|
|
111
114
|
});
|
|
112
115
|
|
|
116
|
+
it('sets aria-invalid="true" when typed input is unparseable', () => {
|
|
117
|
+
render(<DateInput label="Date" onChange={() => {}} />);
|
|
118
|
+
|
|
119
|
+
const input = screen.getByRole('combobox');
|
|
120
|
+
fireEvent.change(input, {target: {value: '13/45/2024'}});
|
|
121
|
+
|
|
122
|
+
expect(input).toHaveAttribute('aria-invalid', 'true');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('does not set aria-invalid when typed input is a valid date', () => {
|
|
126
|
+
render(<DateInput label="Date" onChange={() => {}} />);
|
|
127
|
+
|
|
128
|
+
const input = screen.getByRole('combobox');
|
|
129
|
+
fireEvent.change(input, {target: {value: '03/15/2026'}});
|
|
130
|
+
|
|
131
|
+
expect(input).not.toHaveAttribute('aria-invalid');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('announces an alert message when typed input is invalid', () => {
|
|
135
|
+
render(<DateInput label="Date" onChange={() => {}} />);
|
|
136
|
+
|
|
137
|
+
const input = screen.getByRole('combobox');
|
|
138
|
+
fireEvent.change(input, {target: {value: '13/45/2024'}});
|
|
139
|
+
|
|
140
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Invalid date');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('does not announce an alert message when input is valid', () => {
|
|
144
|
+
render(<DateInput label="Date" onChange={() => {}} />);
|
|
145
|
+
|
|
146
|
+
const input = screen.getByRole('combobox');
|
|
147
|
+
fireEvent.change(input, {target: {value: '03/15/2026'}});
|
|
148
|
+
|
|
149
|
+
expect(screen.getByRole('alert')).toHaveTextContent('');
|
|
150
|
+
expect(screen.queryByText('Invalid date')).not.toBeInTheDocument();
|
|
151
|
+
});
|
|
152
|
+
|
|
113
153
|
it('reverts to previous value on blur when input is invalid', async () => {
|
|
114
154
|
const onChange = vi.fn();
|
|
115
155
|
render(<DateInput label="Date" value="2026-01-25" onChange={onChange} />);
|
|
@@ -280,6 +320,28 @@ describe('DateInput', () => {
|
|
|
280
320
|
);
|
|
281
321
|
});
|
|
282
322
|
|
|
323
|
+
it('opens the calendar popover on ArrowDown (keyboard, forms-13)', () => {
|
|
324
|
+
render(<DateInput label="Date" onChange={() => {}} />);
|
|
325
|
+
const input = screen.getByRole('combobox');
|
|
326
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
327
|
+
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
328
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('opens the calendar popover on Alt+ArrowDown (keyboard, forms-13)', () => {
|
|
332
|
+
render(<DateInput label="Date" onChange={() => {}} />);
|
|
333
|
+
const input = screen.getByRole('combobox');
|
|
334
|
+
fireEvent.keyDown(input, {key: 'ArrowDown', altKey: true});
|
|
335
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('does not open on ArrowDown when disabled', () => {
|
|
339
|
+
render(<DateInput label="Date" isDisabled onChange={() => {}} />);
|
|
340
|
+
const input = screen.getByRole('combobox');
|
|
341
|
+
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
342
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
343
|
+
});
|
|
344
|
+
|
|
283
345
|
it('input has aria-haspopup="dialog"', () => {
|
|
284
346
|
render(<DateInput label="Date" onChange={() => {}} />);
|
|
285
347
|
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
@@ -585,4 +647,188 @@ describe('DateInput', () => {
|
|
|
585
647
|
expect(input).toHaveValue('March 20, 2026');
|
|
586
648
|
});
|
|
587
649
|
});
|
|
650
|
+
|
|
651
|
+
describe('InputGroup', () => {
|
|
652
|
+
it('uses group ARIA and skips standalone Field chrome when grouped', () => {
|
|
653
|
+
render(
|
|
654
|
+
<InputGroup
|
|
655
|
+
label="Availability"
|
|
656
|
+
description="Choose a start date"
|
|
657
|
+
status={{type: 'error', message: 'Date is required'}}>
|
|
658
|
+
<InputGroupText>Starts</InputGroupText>
|
|
659
|
+
<DateInput label="Date" isLabelHidden onChange={() => {}} />
|
|
660
|
+
</InputGroup>,
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
const group = screen.getByRole('group', {name: 'Availability'});
|
|
664
|
+
const input = screen.getByRole('combobox', {
|
|
665
|
+
name: 'Availability Date',
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
expect(document.querySelectorAll('.astryx-field')).toHaveLength(1);
|
|
669
|
+
expect(input).toHaveAttribute('aria-labelledby');
|
|
670
|
+
expect(input.getAttribute('aria-labelledby')).toContain(
|
|
671
|
+
group.getAttribute('aria-labelledby'),
|
|
672
|
+
);
|
|
673
|
+
expect(input).toHaveAttribute(
|
|
674
|
+
'aria-describedby',
|
|
675
|
+
group.getAttribute('aria-describedby'),
|
|
676
|
+
);
|
|
677
|
+
expect(input).not.toHaveAttribute('aria-invalid');
|
|
678
|
+
expect(screen.getByText('Date is required')).toBeInTheDocument();
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it('preserves disabledMessage tooltip wiring when grouped', () => {
|
|
682
|
+
render(
|
|
683
|
+
<InputGroup label="Availability">
|
|
684
|
+
<InputGroupText>Starts</InputGroupText>
|
|
685
|
+
<DateInput
|
|
686
|
+
label="Date"
|
|
687
|
+
isLabelHidden
|
|
688
|
+
isDisabled
|
|
689
|
+
disabledMessage="Scheduling is locked"
|
|
690
|
+
onChange={() => {}}
|
|
691
|
+
/>
|
|
692
|
+
</InputGroup>,
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
const input = screen.getByRole('combobox', {name: 'Availability Date'});
|
|
696
|
+
const tooltip = screen.getByRole('tooltip', {hidden: true});
|
|
697
|
+
|
|
698
|
+
expect(input).not.toBeDisabled();
|
|
699
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
700
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
701
|
+
expect(tooltip).toHaveTextContent('Scheduling is locked');
|
|
702
|
+
expect(
|
|
703
|
+
screen.getByRole('button', {name: 'Open calendar'}),
|
|
704
|
+
).toBeDisabled();
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
describe('disabledMessage', () => {
|
|
709
|
+
// jsdom does not implement the Popover API used by the tooltip, so mock
|
|
710
|
+
// showPopover/hidePopover to toggle a `popover-open` attribute the tests
|
|
711
|
+
// can assert on.
|
|
712
|
+
beforeEach(() => {
|
|
713
|
+
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
714
|
+
this.setAttribute('popover-open', '');
|
|
715
|
+
});
|
|
716
|
+
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
717
|
+
this.removeAttribute('popover-open');
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// jsdom popover content is in the DOM but not "visible" in the
|
|
722
|
+
// accessibility tree; use hidden: true to find it.
|
|
723
|
+
const h = {hidden: true} as const;
|
|
724
|
+
|
|
725
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
726
|
+
render(
|
|
727
|
+
<DateInput
|
|
728
|
+
label="Date"
|
|
729
|
+
isDisabled
|
|
730
|
+
disabledMessage="You need the Editor role"
|
|
731
|
+
/>,
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
const container = screen.getByRole('combobox')
|
|
735
|
+
.parentElement as HTMLElement;
|
|
736
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
737
|
+
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
738
|
+
|
|
739
|
+
fireEvent.mouseEnter(container);
|
|
740
|
+
await waitFor(() => {
|
|
741
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
fireEvent.mouseLeave(container);
|
|
745
|
+
await waitFor(() => {
|
|
746
|
+
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
751
|
+
const user = userEvent.setup();
|
|
752
|
+
render(
|
|
753
|
+
<DateInput
|
|
754
|
+
label="Date"
|
|
755
|
+
isDisabled
|
|
756
|
+
disabledMessage="You need the Editor role"
|
|
757
|
+
/>,
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
761
|
+
await user.tab();
|
|
762
|
+
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
763
|
+
await waitFor(() => {
|
|
764
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it('does not render a tooltip when not disabled', () => {
|
|
769
|
+
render(
|
|
770
|
+
<DateInput label="Date" disabledMessage="You need the Editor role" />,
|
|
771
|
+
);
|
|
772
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
776
|
+
render(<DateInput label="Date" isDisabled />);
|
|
777
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
781
|
+
render(
|
|
782
|
+
<DateInput
|
|
783
|
+
label="Date"
|
|
784
|
+
isDisabled
|
|
785
|
+
disabledMessage="You need the Editor role"
|
|
786
|
+
/>,
|
|
787
|
+
);
|
|
788
|
+
const input = screen.getByRole('combobox');
|
|
789
|
+
expect(input).not.toBeDisabled();
|
|
790
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
791
|
+
expect(input).toHaveAttribute('readonly');
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
it('links the reason tooltip from the input via aria-describedby', () => {
|
|
795
|
+
render(
|
|
796
|
+
<DateInput
|
|
797
|
+
label="Date"
|
|
798
|
+
isDisabled
|
|
799
|
+
disabledMessage="You need the Editor role"
|
|
800
|
+
/>,
|
|
801
|
+
);
|
|
802
|
+
const input = screen.getByRole('combobox');
|
|
803
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
804
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
it('blocks value changes and opening while focusable-disabled', async () => {
|
|
808
|
+
const user = userEvent.setup();
|
|
809
|
+
const onChange = vi.fn();
|
|
810
|
+
render(
|
|
811
|
+
<DateInput
|
|
812
|
+
label="Date"
|
|
813
|
+
onChange={onChange}
|
|
814
|
+
isDisabled
|
|
815
|
+
disabledMessage="You need the Editor role"
|
|
816
|
+
/>,
|
|
817
|
+
);
|
|
818
|
+
|
|
819
|
+
const input = screen.getByRole('combobox');
|
|
820
|
+
await user.click(input);
|
|
821
|
+
await user.type(input, '2026-03-15');
|
|
822
|
+
expect(input).toHaveValue('');
|
|
823
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
824
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it('remains natively disabled when disabled without a reason', () => {
|
|
828
|
+
render(<DateInput label="Date" isDisabled />);
|
|
829
|
+
const input = screen.getByRole('combobox');
|
|
830
|
+
expect(input).toBeDisabled();
|
|
831
|
+
expect(input).not.toHaveAttribute('aria-disabled');
|
|
832
|
+
});
|
|
833
|
+
});
|
|
588
834
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DateInput.tsx
|
|
7
|
-
* @input Uses React, useId, useState, useCallback, useRef, Field, Icon, Calendar, usePopover
|
|
7
|
+
* @input Uses React, useId, useState, useCallback, useRef, Field, Icon, Calendar, usePopover, InputGroupContext
|
|
8
8
|
* @output Exports DateInput component, DateInputProps
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by DateInput.test.tsx
|
|
10
10
|
*
|
|
@@ -44,15 +44,16 @@ import {
|
|
|
44
44
|
inputStatusFocusWithinStyles,
|
|
45
45
|
} from '../Field';
|
|
46
46
|
import {Icon} from '../Icon';
|
|
47
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
48
|
+
import {useInputGroup} from '../InputGroup/InputGroupContext';
|
|
49
|
+
import {groupStyles} from '../InputGroup/groupStyles';
|
|
50
|
+
import {useSize} from '../SizeContext/SizeContext';
|
|
47
51
|
import {Spinner} from '../Spinner';
|
|
48
|
-
import {
|
|
49
|
-
Calendar,
|
|
50
|
-
type ISODateString,
|
|
51
|
-
type CalendarHandle,
|
|
52
|
-
} from '../Calendar';
|
|
52
|
+
import {Calendar, type ISODateString, type CalendarHandle} from '../Calendar';
|
|
53
53
|
import {useCalendarConstraints} from '../Calendar/hooks';
|
|
54
54
|
import {usePopover} from '../Popover';
|
|
55
|
-
import {
|
|
55
|
+
import {useTooltip} from '../Tooltip';
|
|
56
|
+
import {getInputARIA, parseDateInput} from '../utils';
|
|
56
57
|
import {
|
|
57
58
|
plainDateFromISO,
|
|
58
59
|
plainDateToISO,
|
|
@@ -177,6 +178,29 @@ export interface DateInputProps extends Omit<
|
|
|
177
178
|
*/
|
|
178
179
|
isDisabled?: boolean;
|
|
179
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Explains why the input is disabled. When set together with
|
|
183
|
+
* `isDisabled`, the input shows a tooltip with this text on hover and
|
|
184
|
+
* keyboard focus, and the field stays focusable (via `aria-disabled`)
|
|
185
|
+
* so the reason is discoverable by keyboard and assistive technology.
|
|
186
|
+
* Typing and calendar activation stay blocked.
|
|
187
|
+
*
|
|
188
|
+
* Use this instead of wrapping a disabled input in `Tooltip` — disabled
|
|
189
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```
|
|
193
|
+
* <DateInput
|
|
194
|
+
* label="Event date"
|
|
195
|
+
* value={date}
|
|
196
|
+
* onChange={setDate}
|
|
197
|
+
* isDisabled
|
|
198
|
+
* disabledMessage="You need the Editor role to change this"
|
|
199
|
+
* />
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
disabledMessage?: string;
|
|
203
|
+
|
|
180
204
|
/**
|
|
181
205
|
* The selected date in ISO format (YYYY-MM-DD).
|
|
182
206
|
*/
|
|
@@ -279,6 +303,7 @@ export function DateInput({
|
|
|
279
303
|
isOptional = false,
|
|
280
304
|
isRequired = false,
|
|
281
305
|
isDisabled = false,
|
|
306
|
+
disabledMessage,
|
|
282
307
|
value,
|
|
283
308
|
onChange,
|
|
284
309
|
changeAction,
|
|
@@ -287,7 +312,7 @@ export function DateInput({
|
|
|
287
312
|
max,
|
|
288
313
|
dateConstraints,
|
|
289
314
|
placeholder = 'Select a date',
|
|
290
|
-
size
|
|
315
|
+
size: sizeProp,
|
|
291
316
|
status,
|
|
292
317
|
labelTooltip,
|
|
293
318
|
hasClear = false,
|
|
@@ -299,18 +324,37 @@ export function DateInput({
|
|
|
299
324
|
ref,
|
|
300
325
|
...rest
|
|
301
326
|
}: DateInputProps) {
|
|
327
|
+
const size = useSize(sizeProp, 'md');
|
|
302
328
|
const id = useId();
|
|
329
|
+
const inputLabelID = useId();
|
|
303
330
|
const descriptionID = useId();
|
|
304
331
|
const statusMessageID = useId();
|
|
305
332
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
306
333
|
const calendarRef = useRef<CalendarHandle | null>(null);
|
|
307
334
|
const lastFiredValueRef = useRef<ISODateString | undefined>(undefined);
|
|
335
|
+
const inputGroup = useInputGroup();
|
|
308
336
|
|
|
309
337
|
const [, startTransition] = useTransition();
|
|
310
338
|
const [optimisticValue, setOptimisticValue] = useOptimistic(value);
|
|
311
339
|
const isBusy = isLoading || optimisticValue !== value;
|
|
312
340
|
const isEffectivelyDisabled = isDisabled || isBusy;
|
|
313
341
|
|
|
342
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
343
|
+
// tooltip listeners attach to the input container (which already exists) and
|
|
344
|
+
// the text input stays perceivable via aria-disabled instead of the disabled
|
|
345
|
+
// attribute. Typing is blocked with readOnly and value mutation guards;
|
|
346
|
+
// calendar activation is blocked by the isEffectivelyDisabled guards. Only
|
|
347
|
+
// the persistent isDisabled state (not the transient busy state) surfaces a
|
|
348
|
+
// reason.
|
|
349
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
350
|
+
const disabledMessageTooltip = useTooltip({
|
|
351
|
+
placement: 'above',
|
|
352
|
+
// The container div is not naturally focusable; focusin bubbles up from
|
|
353
|
+
// the input, so always attach focus listeners.
|
|
354
|
+
focusTrigger: 'always',
|
|
355
|
+
isEnabled: showsDisabledMessage,
|
|
356
|
+
});
|
|
357
|
+
|
|
314
358
|
// Status icon mapping
|
|
315
359
|
const statusIconMap: Record<InputStatusType, IconName> = {
|
|
316
360
|
warning: 'warning',
|
|
@@ -330,13 +374,15 @@ export function DateInput({
|
|
|
330
374
|
// Constraint checking for text input validation (reuses calendar logic)
|
|
331
375
|
const {isDateDisabled} = useCalendarConstraints({min, max, dateConstraints});
|
|
332
376
|
|
|
333
|
-
const ariaDescribedBy =
|
|
377
|
+
const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
|
|
378
|
+
inputLabelID,
|
|
334
379
|
[
|
|
335
380
|
description ? descriptionID : null,
|
|
336
381
|
status?.message ? statusMessageID : null,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
382
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
383
|
+
],
|
|
384
|
+
inputGroup,
|
|
385
|
+
);
|
|
340
386
|
|
|
341
387
|
// Pending input while user is typing (null = show formatted value)
|
|
342
388
|
const [pendingInput, setPendingInput] = useState<string | null>(null);
|
|
@@ -428,6 +474,11 @@ export function DateInput({
|
|
|
428
474
|
// Handle input text change - update immediately if valid and allowed
|
|
429
475
|
const handleInputChange = useCallback(
|
|
430
476
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
477
|
+
// With a disabledMessage the input drops `disabled` for focusability, so
|
|
478
|
+
// guard value mutation explicitly (readOnly also blocks typing).
|
|
479
|
+
if (isEffectivelyDisabled) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
431
482
|
const newValue = e.target.value;
|
|
432
483
|
setPendingInput(newValue);
|
|
433
484
|
|
|
@@ -445,7 +496,7 @@ export function DateInput({
|
|
|
445
496
|
calendarRef.current?.navigateTo(parsedISO);
|
|
446
497
|
}
|
|
447
498
|
},
|
|
448
|
-
[value, fireChange, isDateDisabled],
|
|
499
|
+
[value, fireChange, isDateDisabled, isEffectivelyDisabled],
|
|
449
500
|
);
|
|
450
501
|
|
|
451
502
|
// Commit pending input (shared by blur and Enter key)
|
|
@@ -483,14 +534,144 @@ export function DateInput({
|
|
|
483
534
|
if (e.key === 'Escape' && popover.isOpen) {
|
|
484
535
|
e.preventDefault();
|
|
485
536
|
popover.hide();
|
|
537
|
+
} else if (
|
|
538
|
+
(e.key === 'ArrowDown' || (e.altKey && e.key === 'ArrowDown')) &&
|
|
539
|
+
!popover.isOpen
|
|
540
|
+
) {
|
|
541
|
+
// APG combobox: ArrowDown (and Alt+ArrowDown) opens the calendar
|
|
542
|
+
// popover from the keyboard, keeping focus in the input (forms-13).
|
|
543
|
+
e.preventDefault();
|
|
544
|
+
if (!isEffectivelyDisabled) {
|
|
545
|
+
popover.show({skipAutoFocus: true});
|
|
546
|
+
}
|
|
486
547
|
} else if (e.key === 'Enter') {
|
|
487
548
|
e.preventDefault();
|
|
488
549
|
commitPendingInput();
|
|
489
550
|
}
|
|
490
551
|
},
|
|
491
|
-
[popover, commitPendingInput],
|
|
552
|
+
[popover, commitPendingInput, isEffectivelyDisabled],
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const inputWrapper = (
|
|
556
|
+
<div
|
|
557
|
+
ref={el => {
|
|
558
|
+
popover.triggerRef(el);
|
|
559
|
+
// Anchor + hover/focus listeners for the disabled-message tooltip.
|
|
560
|
+
// Handlers are gated internally by isEnabled, and anchor names
|
|
561
|
+
// compose, so attaching unconditionally is safe.
|
|
562
|
+
disabledMessageTooltip.ref(el);
|
|
563
|
+
}}
|
|
564
|
+
{...rest}
|
|
565
|
+
{...mergeProps(
|
|
566
|
+
themeProps('date-input', {size, status: status?.type ?? null}),
|
|
567
|
+
stylex.props(
|
|
568
|
+
inputWrapperStyles.base,
|
|
569
|
+
sizeStyles[size],
|
|
570
|
+
isEffectivelyDisabled && inputWrapperStyles.disabled,
|
|
571
|
+
status && inputStatusBorderStyles[status.type],
|
|
572
|
+
status && inputStatusHoverShadowStyles[status.type],
|
|
573
|
+
status && inputStatusFocusWithinStyles[status.type],
|
|
574
|
+
inputGroup && groupStyles.inGroup,
|
|
575
|
+
xstyle,
|
|
576
|
+
),
|
|
577
|
+
className,
|
|
578
|
+
style,
|
|
579
|
+
)}>
|
|
580
|
+
{inputGroup && <VisuallyHidden id={inputLabelID}>{label}</VisuallyHidden>}
|
|
581
|
+
<button
|
|
582
|
+
type="button"
|
|
583
|
+
onClick={handleToggle}
|
|
584
|
+
disabled={isEffectivelyDisabled}
|
|
585
|
+
aria-label={popover.isOpen ? 'Close calendar' : 'Open calendar'}
|
|
586
|
+
{...stylex.props(
|
|
587
|
+
styles.iconButton,
|
|
588
|
+
isEffectivelyDisabled && styles.iconButtonDisabled,
|
|
589
|
+
)}>
|
|
590
|
+
<Icon icon="calendar" size="sm" color="secondary" />
|
|
591
|
+
</button>
|
|
592
|
+
<input
|
|
593
|
+
ref={mergeRefs(ref, inputRef)}
|
|
594
|
+
id={id}
|
|
595
|
+
type="text"
|
|
596
|
+
role="combobox"
|
|
597
|
+
value={displayValue}
|
|
598
|
+
onChange={handleInputChange}
|
|
599
|
+
onBlur={handleBlur}
|
|
600
|
+
onClick={handleInputClick}
|
|
601
|
+
onKeyDown={handleInputKeyDown}
|
|
602
|
+
placeholder={placeholder}
|
|
603
|
+
// With a disabledMessage the input keeps focusability via
|
|
604
|
+
// aria-disabled so the reason is focus-discoverable; typing is
|
|
605
|
+
// blocked with readOnly and the mutation guards, and calendar
|
|
606
|
+
// activation is blocked by the isEffectivelyDisabled guards.
|
|
607
|
+
disabled={isEffectivelyDisabled && !showsDisabledMessage}
|
|
608
|
+
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
609
|
+
readOnly={showsDisabledMessage || undefined}
|
|
610
|
+
aria-labelledby={ariaLabelledBy}
|
|
611
|
+
aria-describedby={ariaDescribedBy}
|
|
612
|
+
aria-required={isRequired === true ? 'true' : undefined}
|
|
613
|
+
aria-invalid={
|
|
614
|
+
status?.type === 'error' || !isInputValid ? 'true' : undefined
|
|
615
|
+
}
|
|
616
|
+
aria-busy={isBusy || undefined}
|
|
617
|
+
aria-expanded={popover.isOpen}
|
|
618
|
+
aria-haspopup="dialog"
|
|
619
|
+
aria-controls={popover.isOpen ? popover.id : undefined}
|
|
620
|
+
aria-autocomplete="none"
|
|
621
|
+
autoComplete="off"
|
|
622
|
+
{...stylex.props(
|
|
623
|
+
styles.input,
|
|
624
|
+
isEffectivelyDisabled && styles.inputDisabled,
|
|
625
|
+
!isInputValid && styles.inputInvalid,
|
|
626
|
+
)}
|
|
627
|
+
/>
|
|
628
|
+
{/*
|
|
629
|
+
Live region announcing invalid typed input to assistive technology.
|
|
630
|
+
The value silently reverts on blur, so without this a screen-reader
|
|
631
|
+
user would get no feedback that their entry was rejected (WCAG 3.3.1).
|
|
632
|
+
*/}
|
|
633
|
+
<VisuallyHidden as="div" role="alert" aria-live="assertive">
|
|
634
|
+
{!isInputValid ? 'Invalid date' : ''}
|
|
635
|
+
</VisuallyHidden>
|
|
636
|
+
{hasClear && value !== undefined && !isEffectivelyDisabled && (
|
|
637
|
+
<button
|
|
638
|
+
type="button"
|
|
639
|
+
onClick={handleClear}
|
|
640
|
+
aria-label={`Clear ${label}`}
|
|
641
|
+
{...stylex.props(styles.iconButton)}>
|
|
642
|
+
<Icon icon="close" size="sm" color="secondary" />
|
|
643
|
+
</button>
|
|
644
|
+
)}
|
|
645
|
+
{isBusy && <Spinner size="sm" />}
|
|
646
|
+
{status && !inputGroup && (
|
|
647
|
+
<Icon
|
|
648
|
+
icon={statusIconMap[status.type]}
|
|
649
|
+
size="md"
|
|
650
|
+
color={statusIconColorMap[status.type]}
|
|
651
|
+
/>
|
|
652
|
+
)}
|
|
653
|
+
{popover.render(
|
|
654
|
+
<Calendar
|
|
655
|
+
handleRef={calendarRef}
|
|
656
|
+
mode="single"
|
|
657
|
+
value={optimisticValue}
|
|
658
|
+
onChange={handleDateSelect}
|
|
659
|
+
min={min}
|
|
660
|
+
max={max}
|
|
661
|
+
dateConstraints={dateConstraints}
|
|
662
|
+
numberOfMonths={numberOfMonths}
|
|
663
|
+
/>,
|
|
664
|
+
{placement: 'below', alignment: 'start'},
|
|
665
|
+
)}
|
|
666
|
+
{showsDisabledMessage &&
|
|
667
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
668
|
+
</div>
|
|
492
669
|
);
|
|
493
670
|
|
|
671
|
+
if (inputGroup) {
|
|
672
|
+
return inputWrapper;
|
|
673
|
+
}
|
|
674
|
+
|
|
494
675
|
return (
|
|
495
676
|
<Field
|
|
496
677
|
label={label}
|
|
@@ -512,92 +693,7 @@ export function DateInput({
|
|
|
512
693
|
}
|
|
513
694
|
labelTooltip={labelTooltip}
|
|
514
695
|
width={width}>
|
|
515
|
-
|
|
516
|
-
ref={popover.triggerRef}
|
|
517
|
-
{...rest}
|
|
518
|
-
{...mergeProps(
|
|
519
|
-
themeProps('date-input', {size, status: status?.type ?? null}),
|
|
520
|
-
stylex.props(
|
|
521
|
-
inputWrapperStyles.base,
|
|
522
|
-
sizeStyles[size],
|
|
523
|
-
isEffectivelyDisabled && inputWrapperStyles.disabled,
|
|
524
|
-
status && inputStatusBorderStyles[status.type],
|
|
525
|
-
status && inputStatusHoverShadowStyles[status.type],
|
|
526
|
-
status && inputStatusFocusWithinStyles[status.type],
|
|
527
|
-
xstyle,
|
|
528
|
-
),
|
|
529
|
-
className,
|
|
530
|
-
style,
|
|
531
|
-
)}>
|
|
532
|
-
<button
|
|
533
|
-
type="button"
|
|
534
|
-
onClick={handleToggle}
|
|
535
|
-
disabled={isEffectivelyDisabled}
|
|
536
|
-
aria-label={popover.isOpen ? 'Close calendar' : 'Open calendar'}
|
|
537
|
-
{...stylex.props(
|
|
538
|
-
styles.iconButton,
|
|
539
|
-
isEffectivelyDisabled && styles.iconButtonDisabled,
|
|
540
|
-
)}>
|
|
541
|
-
<Icon icon="calendar" size="sm" color="secondary" />
|
|
542
|
-
</button>
|
|
543
|
-
<input
|
|
544
|
-
ref={mergeRefs(ref, inputRef)}
|
|
545
|
-
id={id}
|
|
546
|
-
type="text"
|
|
547
|
-
role="combobox"
|
|
548
|
-
value={displayValue}
|
|
549
|
-
onChange={handleInputChange}
|
|
550
|
-
onBlur={handleBlur}
|
|
551
|
-
onClick={handleInputClick}
|
|
552
|
-
onKeyDown={handleInputKeyDown}
|
|
553
|
-
placeholder={placeholder}
|
|
554
|
-
disabled={isEffectivelyDisabled}
|
|
555
|
-
aria-describedby={ariaDescribedBy}
|
|
556
|
-
aria-required={isRequired === true ? 'true' : undefined}
|
|
557
|
-
aria-invalid={status?.type === 'error' ? 'true' : undefined}
|
|
558
|
-
aria-busy={isBusy || undefined}
|
|
559
|
-
aria-expanded={popover.isOpen}
|
|
560
|
-
aria-haspopup="dialog"
|
|
561
|
-
aria-controls={popover.isOpen ? popover.id : undefined}
|
|
562
|
-
aria-autocomplete="none"
|
|
563
|
-
autoComplete="off"
|
|
564
|
-
{...stylex.props(
|
|
565
|
-
styles.input,
|
|
566
|
-
isEffectivelyDisabled && styles.inputDisabled,
|
|
567
|
-
!isInputValid && styles.inputInvalid,
|
|
568
|
-
)}
|
|
569
|
-
/>
|
|
570
|
-
{hasClear && value !== undefined && !isEffectivelyDisabled && (
|
|
571
|
-
<button
|
|
572
|
-
type="button"
|
|
573
|
-
onClick={handleClear}
|
|
574
|
-
aria-label={`Clear ${label}`}
|
|
575
|
-
{...stylex.props(styles.iconButton)}>
|
|
576
|
-
<Icon icon="close" size="sm" color="secondary" />
|
|
577
|
-
</button>
|
|
578
|
-
)}
|
|
579
|
-
{isBusy && <Spinner size="sm" />}
|
|
580
|
-
{status && (
|
|
581
|
-
<Icon
|
|
582
|
-
icon={statusIconMap[status.type]}
|
|
583
|
-
size="md"
|
|
584
|
-
color={statusIconColorMap[status.type]}
|
|
585
|
-
/>
|
|
586
|
-
)}
|
|
587
|
-
</div>
|
|
588
|
-
{popover.render(
|
|
589
|
-
<Calendar
|
|
590
|
-
handleRef={calendarRef}
|
|
591
|
-
mode="single"
|
|
592
|
-
value={optimisticValue}
|
|
593
|
-
onChange={handleDateSelect}
|
|
594
|
-
min={min}
|
|
595
|
-
max={max}
|
|
596
|
-
dateConstraints={dateConstraints}
|
|
597
|
-
numberOfMonths={numberOfMonths}
|
|
598
|
-
/>,
|
|
599
|
-
{placement: 'below', alignment: 'start'},
|
|
600
|
-
)}
|
|
696
|
+
{inputWrapper}
|
|
601
697
|
</Field>
|
|
602
698
|
);
|
|
603
699
|
}
|