@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
|
@@ -200,6 +200,56 @@ describe('TopNavHeading', () => {
|
|
|
200
200
|
expect(screen.getByTestId('logo')).toBeInTheDocument();
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
+
describe('logo link accessible name', () => {
|
|
204
|
+
const logo = <img src="/logo.png" alt="" />;
|
|
205
|
+
|
|
206
|
+
// A logo image is decorative; when the logo is wrapped in a link the link
|
|
207
|
+
// itself needs an accessible name, otherwise axe reports link-name.
|
|
208
|
+
it('names the logo link in the independent-links config', () => {
|
|
209
|
+
render(
|
|
210
|
+
<TopNavHeading
|
|
211
|
+
logo={logo}
|
|
212
|
+
superheading="Suite"
|
|
213
|
+
superheadingHref="/suite"
|
|
214
|
+
heading="Product"
|
|
215
|
+
headingHref="/product"
|
|
216
|
+
/>,
|
|
217
|
+
);
|
|
218
|
+
// No link should have an empty accessible name.
|
|
219
|
+
for (const link of screen.getAllByRole('link')) {
|
|
220
|
+
expect(link).toHaveAccessibleName();
|
|
221
|
+
}
|
|
222
|
+
// The logo link is named from the heading.
|
|
223
|
+
expect(
|
|
224
|
+
screen.getAllByRole('link', {name: 'Product'}).length,
|
|
225
|
+
).toBeGreaterThanOrEqual(1);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('names the logo link in the menu + hrefs config', () => {
|
|
229
|
+
render(
|
|
230
|
+
<TopNavHeading
|
|
231
|
+
logo={logo}
|
|
232
|
+
superheading="Suite"
|
|
233
|
+
superheadingHref="/suite"
|
|
234
|
+
heading="Product"
|
|
235
|
+
headingHref="/product"
|
|
236
|
+
menu={<a href="#menu">Menu item</a>}
|
|
237
|
+
/>,
|
|
238
|
+
);
|
|
239
|
+
for (const link of screen.getAllByRole('link')) {
|
|
240
|
+
expect(link).toHaveAccessibleName();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('names a logo-only link via logoLabel', () => {
|
|
245
|
+
render(<TopNavHeading logo={logo} headingHref="/home" logoLabel="Home" />);
|
|
246
|
+
expect(screen.getByRole('link', {name: 'Home'})).toHaveAttribute(
|
|
247
|
+
'href',
|
|
248
|
+
'/home',
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
203
253
|
it('renders as anchor when href is provided', () => {
|
|
204
254
|
render(<TopNavHeading heading="Home" href="/" />);
|
|
205
255
|
const link = screen.getByRole('link');
|
|
@@ -214,6 +214,12 @@ export interface TopNavHeadingProps extends BaseProps<HTMLElement> {
|
|
|
214
214
|
* Can be an image, NavIcon, or any ReactNode.
|
|
215
215
|
*/
|
|
216
216
|
logo?: ReactNode;
|
|
217
|
+
/**
|
|
218
|
+
* Accessible name for the logo when it links somewhere (`headingHref`) and
|
|
219
|
+
* has no adjacent text to name it (for example a logo-only heading). Defaults
|
|
220
|
+
* to `heading` when available. Ignored when the logo is not a link.
|
|
221
|
+
*/
|
|
222
|
+
logoLabel?: string;
|
|
217
223
|
/**
|
|
218
224
|
* Product/app name.
|
|
219
225
|
*/
|
|
@@ -295,6 +301,7 @@ export interface TopNavHeadingProps extends BaseProps<HTMLElement> {
|
|
|
295
301
|
export function TopNavHeading({
|
|
296
302
|
as,
|
|
297
303
|
logo,
|
|
304
|
+
logoLabel,
|
|
298
305
|
heading,
|
|
299
306
|
headingHref: headingHrefProp,
|
|
300
307
|
href,
|
|
@@ -314,6 +321,10 @@ export function TopNavHeading({
|
|
|
314
321
|
const LinkComponent = useLinkComponent(as);
|
|
315
322
|
// Support both headingHref and legacy href
|
|
316
323
|
const headingHref = headingHrefProp ?? href;
|
|
324
|
+
// When the logo is wrapped in a link it needs its own accessible name (the
|
|
325
|
+
// logo image itself is decorative). Prefer an explicit logoLabel, fall back
|
|
326
|
+
// to the heading text. axe: link-name.
|
|
327
|
+
const logoLinkLabel = logoLabel ?? heading;
|
|
317
328
|
|
|
318
329
|
const rootRef = useRef<HTMLElement>(null);
|
|
319
330
|
|
|
@@ -425,6 +436,7 @@ export function TopNavHeading({
|
|
|
425
436
|
<Element
|
|
426
437
|
ref={ref as React.Ref<HTMLAnchorElement & HTMLDivElement>}
|
|
427
438
|
href={headingHref}
|
|
439
|
+
aria-label={headingHref ? logoLabel : undefined}
|
|
428
440
|
data-testid={testId}
|
|
429
441
|
{...mergeProps(
|
|
430
442
|
themeProps('top-nav-heading'),
|
|
@@ -533,6 +545,7 @@ export function TopNavHeading({
|
|
|
533
545
|
(headingHref ? (
|
|
534
546
|
<LinkComponent
|
|
535
547
|
href={headingHref}
|
|
548
|
+
aria-label={logoLinkLabel}
|
|
536
549
|
onClick={(e: React.MouseEvent) => e.stopPropagation()}
|
|
537
550
|
{...stylex.props(styles.logo)}>
|
|
538
551
|
{logo}
|
|
@@ -593,7 +606,10 @@ export function TopNavHeading({
|
|
|
593
606
|
{...props}>
|
|
594
607
|
{logo &&
|
|
595
608
|
(headingHref ? (
|
|
596
|
-
<LinkComponent
|
|
609
|
+
<LinkComponent
|
|
610
|
+
href={headingHref}
|
|
611
|
+
aria-label={logoLinkLabel}
|
|
612
|
+
{...stylex.props(styles.logo)}>
|
|
597
613
|
{logo}
|
|
598
614
|
</LinkComponent>
|
|
599
615
|
) : (
|
|
@@ -61,6 +61,44 @@ const deepItems: TreeListItemData[] = [
|
|
|
61
61
|
},
|
|
62
62
|
];
|
|
63
63
|
|
|
64
|
+
// APG keyboard fixtures (module-level to satisfy no-unstable-default-props).
|
|
65
|
+
const flatItems: TreeListItemData[] = [
|
|
66
|
+
{id: 'a', label: 'Apple'},
|
|
67
|
+
{id: 'b', label: 'Banana'},
|
|
68
|
+
{id: 'c', label: 'Cherry'},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const withDisabledItems: TreeListItemData[] = [
|
|
72
|
+
{id: 'a', label: 'Apple'},
|
|
73
|
+
{id: 'b', label: 'Banana', isDisabled: true},
|
|
74
|
+
{id: 'c', label: 'Cherry'},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const collapsedParentItems: TreeListItemData[] = [
|
|
78
|
+
{
|
|
79
|
+
id: 'parent',
|
|
80
|
+
label: 'Parent',
|
|
81
|
+
children: [
|
|
82
|
+
{id: 'child-1', label: 'Child 1'},
|
|
83
|
+
{id: 'child-2', label: 'Child 2'},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{id: 'sibling', label: 'Sibling'},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const expandedParentItems: TreeListItemData[] = [
|
|
90
|
+
{
|
|
91
|
+
id: 'parent',
|
|
92
|
+
label: 'Parent',
|
|
93
|
+
isExpanded: true,
|
|
94
|
+
children: [
|
|
95
|
+
{id: 'child-1', label: 'Child 1'},
|
|
96
|
+
{id: 'child-2', label: 'Child 2'},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{id: 'sibling', label: 'Sibling'},
|
|
100
|
+
];
|
|
101
|
+
|
|
64
102
|
describe('TreeList', () => {
|
|
65
103
|
// ===========================================================================
|
|
66
104
|
// Basic rendering
|
|
@@ -160,6 +198,26 @@ describe('TreeList', () => {
|
|
|
160
198
|
expect(item).not.toHaveAttribute('aria-expanded');
|
|
161
199
|
});
|
|
162
200
|
|
|
201
|
+
it('renders a keyboard-focusable toggle button for parents without onClick/href', () => {
|
|
202
|
+
render(<TreeList items={nestedItems} />);
|
|
203
|
+
const toggle = screen.getByRole('button', {name: 'Toggle children'});
|
|
204
|
+
expect(toggle).toBeInTheDocument();
|
|
205
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('expands a parent from the keyboard via the toggle button', async () => {
|
|
209
|
+
const user = userEvent.setup();
|
|
210
|
+
render(<TreeList items={nestedItems} />);
|
|
211
|
+
// Collapsed: children are not rendered.
|
|
212
|
+
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
213
|
+
const toggle = screen.getByRole('button', {name: 'Toggle children'});
|
|
214
|
+
toggle.focus();
|
|
215
|
+
expect(toggle).toHaveFocus();
|
|
216
|
+
await user.keyboard('{Enter}');
|
|
217
|
+
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
218
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
219
|
+
});
|
|
220
|
+
|
|
163
221
|
it('renders group role for nested children', () => {
|
|
164
222
|
render(<TreeList items={nestedItemsExpanded} />);
|
|
165
223
|
const groups = document.querySelectorAll('[role="group"]');
|
|
@@ -210,18 +268,14 @@ describe('TreeList', () => {
|
|
|
210
268
|
it('fires onClick when invisible button is clicked', async () => {
|
|
211
269
|
const user = userEvent.setup();
|
|
212
270
|
const onClick = vi.fn();
|
|
213
|
-
const items: TreeListItemData[] = [
|
|
214
|
-
{id: 'a', label: 'Clickable', onClick},
|
|
215
|
-
];
|
|
271
|
+
const items: TreeListItemData[] = [{id: 'a', label: 'Clickable', onClick}];
|
|
216
272
|
render(<TreeList items={items} />);
|
|
217
273
|
await user.click(screen.getByRole('button'));
|
|
218
274
|
expect(onClick).toHaveBeenCalledTimes(1);
|
|
219
275
|
});
|
|
220
276
|
|
|
221
277
|
it('renders an invisible anchor when href is provided', () => {
|
|
222
|
-
const items: TreeListItemData[] = [
|
|
223
|
-
{id: 'a', label: 'Link', href: '/docs'},
|
|
224
|
-
];
|
|
278
|
+
const items: TreeListItemData[] = [{id: 'a', label: 'Link', href: '/docs'}];
|
|
225
279
|
const {container} = render(<TreeList items={items} />);
|
|
226
280
|
const anchor = container.querySelector('a');
|
|
227
281
|
expect(anchor).toBeInTheDocument();
|
|
@@ -317,4 +371,239 @@ describe('TreeList', () => {
|
|
|
317
371
|
const root = screen.getByTestId('tree');
|
|
318
372
|
expect(root.className).toContain('astryx-tree-list');
|
|
319
373
|
});
|
|
374
|
+
|
|
375
|
+
// ===========================================================================
|
|
376
|
+
// APG structural ARIA (aria-level / aria-posinset / aria-setsize)
|
|
377
|
+
// ===========================================================================
|
|
378
|
+
|
|
379
|
+
it('sets aria-level, aria-posinset, and aria-setsize at the top level', () => {
|
|
380
|
+
render(<TreeList items={flatItems} />);
|
|
381
|
+
const apple = screen.getByText('Apple').closest('li');
|
|
382
|
+
expect(apple).toHaveAttribute('aria-level', '1');
|
|
383
|
+
expect(apple).toHaveAttribute('aria-posinset', '1');
|
|
384
|
+
expect(apple).toHaveAttribute('aria-setsize', '3');
|
|
385
|
+
|
|
386
|
+
const cherry = screen.getByText('Cherry').closest('li');
|
|
387
|
+
expect(cherry).toHaveAttribute('aria-posinset', '3');
|
|
388
|
+
expect(cherry).toHaveAttribute('aria-setsize', '3');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('sets aria-level/posinset/setsize correctly at deeper levels', () => {
|
|
392
|
+
render(<TreeList items={expandedParentItems} />);
|
|
393
|
+
const parent = screen.getByText('Parent').closest('li');
|
|
394
|
+
expect(parent).toHaveAttribute('aria-level', '1');
|
|
395
|
+
expect(parent).toHaveAttribute('aria-setsize', '2');
|
|
396
|
+
|
|
397
|
+
const child1 = screen.getByText('Child 1').closest('li');
|
|
398
|
+
expect(child1).toHaveAttribute('aria-level', '2');
|
|
399
|
+
expect(child1).toHaveAttribute('aria-posinset', '1');
|
|
400
|
+
expect(child1).toHaveAttribute('aria-setsize', '2');
|
|
401
|
+
|
|
402
|
+
const child2 = screen.getByText('Child 2').closest('li');
|
|
403
|
+
expect(child2).toHaveAttribute('aria-level', '2');
|
|
404
|
+
expect(child2).toHaveAttribute('aria-posinset', '2');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('sets aria-level across three depths', () => {
|
|
408
|
+
render(<TreeList items={deepItems} />);
|
|
409
|
+
expect(screen.getByText('Root').closest('li')).toHaveAttribute(
|
|
410
|
+
'aria-level',
|
|
411
|
+
'1',
|
|
412
|
+
);
|
|
413
|
+
expect(screen.getByText('Mid').closest('li')).toHaveAttribute(
|
|
414
|
+
'aria-level',
|
|
415
|
+
'2',
|
|
416
|
+
);
|
|
417
|
+
expect(screen.getByText('Leaf').closest('li')).toHaveAttribute(
|
|
418
|
+
'aria-level',
|
|
419
|
+
'3',
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// ===========================================================================
|
|
424
|
+
// Roving tabindex
|
|
425
|
+
// ===========================================================================
|
|
426
|
+
|
|
427
|
+
it('makes exactly one treeitem tabbable by default (the first enabled)', () => {
|
|
428
|
+
render(<TreeList items={flatItems} />);
|
|
429
|
+
const treeitems = screen.getAllByRole('treeitem');
|
|
430
|
+
const tabbable = treeitems.filter(
|
|
431
|
+
el => el.getAttribute('tabindex') === '0',
|
|
432
|
+
);
|
|
433
|
+
expect(tabbable).toHaveLength(1);
|
|
434
|
+
expect(tabbable[0]).toBe(screen.getByText('Apple').closest('li'));
|
|
435
|
+
expect(screen.getByText('Banana').closest('li')).toHaveAttribute(
|
|
436
|
+
'tabindex',
|
|
437
|
+
'-1',
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('defaults the tab stop to the selected item when one is selected', () => {
|
|
442
|
+
const items: TreeListItemData[] = [
|
|
443
|
+
{id: 'a', label: 'Apple'},
|
|
444
|
+
{id: 'b', label: 'Banana', isSelected: true},
|
|
445
|
+
{id: 'c', label: 'Cherry'},
|
|
446
|
+
];
|
|
447
|
+
render(<TreeList items={items} />);
|
|
448
|
+
expect(screen.getByText('Banana').closest('li')).toHaveAttribute(
|
|
449
|
+
'tabindex',
|
|
450
|
+
'0',
|
|
451
|
+
);
|
|
452
|
+
expect(screen.getByText('Apple').closest('li')).toHaveAttribute(
|
|
453
|
+
'tabindex',
|
|
454
|
+
'-1',
|
|
455
|
+
);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('moves the single tab stop when focus moves via keyboard', async () => {
|
|
459
|
+
const user = userEvent.setup();
|
|
460
|
+
render(<TreeList items={flatItems} />);
|
|
461
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
462
|
+
apple.focus();
|
|
463
|
+
await user.keyboard('{ArrowDown}');
|
|
464
|
+
const treeitems = screen.getAllByRole('treeitem');
|
|
465
|
+
const tabbable = treeitems.filter(
|
|
466
|
+
el => el.getAttribute('tabindex') === '0',
|
|
467
|
+
);
|
|
468
|
+
expect(tabbable).toHaveLength(1);
|
|
469
|
+
expect(tabbable[0]).toBe(screen.getByText('Banana').closest('li'));
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// ===========================================================================
|
|
473
|
+
// APG keyboard navigation
|
|
474
|
+
// ===========================================================================
|
|
475
|
+
|
|
476
|
+
it('ArrowDown / ArrowUp move focus between visible treeitems', async () => {
|
|
477
|
+
const user = userEvent.setup();
|
|
478
|
+
render(<TreeList items={flatItems} />);
|
|
479
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
480
|
+
apple.focus();
|
|
481
|
+
await user.keyboard('{ArrowDown}');
|
|
482
|
+
expect(document.activeElement).toBe(
|
|
483
|
+
screen.getByText('Banana').closest('li'),
|
|
484
|
+
);
|
|
485
|
+
await user.keyboard('{ArrowDown}');
|
|
486
|
+
expect(document.activeElement).toBe(
|
|
487
|
+
screen.getByText('Cherry').closest('li'),
|
|
488
|
+
);
|
|
489
|
+
await user.keyboard('{ArrowUp}');
|
|
490
|
+
expect(document.activeElement).toBe(
|
|
491
|
+
screen.getByText('Banana').closest('li'),
|
|
492
|
+
);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('ArrowDown / ArrowUp skip disabled treeitems', async () => {
|
|
496
|
+
const user = userEvent.setup();
|
|
497
|
+
render(<TreeList items={withDisabledItems} />);
|
|
498
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
499
|
+
apple.focus();
|
|
500
|
+
await user.keyboard('{ArrowDown}');
|
|
501
|
+
// Banana is disabled → skipped, lands on Cherry.
|
|
502
|
+
expect(document.activeElement).toBe(
|
|
503
|
+
screen.getByText('Cherry').closest('li'),
|
|
504
|
+
);
|
|
505
|
+
await user.keyboard('{ArrowUp}');
|
|
506
|
+
expect(document.activeElement).toBe(
|
|
507
|
+
screen.getByText('Apple').closest('li'),
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('ArrowRight expands a collapsed parent, then enters the first child', async () => {
|
|
512
|
+
const user = userEvent.setup();
|
|
513
|
+
render(<TreeList items={collapsedParentItems} />);
|
|
514
|
+
const parent = screen.getByText('Parent').closest('li')!;
|
|
515
|
+
parent.focus();
|
|
516
|
+
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
517
|
+
await user.keyboard('{ArrowRight}');
|
|
518
|
+
// First ArrowRight expands.
|
|
519
|
+
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
520
|
+
expect(document.activeElement).toBe(parent);
|
|
521
|
+
await user.keyboard('{ArrowRight}');
|
|
522
|
+
// Second ArrowRight moves into first child.
|
|
523
|
+
expect(document.activeElement).toBe(
|
|
524
|
+
screen.getByText('Child 1').closest('li'),
|
|
525
|
+
);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('ArrowRight on a leaf is a no-op', async () => {
|
|
529
|
+
const user = userEvent.setup();
|
|
530
|
+
render(<TreeList items={flatItems} />);
|
|
531
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
532
|
+
apple.focus();
|
|
533
|
+
await user.keyboard('{ArrowRight}');
|
|
534
|
+
expect(document.activeElement).toBe(apple);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('ArrowLeft collapses an expanded parent, then moves to parent', async () => {
|
|
538
|
+
const user = userEvent.setup();
|
|
539
|
+
render(<TreeList items={expandedParentItems} />);
|
|
540
|
+
const parent = screen.getByText('Parent').closest('li')!;
|
|
541
|
+
// Focus a child first.
|
|
542
|
+
const child1 = screen.getByText('Child 1').closest('li')!;
|
|
543
|
+
child1.focus();
|
|
544
|
+
await user.keyboard('{ArrowLeft}');
|
|
545
|
+
// Child is a leaf → ArrowLeft moves to parent.
|
|
546
|
+
expect(document.activeElement).toBe(parent);
|
|
547
|
+
await user.keyboard('{ArrowLeft}');
|
|
548
|
+
// Parent is expanded → ArrowLeft collapses it.
|
|
549
|
+
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('Home and End move to the first and last visible treeitems', async () => {
|
|
553
|
+
const user = userEvent.setup();
|
|
554
|
+
render(<TreeList items={flatItems} />);
|
|
555
|
+
const banana = screen.getByText('Banana').closest('li')!;
|
|
556
|
+
banana.focus();
|
|
557
|
+
await user.keyboard('{End}');
|
|
558
|
+
expect(document.activeElement).toBe(
|
|
559
|
+
screen.getByText('Cherry').closest('li'),
|
|
560
|
+
);
|
|
561
|
+
await user.keyboard('{Home}');
|
|
562
|
+
expect(document.activeElement).toBe(
|
|
563
|
+
screen.getByText('Apple').closest('li'),
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('Enter activates the item onClick', async () => {
|
|
568
|
+
const user = userEvent.setup();
|
|
569
|
+
const onClick = vi.fn();
|
|
570
|
+
const items: TreeListItemData[] = [{id: 'a', label: 'Apple', onClick}];
|
|
571
|
+
render(<TreeList items={items} />);
|
|
572
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
573
|
+
apple.focus();
|
|
574
|
+
await user.keyboard('{Enter}');
|
|
575
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('Space activates the item onClick', async () => {
|
|
579
|
+
const user = userEvent.setup();
|
|
580
|
+
const onClick = vi.fn();
|
|
581
|
+
const items: TreeListItemData[] = [{id: 'a', label: 'Apple', onClick}];
|
|
582
|
+
render(<TreeList items={items} />);
|
|
583
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
584
|
+
apple.focus();
|
|
585
|
+
await user.keyboard(' ');
|
|
586
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('Enter toggles expansion for a parent without its own action', async () => {
|
|
590
|
+
const user = userEvent.setup();
|
|
591
|
+
render(<TreeList items={collapsedParentItems} />);
|
|
592
|
+
const parent = screen.getByText('Parent').closest('li')!;
|
|
593
|
+
parent.focus();
|
|
594
|
+
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
595
|
+
await user.keyboard('{Enter}');
|
|
596
|
+
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('typeahead moves focus to the next item matching typed characters', async () => {
|
|
600
|
+
const user = userEvent.setup();
|
|
601
|
+
render(<TreeList items={flatItems} />);
|
|
602
|
+
const apple = screen.getByText('Apple').closest('li')!;
|
|
603
|
+
apple.focus();
|
|
604
|
+
await user.keyboard('c');
|
|
605
|
+
expect(document.activeElement).toBe(
|
|
606
|
+
screen.getByText('Cherry').closest('li'),
|
|
607
|
+
);
|
|
608
|
+
});
|
|
320
609
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file TreeList.tsx
|
|
7
|
-
* @input Uses React, StyleX, theme tokens, TreeListItem, TreeListTypes
|
|
7
|
+
* @input Uses React, StyleX, theme tokens, TreeListItem, TreeListTypes, useTreeFocus
|
|
8
8
|
* @output Exports TreeList component, TreeListProps type
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
@@ -23,6 +23,7 @@ import type {BaseProps} from '../BaseProps';
|
|
|
23
23
|
import {TreeListItem} from './TreeListItem';
|
|
24
24
|
import type {TreeListItemData, TreeListDensity} from './TreeListTypes';
|
|
25
25
|
import {themeProps} from '../utils/themeProps';
|
|
26
|
+
import {useTreeFocus} from '../hooks/useTreeFocus';
|
|
26
27
|
|
|
27
28
|
// =============================================================================
|
|
28
29
|
// Types
|
|
@@ -97,6 +98,35 @@ function collectExpandedKeys(items: TreeListItemData[]): string[] {
|
|
|
97
98
|
return keys;
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Compute the initial roving-tabindex seed: the first selected enabled item in
|
|
103
|
+
* document order, else the first enabled item, else the first item. The hook
|
|
104
|
+
* (useTreeFocus with hasRovingTabIndex) takes ownership after mount — it
|
|
105
|
+
* preserves this seeded `tabindex="0"` on its repair pass and moves the stop
|
|
106
|
+
* with keyboard navigation.
|
|
107
|
+
*/
|
|
108
|
+
function findInitialTabbableId(items: TreeListItemData[]): string | undefined {
|
|
109
|
+
let firstEnabled: string | undefined;
|
|
110
|
+
const walk = (list: TreeListItemData[]): string | undefined => {
|
|
111
|
+
for (const item of list) {
|
|
112
|
+
if (item.isSelected && item.isDisabled !== true) {
|
|
113
|
+
return item.id;
|
|
114
|
+
}
|
|
115
|
+
if (firstEnabled == null && item.isDisabled !== true) {
|
|
116
|
+
firstEnabled = item.id;
|
|
117
|
+
}
|
|
118
|
+
if (item.children != null && item.children.length > 0) {
|
|
119
|
+
const selected = walk(item.children);
|
|
120
|
+
if (selected != null) {
|
|
121
|
+
return selected;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
};
|
|
127
|
+
return walk(items) ?? firstEnabled ?? items[0]?.id;
|
|
128
|
+
}
|
|
129
|
+
|
|
100
130
|
// =============================================================================
|
|
101
131
|
// Component
|
|
102
132
|
// =============================================================================
|
|
@@ -160,6 +190,42 @@ export function TreeList({
|
|
|
160
190
|
[expandedKeysFromProps],
|
|
161
191
|
);
|
|
162
192
|
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Roving tabindex + APG tree keyboard model (via useTreeFocus)
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
// The hook (hasRovingTabIndex) owns the tree's single tab stop: it repairs
|
|
198
|
+
// the stop on mount and moves it with keyboard navigation. We only seed the
|
|
199
|
+
// initially-tabbable treeitem in the render (selected item or first enabled);
|
|
200
|
+
// the hook's repair pass preserves that seeded `tabindex="0"`.
|
|
201
|
+
const initialTabbableId = useMemo(
|
|
202
|
+
() => findInitialTabbableId(items),
|
|
203
|
+
[items],
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Enter/Space activation: prefer the treeitem's own inner action (link or
|
|
207
|
+
// button); return true when handled so the hook does not also toggle. Scoped
|
|
208
|
+
// to this treeitem's own row — never a descendant treeitem's action inside an
|
|
209
|
+
// expanded group.
|
|
210
|
+
const activateItem = useCallback((current: HTMLElement): boolean => {
|
|
211
|
+
const candidates = current.querySelectorAll<HTMLElement>(
|
|
212
|
+
'a[href], button:not([aria-label="Toggle children"])',
|
|
213
|
+
);
|
|
214
|
+
for (const candidate of candidates) {
|
|
215
|
+
if (candidate.closest('[role="treeitem"]') === current) {
|
|
216
|
+
candidate.click();
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}, []);
|
|
222
|
+
|
|
223
|
+
const {treeRef, handleKeyDown, handleFocus} = useTreeFocus<HTMLUListElement>({
|
|
224
|
+
onToggleExpand: handleToggle,
|
|
225
|
+
onActivate: activateItem,
|
|
226
|
+
hasRovingTabIndex: true,
|
|
227
|
+
});
|
|
228
|
+
|
|
163
229
|
function renderItems(
|
|
164
230
|
items: TreeListItemData[],
|
|
165
231
|
nestedLevel: number,
|
|
@@ -206,6 +272,9 @@ export function TreeList({
|
|
|
206
272
|
onToggle={handleToggle}
|
|
207
273
|
density={density}
|
|
208
274
|
renderedChildren={renderedChildren}
|
|
275
|
+
posInSet={index + 1}
|
|
276
|
+
setSize={items.length}
|
|
277
|
+
isTabbable={item.id === initialTabbableId}
|
|
209
278
|
/>
|
|
210
279
|
);
|
|
211
280
|
});
|
|
@@ -227,8 +296,11 @@ export function TreeList({
|
|
|
227
296
|
</div>
|
|
228
297
|
)}
|
|
229
298
|
<ul
|
|
299
|
+
ref={treeRef}
|
|
230
300
|
role="tree"
|
|
231
301
|
aria-labelledby={header != null ? headerId : undefined}
|
|
302
|
+
onKeyDown={handleKeyDown}
|
|
303
|
+
onFocus={handleFocus}
|
|
232
304
|
{...stylex.props(styles.list)}>
|
|
233
305
|
{renderItems(items, 0, [])}
|
|
234
306
|
</ul>
|