@astryxdesign/core 0.1.7 → 0.1.8-canary.0c4d8d7
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 +65 -0
- package/README.md +30 -19
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +6 -2
- package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
- package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
- package/dist/Avatar/Avatar.d.ts +10 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -8
- package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
- package/dist/Avatar/AvatarSizeContext.js +1 -1
- package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
- package/dist/Avatar/AvatarStatusDot.js +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +2 -2
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
- package/dist/Banner/Banner.d.ts +9 -1
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +31 -2
- package/dist/Button/Button.d.ts +8 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +31 -2
- package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +30 -1
- package/dist/Card/Card.d.ts +8 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +28 -2
- package/dist/Carousel/Carousel.d.ts +6 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +55 -5
- package/dist/Chat/ChatComposer.d.ts +8 -0
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +31 -6
- package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +11 -1
- package/dist/Chat/ChatComposerInput.d.ts +20 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +19 -3
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.d.ts +1 -1
- package/dist/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.js +1 -1
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.d.ts +5 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +12 -3
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +10 -4
- package/dist/ClickableCard/ClickableCard.d.ts +9 -2
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -0
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +8 -1
- package/dist/Collapsible/Collapsible.d.ts +11 -1
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +36 -4
- package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +6 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/ContextMenu/index.d.ts +3 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +7 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +10 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +8 -4
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
- package/dist/DropdownMenu/index.d.ts +5 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +10 -1
- package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemRoles.js +15 -0
- package/dist/FieldStatus/FieldStatus.d.ts +8 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +22 -4
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +10 -2
- package/dist/HoverCard/HoverCard.d.ts +9 -1
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +2 -0
- package/dist/HoverCard/useHoverCard.d.ts +8 -0
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +7 -2
- package/dist/Icon/Icon.d.ts +43 -13
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +62 -27
- package/dist/Layer/useLayer.d.ts +6 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +2 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +6 -1
- package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
- package/dist/MobileNav/MobileNavToggle.js +4 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +39 -12
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +16 -0
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/Outline/Outline.d.ts +68 -3
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +128 -26
- package/dist/Outline/useScrollSpy.d.ts +27 -7
- package/dist/Outline/useScrollSpy.d.ts.map +1 -1
- package/dist/Outline/useScrollSpy.js +202 -70
- package/dist/OverflowList/OverflowList.d.ts +20 -3
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +29 -4
- package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.d.ts +9 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +14 -11
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +4 -2
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +16 -0
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +21 -8
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +15 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +2 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/Table.d.ts +1 -1
- package/dist/Table/Table.js +1 -1
- package/dist/Table/index.d.ts +4 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
- package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
- package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/index.js +3 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts +12 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +4 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
- package/dist/Table/plugins/tree/index.d.ts +5 -0
- package/dist/Table/plugins/tree/index.d.ts.map +1 -0
- package/dist/Table/plugins/tree/index.js +4 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +6 -3
- package/dist/Thumbnail/Thumbnail.d.ts +8 -1
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +46 -8
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +15 -4
- package/dist/Tokenizer/Tokenizer.d.ts +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +16 -4
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +8 -1
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +18 -8
- package/dist/TopNav/TopNavMegaMenu.js +10 -4
- package/dist/TopNav/TopNavMenu.d.ts +2 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +68 -6
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +5 -14
- package/dist/astryx.css +51 -16
- package/dist/astryx.umd.js +56 -56
- package/dist/astryx.umd.js.map +4 -4
- package/dist/authoring/doc.d.ts.map +1 -1
- package/dist/hooks/computeOverflow.d.ts +47 -0
- package/dist/hooks/computeOverflow.d.ts.map +1 -0
- package/dist/hooks/computeOverflow.js +213 -0
- package/dist/hooks/useOverflow.d.ts +19 -0
- package/dist/hooks/useOverflow.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +37 -25
- package/dist/theme/Theme.js +1 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +51 -0
- package/dist/theme/hct.d.ts +0 -1
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +0 -3
- package/dist/utils/types.d.ts +13 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/docs.mjs +32 -461
- package/locales/en.json +212 -172
- package/locales/fr-FR.json +10 -0
- package/locales/pseudo.json +30 -0
- package/package.json +3 -1
- package/scripts/postinstall.mjs +93 -0
- package/src/AppShell/AppShell.tsx +6 -0
- package/src/AppShell/AppShellMobileContext.tsx +8 -0
- package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
- package/src/Avatar/Avatar.doc.mjs +6 -6
- package/src/Avatar/Avatar.tsx +16 -11
- package/src/Avatar/AvatarSizeContext.ts +1 -1
- package/src/Avatar/AvatarStatusDot.tsx +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
- package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
- package/src/AvatarGroup/AvatarGroup.tsx +3 -3
- package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
- package/src/Banner/Banner.doc.mjs +9 -0
- package/src/Banner/Banner.test.tsx +30 -0
- package/src/Banner/Banner.tsx +47 -3
- package/src/Button/Button.doc.mjs +9 -0
- package/src/Button/Button.test.tsx +49 -0
- package/src/Button/Button.tsx +25 -1
- package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
- package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
- package/src/ButtonGroup/ButtonGroup.tsx +32 -0
- package/src/Card/Card.doc.mjs +9 -0
- package/src/Card/Card.test.tsx +21 -3
- package/src/Card/Card.tsx +35 -2
- package/src/Carousel/Carousel.doc.mjs +1 -0
- package/src/Carousel/Carousel.test.tsx +149 -1
- package/src/Carousel/Carousel.tsx +62 -5
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatComposer.doc.mjs +9 -0
- package/src/Chat/ChatComposer.test.tsx +35 -0
- package/src/Chat/ChatComposer.tsx +58 -9
- package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
- package/src/Chat/ChatComposerDrawer.tsx +11 -1
- package/src/Chat/ChatComposerInput.doc.mjs +7 -0
- package/src/Chat/ChatComposerInput.test.tsx +87 -0
- package/src/Chat/ChatComposerInput.tsx +35 -3
- package/src/Chat/ChatLayout.tsx +3 -1
- package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
- package/src/Chat/ChatMessage.tsx +4 -2
- package/src/Chat/ChatMessageMetadata.tsx +1 -2
- package/src/Chat/ChatSendButton.tsx +5 -1
- package/src/Chat/ChatToolCalls.test.tsx +64 -0
- package/src/Chat/ChatToolCalls.tsx +13 -1
- package/src/Chat/useTriggerMenu.tsx +3 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
- package/src/CheckboxList/CheckboxList.test.tsx +9 -1
- package/src/Citation/Citation.test.tsx +51 -0
- package/src/Citation/Citation.tsx +8 -2
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
- package/src/ClickableCard/ClickableCard.test.tsx +34 -0
- package/src/ClickableCard/ClickableCard.tsx +11 -1
- package/src/CodeBlock/CodeBlock.test.tsx +56 -0
- package/src/CodeBlock/CodeBlock.tsx +9 -1
- package/src/Collapsible/Collapsible.doc.mjs +7 -0
- package/src/Collapsible/Collapsible.test.tsx +80 -0
- package/src/Collapsible/Collapsible.tsx +58 -5
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
- package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
- package/src/ContextMenu/ContextMenu.test.tsx +75 -1
- package/src/ContextMenu/ContextMenu.tsx +10 -5
- package/src/ContextMenu/index.ts +15 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
- package/src/DateTimeInput/DateTimeInput.tsx +14 -2
- package/src/Dialog/Dialog.tsx +12 -2
- package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
- package/src/DropdownMenu/DropdownMenu.tsx +11 -6
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
- package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
- package/src/DropdownMenu/index.ts +23 -3
- package/src/DropdownMenu/menuItemRoles.ts +22 -0
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/Field.test.tsx +57 -12
- package/src/Field/FieldLabel.doc.mjs +1 -1
- package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
- package/src/FieldStatus/FieldStatus.test.tsx +78 -40
- package/src/FieldStatus/FieldStatus.tsx +22 -4
- package/src/FileInput/FileInput.test.tsx +52 -3
- package/src/FileInput/FileInput.tsx +13 -2
- package/src/HoverCard/HoverCard.doc.mjs +13 -0
- package/src/HoverCard/HoverCard.test.tsx +21 -2
- package/src/HoverCard/HoverCard.tsx +11 -0
- package/src/HoverCard/useHoverCard.doc.mjs +2 -0
- package/src/HoverCard/useHoverCard.tsx +24 -2
- package/src/Icon/Icon.doc.mjs +12 -0
- package/src/Icon/Icon.test.tsx +142 -0
- package/src/Icon/Icon.tsx +98 -30
- package/src/IconButton/IconButton.doc.mjs +8 -0
- package/src/IconButton/IconButton.test.tsx +15 -18
- package/src/Item/Item.doc.mjs +1 -1
- package/src/Layer/useLayer.tsx +8 -0
- package/src/MobileNav/MobileNav.tsx +12 -1
- package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
- package/src/MobileNav/MobileNavToggle.tsx +9 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
- package/src/MultiSelector/MultiSelector.test.tsx +162 -1
- package/src/MultiSelector/MultiSelector.tsx +55 -12
- package/src/MultiSelector/hooks.ts +17 -0
- package/src/NumberInput/NumberInput.doc.mjs +2 -2
- package/src/NumberInput/NumberInput.test.tsx +11 -7
- package/src/NumberInput/NumberInput.tsx +1 -2
- package/src/Outline/Outline.doc.mjs +123 -3
- package/src/Outline/Outline.test.tsx +735 -0
- package/src/Outline/Outline.tsx +183 -33
- package/src/Outline/useScrollSpy.ts +248 -72
- package/src/OverflowList/OverflowList.doc.mjs +39 -6
- package/src/OverflowList/OverflowList.test.tsx +111 -0
- package/src/OverflowList/OverflowList.tsx +48 -8
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
- package/src/PowerSearch/usePowerSearchSource.ts +1 -2
- package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
- package/src/SelectableCard/SelectableCard.test.tsx +46 -4
- package/src/SelectableCard/SelectableCard.tsx +27 -14
- package/src/Selector/Selector.test.tsx +69 -0
- package/src/Selector/Selector.tsx +5 -1
- package/src/Selector/SelectorOption.doc.mjs +1 -1
- package/src/Selector/hooks.ts +17 -0
- package/src/SideNav/SideNav.test.tsx +119 -3
- package/src/SideNav/SideNavCollapseButton.tsx +6 -1
- package/src/SideNav/SideNavHeading.tsx +24 -8
- package/src/SideNav/SideNavItem.doc.mjs +2 -2
- package/src/SideNav/SideNavItem.tsx +5 -1
- package/src/Slider/Slider.test.tsx +62 -2
- package/src/Slider/Slider.tsx +15 -1
- package/src/Switch/Switch.doc.mjs +1 -1
- package/src/Switch/Switch.test.tsx +32 -1
- package/src/TabList/Tab.tsx +5 -1
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/TabList.tsx +8 -0
- package/src/TabList/TabMenu.tsx +4 -1
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.tsx +1 -1
- package/src/Table/index.ts +12 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
- package/src/Table/plugins/rowStatus/index.ts +7 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
- package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +18 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
- package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
- package/src/Table/plugins/tree/index.ts +12 -0
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
- package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
- package/src/Table/useBaseTablePlugins.ts +6 -2
- package/src/Table/useTableRowStatus.doc.mjs +30 -0
- package/src/Table/useTableSelection.doc.mjs +65 -14
- package/src/Table/useTableTreeData.doc.mjs +75 -0
- package/src/Table/useTableTreeState.doc.mjs +98 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +9 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/Thumbnail/Thumbnail.doc.mjs +9 -0
- package/src/Thumbnail/Thumbnail.test.tsx +38 -8
- package/src/Thumbnail/Thumbnail.tsx +28 -1
- package/src/TimeInput/TimeInput.test.tsx +76 -1
- package/src/TimeInput/TimeInput.tsx +19 -6
- package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
- package/src/Tokenizer/Tokenizer.test.tsx +137 -2
- package/src/Tokenizer/Tokenizer.tsx +16 -4
- package/src/Toolbar/Toolbar.tsx +10 -3
- package/src/TopNav/TopNav.test.tsx +80 -1
- package/src/TopNav/TopNavHeading.tsx +24 -8
- package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
- package/src/TopNav/TopNavMegaMenu.tsx +10 -4
- package/src/TopNav/TopNavMenu.test.tsx +118 -4
- package/src/TopNav/TopNavMenu.tsx +83 -5
- package/src/TreeList/TreeList.test.tsx +32 -1
- package/src/TreeList/TreeListItem.tsx +19 -10
- package/src/authoring/doc.ts +1 -3
- package/src/docs-script.test.mjs +18 -13
- package/src/hooks/computeOverflow.test.ts +422 -0
- package/src/hooks/computeOverflow.ts +290 -0
- package/src/hooks/useOverflow.test.ts +96 -4
- package/src/hooks/useOverflow.ts +77 -33
- package/src/postinstall.test.mjs +71 -0
- package/src/theme/Theme.doc.mjs +4 -4
- package/src/theme/Theme.tsx +1 -1
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/generateThemeRules.test.ts +65 -0
- package/src/theme/generateThemeRules.ts +56 -0
- package/src/theme/hct.ts +0 -4
- package/src/utils/types.ts +14 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
- package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
- package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
|
@@ -210,6 +210,62 @@ describe('CodeBlock', () => {
|
|
|
210
210
|
expect(document.getElementById(controlsId as string)).not.toBeNull();
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
it('makes the collapsed region inert so the scroll container is unreachable', () => {
|
|
214
|
+
render(
|
|
215
|
+
<CodeBlock
|
|
216
|
+
code={LONG_CODE}
|
|
217
|
+
language="javascript"
|
|
218
|
+
title="example"
|
|
219
|
+
isCollapsible
|
|
220
|
+
/>,
|
|
221
|
+
);
|
|
222
|
+
const header = screen
|
|
223
|
+
.getAllByRole('button')
|
|
224
|
+
.find(el => el.hasAttribute('aria-expanded'))!;
|
|
225
|
+
const region = document.getElementById(
|
|
226
|
+
header.getAttribute('aria-controls') as string,
|
|
227
|
+
)!;
|
|
228
|
+
// Expanded: the region is not inert and the scroll container is reachable.
|
|
229
|
+
expect(region).not.toHaveAttribute('inert');
|
|
230
|
+
|
|
231
|
+
fireEvent.click(header);
|
|
232
|
+
expect(header).toHaveAttribute('aria-expanded', 'false');
|
|
233
|
+
// Collapsed: the wrapper is inert, so the keyboard-focusable scroll
|
|
234
|
+
// container (tabIndex=0) inside it drops out of the tab order and the
|
|
235
|
+
// accessibility tree instead of remaining an invisible tab stop.
|
|
236
|
+
expect(region).toHaveAttribute('inert');
|
|
237
|
+
const scrollContainer = screen.getByRole('group');
|
|
238
|
+
expect(scrollContainer.closest('[inert]')).toBe(region);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('restores focusability of the scroll container after expanding again', () => {
|
|
242
|
+
render(
|
|
243
|
+
<CodeBlock
|
|
244
|
+
code={LONG_CODE}
|
|
245
|
+
language="javascript"
|
|
246
|
+
title="example"
|
|
247
|
+
isCollapsible
|
|
248
|
+
/>,
|
|
249
|
+
);
|
|
250
|
+
const header = screen
|
|
251
|
+
.getAllByRole('button')
|
|
252
|
+
.find(el => el.hasAttribute('aria-expanded'))!;
|
|
253
|
+
const region = document.getElementById(
|
|
254
|
+
header.getAttribute('aria-controls') as string,
|
|
255
|
+
)!;
|
|
256
|
+
// Collapse, then expand again.
|
|
257
|
+
fireEvent.click(header);
|
|
258
|
+
expect(region).toHaveAttribute('inert');
|
|
259
|
+
fireEvent.click(header);
|
|
260
|
+
expect(header).toHaveAttribute('aria-expanded', 'true');
|
|
261
|
+
// Expanded again: inert is removed and the scroll container is a
|
|
262
|
+
// keyboard-focusable group once more.
|
|
263
|
+
expect(region).not.toHaveAttribute('inert');
|
|
264
|
+
const scrollContainer = screen.getByRole('group');
|
|
265
|
+
expect(scrollContainer.closest('[inert]')).toBeNull();
|
|
266
|
+
expect(scrollContainer).toHaveAttribute('tabindex', '0');
|
|
267
|
+
});
|
|
268
|
+
|
|
213
269
|
it('applies a per-instance syntax theme via the syntaxTheme prop', () => {
|
|
214
270
|
const {container} = render(
|
|
215
271
|
<CodeBlock
|
|
@@ -807,7 +807,9 @@ export function CodeBlock({
|
|
|
807
807
|
e.stopPropagation();
|
|
808
808
|
void handleCopy();
|
|
809
809
|
}}
|
|
810
|
-
aria-label={
|
|
810
|
+
aria-label={
|
|
811
|
+
copied ? t('@astryx.codeBlock.copied') : t('@astryx.codeBlock.copyCode')
|
|
812
|
+
}
|
|
811
813
|
{...stylex.props(
|
|
812
814
|
styles.copyButton,
|
|
813
815
|
!showHeader && styles.copyButtonAbsolute,
|
|
@@ -923,6 +925,12 @@ export function CodeBlock({
|
|
|
923
925
|
{canCollapse ? (
|
|
924
926
|
<div
|
|
925
927
|
id={regionId}
|
|
928
|
+
// While collapsed, the region is only hidden visually (0fr grid
|
|
929
|
+
// row); inert also removes it from the tab order and accessibility
|
|
930
|
+
// tree so keyboard users cannot Tab into the invisible scroll
|
|
931
|
+
// container (tabIndex=0). aria-controls pointing at an inert
|
|
932
|
+
// element remains a valid, resolvable reference.
|
|
933
|
+
inert={isCollapsed ? true : undefined}
|
|
926
934
|
{...stylex.props(
|
|
927
935
|
styles.collapseGrid,
|
|
928
936
|
isCollapsed && styles.collapseGridCollapsed,
|
|
@@ -17,6 +17,7 @@ export const docs = {
|
|
|
17
17
|
theming: {
|
|
18
18
|
targets: [
|
|
19
19
|
{className: 'astryx-collapsible', visualProps: ['density']},
|
|
20
|
+
{className: 'astryx-collapsible-content', visualProps: ['density']},
|
|
20
21
|
{className: 'astryx-collapsible-group', visualProps: ['density']},
|
|
21
22
|
],
|
|
22
23
|
},
|
|
@@ -53,6 +54,12 @@ export const docs = {
|
|
|
53
54
|
type: 'boolean',
|
|
54
55
|
description: 'Controlled open state.',
|
|
55
56
|
},
|
|
57
|
+
{
|
|
58
|
+
name: 'isDisabled',
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
description: "Disable the item so its trigger can't be toggled (dimmed, aria-disabled, and out of the tab order). Doesn't collapse an already-open item.",
|
|
61
|
+
default: 'false',
|
|
62
|
+
},
|
|
56
63
|
{
|
|
57
64
|
name: 'onOpenChange',
|
|
58
65
|
type: '(isOpen: boolean) => void',
|
|
@@ -72,6 +72,12 @@ describe('Collapsible', () => {
|
|
|
72
72
|
expect(screen.getByTestId('root')).toHaveClass('astryx-collapsible');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
it('renders the stable astryx-collapsible-content class on the content area', () => {
|
|
76
|
+
render(<Collapsible trigger="T">c</Collapsible>);
|
|
77
|
+
const content = contentFor(screen.getByRole('button'));
|
|
78
|
+
expect(content).toHaveClass('astryx-collapsible-content');
|
|
79
|
+
});
|
|
80
|
+
|
|
75
81
|
it('renders a ReactNode trigger, not just a string', () => {
|
|
76
82
|
render(
|
|
77
83
|
<Collapsible trigger={<span data-testid="rich">Rich</span>}>
|
|
@@ -240,6 +246,80 @@ describe('Collapsible', () => {
|
|
|
240
246
|
});
|
|
241
247
|
});
|
|
242
248
|
|
|
249
|
+
describe('disabled state', () => {
|
|
250
|
+
it('marks the trigger aria-disabled and drops it from the tab order', () => {
|
|
251
|
+
render(
|
|
252
|
+
<Collapsible trigger="T" isDisabled>
|
|
253
|
+
Body
|
|
254
|
+
</Collapsible>,
|
|
255
|
+
);
|
|
256
|
+
const button = screen.getByRole('button');
|
|
257
|
+
expect(button).toHaveAttribute('aria-disabled', 'true');
|
|
258
|
+
expect(button).toHaveAttribute('tabindex', '-1');
|
|
259
|
+
// Never the native disabled attribute — it stays focusable/perceivable.
|
|
260
|
+
expect(button).not.toBeDisabled();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('is enabled by default (no aria-disabled, stays in tab order)', () => {
|
|
264
|
+
render(<Collapsible trigger="T">Body</Collapsible>);
|
|
265
|
+
const button = screen.getByRole('button');
|
|
266
|
+
expect(button).not.toHaveAttribute('aria-disabled');
|
|
267
|
+
expect(button).not.toHaveAttribute('tabindex', '-1');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('does not toggle when the trigger is clicked while disabled', async () => {
|
|
271
|
+
const user = userEvent.setup();
|
|
272
|
+
render(
|
|
273
|
+
<Collapsible trigger="T" isDisabled defaultIsOpen>
|
|
274
|
+
Body
|
|
275
|
+
</Collapsible>,
|
|
276
|
+
);
|
|
277
|
+
const button = screen.getByRole('button');
|
|
278
|
+
expect(button).toHaveAttribute('aria-expanded', 'true');
|
|
279
|
+
await user.click(button);
|
|
280
|
+
expect(button).toHaveAttribute('aria-expanded', 'true');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('does not fire onOpenChange while disabled', async () => {
|
|
284
|
+
const user = userEvent.setup();
|
|
285
|
+
const onOpenChange = vi.fn();
|
|
286
|
+
render(
|
|
287
|
+
<Collapsible trigger="T" isDisabled onOpenChange={onOpenChange}>
|
|
288
|
+
Body
|
|
289
|
+
</Collapsible>,
|
|
290
|
+
);
|
|
291
|
+
await user.click(screen.getByRole('button'));
|
|
292
|
+
expect(onOpenChange).not.toHaveBeenCalled();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('does not collapse an already-open item — content stays visible', () => {
|
|
296
|
+
render(
|
|
297
|
+
<Collapsible trigger="T" isDisabled defaultIsOpen>
|
|
298
|
+
Body
|
|
299
|
+
</Collapsible>,
|
|
300
|
+
);
|
|
301
|
+
const content = contentFor(screen.getByRole('button'));
|
|
302
|
+
expect(content).not.toHaveStyle({display: 'none'});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('does not toggle its group item when disabled', async () => {
|
|
306
|
+
const user = userEvent.setup();
|
|
307
|
+
render(
|
|
308
|
+
<CollapsibleGroup type="single">
|
|
309
|
+
<Collapsible trigger="A" value="a" isDisabled>
|
|
310
|
+
Body A
|
|
311
|
+
</Collapsible>
|
|
312
|
+
<Collapsible trigger="B" value="b">
|
|
313
|
+
Body B
|
|
314
|
+
</Collapsible>
|
|
315
|
+
</CollapsibleGroup>,
|
|
316
|
+
);
|
|
317
|
+
const triggerA = screen.getByRole('button', {name: /A/});
|
|
318
|
+
await user.click(triggerA);
|
|
319
|
+
expect(triggerA).toHaveAttribute('aria-expanded', 'false');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
243
323
|
describe('prop forwarding', () => {
|
|
244
324
|
it('forwards a ref to the root element', () => {
|
|
245
325
|
const ref = vi.fn();
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
durationVars,
|
|
40
40
|
easeVars,
|
|
41
41
|
} from '../theme/tokens.stylex';
|
|
42
|
+
|
|
42
43
|
import {useCollapsible} from './useCollapsible';
|
|
43
44
|
import {CollapsibleGroupPresentationContext} from './CollapsibleGroupContext';
|
|
44
45
|
import {getIcon} from '../Icon/globalIconRegistry';
|
|
@@ -81,6 +82,13 @@ const styles = stylex.create({
|
|
|
81
82
|
textBoxEdge: 'cap alphabetic',
|
|
82
83
|
textBoxTrim: 'trim-both',
|
|
83
84
|
},
|
|
85
|
+
// Disabled trigger — non-interactive, dimmed. Native `disabled` on the
|
|
86
|
+
// button blocks click + keyboard activation; these styles restore the
|
|
87
|
+
// visual affordance that `all: unset` wipes.
|
|
88
|
+
triggerDisabled: {
|
|
89
|
+
cursor: 'not-allowed',
|
|
90
|
+
opacity: 0.5,
|
|
91
|
+
},
|
|
84
92
|
// Chevron indicator
|
|
85
93
|
chevron: {
|
|
86
94
|
display: 'inline-flex',
|
|
@@ -102,8 +110,17 @@ const styles = stylex.create({
|
|
|
102
110
|
contentHidden: {
|
|
103
111
|
display: 'none',
|
|
104
112
|
},
|
|
113
|
+
// Anchors body typography so revealed text renders at the system's body
|
|
114
|
+
// scale (family/size/weight/leading) instead of inheriting from wherever
|
|
115
|
+
// the Collapsible is placed. External themes override via the
|
|
116
|
+
// `astryx-collapsible-content` target.
|
|
105
117
|
content: {
|
|
106
118
|
paddingBlockStart: spacingVars['--spacing-1'],
|
|
119
|
+
fontFamily: typographyVars['--font-family-body'],
|
|
120
|
+
fontSize: typeScaleVars['--text-body-size'],
|
|
121
|
+
fontWeight: typeScaleVars['--text-body-weight'],
|
|
122
|
+
lineHeight: typeScaleVars['--text-body-leading'],
|
|
123
|
+
color: colorVars['--color-text-primary'],
|
|
107
124
|
},
|
|
108
125
|
// Group divider chrome — a hairline above every item except the first.
|
|
109
126
|
// The group's wrapper (or 'all' mode) owns the outer edges.
|
|
@@ -166,6 +183,17 @@ export interface CollapsibleProps extends BaseProps {
|
|
|
166
183
|
*/
|
|
167
184
|
isOpen?: boolean;
|
|
168
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Whether the collapsible is disabled. A disabled item can't be toggled —
|
|
188
|
+
* its trigger is non-interactive and dimmed. Following the system-wide
|
|
189
|
+
* disabled convention, the trigger uses `aria-disabled` (not the native
|
|
190
|
+
* `disabled` attribute) and drops out of the tab order, staying perceivable
|
|
191
|
+
* to assistive tech. The content stays in whatever open state it was;
|
|
192
|
+
* disabling doesn't collapse an already-open item.
|
|
193
|
+
* @default false
|
|
194
|
+
*/
|
|
195
|
+
isDisabled?: boolean;
|
|
196
|
+
|
|
169
197
|
/**
|
|
170
198
|
* Callback when the open state changes.
|
|
171
199
|
*/
|
|
@@ -225,6 +253,7 @@ export function Collapsible({
|
|
|
225
253
|
children,
|
|
226
254
|
defaultIsOpen,
|
|
227
255
|
isOpen: controlledIsOpen,
|
|
256
|
+
isDisabled = false,
|
|
228
257
|
onOpenChange,
|
|
229
258
|
value,
|
|
230
259
|
ref,
|
|
@@ -244,6 +273,17 @@ export function Collapsible({
|
|
|
244
273
|
value,
|
|
245
274
|
});
|
|
246
275
|
|
|
276
|
+
// Activation is blocked by this guard rather than the native `disabled`
|
|
277
|
+
// attribute, so the trigger keeps `aria-disabled` semantics and stays
|
|
278
|
+
// discoverable. A native `disabled` button would silently swallow events
|
|
279
|
+
// (e.g. a wrapping tooltip's hover) — the system-wide disabled convention.
|
|
280
|
+
const handleToggle = () => {
|
|
281
|
+
if (isDisabled) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
toggle();
|
|
285
|
+
};
|
|
286
|
+
|
|
247
287
|
const presentation = use(CollapsibleGroupPresentationContext);
|
|
248
288
|
const isDivided = presentation?.hasDividers ?? false;
|
|
249
289
|
const density = presentation?.density ?? null;
|
|
@@ -268,12 +308,20 @@ export function Collapsible({
|
|
|
268
308
|
{...props}>
|
|
269
309
|
<button
|
|
270
310
|
type="button"
|
|
271
|
-
onClick={
|
|
311
|
+
onClick={handleToggle}
|
|
312
|
+
aria-disabled={isDisabled || undefined}
|
|
272
313
|
aria-expanded={isOpen}
|
|
273
314
|
aria-controls={contentId}
|
|
315
|
+
// A disabled trigger drops out of the tab order so it isn't a silently
|
|
316
|
+
// dead tab stop; activation stays blocked by the handleToggle guard,
|
|
317
|
+
// and aria-disabled keeps the state perceivable to assistive tech —
|
|
318
|
+
// the system-wide disabled convention (never native `disabled`, which
|
|
319
|
+
// would swallow events like a wrapping tooltip's hover).
|
|
320
|
+
tabIndex={isDisabled ? -1 : undefined}
|
|
274
321
|
{...stylex.props(
|
|
275
322
|
styles.trigger,
|
|
276
323
|
density != null && triggerDensity[density],
|
|
324
|
+
isDisabled && styles.triggerDisabled,
|
|
277
325
|
)}>
|
|
278
326
|
<span {...stylex.props(styles.triggerLabel)}>{trigger}</span>
|
|
279
327
|
<span
|
|
@@ -286,10 +334,15 @@ export function Collapsible({
|
|
|
286
334
|
</button>
|
|
287
335
|
<div
|
|
288
336
|
id={contentId}
|
|
289
|
-
{...
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
337
|
+
{...mergeProps(
|
|
338
|
+
themeProps('collapsible-content', {
|
|
339
|
+
density: density ?? undefined,
|
|
340
|
+
}),
|
|
341
|
+
stylex.props(
|
|
342
|
+
styles.content,
|
|
343
|
+
density != null && contentDensity[density],
|
|
344
|
+
!isOpen && styles.contentHidden,
|
|
345
|
+
),
|
|
293
346
|
)}>
|
|
294
347
|
{presentation != null ? (
|
|
295
348
|
<CollapsibleGroupPresentationContext value={null}>
|
|
@@ -7,7 +7,8 @@ export const docs = {
|
|
|
7
7
|
subComponentOf: 'CommandPalette',
|
|
8
8
|
displayName: 'Command Palette Input',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
|
-
description:
|
|
10
|
+
description:
|
|
11
|
+
'Search input slot. Auto-focuses on mount. Wires to command palette context when used inside CommandPalette.',
|
|
11
12
|
props: [
|
|
12
13
|
{
|
|
13
14
|
name: 'placeholder',
|
|
@@ -15,16 +16,24 @@ export const docs = {
|
|
|
15
16
|
description: 'Placeholder text for the input.',
|
|
16
17
|
default: "'Search...'",
|
|
17
18
|
},
|
|
19
|
+
{
|
|
20
|
+
name: 'label',
|
|
21
|
+
type: 'string',
|
|
22
|
+
description:
|
|
23
|
+
'Accessible label for the combobox input, announced by screen readers. Falls back to the placeholder text when omitted.',
|
|
24
|
+
},
|
|
18
25
|
{
|
|
19
26
|
name: 'hasAutoFocus',
|
|
20
27
|
type: 'boolean',
|
|
21
|
-
description:
|
|
28
|
+
description:
|
|
29
|
+
'Auto-focus the input when mounted. Automatically disabled when inside an inline command palette.',
|
|
22
30
|
default: 'true',
|
|
23
31
|
},
|
|
24
32
|
{
|
|
25
33
|
name: 'endContent',
|
|
26
34
|
type: 'ReactNode',
|
|
27
|
-
description:
|
|
35
|
+
description:
|
|
36
|
+
'Content rendered at the trailing end of the input, after the spinner. Use for clear buttons or keyboard shortcut hints.',
|
|
28
37
|
slotElements: [
|
|
29
38
|
{
|
|
30
39
|
__element: 'Icon',
|
|
@@ -44,17 +53,20 @@ export const docs = {
|
|
|
44
53
|
{
|
|
45
54
|
name: 'value',
|
|
46
55
|
type: 'string',
|
|
47
|
-
description:
|
|
56
|
+
description:
|
|
57
|
+
'Search value. When omitted inside CommandPalette, reads from context.',
|
|
48
58
|
},
|
|
49
59
|
{
|
|
50
60
|
name: 'onValueChange',
|
|
51
61
|
type: '(value: string) => void',
|
|
52
|
-
description:
|
|
62
|
+
description:
|
|
63
|
+
'Called when search value changes. When omitted inside CommandPalette, writes to context.',
|
|
53
64
|
},
|
|
54
65
|
{
|
|
55
66
|
name: 'xstyle',
|
|
56
67
|
type: 'StyleXStyles',
|
|
57
|
-
description:
|
|
68
|
+
description:
|
|
69
|
+
'StyleX styles for layout customization. Must be a stylex.create() value.',
|
|
58
70
|
},
|
|
59
71
|
],
|
|
60
72
|
playground: {
|
|
@@ -69,9 +81,11 @@ export const docsZh = {
|
|
|
69
81
|
name: 'CommandPaletteInput',
|
|
70
82
|
isHiddenFromOverview: true,
|
|
71
83
|
displayName: 'Command Palette Input',
|
|
72
|
-
description:
|
|
84
|
+
description:
|
|
85
|
+
'搜索输入插槽。挂载时自动聚焦。在 CommandPalette 内使用时连接到上下文。',
|
|
73
86
|
propDescriptions: {
|
|
74
87
|
placeholder: '输入框的占位文本。',
|
|
88
|
+
label: '组合框输入的无障碍标签,供屏幕阅读器朗读。省略时回退到占位文本。',
|
|
75
89
|
hasAutoFocus: '挂载时自动聚焦输入框。内联命令面板中自动禁用。',
|
|
76
90
|
endContent: '渲染在输入框末尾的内容,位于加载指示器之后。',
|
|
77
91
|
value: '搜索值。在 CommandPalette 内省略时从上下文读取。',
|
|
@@ -84,13 +98,16 @@ export const docsDense = {
|
|
|
84
98
|
name: 'CommandPaletteInput',
|
|
85
99
|
isHiddenFromOverview: true,
|
|
86
100
|
displayName: 'Command Palette Input',
|
|
87
|
-
description:
|
|
101
|
+
description:
|
|
102
|
+
'search input; auto-focus on mount; wires to context inside CommandPalette',
|
|
88
103
|
propDescriptions: {
|
|
89
104
|
placeholder: 'input placeholder text',
|
|
105
|
+
label: 'accessible label for the combobox; falls back to placeholder',
|
|
90
106
|
hasAutoFocus: 'auto-focus on mount; auto-disabled in inline mode',
|
|
91
107
|
endContent: 'trailing content after spinner',
|
|
92
108
|
value: 'search value; reads context when omitted inside palette',
|
|
93
|
-
onValueChange:
|
|
109
|
+
onValueChange:
|
|
110
|
+
'called on change; writes context when omitted inside palette',
|
|
94
111
|
xstyle: 'StyleX layout styles; must be stylex.create() value',
|
|
95
112
|
},
|
|
96
113
|
};
|
|
@@ -32,6 +32,40 @@ describe('CommandPaletteInput', () => {
|
|
|
32
32
|
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
it('has an accessible name by default', () => {
|
|
36
|
+
render(<CommandPaletteInput />);
|
|
37
|
+
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
38
|
+
'aria-label',
|
|
39
|
+
'Search…',
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('uses the label prop as the accessible name', () => {
|
|
44
|
+
render(<CommandPaletteInput label="Search commands" />);
|
|
45
|
+
const input = screen.getByRole('combobox');
|
|
46
|
+
expect(input).toHaveAttribute('aria-label', 'Search commands');
|
|
47
|
+
// The label prop does not affect the visible placeholder
|
|
48
|
+
expect(input).toHaveAttribute('placeholder', 'Search…');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('falls back to a custom placeholder for the accessible name', () => {
|
|
52
|
+
render(<CommandPaletteInput placeholder="Type a command..." />);
|
|
53
|
+
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
54
|
+
'aria-label',
|
|
55
|
+
'Type a command...',
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('lets a consumer-passed aria-label override the default', () => {
|
|
60
|
+
render(
|
|
61
|
+
<CommandPaletteInput label="Search commands" aria-label="Custom name" />,
|
|
62
|
+
);
|
|
63
|
+
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
64
|
+
'aria-label',
|
|
65
|
+
'Custom name',
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
35
69
|
it('calls onValueChange when typing', () => {
|
|
36
70
|
const handleChange = vi.fn();
|
|
37
71
|
render(<CommandPaletteInput onValueChange={handleChange} />);
|
|
@@ -109,6 +109,13 @@ export interface CommandPaletteInputProps extends Omit<
|
|
|
109
109
|
*/
|
|
110
110
|
placeholder?: string;
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Accessible label for the combobox input, announced by screen readers.
|
|
114
|
+
* Falls back to the placeholder text (`'Search…'` by default), since a
|
|
115
|
+
* placeholder alone is not a reliable accessible name.
|
|
116
|
+
*/
|
|
117
|
+
label?: string;
|
|
118
|
+
|
|
112
119
|
/**
|
|
113
120
|
* Whether to auto-focus the input when mounted.
|
|
114
121
|
* @default true
|
|
@@ -149,6 +156,7 @@ export function CommandPaletteInput({
|
|
|
149
156
|
value: controlledValue,
|
|
150
157
|
onValueChange,
|
|
151
158
|
placeholder: placeholderFromProps,
|
|
159
|
+
label,
|
|
152
160
|
hasAutoFocus = true,
|
|
153
161
|
endContent,
|
|
154
162
|
onChange,
|
|
@@ -215,6 +223,9 @@ export function CommandPaletteInput({
|
|
|
215
223
|
? ctx.getItemId(ctx.highlightedIndex)
|
|
216
224
|
: undefined
|
|
217
225
|
}
|
|
226
|
+
// A placeholder alone is not a reliable accessible name; give the
|
|
227
|
+
// combobox an explicit one (consumer aria-label via rest props wins).
|
|
228
|
+
aria-label={label ?? placeholder}
|
|
218
229
|
placeholder={placeholder}
|
|
219
230
|
value={value}
|
|
220
231
|
data-autofocus={effectiveAutoFocus || undefined}
|
|
@@ -12,7 +12,12 @@ import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
|
12
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
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
ContextMenuItem,
|
|
17
|
+
ContextMenuCheckboxItem,
|
|
18
|
+
ContextMenuRadioGroup,
|
|
19
|
+
ContextMenuRadioItem,
|
|
20
|
+
} from './index';
|
|
16
21
|
import {DropdownMenuItem} from '../DropdownMenu/DropdownMenuItem';
|
|
17
22
|
import {Divider} from '../Divider';
|
|
18
23
|
|
|
@@ -537,3 +542,72 @@ describe('ContextMenu compound mode', () => {
|
|
|
537
542
|
});
|
|
538
543
|
});
|
|
539
544
|
});
|
|
545
|
+
|
|
546
|
+
describe('ContextMenu selectable items', () => {
|
|
547
|
+
it('renders checkbox and radio items with correct roles and state', () => {
|
|
548
|
+
render(
|
|
549
|
+
<ContextMenu
|
|
550
|
+
menuContent={
|
|
551
|
+
<>
|
|
552
|
+
<ContextMenuRadioGroup
|
|
553
|
+
value="name"
|
|
554
|
+
onChange={() => {}}
|
|
555
|
+
aria-label="Sort by">
|
|
556
|
+
<ContextMenuRadioItem value="name" label="Sort by name" />
|
|
557
|
+
<ContextMenuRadioItem value="date" label="Sort by date" />
|
|
558
|
+
</ContextMenuRadioGroup>
|
|
559
|
+
<ContextMenuCheckboxItem label="Show hidden" value={true} />
|
|
560
|
+
</>
|
|
561
|
+
}>
|
|
562
|
+
<div>Right-click me</div>
|
|
563
|
+
</ContextMenu>,
|
|
564
|
+
);
|
|
565
|
+
expect(
|
|
566
|
+
screen.getByRole('menuitemradio', {name: 'Sort by name', hidden: true}),
|
|
567
|
+
).toHaveAttribute('aria-checked', 'true');
|
|
568
|
+
expect(
|
|
569
|
+
screen.getByRole('menuitemradio', {name: 'Sort by date', hidden: true}),
|
|
570
|
+
).toHaveAttribute('aria-checked', 'false');
|
|
571
|
+
expect(
|
|
572
|
+
screen.getByRole('menuitemcheckbox', {name: 'Show hidden', hidden: true}),
|
|
573
|
+
).toHaveAttribute('aria-checked', 'true');
|
|
574
|
+
expect(
|
|
575
|
+
screen.getByRole('group', {name: 'Sort by', hidden: true}),
|
|
576
|
+
).toBeInTheDocument();
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('fires onChange for radio and checkbox items', async () => {
|
|
580
|
+
const user = userEvent.setup();
|
|
581
|
+
const onSort = vi.fn();
|
|
582
|
+
const onToggle = vi.fn();
|
|
583
|
+
render(
|
|
584
|
+
<ContextMenu
|
|
585
|
+
menuContent={
|
|
586
|
+
<>
|
|
587
|
+
<ContextMenuRadioGroup
|
|
588
|
+
value="name"
|
|
589
|
+
onChange={onSort}
|
|
590
|
+
aria-label="Sort by">
|
|
591
|
+
<ContextMenuRadioItem value="name" label="Sort by name" />
|
|
592
|
+
<ContextMenuRadioItem value="date" label="Sort by date" />
|
|
593
|
+
</ContextMenuRadioGroup>
|
|
594
|
+
<ContextMenuCheckboxItem
|
|
595
|
+
label="Show hidden"
|
|
596
|
+
value={false}
|
|
597
|
+
onChange={onToggle}
|
|
598
|
+
/>
|
|
599
|
+
</>
|
|
600
|
+
}>
|
|
601
|
+
<div>Right-click me</div>
|
|
602
|
+
</ContextMenu>,
|
|
603
|
+
);
|
|
604
|
+
await user.click(
|
|
605
|
+
screen.getByRole('menuitemradio', {name: 'Sort by date', hidden: true}),
|
|
606
|
+
);
|
|
607
|
+
expect(onSort).toHaveBeenCalledWith('date');
|
|
608
|
+
await user.click(
|
|
609
|
+
screen.getByRole('menuitemcheckbox', {name: 'Show hidden', hidden: true}),
|
|
610
|
+
);
|
|
611
|
+
expect(onToggle).toHaveBeenCalledWith(true);
|
|
612
|
+
});
|
|
613
|
+
});
|
|
@@ -46,6 +46,10 @@ import {
|
|
|
46
46
|
DropdownMenuContext,
|
|
47
47
|
type DropdownMenuContextValue,
|
|
48
48
|
} from '../DropdownMenu/DropdownMenuContext';
|
|
49
|
+
import {
|
|
50
|
+
MENU_ITEM_ROLES,
|
|
51
|
+
MENU_ITEM_SELECTOR,
|
|
52
|
+
} from '../DropdownMenu/menuItemRoles';
|
|
49
53
|
import {useListFocus} from '../hooks/useListFocus';
|
|
50
54
|
import {useTypeahead} from '../hooks/useTypeahead';
|
|
51
55
|
import {useLongPress} from '../hooks/useLongPress';
|
|
@@ -265,7 +269,7 @@ export function ContextMenu({
|
|
|
265
269
|
focusFirst,
|
|
266
270
|
focusItem,
|
|
267
271
|
} = useListFocus<HTMLDivElement>({
|
|
268
|
-
itemSelector:
|
|
272
|
+
itemSelector: MENU_ITEM_SELECTOR,
|
|
269
273
|
wrap: false,
|
|
270
274
|
onEscape: closeMenu,
|
|
271
275
|
});
|
|
@@ -275,9 +279,7 @@ export function ContextMenu({
|
|
|
275
279
|
(): HTMLElement[] =>
|
|
276
280
|
listRef.current
|
|
277
281
|
? Array.from(
|
|
278
|
-
listRef.current.querySelectorAll<HTMLElement>(
|
|
279
|
-
'[role="menuitem"]:not([aria-disabled="true"])',
|
|
280
|
-
),
|
|
282
|
+
listRef.current.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR),
|
|
281
283
|
)
|
|
282
284
|
: [],
|
|
283
285
|
[listRef],
|
|
@@ -341,7 +343,10 @@ export function ContextMenu({
|
|
|
341
343
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
342
344
|
e.preventDefault();
|
|
343
345
|
const focused = document.activeElement as HTMLElement | null;
|
|
344
|
-
if (
|
|
346
|
+
if (
|
|
347
|
+
focused &&
|
|
348
|
+
MENU_ITEM_ROLES.has(focused.getAttribute('role') ?? '')
|
|
349
|
+
) {
|
|
345
350
|
focused.click();
|
|
346
351
|
}
|
|
347
352
|
return;
|
package/src/ContextMenu/index.ts
CHANGED
|
@@ -20,3 +20,18 @@ export {
|
|
|
20
20
|
DropdownMenuItem as ContextMenuItem,
|
|
21
21
|
type DropdownMenuItemProps as ContextMenuItemProps,
|
|
22
22
|
} from '../DropdownMenu/DropdownMenuItem';
|
|
23
|
+
|
|
24
|
+
// Selectable items work inside a ContextMenu too — re-exported under the
|
|
25
|
+
// ContextMenu name for a coherent API.
|
|
26
|
+
export {
|
|
27
|
+
DropdownMenuCheckboxItem as ContextMenuCheckboxItem,
|
|
28
|
+
type DropdownMenuCheckboxItemProps as ContextMenuCheckboxItemProps,
|
|
29
|
+
} from '../DropdownMenu/DropdownMenuCheckboxItem';
|
|
30
|
+
export {
|
|
31
|
+
DropdownMenuRadioGroup as ContextMenuRadioGroup,
|
|
32
|
+
type DropdownMenuRadioGroupProps as ContextMenuRadioGroupProps,
|
|
33
|
+
} from '../DropdownMenu/DropdownMenuRadioGroup';
|
|
34
|
+
export {
|
|
35
|
+
DropdownMenuRadioItem as ContextMenuRadioItem,
|
|
36
|
+
type DropdownMenuRadioItemProps as ContextMenuRadioItemProps,
|
|
37
|
+
} from '../DropdownMenu/DropdownMenuRadioItem';
|