@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,8 +9,9 @@
|
|
|
9
9
|
* SYNC: When DateTimeInput.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 {DateTimeInput} from './DateTimeInput';
|
|
15
16
|
import type {ISODateTimeString} from './DateTimeInput';
|
|
16
17
|
|
|
@@ -20,6 +21,25 @@ describe('DateTimeInput', () => {
|
|
|
20
21
|
expect(screen.getByLabelText('Meeting time')).toBeInTheDocument();
|
|
21
22
|
});
|
|
22
23
|
|
|
24
|
+
it('derives the time input label from the field label (forms-15)', () => {
|
|
25
|
+
render(<DateTimeInput label="Meeting time" onChange={() => {}} />);
|
|
26
|
+
// Not a hardcoded "Time" — tied to the field label so it is localizable
|
|
27
|
+
// and unambiguous when multiple date-time fields share a page.
|
|
28
|
+
expect(screen.getByLabelText('Meeting time time')).toBeInTheDocument();
|
|
29
|
+
expect(screen.queryByLabelText('Time')).not.toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('uses an explicit timeLabel when provided', () => {
|
|
33
|
+
render(
|
|
34
|
+
<DateTimeInput
|
|
35
|
+
label="Meeting time"
|
|
36
|
+
timeLabel="Start time"
|
|
37
|
+
onChange={() => {}}
|
|
38
|
+
/>,
|
|
39
|
+
);
|
|
40
|
+
expect(screen.getByLabelText('Start time')).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
23
43
|
it('renders with placeholder', () => {
|
|
24
44
|
render(
|
|
25
45
|
<DateTimeInput
|
|
@@ -31,10 +51,32 @@ describe('DateTimeInput', () => {
|
|
|
31
51
|
expect(screen.getByPlaceholderText('Pick a date')).toBeInTheDocument();
|
|
32
52
|
});
|
|
33
53
|
|
|
54
|
+
it('defaults the time portion placeholder to "Select a time"', () => {
|
|
55
|
+
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
56
|
+
expect(screen.getByPlaceholderText('Select a time')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('applies a custom timePlaceholder to the time portion', () => {
|
|
60
|
+
render(
|
|
61
|
+
<DateTimeInput
|
|
62
|
+
label="Meeting"
|
|
63
|
+
onChange={() => {}}
|
|
64
|
+
placeholder="Pick a date"
|
|
65
|
+
timePlaceholder="Pick a time"
|
|
66
|
+
/>,
|
|
67
|
+
);
|
|
68
|
+
// Time portion uses the override; date portion keeps its own placeholder.
|
|
69
|
+
expect(screen.getByPlaceholderText('Pick a time')).toBeInTheDocument();
|
|
70
|
+
expect(screen.getByPlaceholderText('Pick a date')).toBeInTheDocument();
|
|
71
|
+
expect(
|
|
72
|
+
screen.queryByPlaceholderText('Select a time'),
|
|
73
|
+
).not.toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
34
76
|
it('renders both date and time inputs', () => {
|
|
35
77
|
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
36
78
|
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
37
|
-
expect(screen.getByLabelText('
|
|
79
|
+
expect(screen.getByLabelText('Meeting time')).toBeInTheDocument();
|
|
38
80
|
});
|
|
39
81
|
|
|
40
82
|
it('displays formatted date in date input when value is provided', () => {
|
|
@@ -90,9 +132,7 @@ describe('DateTimeInput', () => {
|
|
|
90
132
|
});
|
|
91
133
|
|
|
92
134
|
it('visually hides label when isLabelHidden is true', () => {
|
|
93
|
-
render(
|
|
94
|
-
<DateTimeInput label="Meeting" isLabelHidden onChange={() => {}} />,
|
|
95
|
-
);
|
|
135
|
+
render(<DateTimeInput label="Meeting" isLabelHidden onChange={() => {}} />);
|
|
96
136
|
const label = screen.getByText('Meeting');
|
|
97
137
|
expect(label).toBeInTheDocument();
|
|
98
138
|
expect(screen.getByLabelText('Meeting')).toBeInTheDocument();
|
|
@@ -114,13 +154,13 @@ describe('DateTimeInput', () => {
|
|
|
114
154
|
it('sets disabled on both inputs when isDisabled is true', () => {
|
|
115
155
|
render(<DateTimeInput label="Meeting" isDisabled onChange={() => {}} />);
|
|
116
156
|
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
117
|
-
expect(screen.getByLabelText('
|
|
157
|
+
expect(screen.getByLabelText('Meeting time')).toBeDisabled();
|
|
118
158
|
});
|
|
119
159
|
|
|
120
160
|
it('is not disabled by default', () => {
|
|
121
161
|
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
122
162
|
expect(screen.getByRole('combobox')).not.toBeDisabled();
|
|
123
|
-
expect(screen.getByLabelText('
|
|
163
|
+
expect(screen.getByLabelText('Meeting time')).not.toBeDisabled();
|
|
124
164
|
});
|
|
125
165
|
|
|
126
166
|
it('date input has role="combobox"', () => {
|
|
@@ -160,7 +200,7 @@ describe('DateTimeInput', () => {
|
|
|
160
200
|
it('disables inputs and button when isLoading is true', () => {
|
|
161
201
|
render(<DateTimeInput label="Meeting" isLoading onChange={() => {}} />);
|
|
162
202
|
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
163
|
-
expect(screen.getByLabelText('
|
|
203
|
+
expect(screen.getByLabelText('Meeting time')).toBeDisabled();
|
|
164
204
|
expect(screen.getByRole('button', {name: 'Open calendar'})).toBeDisabled();
|
|
165
205
|
});
|
|
166
206
|
|
|
@@ -284,7 +324,7 @@ describe('DateTimeInput', () => {
|
|
|
284
324
|
const onChange = vi.fn();
|
|
285
325
|
render(<DateTimeInput label="Meeting" onChange={onChange} />);
|
|
286
326
|
|
|
287
|
-
const timeInput = screen.getByLabelText('
|
|
327
|
+
const timeInput = screen.getByLabelText('Meeting time');
|
|
288
328
|
fireEvent.change(timeInput, {target: {value: '3:45 pm'}});
|
|
289
329
|
|
|
290
330
|
expect(onChange).not.toHaveBeenCalled();
|
|
@@ -300,7 +340,7 @@ describe('DateTimeInput', () => {
|
|
|
300
340
|
/>,
|
|
301
341
|
);
|
|
302
342
|
|
|
303
|
-
const timeInput = screen.getByLabelText('
|
|
343
|
+
const timeInput = screen.getByLabelText('Meeting time');
|
|
304
344
|
fireEvent.change(timeInput, {target: {value: '3:45 pm'}});
|
|
305
345
|
|
|
306
346
|
expect(onChange).toHaveBeenCalledWith('2026-03-15T15:45');
|
|
@@ -409,4 +449,232 @@ describe('DateTimeInput', () => {
|
|
|
409
449
|
expect(dateInput).toHaveValue('March 20, 2026');
|
|
410
450
|
});
|
|
411
451
|
});
|
|
452
|
+
|
|
453
|
+
describe('invalid typed input feedback (WCAG 3.3.1)', () => {
|
|
454
|
+
it('sets aria-invalid="true" on the date input when typed date is unparseable', () => {
|
|
455
|
+
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
456
|
+
|
|
457
|
+
const dateInput = screen.getByRole('combobox');
|
|
458
|
+
fireEvent.change(dateInput, {target: {value: '13/45/2024'}});
|
|
459
|
+
|
|
460
|
+
expect(dateInput).toHaveAttribute('aria-invalid', 'true');
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('does not set aria-invalid on the date input when typed date is valid', () => {
|
|
464
|
+
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
465
|
+
|
|
466
|
+
const dateInput = screen.getByRole('combobox');
|
|
467
|
+
fireEvent.change(dateInput, {target: {value: '03/15/2026'}});
|
|
468
|
+
|
|
469
|
+
expect(dateInput).not.toHaveAttribute('aria-invalid');
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('announces an alert message when the typed date is invalid', () => {
|
|
473
|
+
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
474
|
+
|
|
475
|
+
const dateInput = screen.getByRole('combobox');
|
|
476
|
+
fireEvent.change(dateInput, {target: {value: '13/45/2024'}});
|
|
477
|
+
|
|
478
|
+
expect(screen.getByText('Invalid date')).toBeInTheDocument();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('sets aria-invalid="true" on the time input when typed time is unparseable', () => {
|
|
482
|
+
render(
|
|
483
|
+
<DateTimeInput
|
|
484
|
+
label="Meeting"
|
|
485
|
+
value={'2026-03-15T10:00' as ISODateTimeString}
|
|
486
|
+
onChange={() => {}}
|
|
487
|
+
/>,
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
const timeInput = screen.getByLabelText('Meeting time');
|
|
491
|
+
fireEvent.change(timeInput, {target: {value: '99:99 zz'}});
|
|
492
|
+
|
|
493
|
+
expect(timeInput).toHaveAttribute('aria-invalid', 'true');
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it('does not set aria-invalid on the time input when typed time is valid', () => {
|
|
497
|
+
render(
|
|
498
|
+
<DateTimeInput
|
|
499
|
+
label="Meeting"
|
|
500
|
+
value={'2026-03-15T10:00' as ISODateTimeString}
|
|
501
|
+
onChange={() => {}}
|
|
502
|
+
/>,
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
const timeInput = screen.getByLabelText('Meeting time');
|
|
506
|
+
fireEvent.change(timeInput, {target: {value: '3:45 pm'}});
|
|
507
|
+
|
|
508
|
+
expect(timeInput).not.toHaveAttribute('aria-invalid');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('announces an alert message when the typed time is invalid', () => {
|
|
512
|
+
render(
|
|
513
|
+
<DateTimeInput
|
|
514
|
+
label="Meeting"
|
|
515
|
+
value={'2026-03-15T10:00' as ISODateTimeString}
|
|
516
|
+
onChange={() => {}}
|
|
517
|
+
/>,
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
const timeInput = screen.getByLabelText('Meeting time');
|
|
521
|
+
fireEvent.change(timeInput, {target: {value: '99:99 zz'}});
|
|
522
|
+
|
|
523
|
+
expect(screen.getByText('Invalid time')).toBeInTheDocument();
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
describe('disabledMessage', () => {
|
|
527
|
+
// jsdom does not implement the Popover API used by the tooltip, so mock
|
|
528
|
+
// showPopover/hidePopover to toggle a `popover-open` attribute the tests
|
|
529
|
+
// can assert on.
|
|
530
|
+
beforeEach(() => {
|
|
531
|
+
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
532
|
+
this.setAttribute('popover-open', '');
|
|
533
|
+
});
|
|
534
|
+
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
535
|
+
this.removeAttribute('popover-open');
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// jsdom popover content is in the DOM but not "visible" in the
|
|
540
|
+
// accessibility tree; use hidden: true to find it.
|
|
541
|
+
const h = {hidden: true} as const;
|
|
542
|
+
|
|
543
|
+
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
544
|
+
render(
|
|
545
|
+
<DateTimeInput
|
|
546
|
+
label="When"
|
|
547
|
+
onChange={() => {}}
|
|
548
|
+
isDisabled
|
|
549
|
+
disabledMessage="You need the Editor role"
|
|
550
|
+
/>,
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
// The tooltip anchors on the outer row that wraps both inputs.
|
|
554
|
+
const dateInput = screen.getByRole('combobox');
|
|
555
|
+
const row = dateInput.parentElement?.parentElement as HTMLElement;
|
|
556
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
557
|
+
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
558
|
+
|
|
559
|
+
fireEvent.mouseEnter(row);
|
|
560
|
+
await waitFor(() => {
|
|
561
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
fireEvent.mouseLeave(row);
|
|
565
|
+
await waitFor(() => {
|
|
566
|
+
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('shows the reason tooltip on keyboard focus', async () => {
|
|
571
|
+
const user = userEvent.setup();
|
|
572
|
+
render(
|
|
573
|
+
<DateTimeInput
|
|
574
|
+
label="When"
|
|
575
|
+
onChange={() => {}}
|
|
576
|
+
isDisabled
|
|
577
|
+
disabledMessage="You need the Editor role"
|
|
578
|
+
/>,
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
582
|
+
await user.tab();
|
|
583
|
+
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
584
|
+
await waitFor(() => {
|
|
585
|
+
expect(tooltip).toHaveAttribute('popover-open');
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('does not render a tooltip when not disabled', () => {
|
|
590
|
+
render(
|
|
591
|
+
<DateTimeInput
|
|
592
|
+
label="When"
|
|
593
|
+
onChange={() => {}}
|
|
594
|
+
disabledMessage="You need the Editor role"
|
|
595
|
+
/>,
|
|
596
|
+
);
|
|
597
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('does not render a tooltip when disabled without a reason', () => {
|
|
601
|
+
render(<DateTimeInput label="When" onChange={() => {}} isDisabled />);
|
|
602
|
+
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it('keeps both inputs focusable via aria-disabled when a reason is provided', () => {
|
|
606
|
+
render(
|
|
607
|
+
<DateTimeInput
|
|
608
|
+
label="When"
|
|
609
|
+
onChange={() => {}}
|
|
610
|
+
isDisabled
|
|
611
|
+
disabledMessage="You need the Editor role"
|
|
612
|
+
/>,
|
|
613
|
+
);
|
|
614
|
+
const dateInput = screen.getByRole('combobox');
|
|
615
|
+
const timeInput = screen.getByLabelText('When time');
|
|
616
|
+
expect(dateInput).not.toBeDisabled();
|
|
617
|
+
expect(dateInput).toHaveAttribute('aria-disabled', 'true');
|
|
618
|
+
expect(dateInput).toHaveAttribute('readonly');
|
|
619
|
+
expect(timeInput).not.toBeDisabled();
|
|
620
|
+
expect(timeInput).toHaveAttribute('aria-disabled', 'true');
|
|
621
|
+
expect(timeInput).toHaveAttribute('readonly');
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('links the reason tooltip from the date input via aria-describedby', () => {
|
|
625
|
+
render(
|
|
626
|
+
<DateTimeInput
|
|
627
|
+
label="When"
|
|
628
|
+
onChange={() => {}}
|
|
629
|
+
isDisabled
|
|
630
|
+
disabledMessage="You need the Editor role"
|
|
631
|
+
/>,
|
|
632
|
+
);
|
|
633
|
+
const dateInput = screen.getByRole('combobox');
|
|
634
|
+
const tooltip = screen.getByRole('tooltip', h);
|
|
635
|
+
expect(dateInput.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it('blocks value changes and opening while focusable-disabled', async () => {
|
|
639
|
+
const user = userEvent.setup();
|
|
640
|
+
const onChange = vi.fn();
|
|
641
|
+
render(
|
|
642
|
+
<DateTimeInput
|
|
643
|
+
label="When"
|
|
644
|
+
onChange={onChange}
|
|
645
|
+
isDisabled
|
|
646
|
+
disabledMessage="You need the Editor role"
|
|
647
|
+
/>,
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
const dateInput = screen.getByRole('combobox');
|
|
651
|
+
await user.click(dateInput);
|
|
652
|
+
await user.type(dateInput, '2026-03-15');
|
|
653
|
+
expect(dateInput).toHaveValue('');
|
|
654
|
+
expect(dateInput).toHaveAttribute('aria-expanded', 'false');
|
|
655
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
it('remains natively disabled when disabled without a reason', () => {
|
|
659
|
+
render(<DateTimeInput label="When" onChange={() => {}} isDisabled />);
|
|
660
|
+
const dateInput = screen.getByRole('combobox');
|
|
661
|
+
expect(dateInput).toBeDisabled();
|
|
662
|
+
expect(dateInput).not.toHaveAttribute('aria-disabled');
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('does not swap in the time format-hint placeholder on focus while disabled', () => {
|
|
666
|
+
render(
|
|
667
|
+
<DateTimeInput
|
|
668
|
+
label="When"
|
|
669
|
+
onChange={() => {}}
|
|
670
|
+
isDisabled
|
|
671
|
+
disabledMessage="You need the Editor role"
|
|
672
|
+
/>,
|
|
673
|
+
);
|
|
674
|
+
const timeInput = screen.getByLabelText('When time');
|
|
675
|
+
timeInput.focus();
|
|
676
|
+
fireEvent.focus(timeInput);
|
|
677
|
+
expect(timeInput).toHaveAttribute('placeholder', 'Select a time');
|
|
678
|
+
});
|
|
679
|
+
});
|
|
412
680
|
});
|
|
@@ -47,14 +47,12 @@ import {
|
|
|
47
47
|
inputStatusFocusWithinStyles,
|
|
48
48
|
} from '../Field';
|
|
49
49
|
import {Icon} from '../Icon';
|
|
50
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
50
51
|
import {Spinner} from '../Spinner';
|
|
51
|
-
import {
|
|
52
|
-
Calendar,
|
|
53
|
-
type ISODateString,
|
|
54
|
-
type CalendarHandle,
|
|
55
|
-
} from '../Calendar';
|
|
52
|
+
import {Calendar, type ISODateString, type CalendarHandle} from '../Calendar';
|
|
56
53
|
import {useCalendarConstraints} from '../Calendar/hooks';
|
|
57
54
|
import {usePopover} from '../Popover';
|
|
55
|
+
import {useTooltip} from '../Tooltip';
|
|
58
56
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
59
57
|
import {
|
|
60
58
|
type ISOTimeString,
|
|
@@ -213,6 +211,29 @@ export interface DateTimeInputProps extends Omit<
|
|
|
213
211
|
*/
|
|
214
212
|
isDisabled?: boolean;
|
|
215
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Explains why the input is disabled. When set together with
|
|
216
|
+
* `isDisabled`, the input shows a tooltip with this text on hover and
|
|
217
|
+
* keyboard focus, and the date and time fields stay focusable (via
|
|
218
|
+
* `aria-disabled`) so the reason is discoverable by keyboard and assistive
|
|
219
|
+
* technology. Typing and calendar activation stay blocked.
|
|
220
|
+
*
|
|
221
|
+
* Use this instead of wrapping a disabled input in `Tooltip` — disabled
|
|
222
|
+
* controls don't emit the pointer events an external tooltip needs.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```
|
|
226
|
+
* <DateTimeInput
|
|
227
|
+
* label="Meeting time"
|
|
228
|
+
* value={dateTime}
|
|
229
|
+
* onChange={setDateTime}
|
|
230
|
+
* isDisabled
|
|
231
|
+
* disabledMessage="You need the Editor role to change this"
|
|
232
|
+
* />
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
disabledMessage?: string;
|
|
236
|
+
|
|
216
237
|
/**
|
|
217
238
|
* The selected datetime in ISO 8601 format ("YYYY-MM-DDTHH:MM" or "YYYY-MM-DDTHH:MM:SS").
|
|
218
239
|
*/
|
|
@@ -278,11 +299,24 @@ export interface DateTimeInputProps extends Omit<
|
|
|
278
299
|
hasClear?: boolean;
|
|
279
300
|
|
|
280
301
|
/**
|
|
281
|
-
* Placeholder text shown when no date is selected.
|
|
302
|
+
* Placeholder text shown in the date portion when no date is selected.
|
|
282
303
|
* @default "Select a date"
|
|
283
304
|
*/
|
|
284
305
|
placeholder?: string;
|
|
285
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Placeholder text shown in the time portion when no time is selected.
|
|
309
|
+
* @default "Select a time"
|
|
310
|
+
*/
|
|
311
|
+
timePlaceholder?: string;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Accessible label for the time portion of the field. Defaults to
|
|
315
|
+
* `"{label} time"` so it is tied to the field's own label and localizable,
|
|
316
|
+
* rather than a hardcoded English "Time".
|
|
317
|
+
*/
|
|
318
|
+
timeLabel?: string;
|
|
319
|
+
|
|
286
320
|
/**
|
|
287
321
|
* The size of the inputs.
|
|
288
322
|
* @default 'md'
|
|
@@ -373,6 +407,7 @@ export function DateTimeInput({
|
|
|
373
407
|
isOptional = false,
|
|
374
408
|
isRequired = false,
|
|
375
409
|
isDisabled = false,
|
|
410
|
+
disabledMessage,
|
|
376
411
|
value,
|
|
377
412
|
onChange,
|
|
378
413
|
changeAction,
|
|
@@ -385,6 +420,8 @@ export function DateTimeInput({
|
|
|
385
420
|
timeIncrement = 1,
|
|
386
421
|
hasClear = false,
|
|
387
422
|
placeholder = 'Select a date',
|
|
423
|
+
timePlaceholder = 'Select a time',
|
|
424
|
+
timeLabel,
|
|
388
425
|
size: sizeProp,
|
|
389
426
|
status,
|
|
390
427
|
labelTooltip,
|
|
@@ -412,6 +449,22 @@ export function DateTimeInput({
|
|
|
412
449
|
const isBusy = isLoading || optimisticValue !== value;
|
|
413
450
|
const isEffectivelyDisabled = isDisabled || isBusy;
|
|
414
451
|
|
|
452
|
+
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
453
|
+
// tooltip listeners attach to the outer row container (which already exists)
|
|
454
|
+
// and both the date and time inputs stay perceivable via aria-disabled
|
|
455
|
+
// instead of the disabled attribute. Typing is blocked with readOnly and the
|
|
456
|
+
// value mutation guards; calendar activation is blocked by the
|
|
457
|
+
// isEffectivelyDisabled guards. Only the persistent isDisabled state (not the
|
|
458
|
+
// transient busy state) surfaces a reason.
|
|
459
|
+
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
460
|
+
const disabledMessageTooltip = useTooltip({
|
|
461
|
+
placement: 'above',
|
|
462
|
+
// The container div is not naturally focusable; focusin bubbles up from
|
|
463
|
+
// the inputs, so always attach focus listeners.
|
|
464
|
+
focusTrigger: 'always',
|
|
465
|
+
isEnabled: showsDisabledMessage,
|
|
466
|
+
});
|
|
467
|
+
|
|
415
468
|
const statusIconMap: Record<InputStatusType, IconName> = {
|
|
416
469
|
warning: 'warning',
|
|
417
470
|
error: 'error',
|
|
@@ -431,6 +484,7 @@ export function DateTimeInput({
|
|
|
431
484
|
[
|
|
432
485
|
description ? descriptionID : null,
|
|
433
486
|
status?.message ? statusMessageID : null,
|
|
487
|
+
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
434
488
|
]
|
|
435
489
|
.filter(Boolean)
|
|
436
490
|
.join(' ') || undefined;
|
|
@@ -520,12 +574,12 @@ export function DateTimeInput({
|
|
|
520
574
|
return isTimeInRange(parsed, timeMin, timeMax);
|
|
521
575
|
}, [timePendingInput, hasSeconds, timeMin, timeMax]);
|
|
522
576
|
|
|
523
|
-
const
|
|
577
|
+
const resolvedTimePlaceholder = useMemo(() => {
|
|
524
578
|
if (isTimeFocused && !timeDisplayValue) {
|
|
525
579
|
return hourFormat === '12h' ? 'e.g., 2:30 PM' : 'e.g., 14:30';
|
|
526
580
|
}
|
|
527
|
-
return
|
|
528
|
-
}, [isTimeFocused, timeDisplayValue, hourFormat]);
|
|
581
|
+
return timePlaceholder;
|
|
582
|
+
}, [isTimeFocused, timeDisplayValue, hourFormat, timePlaceholder]);
|
|
529
583
|
|
|
530
584
|
// --- Unified change handler ---
|
|
531
585
|
const fireChange = useCallback(
|
|
@@ -598,6 +652,11 @@ export function DateTimeInput({
|
|
|
598
652
|
|
|
599
653
|
const handleDateInputChange = useCallback(
|
|
600
654
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
655
|
+
// With a disabledMessage the input drops `disabled` for focusability, so
|
|
656
|
+
// guard value mutation explicitly (readOnly also blocks typing).
|
|
657
|
+
if (isEffectivelyDisabled) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
601
660
|
const text = e.target.value;
|
|
602
661
|
setDatePendingInput(text);
|
|
603
662
|
|
|
@@ -613,7 +672,7 @@ export function DateTimeInput({
|
|
|
613
672
|
calendarRef.current?.navigateTo(parsedISO);
|
|
614
673
|
}
|
|
615
674
|
},
|
|
616
|
-
[valueParts.date, isDateDisabled, handleDateChange],
|
|
675
|
+
[valueParts.date, isDateDisabled, handleDateChange, isEffectivelyDisabled],
|
|
617
676
|
);
|
|
618
677
|
|
|
619
678
|
const commitDatePendingInput = useCallback(() => {
|
|
@@ -666,6 +725,11 @@ export function DateTimeInput({
|
|
|
666
725
|
// --- Time handlers ---
|
|
667
726
|
const handleTimeInputChange = useCallback(
|
|
668
727
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
728
|
+
// With a disabledMessage the input drops `disabled` for focusability, so
|
|
729
|
+
// guard value mutation explicitly (readOnly also blocks typing).
|
|
730
|
+
if (isEffectivelyDisabled) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
669
733
|
const text = e.target.value;
|
|
670
734
|
setTimePendingInput(text);
|
|
671
735
|
|
|
@@ -690,10 +754,19 @@ export function DateTimeInput({
|
|
|
690
754
|
valueParts.time,
|
|
691
755
|
valueParts.date,
|
|
692
756
|
fireChange,
|
|
757
|
+
isEffectivelyDisabled,
|
|
693
758
|
],
|
|
694
759
|
);
|
|
695
760
|
|
|
696
|
-
const handleTimeFocus = useCallback(() =>
|
|
761
|
+
const handleTimeFocus = useCallback(() => {
|
|
762
|
+
// A disabled/busy input stays focusable (via aria-disabled) so its reason
|
|
763
|
+
// is discoverable, but it must not present editing affordances — keep the
|
|
764
|
+
// static placeholder rather than swapping in the format hint.
|
|
765
|
+
if (isEffectivelyDisabled) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
setIsTimeFocused(true);
|
|
769
|
+
}, [isEffectivelyDisabled]);
|
|
697
770
|
|
|
698
771
|
const handleTimeBlur = useCallback(() => {
|
|
699
772
|
setIsTimeFocused(false);
|
|
@@ -788,6 +861,7 @@ export function DateTimeInput({
|
|
|
788
861
|
statusVariant="detached"
|
|
789
862
|
width={width}>
|
|
790
863
|
<div
|
|
864
|
+
ref={disabledMessageTooltip.ref}
|
|
791
865
|
{...rest}
|
|
792
866
|
{...mergeProps(
|
|
793
867
|
themeProps('date-time-input', {
|
|
@@ -832,10 +906,18 @@ export function DateTimeInput({
|
|
|
832
906
|
onClick={handleDateInputClick}
|
|
833
907
|
onKeyDown={handleDateKeyDown}
|
|
834
908
|
placeholder={placeholder}
|
|
835
|
-
|
|
909
|
+
// With a disabledMessage the input keeps focusability via
|
|
910
|
+
// aria-disabled so the reason is focus-discoverable; typing is
|
|
911
|
+
// blocked with readOnly and the mutation guards, and calendar
|
|
912
|
+
// activation is blocked by the isEffectivelyDisabled guards.
|
|
913
|
+
disabled={isEffectivelyDisabled && !showsDisabledMessage}
|
|
914
|
+
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
915
|
+
readOnly={showsDisabledMessage || undefined}
|
|
836
916
|
aria-describedby={ariaDescribedBy}
|
|
837
917
|
aria-required={isRequired === true ? 'true' : undefined}
|
|
838
|
-
aria-invalid={
|
|
918
|
+
aria-invalid={
|
|
919
|
+
status?.type === 'error' || !isDateInputValid ? 'true' : undefined
|
|
920
|
+
}
|
|
839
921
|
aria-busy={isBusy || undefined}
|
|
840
922
|
aria-expanded={popover.isOpen}
|
|
841
923
|
aria-haspopup="dialog"
|
|
@@ -848,6 +930,15 @@ export function DateTimeInput({
|
|
|
848
930
|
!isDateInputValid && styles.inputInvalid,
|
|
849
931
|
)}
|
|
850
932
|
/>
|
|
933
|
+
{/*
|
|
934
|
+
Live region announcing invalid typed date input to assistive
|
|
935
|
+
technology. The value silently reverts on blur, so without this a
|
|
936
|
+
screen-reader user would get no feedback that their entry was
|
|
937
|
+
rejected (WCAG 3.3.1).
|
|
938
|
+
*/}
|
|
939
|
+
<VisuallyHidden as="div" role="alert" aria-live="assertive">
|
|
940
|
+
{!isDateInputValid ? 'Invalid date' : ''}
|
|
941
|
+
</VisuallyHidden>
|
|
851
942
|
{hasClear && value !== undefined && !isEffectivelyDisabled && (
|
|
852
943
|
<button
|
|
853
944
|
type="button"
|
|
@@ -893,17 +984,31 @@ export function DateTimeInput({
|
|
|
893
984
|
onFocus={handleTimeFocus}
|
|
894
985
|
onBlur={handleTimeBlur}
|
|
895
986
|
onKeyDown={handleTimeKeyDown}
|
|
896
|
-
placeholder={
|
|
897
|
-
|
|
898
|
-
aria-
|
|
987
|
+
placeholder={resolvedTimePlaceholder}
|
|
988
|
+
// With a disabledMessage the input keeps focusability via
|
|
989
|
+
// aria-disabled so the reason is focus-discoverable; typing is
|
|
990
|
+
// blocked with readOnly and the mutation guards.
|
|
991
|
+
disabled={isEffectivelyDisabled && !showsDisabledMessage}
|
|
992
|
+
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
993
|
+
readOnly={showsDisabledMessage || undefined}
|
|
994
|
+
aria-label={timeLabel ?? `${label} time`}
|
|
899
995
|
aria-required={isRequired === true ? 'true' : undefined}
|
|
900
|
-
aria-invalid={
|
|
996
|
+
aria-invalid={
|
|
997
|
+
status?.type === 'error' || !isTimeInputValid ? 'true' : undefined
|
|
998
|
+
}
|
|
901
999
|
{...stylex.props(
|
|
902
1000
|
styles.input,
|
|
903
1001
|
isEffectivelyDisabled && styles.inputDisabled,
|
|
904
1002
|
!isTimeInputValid && styles.inputInvalid,
|
|
905
1003
|
)}
|
|
906
1004
|
/>
|
|
1005
|
+
{/*
|
|
1006
|
+
Live region announcing invalid typed time input to assistive
|
|
1007
|
+
technology (WCAG 3.3.1).
|
|
1008
|
+
*/}
|
|
1009
|
+
<VisuallyHidden as="div" role="alert" aria-live="assertive">
|
|
1010
|
+
{!isTimeInputValid ? 'Invalid time' : ''}
|
|
1011
|
+
</VisuallyHidden>
|
|
907
1012
|
</div>
|
|
908
1013
|
</div>
|
|
909
1014
|
|
|
@@ -920,6 +1025,9 @@ export function DateTimeInput({
|
|
|
920
1025
|
/>,
|
|
921
1026
|
{placement: 'below', alignment: 'start'},
|
|
922
1027
|
)}
|
|
1028
|
+
|
|
1029
|
+
{showsDisabledMessage &&
|
|
1030
|
+
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
923
1031
|
</Field>
|
|
924
1032
|
);
|
|
925
1033
|
}
|
|
@@ -40,13 +40,13 @@ export const docs = {
|
|
|
40
40
|
{
|
|
41
41
|
name: 'isOpen',
|
|
42
42
|
type: 'boolean',
|
|
43
|
-
description: 'Whether the dialog is open
|
|
43
|
+
description: 'Whether the dialog is open.',
|
|
44
44
|
required: true,
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
name: 'onOpenChange',
|
|
48
48
|
type: '(isOpen: boolean) => unknown',
|
|
49
|
-
description: 'Callback when dialog visibility changes
|
|
49
|
+
description: 'Callback when dialog visibility changes.',
|
|
50
50
|
required: true,
|
|
51
51
|
},
|
|
52
52
|
{
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -20,6 +20,7 @@ import {useEffect, useMemo, useRef, type ReactNode} from 'react';
|
|
|
20
20
|
import type {BaseProps} from '../BaseProps';
|
|
21
21
|
import * as stylex from '@stylexjs/stylex';
|
|
22
22
|
import {useScrollLock} from '../hooks/useScrollLock';
|
|
23
|
+
import {hasActiveFocusTrapEscape, isImeKeyEvent} from '../hooks/useFocusTrap';
|
|
23
24
|
import {
|
|
24
25
|
colorVars,
|
|
25
26
|
radiusVars,
|
|
@@ -422,6 +423,12 @@ export function Dialog({
|
|
|
422
423
|
|
|
423
424
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
424
425
|
if (event.key === 'Escape') {
|
|
426
|
+
// Ignore IME composition-cancel, and defer to any popover/menu layered
|
|
427
|
+
// on top of this dialog so a single Escape closes only the top-most
|
|
428
|
+
// layer (the popover's own focus trap handles it and stops propagation).
|
|
429
|
+
if (isImeKeyEvent(event) || hasActiveFocusTrapEscape()) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
425
432
|
event.preventDefault();
|
|
426
433
|
if (allowEscape) {
|
|
427
434
|
onOpenChange(false);
|
|
@@ -447,6 +454,11 @@ export function Dialog({
|
|
|
447
454
|
// Handle native cancel event (browser Escape handling)
|
|
448
455
|
const handleCancel = (event: React.SyntheticEvent<HTMLDialogElement>) => {
|
|
449
456
|
event.preventDefault();
|
|
457
|
+
// Defer to a popover/menu layered on top of this dialog; it will dismiss
|
|
458
|
+
// itself on the same Escape press.
|
|
459
|
+
if (hasActiveFocusTrapEscape()) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
450
462
|
if (allowEscape) {
|
|
451
463
|
onOpenChange(false);
|
|
452
464
|
}
|