@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file MobileNavToggle.test.tsx
|
|
5
|
+
* @input Uses vitest, @testing-library/react, AppShell + SideNav
|
|
6
|
+
* @output Unit tests for MobileNavToggle ARIA wiring
|
|
7
|
+
* @position Testing; validates MobileNavToggle.tsx exposes aria-expanded and
|
|
8
|
+
* aria-controls so screen-reader users know the drawer state and target.
|
|
9
|
+
*
|
|
10
|
+
* SYNC: When MobileNavToggle.tsx changes, update tests to match new behavior
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
describe,
|
|
15
|
+
it,
|
|
16
|
+
expect,
|
|
17
|
+
vi,
|
|
18
|
+
beforeAll,
|
|
19
|
+
beforeEach,
|
|
20
|
+
afterEach,
|
|
21
|
+
} from 'vitest';
|
|
22
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
23
|
+
import {AppShell} from '../AppShell/AppShell';
|
|
24
|
+
import {SideNav, SideNavItem, SideNavSection} from '../SideNav';
|
|
25
|
+
|
|
26
|
+
// jsdom doesn't implement showModal/close on <dialog>, so we mock them
|
|
27
|
+
beforeAll(() => {
|
|
28
|
+
HTMLDialogElement.prototype.showModal =
|
|
29
|
+
HTMLDialogElement.prototype.showModal ||
|
|
30
|
+
function (this: HTMLDialogElement) {
|
|
31
|
+
this.setAttribute('open', '');
|
|
32
|
+
};
|
|
33
|
+
HTMLDialogElement.prototype.close =
|
|
34
|
+
HTMLDialogElement.prototype.close ||
|
|
35
|
+
function (this: HTMLDialogElement) {
|
|
36
|
+
this.removeAttribute('open');
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
class MockResizeObserver {
|
|
41
|
+
observe() {}
|
|
42
|
+
unobserve() {}
|
|
43
|
+
disconnect() {}
|
|
44
|
+
}
|
|
45
|
+
vi.stubGlobal('ResizeObserver', MockResizeObserver);
|
|
46
|
+
|
|
47
|
+
function createMockMatchMedia(matches: boolean) {
|
|
48
|
+
return {
|
|
49
|
+
matches,
|
|
50
|
+
media: '',
|
|
51
|
+
onchange: null,
|
|
52
|
+
addEventListener: vi.fn(),
|
|
53
|
+
removeEventListener: vi.fn(),
|
|
54
|
+
addListener: vi.fn(),
|
|
55
|
+
removeListener: vi.fn(),
|
|
56
|
+
dispatchEvent: vi.fn(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
// Below the breakpoint so the mobile nav (and its toggle) render.
|
|
62
|
+
vi.stubGlobal(
|
|
63
|
+
'matchMedia',
|
|
64
|
+
vi.fn().mockReturnValue(createMockMatchMedia(true)),
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
vi.restoreAllMocks();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
function TestShell() {
|
|
73
|
+
return (
|
|
74
|
+
<AppShell
|
|
75
|
+
sideNav={
|
|
76
|
+
<SideNav>
|
|
77
|
+
<SideNavSection title="Test" isHeaderHidden>
|
|
78
|
+
<SideNavItem label="Home" />
|
|
79
|
+
</SideNavSection>
|
|
80
|
+
</SideNav>
|
|
81
|
+
}
|
|
82
|
+
mobileNav={{breakpoint: 'md'}}>
|
|
83
|
+
<div>Content</div>
|
|
84
|
+
</AppShell>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe('MobileNavToggle ARIA wiring', () => {
|
|
89
|
+
it('exposes aria-expanded="false" when the drawer is closed', () => {
|
|
90
|
+
render(<TestShell />);
|
|
91
|
+
|
|
92
|
+
const toggle = screen.getByRole('button', {name: /open navigation/i});
|
|
93
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('exposes aria-expanded="true" after opening the drawer', () => {
|
|
97
|
+
render(<TestShell />);
|
|
98
|
+
|
|
99
|
+
const toggle = screen.getByRole('button', {name: /open navigation/i});
|
|
100
|
+
fireEvent.click(toggle);
|
|
101
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('references the nav drawer via aria-controls that resolves to the dialog', () => {
|
|
105
|
+
render(<TestShell />);
|
|
106
|
+
|
|
107
|
+
const toggle = screen.getByRole('button', {name: /open navigation/i});
|
|
108
|
+
const controls = toggle.getAttribute('aria-controls');
|
|
109
|
+
expect(controls).toBeTruthy();
|
|
110
|
+
|
|
111
|
+
const target = document.getElementById(controls!);
|
|
112
|
+
expect(target).not.toBeNull();
|
|
113
|
+
expect(target).toBe(screen.getAllByRole('dialog', {hidden: true})[0]);
|
|
114
|
+
expect(target?.tagName).toBe('DIALOG');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -69,7 +69,13 @@ export function MobileNavToggle({
|
|
|
69
69
|
}: MobileNavToggleProps) {
|
|
70
70
|
const t = useTranslator();
|
|
71
71
|
const label = labelFromProps ?? t('@astryx.mobileNav.toggle.open');
|
|
72
|
-
const {
|
|
72
|
+
const {
|
|
73
|
+
isMobile,
|
|
74
|
+
isMobileNavEnabled,
|
|
75
|
+
isMobileNavOpen,
|
|
76
|
+
mobileNavId,
|
|
77
|
+
toggleMobileNav,
|
|
78
|
+
} = useAppShellMobile();
|
|
73
79
|
|
|
74
80
|
// Don't render above the breakpoint or when mobile nav is disabled
|
|
75
81
|
if (!isMobile || !isMobileNavEnabled) {
|
|
@@ -83,6 +89,8 @@ export function MobileNavToggle({
|
|
|
83
89
|
label={label}
|
|
84
90
|
icon={children ?? <Icon icon="menu" color="inherit" />}
|
|
85
91
|
onClick={toggleMobileNav}
|
|
92
|
+
aria-expanded={isMobileNavOpen}
|
|
93
|
+
aria-controls={mobileNavId || undefined}
|
|
86
94
|
data-testid={testId ?? 'mobile-nav-toggle'}
|
|
87
95
|
xstyle={xstyle}
|
|
88
96
|
className={className}
|
|
@@ -99,7 +99,8 @@ export const docs = {
|
|
|
99
99
|
{
|
|
100
100
|
name: 'hasSearch',
|
|
101
101
|
type: 'boolean',
|
|
102
|
-
description:
|
|
102
|
+
description:
|
|
103
|
+
'Whether to show a search input for filtering options. As the user types, the match count (or "No results found") is announced to screen readers via a polite live region.',
|
|
103
104
|
},
|
|
104
105
|
{
|
|
105
106
|
name: 'searchPlaceholder',
|
|
@@ -239,7 +240,8 @@ export const docsZh = {
|
|
|
239
240
|
hasSearch: '是否显示用于过滤选项的搜索输入。',
|
|
240
241
|
searchPlaceholder: '搜索输入的占位文本。',
|
|
241
242
|
isDisabled: '禁用选择器。',
|
|
242
|
-
htmlName:
|
|
243
|
+
htmlName:
|
|
244
|
+
'用于表单提交的 HTML name 属性。为每个已选值渲染一个隐藏输入,类似原生多选。',
|
|
243
245
|
disabledMessage:
|
|
244
246
|
'解释选择器被禁用的原因。与 isDisabled 一起使用时,悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持触发器可聚焦(仍无法激活)。请使用此属性,而不是用 Tooltip 包裹被禁用的选择器。',
|
|
245
247
|
isLabelHidden: '视觉上隐藏标签同时保持其可访问性。',
|
|
@@ -360,6 +360,30 @@ describe('MultiSelector', () => {
|
|
|
360
360
|
expect(activeId).toBeTruthy();
|
|
361
361
|
});
|
|
362
362
|
|
|
363
|
+
it('End/Home jump the highlight to the last/first option (non-search)', async () => {
|
|
364
|
+
const user = userEvent.setup();
|
|
365
|
+
render(
|
|
366
|
+
<MultiSelector
|
|
367
|
+
label="Fruit"
|
|
368
|
+
options={defaultOptions}
|
|
369
|
+
value={[]}
|
|
370
|
+
onChange={() => {}}
|
|
371
|
+
/>,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const trigger = screen.getByRole('combobox');
|
|
375
|
+
await user.click(trigger);
|
|
376
|
+
const options = screen.getAllByRole('option', h);
|
|
377
|
+
|
|
378
|
+
await user.keyboard('{End}');
|
|
379
|
+
expect(trigger).toHaveAttribute(
|
|
380
|
+
'aria-activedescendant',
|
|
381
|
+
options[options.length - 1].id,
|
|
382
|
+
);
|
|
383
|
+
await user.keyboard('{Home}');
|
|
384
|
+
expect(trigger).toHaveAttribute('aria-activedescendant', options[0].id);
|
|
385
|
+
});
|
|
386
|
+
|
|
363
387
|
it('toggles item with Enter key', async () => {
|
|
364
388
|
const user = userEvent.setup();
|
|
365
389
|
const onChange = vi.fn();
|
|
@@ -535,6 +559,65 @@ describe('MultiSelector', () => {
|
|
|
535
559
|
expect(options).toHaveLength(1);
|
|
536
560
|
});
|
|
537
561
|
|
|
562
|
+
it('PageDown/PageUp jump the highlight to the last/first filtered option', async () => {
|
|
563
|
+
const user = userEvent.setup();
|
|
564
|
+
render(
|
|
565
|
+
<MultiSelector
|
|
566
|
+
label="Fruit"
|
|
567
|
+
options={defaultOptions}
|
|
568
|
+
value={[]}
|
|
569
|
+
onChange={() => {}}
|
|
570
|
+
hasSearch
|
|
571
|
+
/>,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
575
|
+
const searchInput = screen.getByRole('combobox', h);
|
|
576
|
+
// Filter to Banana and Orange so "last" means last *visible* option.
|
|
577
|
+
await user.type(searchInput, 'an');
|
|
578
|
+
const options = screen.getAllByRole('option', h);
|
|
579
|
+
expect(options).toHaveLength(2);
|
|
580
|
+
|
|
581
|
+
await user.keyboard('{PageDown}');
|
|
582
|
+
expect(searchInput).toHaveAttribute(
|
|
583
|
+
'aria-activedescendant',
|
|
584
|
+
options[options.length - 1].id,
|
|
585
|
+
);
|
|
586
|
+
await user.keyboard('{PageUp}');
|
|
587
|
+
expect(searchInput).toHaveAttribute('aria-activedescendant', options[0].id);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('Home/End move the search caret, not the option highlight', async () => {
|
|
591
|
+
const user = userEvent.setup();
|
|
592
|
+
render(
|
|
593
|
+
<MultiSelector
|
|
594
|
+
label="Fruit"
|
|
595
|
+
options={defaultOptions}
|
|
596
|
+
value={[]}
|
|
597
|
+
onChange={() => {}}
|
|
598
|
+
hasSearch
|
|
599
|
+
/>,
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
603
|
+
const searchInput = screen.getByRole<HTMLInputElement>('combobox', h);
|
|
604
|
+
await user.type(searchInput, 'an');
|
|
605
|
+
expect(searchInput.selectionStart).toBe(2);
|
|
606
|
+
const activeBefore = searchInput.getAttribute('aria-activedescendant');
|
|
607
|
+
// Home/End stay on the input for caret movement (APG editable combobox);
|
|
608
|
+
// the option highlight must not move.
|
|
609
|
+
await user.keyboard('{Home}');
|
|
610
|
+
expect(searchInput.selectionStart).toBe(0);
|
|
611
|
+
expect(searchInput.getAttribute('aria-activedescendant')).toBe(
|
|
612
|
+
activeBefore,
|
|
613
|
+
);
|
|
614
|
+
await user.keyboard('{End}');
|
|
615
|
+
expect(searchInput.selectionStart).toBe(2);
|
|
616
|
+
expect(searchInput.getAttribute('aria-activedescendant')).toBe(
|
|
617
|
+
activeBefore,
|
|
618
|
+
);
|
|
619
|
+
});
|
|
620
|
+
|
|
538
621
|
it('shows empty state when search has no results', async () => {
|
|
539
622
|
const user = userEvent.setup();
|
|
540
623
|
render(
|
|
@@ -554,6 +637,82 @@ describe('MultiSelector', () => {
|
|
|
554
637
|
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
555
638
|
});
|
|
556
639
|
|
|
640
|
+
describe('result announcements', () => {
|
|
641
|
+
it('announces the match count politely while searching', async () => {
|
|
642
|
+
const user = userEvent.setup();
|
|
643
|
+
render(
|
|
644
|
+
<MultiSelector
|
|
645
|
+
label="Fruit"
|
|
646
|
+
options={defaultOptions}
|
|
647
|
+
value={EMPTY_VALUE}
|
|
648
|
+
onChange={() => {}}
|
|
649
|
+
hasSearch
|
|
650
|
+
/>,
|
|
651
|
+
);
|
|
652
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
653
|
+
// "an" matches Banana and Orange.
|
|
654
|
+
await user.type(screen.getByRole('combobox', h), 'an');
|
|
655
|
+
await waitFor(() => {
|
|
656
|
+
expect(politeRegion()).toHaveTextContent('2 results');
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it('announces the singular form when one option matches', async () => {
|
|
661
|
+
const user = userEvent.setup();
|
|
662
|
+
render(
|
|
663
|
+
<MultiSelector
|
|
664
|
+
label="Fruit"
|
|
665
|
+
options={defaultOptions}
|
|
666
|
+
value={EMPTY_VALUE}
|
|
667
|
+
onChange={() => {}}
|
|
668
|
+
hasSearch
|
|
669
|
+
/>,
|
|
670
|
+
);
|
|
671
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
672
|
+
// "app" matches only Apple. Anchored so it cannot pass on "1 results".
|
|
673
|
+
await user.type(screen.getByRole('combobox', h), 'app');
|
|
674
|
+
await waitFor(() => {
|
|
675
|
+
expect(politeRegion()).toHaveTextContent(/^1 result$/);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('announces "No results found" when nothing matches', async () => {
|
|
680
|
+
const user = userEvent.setup();
|
|
681
|
+
render(
|
|
682
|
+
<MultiSelector
|
|
683
|
+
label="Fruit"
|
|
684
|
+
options={defaultOptions}
|
|
685
|
+
value={EMPTY_VALUE}
|
|
686
|
+
onChange={() => {}}
|
|
687
|
+
hasSearch
|
|
688
|
+
/>,
|
|
689
|
+
);
|
|
690
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
691
|
+
await user.type(screen.getByRole('combobox', h), 'xyz');
|
|
692
|
+
await waitFor(() => {
|
|
693
|
+
expect(politeRegion()).toHaveTextContent('No results found');
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('does not announce results until the user searches', async () => {
|
|
698
|
+
const user = userEvent.setup();
|
|
699
|
+
render(
|
|
700
|
+
<MultiSelector
|
|
701
|
+
label="Fruit"
|
|
702
|
+
options={defaultOptions}
|
|
703
|
+
value={EMPTY_VALUE}
|
|
704
|
+
onChange={() => {}}
|
|
705
|
+
hasSearch
|
|
706
|
+
/>,
|
|
707
|
+
);
|
|
708
|
+
// Popover closed: nothing announced.
|
|
709
|
+
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
710
|
+
// Open with an empty query: still nothing announced.
|
|
711
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
712
|
+
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
713
|
+
});
|
|
714
|
+
});
|
|
715
|
+
|
|
557
716
|
it('renders with description', () => {
|
|
558
717
|
render(
|
|
559
718
|
<MultiSelector
|
|
@@ -1072,7 +1231,9 @@ describe('MultiSelector', () => {
|
|
|
1072
1231
|
/>
|
|
1073
1232
|
</form>,
|
|
1074
1233
|
);
|
|
1075
|
-
expect([
|
|
1234
|
+
expect([
|
|
1235
|
+
...new FormData(container.querySelector('form')!).keys(),
|
|
1236
|
+
]).toEqual([]);
|
|
1076
1237
|
});
|
|
1077
1238
|
});
|
|
1078
1239
|
});
|
|
@@ -562,6 +562,23 @@ export interface MultiSelectorProps<
|
|
|
562
562
|
'data-testid'?: string;
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
// Case-insensitive substring filter over the selectable options. Shared by the
|
|
566
|
+
// `filteredItems` memo (rendering) and the search-change handler, which needs
|
|
567
|
+
// the count for the *next* query synchronously to announce it exactly once per
|
|
568
|
+
// keystroke rather than reacting to state in an effect.
|
|
569
|
+
function filterOptionsByQuery(
|
|
570
|
+
items: MultiSelectorOptionData[],
|
|
571
|
+
query: string,
|
|
572
|
+
): MultiSelectorOptionData[] {
|
|
573
|
+
if (!query) {
|
|
574
|
+
return items;
|
|
575
|
+
}
|
|
576
|
+
const q = query.toLowerCase();
|
|
577
|
+
return items.filter(item =>
|
|
578
|
+
(item.label ?? item.value).toLowerCase().includes(q),
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
565
582
|
/**
|
|
566
583
|
* A multi-select dropdown component with checkboxes for choosing
|
|
567
584
|
* multiple items from a list of options.
|
|
@@ -692,15 +709,10 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
692
709
|
);
|
|
693
710
|
|
|
694
711
|
// Filter items by search query
|
|
695
|
-
const filteredItems = useMemo(
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
const query = searchQuery.toLowerCase();
|
|
700
|
-
return selectableItems.filter(item =>
|
|
701
|
-
(item.label ?? item.value).toLowerCase().includes(query),
|
|
702
|
-
);
|
|
703
|
-
}, [selectableItems, searchQuery]);
|
|
712
|
+
const filteredItems = useMemo(
|
|
713
|
+
() => filterOptionsByQuery(selectableItems, searchQuery),
|
|
714
|
+
[selectableItems, searchQuery],
|
|
715
|
+
);
|
|
704
716
|
|
|
705
717
|
// Single source of truth for item order. Both the hook (keyboard navigation)
|
|
706
718
|
// and renderOptions (DOM rendering) consume this list — no independent sorting.
|
|
@@ -772,8 +784,11 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
772
784
|
const handleLayerHide = useCallback(() => {
|
|
773
785
|
setSearchQuery('');
|
|
774
786
|
setSelectedAtOpen(null);
|
|
787
|
+
// Clear any lingering result count when the popover closes so stale status
|
|
788
|
+
// text does not linger in the a11y tree.
|
|
789
|
+
announce('');
|
|
775
790
|
triggerRef.current?.focus();
|
|
776
|
-
}, []);
|
|
791
|
+
}, [announce]);
|
|
777
792
|
|
|
778
793
|
const popover = usePopover({
|
|
779
794
|
hasLightDismiss: true,
|
|
@@ -793,6 +808,30 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
793
808
|
// eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
|
|
794
809
|
}, []);
|
|
795
810
|
|
|
811
|
+
// Announce the filtered result count from the query-change handler (matching
|
|
812
|
+
// BaseTypeahead) rather than a reactive effect: computing the count for the
|
|
813
|
+
// next query here fires the announcement exactly once per keystroke and does
|
|
814
|
+
// not re-speak on unrelated re-renders. Reuses the announce instance shared
|
|
815
|
+
// with the selection-count announcements above.
|
|
816
|
+
const handleSearchChange = useCallback(
|
|
817
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
818
|
+
const nextQuery = event.target.value;
|
|
819
|
+
setSearchQuery(nextQuery);
|
|
820
|
+
if (nextQuery.length === 0) {
|
|
821
|
+
// Emptying the query clears the region rather than announcing a count.
|
|
822
|
+
announce('');
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
const count = filterOptionsByQuery(selectableItems, nextQuery).length;
|
|
826
|
+
announce(
|
|
827
|
+
count === 0
|
|
828
|
+
? 'No results found'
|
|
829
|
+
: `${count} result${count === 1 ? '' : 's'}`,
|
|
830
|
+
);
|
|
831
|
+
},
|
|
832
|
+
[announce, selectableItems],
|
|
833
|
+
);
|
|
834
|
+
|
|
796
835
|
// Handle toggle
|
|
797
836
|
// Clear all selected values. Shared by the clear button and the keyboard
|
|
798
837
|
// Delete/Backspace path so clearing is reachable without a mouse.
|
|
@@ -1041,14 +1080,17 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1041
1080
|
aria-label={t('@astryx.multiSelector.searchOptions')}
|
|
1042
1081
|
type="text"
|
|
1043
1082
|
value={searchQuery}
|
|
1044
|
-
onChange={
|
|
1083
|
+
onChange={handleSearchChange}
|
|
1045
1084
|
onKeyDown={e => {
|
|
1046
1085
|
// Arrow keys navigate options; Enter toggles; Escape/Tab close.
|
|
1047
1086
|
// Space and Home/End are left to the input (type a space / move
|
|
1048
|
-
// the caret)
|
|
1087
|
+
// the caret) per the APG editable combobox; PageUp/PageDown are
|
|
1088
|
+
// the sanctioned substitute for jumping to the first/last option.
|
|
1049
1089
|
if (
|
|
1050
1090
|
e.key === 'ArrowDown' ||
|
|
1051
1091
|
e.key === 'ArrowUp' ||
|
|
1092
|
+
e.key === 'PageUp' ||
|
|
1093
|
+
e.key === 'PageDown' ||
|
|
1052
1094
|
e.key === 'Enter' ||
|
|
1053
1095
|
e.key === 'Escape' ||
|
|
1054
1096
|
e.key === 'Tab'
|
|
@@ -1067,6 +1109,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1067
1109
|
listboxId,
|
|
1068
1110
|
searchQuery,
|
|
1069
1111
|
searchPlaceholder,
|
|
1112
|
+
handleSearchChange,
|
|
1070
1113
|
onKeyDown,
|
|
1071
1114
|
popover.isOpen,
|
|
1072
1115
|
highlightedIndex,
|
|
@@ -192,6 +192,23 @@ export function useMultiCombobox({
|
|
|
192
192
|
}
|
|
193
193
|
break;
|
|
194
194
|
|
|
195
|
+
// PageUp/PageDown mirror Home/End. In search mode Home/End stay on
|
|
196
|
+
// the input for caret movement (APG editable combobox), so these are
|
|
197
|
+
// the sanctioned substitute for jumping to the first/last option.
|
|
198
|
+
case 'PageUp':
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
if (isOpen && enabledIndices.length > 0) {
|
|
201
|
+
setHighlightedIndex(enabledIndices[0]);
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case 'PageDown':
|
|
206
|
+
e.preventDefault();
|
|
207
|
+
if (isOpen && enabledIndices.length > 0) {
|
|
208
|
+
setHighlightedIndex(enabledIndices[enabledIndices.length - 1]);
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
|
|
195
212
|
case 'Delete':
|
|
196
213
|
case 'Backspace':
|
|
197
214
|
// Keyboard equivalent of the clear button (comboboxes-2): clear all
|
|
@@ -82,12 +82,12 @@ export const docs = {
|
|
|
82
82
|
{
|
|
83
83
|
name: 'startIcon',
|
|
84
84
|
type: 'IconType',
|
|
85
|
-
description: 'Icon to display at the start of the input. See `
|
|
85
|
+
description: 'Icon to display at the start of the input. See `astryx docs icons` for valid semantic names.',
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
name: 'labelIcon',
|
|
89
89
|
type: 'IconType',
|
|
90
|
-
description: 'Icon to display before the label text. See `
|
|
90
|
+
description: 'Icon to display before the label text. See `astryx docs icons` for valid semantic names.',
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
name: 'status',
|
|
@@ -9,11 +9,19 @@
|
|
|
9
9
|
* SYNC: When NumberInput.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
12
|
+
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
13
13
|
import {render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {TestIcon} from '../__tests__/TestIcon';
|
|
16
16
|
import {NumberInput} from './NumberInput';
|
|
17
|
+
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
18
|
+
|
|
19
|
+
// FieldStatus announces status messages through the persistent useAnnounce
|
|
20
|
+
// singletons; remove them between tests so role/aria-live queries in this
|
|
21
|
+
// file never match a leftover region.
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
__resetLiveRegionsForTest();
|
|
24
|
+
});
|
|
17
25
|
|
|
18
26
|
// Mock showPopover/hidePopover since jsdom does not implement them. Used by the
|
|
19
27
|
// disabledMessage tooltip.
|
|
@@ -871,9 +879,7 @@ describe('NumberInput', () => {
|
|
|
871
879
|
describe('keyboard clearing with hasClear (#3599)', () => {
|
|
872
880
|
it('commits null when the input is emptied and blurred', () => {
|
|
873
881
|
const onChange = vi.fn();
|
|
874
|
-
render(
|
|
875
|
-
<NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
|
|
876
|
-
);
|
|
882
|
+
render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
|
|
877
883
|
const input = screen.getByLabelText('Qty');
|
|
878
884
|
fireEvent.change(input, {target: {value: ''}});
|
|
879
885
|
fireEvent.blur(input);
|
|
@@ -882,9 +888,7 @@ describe('keyboard clearing with hasClear (#3599)', () => {
|
|
|
882
888
|
|
|
883
889
|
it('commits null when the input is emptied and Enter is pressed', () => {
|
|
884
890
|
const onChange = vi.fn();
|
|
885
|
-
render(
|
|
886
|
-
<NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
|
|
887
|
-
);
|
|
891
|
+
render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
|
|
888
892
|
const input = screen.getByLabelText('Qty');
|
|
889
893
|
fireEvent.change(input, {target: {value: ''}});
|
|
890
894
|
fireEvent.keyDown(input, {key: 'Enter'});
|
|
@@ -311,8 +311,7 @@ type NumberInputPropsClearable = NumberInputPropsBase & {
|
|
|
311
311
|
};
|
|
312
312
|
|
|
313
313
|
export type NumberInputProps =
|
|
314
|
-
|
|
|
315
|
-
| NumberInputPropsClearable;
|
|
314
|
+
NumberInputPropsNonClearable | NumberInputPropsClearable;
|
|
316
315
|
|
|
317
316
|
/**
|
|
318
317
|
* Parse and validate a string input as a number.
|