@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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file CodeBlock.test.tsx
|
|
5
|
+
* @input Uses vitest, @testing-library/react, CodeBlock component
|
|
6
|
+
* @output Unit tests for CodeBlock (copy button, collapse, scroll region a11y)
|
|
7
|
+
* @position Testing; validates CodeBlock implementation
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When CodeBlock.tsx changes, update tests to match new behavior
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
13
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
14
|
+
import {CodeBlock} from './CodeBlock';
|
|
15
|
+
|
|
16
|
+
// A code sample long enough to exceed the default collapsible threshold (10).
|
|
17
|
+
const LONG_CODE = Array.from(
|
|
18
|
+
{length: 15},
|
|
19
|
+
(_, i) => `const line${i} = ${i};`,
|
|
20
|
+
).join('\n');
|
|
21
|
+
|
|
22
|
+
describe('CodeBlock', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// jsdom does not implement the async Clipboard API.
|
|
25
|
+
Object.assign(navigator, {
|
|
26
|
+
clipboard: {writeText: vi.fn().mockResolvedValue(undefined)},
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders the code', () => {
|
|
31
|
+
render(<CodeBlock code="const x = 1;" language="javascript" />);
|
|
32
|
+
expect(screen.getByText(/const/)).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('makes the scroll container keyboard-focusable', () => {
|
|
36
|
+
render(<CodeBlock code="const x = 1;" language="javascript" />);
|
|
37
|
+
const region = screen.getByRole('group');
|
|
38
|
+
expect(region).toHaveAttribute('tabindex', '0');
|
|
39
|
+
expect(region).toHaveAttribute('aria-label', 'javascript');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('labels the scroll region "Code" when no language label is shown', () => {
|
|
43
|
+
render(<CodeBlock code="hello" hasLanguageLabel={false} />);
|
|
44
|
+
const region = screen.getByRole('group');
|
|
45
|
+
expect(region).toHaveAttribute('tabindex', '0');
|
|
46
|
+
expect(region).toHaveAttribute('aria-label', 'Code');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('copies code when the copy button is clicked', () => {
|
|
50
|
+
render(<CodeBlock code="const x = 1;" language="javascript" />);
|
|
51
|
+
const copyButton = screen.getByRole('button', {name: 'Copy code'});
|
|
52
|
+
fireEvent.click(copyButton);
|
|
53
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('const x = 1;');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('does NOT collapse the block when the copy button is clicked', () => {
|
|
57
|
+
render(
|
|
58
|
+
<CodeBlock
|
|
59
|
+
code={LONG_CODE}
|
|
60
|
+
language="javascript"
|
|
61
|
+
title="example"
|
|
62
|
+
isCollapsible
|
|
63
|
+
/>,
|
|
64
|
+
);
|
|
65
|
+
// The collapsible header exposes aria-expanded.
|
|
66
|
+
const header = screen
|
|
67
|
+
.getAllByRole('button')
|
|
68
|
+
.find(el => el.hasAttribute('aria-expanded'));
|
|
69
|
+
expect(header).toBeTruthy();
|
|
70
|
+
expect(header).toHaveAttribute('aria-expanded', 'true');
|
|
71
|
+
|
|
72
|
+
const copyButton = screen.getByRole('button', {name: 'Copy code'});
|
|
73
|
+
fireEvent.click(copyButton);
|
|
74
|
+
|
|
75
|
+
// Clicking Copy must not toggle the collapsible header.
|
|
76
|
+
expect(header).toHaveAttribute('aria-expanded', 'true');
|
|
77
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('does not nest the copy button inside the collapsible header role="button"', () => {
|
|
81
|
+
render(
|
|
82
|
+
<CodeBlock
|
|
83
|
+
code={LONG_CODE}
|
|
84
|
+
language="javascript"
|
|
85
|
+
title="example"
|
|
86
|
+
isCollapsible
|
|
87
|
+
/>,
|
|
88
|
+
);
|
|
89
|
+
const header = screen
|
|
90
|
+
.getAllByRole('button')
|
|
91
|
+
.find(el => el.hasAttribute('aria-expanded'));
|
|
92
|
+
const copyButton = screen.getByRole('button', {name: 'Copy code'});
|
|
93
|
+
expect(header).toBeTruthy();
|
|
94
|
+
// The copy button must be a sibling, not a descendant of the interactive
|
|
95
|
+
// header — nested interactive controls are invalid ARIA.
|
|
96
|
+
expect(header!.contains(copyButton)).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('still toggles collapse when the header itself is clicked', () => {
|
|
100
|
+
render(
|
|
101
|
+
<CodeBlock
|
|
102
|
+
code={LONG_CODE}
|
|
103
|
+
language="javascript"
|
|
104
|
+
title="example"
|
|
105
|
+
isCollapsible
|
|
106
|
+
/>,
|
|
107
|
+
);
|
|
108
|
+
const header = screen
|
|
109
|
+
.getAllByRole('button')
|
|
110
|
+
.find(el => el.hasAttribute('aria-expanded'))!;
|
|
111
|
+
expect(header).toHaveAttribute('aria-expanded', 'true');
|
|
112
|
+
fireEvent.click(header);
|
|
113
|
+
expect(header).toHaveAttribute('aria-expanded', 'false');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -87,7 +87,7 @@ const styles = stylex.create({
|
|
|
87
87
|
backgroundColor: 'var(--color-syntax-background)',
|
|
88
88
|
overflow: 'hidden',
|
|
89
89
|
},
|
|
90
|
-
|
|
90
|
+
headerRow: {
|
|
91
91
|
display: 'flex',
|
|
92
92
|
alignItems: 'center',
|
|
93
93
|
justifyContent: 'space-between',
|
|
@@ -97,6 +97,19 @@ const styles = stylex.create({
|
|
|
97
97
|
top: 0,
|
|
98
98
|
zIndex: 1,
|
|
99
99
|
},
|
|
100
|
+
header: {
|
|
101
|
+
display: 'flex',
|
|
102
|
+
alignItems: 'center',
|
|
103
|
+
flex: 1,
|
|
104
|
+
minWidth: 0,
|
|
105
|
+
// Reset default <button> appearance for the collapsible title control.
|
|
106
|
+
padding: 0,
|
|
107
|
+
border: 'none',
|
|
108
|
+
backgroundColor: 'transparent',
|
|
109
|
+
color: 'inherit',
|
|
110
|
+
font: 'inherit',
|
|
111
|
+
textAlign: 'start',
|
|
112
|
+
},
|
|
100
113
|
headerWithDivider: {
|
|
101
114
|
paddingBlock: spacingVars['--spacing-2'],
|
|
102
115
|
borderBottomWidth: borderVars['--border-width'],
|
|
@@ -469,7 +482,10 @@ function useTokenLines(
|
|
|
469
482
|
// Span-mode code element
|
|
470
483
|
// ---------------------------------------------------------------------------
|
|
471
484
|
|
|
472
|
-
function buildSpanLine(
|
|
485
|
+
function buildSpanLine(
|
|
486
|
+
lineText: string,
|
|
487
|
+
tokens: SyntaxToken[],
|
|
488
|
+
): React.ReactNode {
|
|
473
489
|
if (tokens.length === 0) {
|
|
474
490
|
return lineText || '\u200b';
|
|
475
491
|
}
|
|
@@ -677,7 +693,9 @@ export function CodeBlock({
|
|
|
677
693
|
const copyButtonEl = hasCopyButton ? (
|
|
678
694
|
<button
|
|
679
695
|
type="button"
|
|
680
|
-
onClick={
|
|
696
|
+
onClick={e => {
|
|
697
|
+
// Stop propagation so copying does not toggle the collapsible header.
|
|
698
|
+
e.stopPropagation();
|
|
681
699
|
void handleCopy();
|
|
682
700
|
}}
|
|
683
701
|
aria-label={copied ? 'Copied' : 'Copy code'}
|
|
@@ -691,39 +709,44 @@ export function CodeBlock({
|
|
|
691
709
|
|
|
692
710
|
const headerEl = showHeader ? (
|
|
693
711
|
<div
|
|
694
|
-
role={canCollapse ? 'button' : undefined}
|
|
695
|
-
tabIndex={canCollapse ? 0 : undefined}
|
|
696
|
-
aria-expanded={canCollapse ? !isCollapsed : undefined}
|
|
697
|
-
onClick={canCollapse ? () => setIsCollapsed(prev => !prev) : undefined}
|
|
698
|
-
onKeyDown={
|
|
699
|
-
canCollapse
|
|
700
|
-
? (e: React.KeyboardEvent) => {
|
|
701
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
702
|
-
e.preventDefault();
|
|
703
|
-
setIsCollapsed(prev => !prev);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
: undefined
|
|
707
|
-
}
|
|
708
712
|
{...stylex.props(
|
|
709
|
-
styles.
|
|
713
|
+
styles.headerRow,
|
|
710
714
|
hasLineNumbers ? styles.headerWithDivider : styles.headerCompact,
|
|
711
|
-
canCollapse && styles.headerCollapsible,
|
|
712
715
|
)}>
|
|
713
|
-
<
|
|
714
|
-
{
|
|
715
|
-
{
|
|
716
|
-
{
|
|
717
|
-
{canCollapse
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
716
|
+
<div
|
|
717
|
+
role={canCollapse ? 'button' : undefined}
|
|
718
|
+
tabIndex={canCollapse ? 0 : undefined}
|
|
719
|
+
aria-expanded={canCollapse ? !isCollapsed : undefined}
|
|
720
|
+
onClick={canCollapse ? () => setIsCollapsed(prev => !prev) : undefined}
|
|
721
|
+
onKeyDown={
|
|
722
|
+
canCollapse
|
|
723
|
+
? (e: React.KeyboardEvent) => {
|
|
724
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
725
|
+
e.preventDefault();
|
|
726
|
+
setIsCollapsed(prev => !prev);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
: undefined
|
|
730
|
+
}
|
|
731
|
+
{...stylex.props(
|
|
732
|
+
styles.header,
|
|
733
|
+
canCollapse && styles.headerCollapsible,
|
|
734
|
+
)}>
|
|
735
|
+
<span {...stylex.props(styles.headerTitle)}>
|
|
736
|
+
{title}
|
|
737
|
+
{title && languageLabel ? ' — ' : ''}
|
|
738
|
+
{languageLabel}
|
|
739
|
+
{canCollapse && (
|
|
740
|
+
<span
|
|
741
|
+
{...stylex.props(
|
|
742
|
+
styles.collapseChevron,
|
|
743
|
+
isCollapsed && styles.collapseChevronCollapsed,
|
|
744
|
+
)}>
|
|
745
|
+
<Icon icon="chevronDown" size="xsm" color="inherit" />
|
|
746
|
+
</span>
|
|
747
|
+
)}
|
|
748
|
+
</span>
|
|
749
|
+
</div>
|
|
727
750
|
{copyButtonEl}
|
|
728
751
|
</div>
|
|
729
752
|
) : null;
|
|
@@ -731,6 +754,13 @@ export function CodeBlock({
|
|
|
731
754
|
const codeBody = (
|
|
732
755
|
<div
|
|
733
756
|
ref={scrollContainerRef}
|
|
757
|
+
// The scroll container is keyboard-focusable so keyboard users can
|
|
758
|
+
// scroll long or wide code that overflows the viewport. Uses
|
|
759
|
+
// role="group" (not "region") so multiple code blocks on a page don't
|
|
760
|
+
// create duplicate same-named landmarks (axe: landmark-unique).
|
|
761
|
+
tabIndex={0}
|
|
762
|
+
role="group"
|
|
763
|
+
aria-label={languageLabel ?? 'Code'}
|
|
734
764
|
{...mergeProps(stylex.props(styles.scrollContainer), {
|
|
735
765
|
style: scrollStyle,
|
|
736
766
|
})}>
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
11
|
-
import {render, screen, waitFor} from '@testing-library/react';
|
|
11
|
+
import {render, screen, waitFor, fireEvent} from '@testing-library/react';
|
|
12
12
|
import {CommandPalette} from './CommandPalette';
|
|
13
13
|
import {createStaticSource} from '@astryxdesign/core/Typeahead';
|
|
14
|
+
import type {SearchSource, SearchableItem} from '@astryxdesign/core/Typeahead';
|
|
14
15
|
|
|
15
16
|
const simpleSource = createStaticSource([
|
|
16
17
|
{id: 'home', label: 'Home'},
|
|
@@ -223,4 +224,49 @@ describe('CommandPalette', () => {
|
|
|
223
224
|
dialog.dispatchEvent(escapeEvent);
|
|
224
225
|
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
225
226
|
});
|
|
227
|
+
|
|
228
|
+
it('keeps the empty state mounted while a no-result search is pending', async () => {
|
|
229
|
+
// A source whose searches resolve only when we release them, so we can
|
|
230
|
+
// observe the render output while a search transition is in flight.
|
|
231
|
+
const resolvers: ((items: SearchableItem[]) => void)[] = [];
|
|
232
|
+
const source: SearchSource = {
|
|
233
|
+
bootstrap: () => [],
|
|
234
|
+
async search(): Promise<SearchableItem[]> {
|
|
235
|
+
return new Promise<SearchableItem[]>(resolve => {
|
|
236
|
+
resolvers.push(resolve);
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
render(
|
|
242
|
+
<CommandPalette
|
|
243
|
+
isOpen={true}
|
|
244
|
+
onOpenChange={() => {}}
|
|
245
|
+
searchSource={source}
|
|
246
|
+
emptyBootstrapText="Type to search"
|
|
247
|
+
emptySearchText="No results"
|
|
248
|
+
/>,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const input = screen.getByRole('combobox');
|
|
252
|
+
|
|
253
|
+
// First search: commits an empty result for query "z".
|
|
254
|
+
fireEvent.change(input, {target: {value: 'z'}});
|
|
255
|
+
await waitFor(() => expect(resolvers).toHaveLength(1));
|
|
256
|
+
resolvers[0]([]);
|
|
257
|
+
await waitFor(() =>
|
|
258
|
+
expect(screen.getByText('No results')).toBeInTheDocument(),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Second keystroke while already empty: the empty state must remain in the
|
|
262
|
+
// DOM for the whole pending window — no unmount/remount flash.
|
|
263
|
+
fireEvent.change(input, {target: {value: 'zz'}});
|
|
264
|
+
expect(screen.getByText('No results')).toBeInTheDocument();
|
|
265
|
+
await waitFor(() => expect(resolvers).toHaveLength(2));
|
|
266
|
+
expect(screen.getByText('No results')).toBeInTheDocument();
|
|
267
|
+
resolvers[1]([]);
|
|
268
|
+
await waitFor(() =>
|
|
269
|
+
expect(screen.getByText('No results')).toBeInTheDocument(),
|
|
270
|
+
);
|
|
271
|
+
});
|
|
226
272
|
});
|
|
@@ -24,12 +24,7 @@ import {
|
|
|
24
24
|
type ReactNode,
|
|
25
25
|
} from 'react';
|
|
26
26
|
import {Dialog} from '../Dialog';
|
|
27
|
-
import {
|
|
28
|
-
Layout,
|
|
29
|
-
LayoutHeader,
|
|
30
|
-
LayoutContent,
|
|
31
|
-
LayoutFooter,
|
|
32
|
-
} from '../Layout';
|
|
27
|
+
import {Layout, LayoutHeader, LayoutContent, LayoutFooter} from '../Layout';
|
|
33
28
|
import type {SearchSource, SearchableItem} from '../Typeahead';
|
|
34
29
|
import {useCombobox} from '../Selector';
|
|
35
30
|
import type {SelectorOptionData} from '../Selector';
|
|
@@ -133,9 +128,7 @@ function getGroup(item: SearchableItem): string | undefined {
|
|
|
133
128
|
* When groups are present, items are ordered by group (preserving insertion order),
|
|
134
129
|
* with ungrouped items at the end — matching the DefaultRenderer layout.
|
|
135
130
|
*/
|
|
136
|
-
function buildSelectableItems(
|
|
137
|
-
items: SearchableItem[],
|
|
138
|
-
): SelectorOptionData[] {
|
|
131
|
+
function buildSelectableItems(items: SearchableItem[]): SelectorOptionData[] {
|
|
139
132
|
const hasGroups = items.some(item => getGroup(item) != null);
|
|
140
133
|
|
|
141
134
|
if (!hasGroups) {
|
|
@@ -258,9 +251,7 @@ function ItemRenderer<T extends SearchableItem>({
|
|
|
258
251
|
* />
|
|
259
252
|
* ```
|
|
260
253
|
*/
|
|
261
|
-
export function CommandPalette<
|
|
262
|
-
T extends SearchableItem = SearchableItem,
|
|
263
|
-
>({
|
|
254
|
+
export function CommandPalette<T extends SearchableItem = SearchableItem>({
|
|
264
255
|
ref,
|
|
265
256
|
isOpen,
|
|
266
257
|
isInline,
|
|
@@ -488,13 +479,12 @@ export function CommandPalette<
|
|
|
488
479
|
],
|
|
489
480
|
);
|
|
490
481
|
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
482
|
+
// `search` is the committed query the on-screen results correspond to (it
|
|
483
|
+
// still holds the previous query while a transition is pending). Keeping both
|
|
484
|
+
// flags ungated by `isPending` makes them exhaustive over the empty case, so
|
|
485
|
+
// the empty state is never unmounted and re-added mid-search (which flashed).
|
|
495
486
|
const showEmptyBootstrap = search === '' && optimisticResults.length === 0;
|
|
496
|
-
const showEmptySearch =
|
|
497
|
-
!isPending && search !== '' && optimisticResults.length === 0;
|
|
487
|
+
const showEmptySearch = search !== '' && optimisticResults.length === 0;
|
|
498
488
|
|
|
499
489
|
let listContent: ReactNode;
|
|
500
490
|
if (showEmptyBootstrap) {
|
|
@@ -502,9 +492,7 @@ export function CommandPalette<
|
|
|
502
492
|
<CommandPaletteEmpty>{emptyBootstrapText}</CommandPaletteEmpty>
|
|
503
493
|
);
|
|
504
494
|
} else if (showEmptySearch) {
|
|
505
|
-
listContent =
|
|
506
|
-
<CommandPaletteEmpty>{emptySearchText}</CommandPaletteEmpty>
|
|
507
|
-
);
|
|
495
|
+
listContent = <CommandPaletteEmpty>{emptySearchText}</CommandPaletteEmpty>;
|
|
508
496
|
} else {
|
|
509
497
|
listContent = (
|
|
510
498
|
<ItemRenderer
|
|
@@ -53,10 +53,10 @@ export const docs = {
|
|
|
53
53
|
default: "'md'",
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
|
-
name: '
|
|
57
|
-
type: '
|
|
58
|
-
description: '
|
|
59
|
-
default: '
|
|
56
|
+
name: 'label',
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: 'Accessible name for the menu surface, announced when it opens.',
|
|
59
|
+
default: "'Context menu'",
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
name: 'isDisabled',
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
12
|
-
import {render, screen, fireEvent} from '@testing-library/react';
|
|
12
|
+
import {render, screen, fireEvent, act} from '@testing-library/react';
|
|
13
13
|
import userEvent from '@testing-library/user-event';
|
|
14
14
|
import {ContextMenu} from './ContextMenu';
|
|
15
15
|
import {ContextMenuItem} from './index';
|
|
@@ -60,6 +60,52 @@ describe('ContextMenu', () => {
|
|
|
60
60
|
expect(screen.getByRole('menu', {hidden: true})).toBeInTheDocument();
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
it('typeahead focuses the matching menu item (menus-11)', () => {
|
|
64
|
+
render(
|
|
65
|
+
<ContextMenu items={[{label: 'Cut'}, {label: 'Copy'}, {label: 'Paste'}]}>
|
|
66
|
+
<div>Right-click me</div>
|
|
67
|
+
</ContextMenu>,
|
|
68
|
+
);
|
|
69
|
+
fireEvent.contextMenu(screen.getByText('Right-click me'));
|
|
70
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
71
|
+
fireEvent.keyDown(menu, {key: 'p'});
|
|
72
|
+
expect(
|
|
73
|
+
screen.getByRole('menuitem', {name: 'Paste', hidden: true}),
|
|
74
|
+
).toHaveFocus();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('gives the menu an accessible name (menus-13)', () => {
|
|
78
|
+
render(
|
|
79
|
+
<ContextMenu items={[{label: 'Item 1'}]}>
|
|
80
|
+
<div>Right-click me</div>
|
|
81
|
+
</ContextMenu>,
|
|
82
|
+
);
|
|
83
|
+
// Defaults to "Context menu"; overridable via label.
|
|
84
|
+
expect(
|
|
85
|
+
screen.getByRole('menu', {name: 'Context menu', hidden: true}),
|
|
86
|
+
).toBeInTheDocument();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('uses a custom label', () => {
|
|
90
|
+
render(
|
|
91
|
+
<ContextMenu items={[{label: 'Item 1'}]} label="Row actions">
|
|
92
|
+
<div>Right-click me</div>
|
|
93
|
+
</ContextMenu>,
|
|
94
|
+
);
|
|
95
|
+
expect(
|
|
96
|
+
screen.getByRole('menu', {name: 'Row actions', hidden: true}),
|
|
97
|
+
).toBeInTheDocument();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('does not put aria-haspopup on the role-less trigger wrapper (menus-15)', () => {
|
|
101
|
+
render(
|
|
102
|
+
<ContextMenu items={[{label: 'Item 1'}]} data-testid="ctx">
|
|
103
|
+
<div>Right-click me</div>
|
|
104
|
+
</ContextMenu>,
|
|
105
|
+
);
|
|
106
|
+
expect(screen.getByTestId('ctx')).not.toHaveAttribute('aria-haspopup');
|
|
107
|
+
});
|
|
108
|
+
|
|
63
109
|
it('opens menu on right-click', () => {
|
|
64
110
|
render(
|
|
65
111
|
<ContextMenu items={[{label: 'Item 1'}]}>
|
|
@@ -71,6 +117,48 @@ describe('ContextMenu', () => {
|
|
|
71
117
|
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
72
118
|
});
|
|
73
119
|
|
|
120
|
+
it('closes on Escape even when opened without auto-focus', () => {
|
|
121
|
+
render(
|
|
122
|
+
<ContextMenu items={[{label: 'Item 1'}]}>
|
|
123
|
+
<div>Right-click me</div>
|
|
124
|
+
</ContextMenu>,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
fireEvent.contextMenu(screen.getByText('Right-click me'));
|
|
128
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
129
|
+
// Focus is not inside the menu, so the Escape path must be document-level.
|
|
130
|
+
fireEvent.keyDown(document, {key: 'Escape'});
|
|
131
|
+
expect(HTMLElement.prototype.hidePopover).toHaveBeenCalled();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('ignores Escape during IME composition', () => {
|
|
135
|
+
render(
|
|
136
|
+
<ContextMenu items={[{label: 'Item 1'}]}>
|
|
137
|
+
<div>Right-click me</div>
|
|
138
|
+
</ContextMenu>,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
fireEvent.contextMenu(screen.getByText('Right-click me'));
|
|
142
|
+
fireEvent.keyDown(document, {key: 'Escape', isComposing: true});
|
|
143
|
+
expect(HTMLElement.prototype.hidePopover).not.toHaveBeenCalled();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('restores focus to the trigger on close', () => {
|
|
147
|
+
render(
|
|
148
|
+
<ContextMenu items={[{label: 'Item 1'}]}>
|
|
149
|
+
<button type="button">Right-click me</button>
|
|
150
|
+
</ContextMenu>,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const trigger = screen.getByRole('button', {name: 'Right-click me'});
|
|
154
|
+
trigger.focus();
|
|
155
|
+
expect(trigger).toHaveFocus();
|
|
156
|
+
|
|
157
|
+
fireEvent.contextMenu(trigger);
|
|
158
|
+
fireEvent.keyDown(document, {key: 'Escape'});
|
|
159
|
+
expect(trigger).toHaveFocus();
|
|
160
|
+
});
|
|
161
|
+
|
|
74
162
|
it('prevents default context menu on right-click', () => {
|
|
75
163
|
render(
|
|
76
164
|
<ContextMenu items={[{label: 'Item 1'}]}>
|
|
@@ -103,6 +191,72 @@ describe('ContextMenu', () => {
|
|
|
103
191
|
);
|
|
104
192
|
expect(screen.getByTestId('my-context-menu')).toBeInTheDocument();
|
|
105
193
|
});
|
|
194
|
+
|
|
195
|
+
it('opens from a keyboard-invoked contextmenu (Shift+F10 / Menu key)', () => {
|
|
196
|
+
render(
|
|
197
|
+
<ContextMenu items={[{label: 'Item 1'}]} data-testid="ctx">
|
|
198
|
+
<div>Right-click me</div>
|
|
199
|
+
</ContextMenu>,
|
|
200
|
+
);
|
|
201
|
+
const trigger = screen.getByTestId('ctx');
|
|
202
|
+
// Anchor the trigger box so the rect fallback has a position to read.
|
|
203
|
+
trigger.getBoundingClientRect = () =>
|
|
204
|
+
({
|
|
205
|
+
left: 40,
|
|
206
|
+
top: 10,
|
|
207
|
+
bottom: 30,
|
|
208
|
+
right: 100,
|
|
209
|
+
width: 60,
|
|
210
|
+
height: 20,
|
|
211
|
+
}) as DOMRect;
|
|
212
|
+
// Keyboard-initiated contextmenu: coords are (0,0) and detail is 0.
|
|
213
|
+
fireEvent.contextMenu(trigger, {clientX: 0, clientY: 0, detail: 0});
|
|
214
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('opens on touch long-press', () => {
|
|
218
|
+
vi.useFakeTimers();
|
|
219
|
+
try {
|
|
220
|
+
render(
|
|
221
|
+
<ContextMenu items={[{label: 'Item 1'}]} data-testid="ctx">
|
|
222
|
+
<div>Long-press me</div>
|
|
223
|
+
</ContextMenu>,
|
|
224
|
+
);
|
|
225
|
+
const trigger = screen.getByTestId('ctx');
|
|
226
|
+
fireEvent.touchStart(trigger, {
|
|
227
|
+
touches: [{clientX: 20, clientY: 20}],
|
|
228
|
+
});
|
|
229
|
+
// Not open until the long-press threshold elapses.
|
|
230
|
+
expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
|
|
231
|
+
act(() => {
|
|
232
|
+
vi.advanceTimersByTime(500);
|
|
233
|
+
});
|
|
234
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
235
|
+
} finally {
|
|
236
|
+
vi.useRealTimers();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('cancels the long-press when the finger moves past the threshold', () => {
|
|
241
|
+
vi.useFakeTimers();
|
|
242
|
+
try {
|
|
243
|
+
render(
|
|
244
|
+
<ContextMenu items={[{label: 'Item 1'}]} data-testid="ctx">
|
|
245
|
+
<div>Long-press me</div>
|
|
246
|
+
</ContextMenu>,
|
|
247
|
+
);
|
|
248
|
+
const trigger = screen.getByTestId('ctx');
|
|
249
|
+
fireEvent.touchStart(trigger, {touches: [{clientX: 20, clientY: 20}]});
|
|
250
|
+
// Move past MOVE_CANCEL_PX (10px) — treated as a scroll, not a press.
|
|
251
|
+
fireEvent.touchMove(trigger, {touches: [{clientX: 20, clientY: 40}]});
|
|
252
|
+
act(() => {
|
|
253
|
+
vi.advanceTimersByTime(500);
|
|
254
|
+
});
|
|
255
|
+
expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
|
|
256
|
+
} finally {
|
|
257
|
+
vi.useRealTimers();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
106
260
|
});
|
|
107
261
|
|
|
108
262
|
describe('ContextMenu items', () => {
|