@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
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
* SYNC: When TimeInput.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 {TimeInput} from './TimeInput';
|
|
16
|
+
import {InputGroup, InputGroupText} from '../InputGroup';
|
|
16
17
|
import type {ISOTimeString} from '../utils';
|
|
17
18
|
|
|
18
19
|
describe('TimeInput', () => {
|
|
@@ -23,11 +24,7 @@ describe('TimeInput', () => {
|
|
|
23
24
|
|
|
24
25
|
it('renders with placeholder', () => {
|
|
25
26
|
render(
|
|
26
|
-
<TimeInput
|
|
27
|
-
label="Time"
|
|
28
|
-
onChange={() => {}}
|
|
29
|
-
placeholder="Pick a time"
|
|
30
|
-
/>,
|
|
27
|
+
<TimeInput label="Time" onChange={() => {}} placeholder="Pick a time" />,
|
|
31
28
|
);
|
|
32
29
|
expect(screen.getByPlaceholderText('Pick a time')).toBeInTheDocument();
|
|
33
30
|
});
|
|
@@ -210,4 +207,255 @@ describe('TimeInput', () => {
|
|
|
210
207
|
fireEvent.click(wrapper);
|
|
211
208
|
expect(input).toHaveFocus();
|
|
212
209
|
});
|
|
210
|
+
describe('InputGroup integration', () => {
|
|
211
|
+
it('labels grouped TimeInput from the group and inner input labels', () => {
|
|
212
|
+
render(
|
|
213
|
+
<InputGroup label="Schedule" description="Use local time">
|
|
214
|
+
<InputGroupText>Starts</InputGroupText>
|
|
215
|
+
<TimeInput
|
|
216
|
+
label="Start time"
|
|
217
|
+
isLabelHidden
|
|
218
|
+
value={'09:00' as ISOTimeString}
|
|
219
|
+
onChange={() => {}}
|
|
220
|
+
/>
|
|
221
|
+
</InputGroup>,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const group = screen.getByRole('group', {name: 'Schedule'});
|
|
225
|
+
const groupLabelID = group.getAttribute('aria-labelledby');
|
|
226
|
+
const input = screen.getByRole('textbox', {
|
|
227
|
+
name: 'Schedule Start time',
|
|
228
|
+
});
|
|
229
|
+
const labelledByIDs =
|
|
230
|
+
input.getAttribute('aria-labelledby')?.split(' ') ?? [];
|
|
231
|
+
|
|
232
|
+
expect(labelledByIDs).toHaveLength(2);
|
|
233
|
+
expect(labelledByIDs[0]).toBe(groupLabelID);
|
|
234
|
+
expect(document.getElementById(labelledByIDs[1])).toHaveTextContent(
|
|
235
|
+
'Start time',
|
|
236
|
+
);
|
|
237
|
+
expect(input).not.toHaveAttribute('aria-label');
|
|
238
|
+
expect(input).toHaveAttribute(
|
|
239
|
+
'aria-describedby',
|
|
240
|
+
group.getAttribute('aria-describedby'),
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('includes group and local described-by content when grouped', () => {
|
|
245
|
+
render(
|
|
246
|
+
<InputGroup
|
|
247
|
+
label="Schedule"
|
|
248
|
+
description="Use local time"
|
|
249
|
+
status={{type: 'warning', message: 'Schedule is unusual'}}>
|
|
250
|
+
<InputGroupText>Starts</InputGroupText>
|
|
251
|
+
<TimeInput
|
|
252
|
+
label="Start time"
|
|
253
|
+
isLabelHidden
|
|
254
|
+
value={'09:00' as ISOTimeString}
|
|
255
|
+
onChange={() => {}}
|
|
256
|
+
description="Business hours only"
|
|
257
|
+
status={{type: 'error', message: 'Start time is required'}}
|
|
258
|
+
isDisabled
|
|
259
|
+
disabledMessage="Time edits are locked"
|
|
260
|
+
/>
|
|
261
|
+
</InputGroup>,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const input = screen.getByRole('textbox', {
|
|
265
|
+
name: 'Schedule Start time',
|
|
266
|
+
});
|
|
267
|
+
const describedByIDs =
|
|
268
|
+
input.getAttribute('aria-describedby')?.split(' ') ?? [];
|
|
269
|
+
const describedText = describedByIDs
|
|
270
|
+
.map(id => document.getElementById(id)?.textContent)
|
|
271
|
+
.join(' ');
|
|
272
|
+
|
|
273
|
+
expect(describedText).toContain('Use local time');
|
|
274
|
+
expect(describedText).toContain('Schedule is unusual');
|
|
275
|
+
expect(describedText).toContain('Business hours only');
|
|
276
|
+
expect(describedText).toContain('Start time is required');
|
|
277
|
+
expect(describedText).toContain('Time edits are locked');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('does not render duplicate Field label chrome when grouped', () => {
|
|
281
|
+
render(
|
|
282
|
+
<InputGroup label="Schedule">
|
|
283
|
+
<InputGroupText>Starts</InputGroupText>
|
|
284
|
+
<TimeInput
|
|
285
|
+
label="Start time"
|
|
286
|
+
isLabelHidden
|
|
287
|
+
value={'09:00' as ISOTimeString}
|
|
288
|
+
onChange={() => {}}
|
|
289
|
+
/>
|
|
290
|
+
</InputGroup>,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
expect(screen.getByText('Schedule')).toBeInTheDocument();
|
|
294
|
+
expect(screen.getByText('Start time')).toBeInTheDocument();
|
|
295
|
+
expect(screen.getByText('Start time').tagName).toBe('SPAN');
|
|
296
|
+
expect(document.querySelector('label')).toBeNull();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('suppresses the local status icon when grouped', () => {
|
|
300
|
+
const {container} = render(
|
|
301
|
+
<InputGroup label="Schedule">
|
|
302
|
+
<InputGroupText>Starts</InputGroupText>
|
|
303
|
+
<TimeInput
|
|
304
|
+
label="Start time"
|
|
305
|
+
isLabelHidden
|
|
306
|
+
value={'09:00' as ISOTimeString}
|
|
307
|
+
onChange={() => {}}
|
|
308
|
+
status={{type: 'error'}}
|
|
309
|
+
/>
|
|
310
|
+
</InputGroup>,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// The clock icon remains, but the trailing status icon is suppressed in
|
|
314
|
+
// grouped mode so the shared InputGroup border/status treatment is not
|
|
315
|
+
// duplicated.
|
|
316
|
+
expect(container.querySelectorAll('svg')).toHaveLength(1);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('disabledMessage', () => {
|
|
321
|
+
// jsdom does not implement the Popover API used by the tooltip, so mock
|
|
322
|
+
// showPopover/hidePopover to toggle a `popover-open` attribute the tests
|
|
323
|
+
// can assert on.
|
|
324
|
+
beforeEach(() => {
|
|
325
|
+
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
326
|
+
this.setAttribute('popover-open', '');
|
|
327
|
+
});
|
|
328
|
+
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
329
|
+
this.removeAttribute('popover-open');
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// jsdom popover content is in the DOM but not "visible" in the
|
|
334
|
+
// accessibility tree; use hidden: true to find it.
|
|
335
|
+
const h = {hidden: true} as const;
|
|
336
|
+
|
|
337
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
338
|
+
render(
|
|
339
|
+
<TimeInput
|
|
340
|
+
label="Time"
|
|
341
|
+
isDisabled
|
|
342
|
+
disabledMessage="You need the Editor role"
|
|
343
|
+
/>,
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
const container = screen.getByLabelText('Time')
|
|
347
|
+
.parentElement as HTMLElement;
|
|
348
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
349
|
+
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
350
|
+
|
|
351
|
+
fireEvent.mouseEnter(container);
|
|
352
|
+
await waitFor(() => {
|
|
353
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
fireEvent.mouseLeave(container);
|
|
357
|
+
await waitFor(() => {
|
|
358
|
+
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
363
|
+
const user = userEvent.setup();
|
|
364
|
+
render(
|
|
365
|
+
<TimeInput
|
|
366
|
+
label="Time"
|
|
367
|
+
isDisabled
|
|
368
|
+
disabledMessage="You need the Editor role"
|
|
369
|
+
/>,
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
373
|
+
await user.tab();
|
|
374
|
+
expect(screen.getByLabelText('Time')).toHaveFocus();
|
|
375
|
+
await waitFor(() => {
|
|
376
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('does not render a tooltip when not disabled', () => {
|
|
381
|
+
render(
|
|
382
|
+
<TimeInput label="Time" disabledMessage="You need the Editor role" />,
|
|
383
|
+
);
|
|
384
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
388
|
+
render(<TimeInput label="Time" isDisabled />);
|
|
389
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
393
|
+
render(
|
|
394
|
+
<TimeInput
|
|
395
|
+
label="Time"
|
|
396
|
+
isDisabled
|
|
397
|
+
disabledMessage="You need the Editor role"
|
|
398
|
+
/>,
|
|
399
|
+
);
|
|
400
|
+
const input = screen.getByLabelText('Time');
|
|
401
|
+
expect(input).not.toBeDisabled();
|
|
402
|
+
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
403
|
+
expect(input).toHaveAttribute('readonly');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('links the reason tooltip from the input via aria-describedby', () => {
|
|
407
|
+
render(
|
|
408
|
+
<TimeInput
|
|
409
|
+
label="Time"
|
|
410
|
+
isDisabled
|
|
411
|
+
disabledMessage="You need the Editor role"
|
|
412
|
+
/>,
|
|
413
|
+
);
|
|
414
|
+
const input = screen.getByLabelText('Time');
|
|
415
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
416
|
+
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('blocks typing and arrow-key changes while focusable-disabled', async () => {
|
|
420
|
+
const user = userEvent.setup();
|
|
421
|
+
const onChange = vi.fn();
|
|
422
|
+
render(
|
|
423
|
+
<TimeInput
|
|
424
|
+
label="Time"
|
|
425
|
+
onChange={onChange}
|
|
426
|
+
isDisabled
|
|
427
|
+
disabledMessage="You need the Editor role"
|
|
428
|
+
/>,
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
const input = screen.getByLabelText('Time');
|
|
432
|
+
await user.type(input, '2:30 PM');
|
|
433
|
+
expect(input).toHaveValue('');
|
|
434
|
+
input.focus();
|
|
435
|
+
fireEvent.keyDown(input, {key: 'ArrowUp'});
|
|
436
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('remains natively disabled when disabled without a reason', () => {
|
|
440
|
+
render(<TimeInput label="Time" isDisabled />);
|
|
441
|
+
const input = screen.getByLabelText('Time');
|
|
442
|
+
expect(input).toBeDisabled();
|
|
443
|
+
expect(input).not.toHaveAttribute('aria-disabled');
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('does not swap in the format-hint placeholder on focus while disabled', () => {
|
|
447
|
+
render(
|
|
448
|
+
<TimeInput
|
|
449
|
+
label="Time"
|
|
450
|
+
isDisabled
|
|
451
|
+
disabledMessage="You need the Editor role"
|
|
452
|
+
placeholder="Select a time"
|
|
453
|
+
/>,
|
|
454
|
+
);
|
|
455
|
+
const input = screen.getByLabelText('Time');
|
|
456
|
+
input.focus();
|
|
457
|
+
fireEvent.focus(input);
|
|
458
|
+
expect(input).toHaveAttribute('placeholder', 'Select a time');
|
|
459
|
+
});
|
|
460
|
+
});
|
|
213
461
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file TimeInput.tsx
|
|
7
|
-
* @input Uses React, useId, useState,
|
|
7
|
+
* @input Uses React, useId, useState, useCallback, useRef, Field, Icon, InputGroupContext
|
|
8
8
|
* @output Exports TimeInput component, TimeInputProps
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by TimeInput.test.tsx
|
|
10
10
|
*
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
} from '../Field';
|
|
49
49
|
import {Icon} from '../Icon';
|
|
50
50
|
import {Spinner} from '../Spinner';
|
|
51
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
51
52
|
import {
|
|
52
53
|
type ISOTimeString,
|
|
53
54
|
parseTimeInput,
|
|
@@ -58,11 +59,15 @@ import {
|
|
|
58
59
|
isTimeInRange,
|
|
59
60
|
mergeProps,
|
|
60
61
|
mergeRefs,
|
|
62
|
+
getInputARIA,
|
|
61
63
|
} from '../utils';
|
|
62
64
|
import type {BaseProps} from '../BaseProps';
|
|
63
65
|
import type {SizeValue} from '../utils/types';
|
|
64
66
|
import {useSize} from '../SizeContext/SizeContext';
|
|
65
67
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
68
|
+
import {useInputGroup} from '../InputGroup/InputGroupContext';
|
|
69
|
+
import {groupStyles} from '../InputGroup/groupStyles';
|
|
70
|
+
import {useTooltip} from '../Tooltip';
|
|
66
71
|
import {themeProps} from '../utils/themeProps';
|
|
67
72
|
|
|
68
73
|
const styles = stylex.create({
|
|
@@ -183,6 +188,29 @@ export interface TimeInputProps extends Omit<
|
|
|
183
188
|
*/
|
|
184
189
|
isDisabled?: boolean;
|
|
185
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Explains why the input is disabled. When set together with
|
|
193
|
+
* `isDisabled`, the input shows a tooltip with this text on hover and
|
|
194
|
+
* keyboard focus, and the field stays focusable (via `aria-disabled`)
|
|
195
|
+
* so the reason is discoverable by keyboard and assistive technology.
|
|
196
|
+
* Typing and arrow-key adjustment stay blocked.
|
|
197
|
+
*
|
|
198
|
+
* Use this instead of wrapping a disabled input in `Tooltip` — disabled
|
|
199
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```
|
|
203
|
+
* <TimeInput
|
|
204
|
+
* label="Start time"
|
|
205
|
+
* value={time}
|
|
206
|
+
* onChange={setTime}
|
|
207
|
+
* isDisabled
|
|
208
|
+
* disabledMessage="You need the Editor role to change this"
|
|
209
|
+
* />
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
disabledMessage?: string;
|
|
213
|
+
|
|
186
214
|
/**
|
|
187
215
|
* The selected time in ISO format (HH:MM or HH:MM:SS).
|
|
188
216
|
*/
|
|
@@ -301,6 +329,7 @@ export function TimeInput({
|
|
|
301
329
|
isOptional = false,
|
|
302
330
|
isRequired = false,
|
|
303
331
|
isDisabled = false,
|
|
332
|
+
disabledMessage,
|
|
304
333
|
value,
|
|
305
334
|
onChange,
|
|
306
335
|
changeAction,
|
|
@@ -325,15 +354,30 @@ export function TimeInput({
|
|
|
325
354
|
const size = useSize(sizeProp, 'md');
|
|
326
355
|
|
|
327
356
|
const id = useId();
|
|
357
|
+
const inputLabelID = useId();
|
|
328
358
|
const descriptionID = useId();
|
|
329
359
|
const statusMessageID = useId();
|
|
330
360
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
331
361
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
362
|
+
const inputGroup = useInputGroup();
|
|
332
363
|
|
|
333
364
|
const [, startTransition] = useTransition();
|
|
334
365
|
const [optimisticValue, setOptimisticValue] = useOptimistic(value);
|
|
335
366
|
const isBusy = isLoading || optimisticValue !== value;
|
|
336
367
|
|
|
368
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
369
|
+
// tooltip listeners attach to the input container (which already exists) and
|
|
370
|
+
// the input stays perceivable via aria-disabled instead of the disabled
|
|
371
|
+
// attribute. Typing is blocked with readOnly and value mutation guards.
|
|
372
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
373
|
+
const disabledMessageTooltip = useTooltip({
|
|
374
|
+
placement: 'above',
|
|
375
|
+
// The container div is not naturally focusable; focusin bubbles up from
|
|
376
|
+
// the input, so always attach focus listeners.
|
|
377
|
+
focusTrigger: 'always',
|
|
378
|
+
isEnabled: showsDisabledMessage,
|
|
379
|
+
});
|
|
380
|
+
|
|
337
381
|
// Status icon mapping
|
|
338
382
|
const statusIconMap: Record<InputStatusType, IconName> = {
|
|
339
383
|
warning: 'warning',
|
|
@@ -350,13 +394,15 @@ export function TimeInput({
|
|
|
350
394
|
success: 'success',
|
|
351
395
|
};
|
|
352
396
|
|
|
353
|
-
const ariaDescribedBy =
|
|
397
|
+
const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
|
|
398
|
+
inputLabelID,
|
|
354
399
|
[
|
|
355
400
|
description ? descriptionID : null,
|
|
356
401
|
status?.message ? statusMessageID : null,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
402
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
403
|
+
],
|
|
404
|
+
inputGroup,
|
|
405
|
+
);
|
|
360
406
|
|
|
361
407
|
// Pending input while user is typing (null = show formatted value)
|
|
362
408
|
const [pendingInput, setPendingInput] = useState<string | null>(null);
|
|
@@ -415,6 +461,11 @@ export function TimeInput({
|
|
|
415
461
|
// Handle input text change - update immediately if valid
|
|
416
462
|
const handleInputChange = useCallback(
|
|
417
463
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
464
|
+
// With a disabledMessage the input drops `disabled` for focusability, so
|
|
465
|
+
// guard value mutation explicitly (readOnly also blocks typing).
|
|
466
|
+
if (isDisabled) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
418
469
|
const newValue = e.target.value;
|
|
419
470
|
setPendingInput(newValue);
|
|
420
471
|
|
|
@@ -424,13 +475,19 @@ export function TimeInput({
|
|
|
424
475
|
fireChange(parsed);
|
|
425
476
|
}
|
|
426
477
|
},
|
|
427
|
-
[hasSeconds, min, max, value, fireChange],
|
|
478
|
+
[hasSeconds, min, max, value, fireChange, isDisabled],
|
|
428
479
|
);
|
|
429
480
|
|
|
430
481
|
// Handle focus
|
|
431
482
|
const handleFocus = useCallback(() => {
|
|
483
|
+
// A disabled input stays focusable (via aria-disabled) so its reason is
|
|
484
|
+
// discoverable, but it must not present editing affordances — keep the
|
|
485
|
+
// static placeholder rather than swapping in the format hint.
|
|
486
|
+
if (isDisabled) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
432
489
|
setIsFocused(true);
|
|
433
|
-
}, []);
|
|
490
|
+
}, [isDisabled]);
|
|
434
491
|
|
|
435
492
|
// Handle blur - validate and clear pending input
|
|
436
493
|
const handleBlur = useCallback(
|
|
@@ -466,6 +523,11 @@ export function TimeInput({
|
|
|
466
523
|
// Handle keyboard navigation on input
|
|
467
524
|
const handleInputKeyDown = useCallback(
|
|
468
525
|
(e: KeyboardEvent<HTMLInputElement>) => {
|
|
526
|
+
// Arrow-key adjustment mutates the value; block it while showing a
|
|
527
|
+
// disabled reason (the input keeps focusability via aria-disabled).
|
|
528
|
+
if (isDisabled) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
469
531
|
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
470
532
|
e.preventDefault();
|
|
471
533
|
|
|
@@ -492,7 +554,7 @@ export function TimeInput({
|
|
|
492
554
|
}
|
|
493
555
|
}
|
|
494
556
|
},
|
|
495
|
-
[value, hasSeconds, increment, min, max, fireChange],
|
|
557
|
+
[value, hasSeconds, increment, min, max, fireChange, isDisabled],
|
|
496
558
|
);
|
|
497
559
|
|
|
498
560
|
// Handle clear button click
|
|
@@ -509,6 +571,109 @@ export function TimeInput({
|
|
|
509
571
|
disabled: isDisabled,
|
|
510
572
|
});
|
|
511
573
|
|
|
574
|
+
const inputWrapper = (
|
|
575
|
+
<div
|
|
576
|
+
ref={el => {
|
|
577
|
+
containerRef.current = el;
|
|
578
|
+
// Anchor + hover/focus listeners for the disabled-message tooltip.
|
|
579
|
+
// Handlers are gated internally by isEnabled, so attaching
|
|
580
|
+
// unconditionally is safe.
|
|
581
|
+
disabledMessageTooltip.ref(el);
|
|
582
|
+
}}
|
|
583
|
+
onClick={handleWrapperClick}
|
|
584
|
+
onMouseUp={handleWrapperMouseUp}
|
|
585
|
+
{...mergeProps(
|
|
586
|
+
themeProps('time-input', {size, status: status?.type ?? null}),
|
|
587
|
+
stylex.props(
|
|
588
|
+
inputWrapperStyles.base,
|
|
589
|
+
sizeStyles[size],
|
|
590
|
+
isDisabled && inputWrapperStyles.disabled,
|
|
591
|
+
status && inputStatusBorderStyles[status.type],
|
|
592
|
+
status && inputStatusHoverShadowStyles[status.type],
|
|
593
|
+
status && inputStatusFocusWithinStyles[status.type],
|
|
594
|
+
inputGroup && groupStyles.inGroup,
|
|
595
|
+
xstyle,
|
|
596
|
+
),
|
|
597
|
+
className,
|
|
598
|
+
style,
|
|
599
|
+
)}>
|
|
600
|
+
<div {...stylex.props(styles.icon)}>
|
|
601
|
+
<Icon icon="clock" size="sm" color="secondary" />
|
|
602
|
+
</div>
|
|
603
|
+
{inputGroup && <VisuallyHidden id={inputLabelID}>{label}</VisuallyHidden>}
|
|
604
|
+
{inputGroup && description && (
|
|
605
|
+
<VisuallyHidden as="div" id={descriptionID}>
|
|
606
|
+
{description}
|
|
607
|
+
</VisuallyHidden>
|
|
608
|
+
)}
|
|
609
|
+
{inputGroup && status?.message && (
|
|
610
|
+
<VisuallyHidden
|
|
611
|
+
as="div"
|
|
612
|
+
id={statusMessageID}
|
|
613
|
+
role={status.type === 'error' ? 'alert' : 'status'}
|
|
614
|
+
aria-live={status.type === 'error' ? 'assertive' : 'polite'}>
|
|
615
|
+
{status.message}
|
|
616
|
+
</VisuallyHidden>
|
|
617
|
+
)}
|
|
618
|
+
<input
|
|
619
|
+
ref={mergeRefs(ref, inputRef)}
|
|
620
|
+
id={id}
|
|
621
|
+
type="text"
|
|
622
|
+
value={displayValue}
|
|
623
|
+
onChange={handleInputChange}
|
|
624
|
+
onFocus={handleFocus}
|
|
625
|
+
onBlur={handleBlur}
|
|
626
|
+
onKeyDown={handleInputKeyDown}
|
|
627
|
+
placeholder={displayPlaceholder}
|
|
628
|
+
// With a disabledMessage the input keeps focusability via
|
|
629
|
+
// aria-disabled so the reason is focus-discoverable; typing and
|
|
630
|
+
// arrow-key adjustment are blocked with readOnly and the guards.
|
|
631
|
+
disabled={isDisabled && !showsDisabledMessage}
|
|
632
|
+
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
633
|
+
readOnly={showsDisabledMessage || undefined}
|
|
634
|
+
autoFocus={hasAutoFocus}
|
|
635
|
+
data-autofocus={hasAutoFocus || undefined}
|
|
636
|
+
aria-describedby={ariaDescribedBy}
|
|
637
|
+
aria-required={isRequired === true ? 'true' : undefined}
|
|
638
|
+
aria-invalid={status?.type === 'error' ? 'true' : undefined}
|
|
639
|
+
aria-busy={isBusy || undefined}
|
|
640
|
+
aria-labelledby={ariaLabelledBy}
|
|
641
|
+
{...stylex.props(
|
|
642
|
+
styles.input,
|
|
643
|
+
isDisabled && styles.inputDisabled,
|
|
644
|
+
!isInputValid && styles.inputInvalid,
|
|
645
|
+
)}
|
|
646
|
+
/>
|
|
647
|
+
{isBusy && <Spinner size="sm" />}
|
|
648
|
+
{hasClear && value && !isDisabled && (
|
|
649
|
+
<button
|
|
650
|
+
type="button"
|
|
651
|
+
onClick={handleClear}
|
|
652
|
+
aria-label={`Clear ${label}`}
|
|
653
|
+
{...stylex.props(styles.clearButton)}>
|
|
654
|
+
<Icon icon="close" size="sm" color="secondary" />
|
|
655
|
+
</button>
|
|
656
|
+
)}
|
|
657
|
+
{status && !inputGroup && (
|
|
658
|
+
<Icon
|
|
659
|
+
icon={statusIconMap[status.type]}
|
|
660
|
+
size="md"
|
|
661
|
+
color={statusIconColorMap[status.type]}
|
|
662
|
+
/>
|
|
663
|
+
)}
|
|
664
|
+
</div>
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
if (inputGroup) {
|
|
668
|
+
return (
|
|
669
|
+
<>
|
|
670
|
+
{inputWrapper}
|
|
671
|
+
{showsDisabledMessage &&
|
|
672
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
673
|
+
</>
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
512
677
|
return (
|
|
513
678
|
<Field
|
|
514
679
|
label={label}
|
|
@@ -530,68 +695,9 @@ export function TimeInput({
|
|
|
530
695
|
}
|
|
531
696
|
labelTooltip={labelTooltip}
|
|
532
697
|
width={width}>
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
onMouseUp={handleWrapperMouseUp}
|
|
537
|
-
{...mergeProps(
|
|
538
|
-
themeProps('time-input', {size, status: status?.type ?? null}),
|
|
539
|
-
stylex.props(
|
|
540
|
-
inputWrapperStyles.base,
|
|
541
|
-
sizeStyles[size],
|
|
542
|
-
isDisabled && inputWrapperStyles.disabled,
|
|
543
|
-
status && inputStatusBorderStyles[status.type],
|
|
544
|
-
status && inputStatusHoverShadowStyles[status.type],
|
|
545
|
-
status && inputStatusFocusWithinStyles[status.type],
|
|
546
|
-
xstyle,
|
|
547
|
-
),
|
|
548
|
-
className,
|
|
549
|
-
style,
|
|
550
|
-
)}>
|
|
551
|
-
<div {...stylex.props(styles.icon)}>
|
|
552
|
-
<Icon icon="clock" size="sm" color="secondary" />
|
|
553
|
-
</div>
|
|
554
|
-
<input
|
|
555
|
-
ref={mergeRefs(ref, inputRef)}
|
|
556
|
-
id={id}
|
|
557
|
-
type="text"
|
|
558
|
-
value={displayValue}
|
|
559
|
-
onChange={handleInputChange}
|
|
560
|
-
onFocus={handleFocus}
|
|
561
|
-
onBlur={handleBlur}
|
|
562
|
-
onKeyDown={handleInputKeyDown}
|
|
563
|
-
placeholder={displayPlaceholder}
|
|
564
|
-
disabled={isDisabled}
|
|
565
|
-
autoFocus={hasAutoFocus}
|
|
566
|
-
data-autofocus={hasAutoFocus || undefined}
|
|
567
|
-
aria-describedby={ariaDescribedBy}
|
|
568
|
-
aria-required={isRequired === true ? 'true' : undefined}
|
|
569
|
-
aria-invalid={status?.type === 'error' ? 'true' : undefined}
|
|
570
|
-
aria-busy={isBusy || undefined}
|
|
571
|
-
{...stylex.props(
|
|
572
|
-
styles.input,
|
|
573
|
-
isDisabled && styles.inputDisabled,
|
|
574
|
-
!isInputValid && styles.inputInvalid,
|
|
575
|
-
)}
|
|
576
|
-
/>
|
|
577
|
-
{isBusy && <Spinner size="sm" />}
|
|
578
|
-
{hasClear && value && !isDisabled && (
|
|
579
|
-
<button
|
|
580
|
-
type="button"
|
|
581
|
-
onClick={handleClear}
|
|
582
|
-
aria-label={`Clear ${label}`}
|
|
583
|
-
{...stylex.props(styles.clearButton)}>
|
|
584
|
-
<Icon icon="close" size="sm" color="secondary" />
|
|
585
|
-
</button>
|
|
586
|
-
)}
|
|
587
|
-
{status && (
|
|
588
|
-
<Icon
|
|
589
|
-
icon={statusIconMap[status.type]}
|
|
590
|
-
size="md"
|
|
591
|
-
color={statusIconColorMap[status.type]}
|
|
592
|
-
/>
|
|
593
|
-
)}
|
|
594
|
-
</div>
|
|
698
|
+
{inputWrapper}
|
|
699
|
+
{showsDisabledMessage &&
|
|
700
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
595
701
|
</Field>
|
|
596
702
|
);
|
|
597
703
|
}
|