@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
|
@@ -337,7 +337,9 @@ describe('SideNavHeading', () => {
|
|
|
337
337
|
it('whole heading is popover trigger when menu provided without hrefs', () => {
|
|
338
338
|
render(<SideNavHeading heading="My App" menu={<div>Menu</div>} />);
|
|
339
339
|
const button = screen.getByRole('button');
|
|
340
|
-
|
|
340
|
+
// The popup is no longer a dialog (role: 'none' on usePopover), so the
|
|
341
|
+
// trigger advertises a generic popup rather than aria-haspopup="dialog".
|
|
342
|
+
expect(button).toHaveAttribute('aria-haspopup', 'true');
|
|
341
343
|
expect(button).toHaveAttribute('aria-expanded', 'false');
|
|
342
344
|
});
|
|
343
345
|
|
|
@@ -351,7 +353,7 @@ describe('SideNavHeading', () => {
|
|
|
351
353
|
const button = screen.getByRole('button');
|
|
352
354
|
// The trigger button uses aria attributes from usePopover and
|
|
353
355
|
// an onClick handler from useMenuHover for click-to-lock toggle.
|
|
354
|
-
expect(button).toHaveAttribute('aria-haspopup', '
|
|
356
|
+
expect(button).toHaveAttribute('aria-haspopup', 'true');
|
|
355
357
|
expect(button).toHaveAttribute('aria-expanded');
|
|
356
358
|
});
|
|
357
359
|
|
|
@@ -364,13 +366,92 @@ describe('SideNavHeading', () => {
|
|
|
364
366
|
/>,
|
|
365
367
|
);
|
|
366
368
|
const button = screen.getByRole('button', {name: 'Open menu'});
|
|
367
|
-
expect(button).toHaveAttribute('aria-haspopup', '
|
|
369
|
+
expect(button).toHaveAttribute('aria-haspopup', 'true');
|
|
368
370
|
});
|
|
369
371
|
|
|
370
372
|
it('passes data-testid', () => {
|
|
371
373
|
render(<SideNavHeading heading="My App" data-testid="nav-header" />);
|
|
372
374
|
expect(screen.getByTestId('nav-header')).toBeInTheDocument();
|
|
373
375
|
});
|
|
376
|
+
|
|
377
|
+
// ===========================================================================
|
|
378
|
+
// Menu popover semantics — the popup must not be announced as a modal
|
|
379
|
+
// dialog, and role="menu" must be scoped to the actual menu items so the
|
|
380
|
+
// heading button is not an invalid child of the menu.
|
|
381
|
+
// ===========================================================================
|
|
382
|
+
|
|
383
|
+
// The popover layer keeps `popover` content display:none in jsdom even
|
|
384
|
+
// when open, hiding it from role queries — so these tests assert the
|
|
385
|
+
// popup's ARIA semantics at the DOM level instead.
|
|
386
|
+
describe('menu popover semantics', () => {
|
|
387
|
+
const menuItems = (
|
|
388
|
+
<>
|
|
389
|
+
<div role="menuitem">Alpha</div>
|
|
390
|
+
<div role="menuitem">Beta</div>
|
|
391
|
+
</>
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
it('does not wrap the heading menu popup in a modal dialog', async () => {
|
|
395
|
+
const user = userEvent.setup();
|
|
396
|
+
render(<SideNavHeading heading="My App" menu={menuItems} />);
|
|
397
|
+
await user.click(screen.getByRole('button', {name: 'Open menu'}));
|
|
398
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
399
|
+
expect(document.querySelector('[aria-modal="true"]')).toBeNull();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('scopes role="menu" to only menuitem children with an accessible name', async () => {
|
|
403
|
+
const user = userEvent.setup();
|
|
404
|
+
render(<SideNavHeading heading="My App" menu={menuItems} />);
|
|
405
|
+
await user.click(screen.getByRole('button', {name: 'Open menu'}));
|
|
406
|
+
const menu = document.querySelector('[role="menu"]');
|
|
407
|
+
expect(menu).not.toBeNull();
|
|
408
|
+
expect(menu).toHaveAttribute('aria-label', 'My App');
|
|
409
|
+
const children = Array.from(menu!.children);
|
|
410
|
+
expect(children.length).toBeGreaterThan(0);
|
|
411
|
+
for (const child of children) {
|
|
412
|
+
expect(child).toHaveAttribute('role', 'menuitem');
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('keeps the popup heading button outside the menu and closes on click', async () => {
|
|
417
|
+
const user = userEvent.setup();
|
|
418
|
+
render(<SideNavHeading heading="My App" menu={menuItems} />);
|
|
419
|
+
const trigger = screen.getByRole('button', {name: 'Open menu'});
|
|
420
|
+
await user.click(trigger);
|
|
421
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
422
|
+
const menu = document.querySelector('[role="menu"]');
|
|
423
|
+
expect(menu).not.toBeNull();
|
|
424
|
+
// The heading replica button in the popup is a sibling of the menu,
|
|
425
|
+
// not an invalid menu child.
|
|
426
|
+
const headingButton = Array.from(
|
|
427
|
+
document.querySelectorAll('button'),
|
|
428
|
+
).find(b => b !== trigger && b.textContent?.includes('My App'));
|
|
429
|
+
expect(headingButton).toBeDefined();
|
|
430
|
+
expect(menu!.contains(headingButton!)).toBe(false);
|
|
431
|
+
// Clicking it still closes the popup.
|
|
432
|
+
fireEvent.click(headingButton!);
|
|
433
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('applies the same semantics in mixed mode (menu + hrefs)', async () => {
|
|
437
|
+
const user = userEvent.setup();
|
|
438
|
+
render(
|
|
439
|
+
<SideNavHeading
|
|
440
|
+
heading="Product"
|
|
441
|
+
headingHref="/product"
|
|
442
|
+
menu={menuItems}
|
|
443
|
+
/>,
|
|
444
|
+
);
|
|
445
|
+
await user.click(screen.getByRole('button', {name: 'Open menu'}));
|
|
446
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
447
|
+
const menu = document.querySelector('[role="menu"]');
|
|
448
|
+
expect(menu).not.toBeNull();
|
|
449
|
+
expect(menu).toHaveAttribute('aria-label', 'Product');
|
|
450
|
+
for (const child of Array.from(menu!.children)) {
|
|
451
|
+
expect(child).toHaveAttribute('role', 'menuitem');
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
});
|
|
374
455
|
});
|
|
375
456
|
|
|
376
457
|
// =============================================================================
|
|
@@ -476,6 +557,41 @@ describe('SideNavHeading collapsed', () => {
|
|
|
476
557
|
);
|
|
477
558
|
expect(screen.getByTestId('nav-header')).toBeInTheDocument();
|
|
478
559
|
});
|
|
560
|
+
|
|
561
|
+
it('collapsed menu popup is not a dialog and scopes role="menu" to menu items', async () => {
|
|
562
|
+
const user = userEvent.setup();
|
|
563
|
+
render(
|
|
564
|
+
<CollapsedWrapper>
|
|
565
|
+
<SideNavHeading
|
|
566
|
+
heading="My App"
|
|
567
|
+
icon={<span data-testid="app-icon">🏠</span>}
|
|
568
|
+
menu={
|
|
569
|
+
<>
|
|
570
|
+
<div role="menuitem">Alpha</div>
|
|
571
|
+
<div role="menuitem">Beta</div>
|
|
572
|
+
</>
|
|
573
|
+
}
|
|
574
|
+
/>
|
|
575
|
+
</CollapsedWrapper>,
|
|
576
|
+
);
|
|
577
|
+
await user.click(screen.getByRole('button', {name: 'My App'}));
|
|
578
|
+
// No modal dialog wrapper around the menu popup.
|
|
579
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
580
|
+
expect(document.querySelector('[aria-modal="true"]')).toBeNull();
|
|
581
|
+
// role="menu" is scoped to the menu items and has a direct name.
|
|
582
|
+
const menu = document.querySelector('[role="menu"]');
|
|
583
|
+
expect(menu).not.toBeNull();
|
|
584
|
+
expect(menu).toHaveAttribute('aria-label', 'My App');
|
|
585
|
+
const children = Array.from(menu!.children);
|
|
586
|
+
expect(children.length).toBeGreaterThan(0);
|
|
587
|
+
for (const child of children) {
|
|
588
|
+
expect(child).toHaveAttribute('role', 'menuitem');
|
|
589
|
+
}
|
|
590
|
+
// No button (trigger or heading replica) lives inside the menu.
|
|
591
|
+
for (const button of Array.from(document.querySelectorAll('button'))) {
|
|
592
|
+
expect(menu!.contains(button)).toBe(false);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
479
595
|
});
|
|
480
596
|
|
|
481
597
|
// =============================================================================
|
|
@@ -120,7 +120,12 @@ export function SideNavCollapseButton({
|
|
|
120
120
|
return (
|
|
121
121
|
<Button
|
|
122
122
|
ref={ref}
|
|
123
|
-
label={
|
|
123
|
+
label={
|
|
124
|
+
label ??
|
|
125
|
+
(isCollapsed
|
|
126
|
+
? t('@astryx.sideNavCollapseButton.expandSidebar')
|
|
127
|
+
: t('@astryx.sideNavCollapseButton.collapseSidebar'))
|
|
128
|
+
}
|
|
124
129
|
variant="ghost"
|
|
125
130
|
{...props}
|
|
126
131
|
onClick={composeEventHandlers(onClickProp, toggle)}
|
|
@@ -351,6 +351,10 @@ export function SideNavHeading({
|
|
|
351
351
|
|
|
352
352
|
const popover = usePopover({
|
|
353
353
|
dialogLabel: t('@astryx.sideNav.heading.dialogLabel'),
|
|
354
|
+
// The popup exposes its own role="menu" semantics; a role="dialog"
|
|
355
|
+
// aria-modal wrapper would announce "dialog, Navigation menu" around a
|
|
356
|
+
// menu (the anti-pattern removed in a478a3dcf).
|
|
357
|
+
role: 'none',
|
|
354
358
|
hasCloseButton: false,
|
|
355
359
|
});
|
|
356
360
|
|
|
@@ -428,7 +432,6 @@ export function SideNavHeading({
|
|
|
428
432
|
{popover.render(
|
|
429
433
|
<div
|
|
430
434
|
ref={menuRef}
|
|
431
|
-
role="menu"
|
|
432
435
|
{...stylex.props(styles.popoverContent)}
|
|
433
436
|
{...contentProps}>
|
|
434
437
|
<button
|
|
@@ -461,9 +464,14 @@ export function SideNavHeading({
|
|
|
461
464
|
)}
|
|
462
465
|
</span>
|
|
463
466
|
</button>
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
+
{/* The menu role is scoped to the actual menu items so the
|
|
468
|
+
heading button above stays a valid sibling, not an invalid
|
|
469
|
+
child of a role="menu" element. */}
|
|
470
|
+
<div role="menu" aria-label={heading}>
|
|
471
|
+
<NavHeadingCloseContext value={closeMenuCtx}>
|
|
472
|
+
{menu}
|
|
473
|
+
</NavHeadingCloseContext>
|
|
474
|
+
</div>
|
|
467
475
|
</div>,
|
|
468
476
|
{placement: 'below', alignment: 'start', xstyle: styles.popover},
|
|
469
477
|
)}
|
|
@@ -620,11 +628,15 @@ export function SideNavHeading({
|
|
|
620
628
|
{popover.render(
|
|
621
629
|
<div
|
|
622
630
|
ref={menuRef}
|
|
623
|
-
role="menu"
|
|
624
631
|
{...stylex.props(styles.popoverContent)}
|
|
625
632
|
{...contentProps}>
|
|
626
633
|
{popoverHeadingContent}
|
|
627
|
-
{menu
|
|
634
|
+
{/* The menu role is scoped to the actual menu items so the
|
|
635
|
+
heading button above stays a valid sibling, not an invalid
|
|
636
|
+
child of a role="menu" element. */}
|
|
637
|
+
<div role="menu" aria-label={heading}>
|
|
638
|
+
{menu}
|
|
639
|
+
</div>
|
|
628
640
|
</div>,
|
|
629
641
|
{
|
|
630
642
|
placement: 'below',
|
|
@@ -683,11 +695,15 @@ export function SideNavHeading({
|
|
|
683
695
|
{popover.render(
|
|
684
696
|
<div
|
|
685
697
|
ref={menuRef}
|
|
686
|
-
role="menu"
|
|
687
698
|
{...stylex.props(styles.popoverContent)}
|
|
688
699
|
{...contentProps}>
|
|
689
700
|
{popoverHeadingContent}
|
|
690
|
-
{menu
|
|
701
|
+
{/* The menu role is scoped to the actual menu items so the
|
|
702
|
+
heading button above stays a valid sibling, not an invalid
|
|
703
|
+
child of a role="menu" element. */}
|
|
704
|
+
<div role="menu" aria-label={heading}>
|
|
705
|
+
{menu}
|
|
706
|
+
</div>
|
|
691
707
|
</div>,
|
|
692
708
|
{
|
|
693
709
|
placement: 'below',
|
|
@@ -23,12 +23,12 @@ export const docs = {
|
|
|
23
23
|
{
|
|
24
24
|
name: 'icon',
|
|
25
25
|
type: 'IconType',
|
|
26
|
-
description: 'Icon displayed in the outline (unselected) variant. See `
|
|
26
|
+
description: 'Icon displayed in the outline (unselected) variant. See `astryx docs icons` for valid semantic names.',
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
name: 'selectedIcon',
|
|
30
30
|
type: 'IconType',
|
|
31
|
-
description: 'Icon displayed when the item is selected (filled variant). See `
|
|
31
|
+
description: 'Icon displayed when the item is selected (filled variant). See `astryx docs icons` for valid semantic names.',
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
name: 'isSelected',
|
|
@@ -628,7 +628,11 @@ export function SideNavItem({
|
|
|
628
628
|
<button
|
|
629
629
|
type="button"
|
|
630
630
|
onClick={handleToggleClick}
|
|
631
|
-
aria-label={
|
|
631
|
+
aria-label={
|
|
632
|
+
isItemCollapsed
|
|
633
|
+
? t('@astryx.sideNavItem.expand', {label})
|
|
634
|
+
: t('@astryx.sideNavItem.collapse', {label})
|
|
635
|
+
}
|
|
632
636
|
aria-expanded={!isItemCollapsed}
|
|
633
637
|
aria-controls={`${id}-children`}
|
|
634
638
|
{...stylex.props(styles.expandToggle)}>
|
|
@@ -155,6 +155,60 @@ describe('Slider', () => {
|
|
|
155
155
|
expect(ariaHidden.length).toBeGreaterThanOrEqual(2);
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
// --- Required state ---
|
|
159
|
+
|
|
160
|
+
it('conveys required state through the accessible description', () => {
|
|
161
|
+
render(<Slider label="Volume" value={50} isRequired />);
|
|
162
|
+
const slider = screen.getByRole('slider');
|
|
163
|
+
expect(slider).toHaveAccessibleDescription(/Required/);
|
|
164
|
+
// aria-required is not a supported property of role="slider" in
|
|
165
|
+
// WAI-ARIA 1.2, so it must never appear on the thumb.
|
|
166
|
+
expect(slider).not.toHaveAttribute('aria-required');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('conveys required state on both thumbs of a range slider', () => {
|
|
170
|
+
render(
|
|
171
|
+
<Slider
|
|
172
|
+
label="Price range"
|
|
173
|
+
value={[20, 80] as [number, number]}
|
|
174
|
+
isRequired
|
|
175
|
+
/>,
|
|
176
|
+
);
|
|
177
|
+
const sliders = screen.getAllByRole('slider');
|
|
178
|
+
expect(sliders).toHaveLength(2);
|
|
179
|
+
for (const thumb of sliders) {
|
|
180
|
+
expect(thumb).toHaveAccessibleDescription(/Required/);
|
|
181
|
+
expect(thumb).not.toHaveAttribute('aria-required');
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('combines required with other describedby parts in the description', () => {
|
|
186
|
+
render(
|
|
187
|
+
<Slider
|
|
188
|
+
label="Volume"
|
|
189
|
+
value={50}
|
|
190
|
+
description="Adjust the volume level"
|
|
191
|
+
isRequired
|
|
192
|
+
/>,
|
|
193
|
+
);
|
|
194
|
+
const slider = screen.getByRole('slider');
|
|
195
|
+
expect(slider).toHaveAccessibleDescription(/Adjust the volume level/);
|
|
196
|
+
expect(slider).toHaveAccessibleDescription(/Required/);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('does not mention required without isRequired', () => {
|
|
200
|
+
render(
|
|
201
|
+
<Slider
|
|
202
|
+
label="Volume"
|
|
203
|
+
value={50}
|
|
204
|
+
description="Adjust the volume level"
|
|
205
|
+
/>,
|
|
206
|
+
);
|
|
207
|
+
const slider = screen.getByRole('slider');
|
|
208
|
+
expect(slider).not.toHaveAccessibleDescription(/Required/);
|
|
209
|
+
expect(slider).not.toHaveAttribute('aria-required');
|
|
210
|
+
});
|
|
211
|
+
|
|
158
212
|
// --- Disabled guards ---
|
|
159
213
|
|
|
160
214
|
it('disables thumbs when isDisabled is true', () => {
|
|
@@ -549,7 +603,11 @@ describe('Slider', () => {
|
|
|
549
603
|
it('submits both range values under the same name', () => {
|
|
550
604
|
const {container} = render(
|
|
551
605
|
<form>
|
|
552
|
-
<Slider
|
|
606
|
+
<Slider
|
|
607
|
+
label="Price"
|
|
608
|
+
htmlName="price"
|
|
609
|
+
value={[20, 80] as [number, number]}
|
|
610
|
+
/>
|
|
553
611
|
</form>,
|
|
554
612
|
);
|
|
555
613
|
const data = new FormData(container.querySelector('form')!);
|
|
@@ -562,7 +620,9 @@ describe('Slider', () => {
|
|
|
562
620
|
<Slider label="Volume" htmlName="volume" value={50} isDisabled />
|
|
563
621
|
</form>,
|
|
564
622
|
);
|
|
565
|
-
expect([
|
|
623
|
+
expect([
|
|
624
|
+
...new FormData(container.querySelector('form')!).keys(),
|
|
625
|
+
]).toEqual([]);
|
|
566
626
|
});
|
|
567
627
|
});
|
|
568
628
|
});
|
package/src/Slider/Slider.tsx
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Slider.tsx
|
|
7
|
-
* @input Uses React, useId, useRef, useCallback, Field, Tooltip, useTooltip
|
|
7
|
+
* @input Uses React, useId, useRef, useCallback, Field, Tooltip, useTooltip, VisuallyHidden
|
|
8
8
|
* @output Exports Slider component, SliderProps, SliderSingleProps, SliderRangeProps, SliderBaseProps
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by Slider.test.tsx
|
|
10
10
|
*
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
import {Field} from '../Field/Field';
|
|
39
39
|
import {Tooltip} from '../Tooltip/Tooltip';
|
|
40
40
|
import {useTooltip} from '../Tooltip';
|
|
41
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
41
42
|
import type {InputStatus} from '../Field/types';
|
|
42
43
|
import {mergeProps, mergeRefs} from '../utils';
|
|
43
44
|
import type {BaseProps} from '../BaseProps';
|
|
@@ -393,6 +394,7 @@ export function Slider({ref, ...props}: SliderProps) {
|
|
|
393
394
|
const id = useId();
|
|
394
395
|
const descriptionID = useId();
|
|
395
396
|
const statusMessageID = useId();
|
|
397
|
+
const requiredID = useId();
|
|
396
398
|
|
|
397
399
|
const trackRef = useRef<HTMLDivElement>(null);
|
|
398
400
|
const draggingThumbRef = useRef<number | null>(null);
|
|
@@ -412,6 +414,12 @@ export function Slider({ref, ...props}: SliderProps) {
|
|
|
412
414
|
isEnabled: showsDisabledMessage,
|
|
413
415
|
});
|
|
414
416
|
|
|
417
|
+
// Required state. `aria-required` is not a supported property of
|
|
418
|
+
// role="slider" in WAI-ARIA 1.2, so the thumb instead points its
|
|
419
|
+
// aria-describedby at a visually hidden "Required" span — mirroring the
|
|
420
|
+
// Field label's visible indicator (where isOptional takes precedence).
|
|
421
|
+
const conveysRequired = isRequired && !isOptional;
|
|
422
|
+
|
|
415
423
|
// Build aria-describedby
|
|
416
424
|
const describedByParts: string[] = [];
|
|
417
425
|
if (description) {
|
|
@@ -420,6 +428,9 @@ export function Slider({ref, ...props}: SliderProps) {
|
|
|
420
428
|
if (status?.message) {
|
|
421
429
|
describedByParts.push(statusMessageID);
|
|
422
430
|
}
|
|
431
|
+
if (conveysRequired) {
|
|
432
|
+
describedByParts.push(requiredID);
|
|
433
|
+
}
|
|
423
434
|
if (showsDisabledMessage) {
|
|
424
435
|
describedByParts.push(disabledMessageTooltip.describedBy);
|
|
425
436
|
}
|
|
@@ -921,6 +932,9 @@ export function Slider({ref, ...props}: SliderProps) {
|
|
|
921
932
|
|
|
922
933
|
{textDisplay}
|
|
923
934
|
</div>
|
|
935
|
+
{conveysRequired && (
|
|
936
|
+
<VisuallyHidden id={requiredID}>Required</VisuallyHidden>
|
|
937
|
+
)}
|
|
924
938
|
{showsDisabledMessage &&
|
|
925
939
|
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
926
940
|
</Field>
|
|
@@ -107,7 +107,7 @@ export const docs = {
|
|
|
107
107
|
{
|
|
108
108
|
name: 'labelIcon',
|
|
109
109
|
type: 'IconType',
|
|
110
|
-
description: 'Icon displayed before the label text. See `
|
|
110
|
+
description: 'Icon displayed before the label text. See `astryx docs icons` for valid semantic names.',
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
name: 'labelTooltip',
|
|
@@ -9,10 +9,15 @@
|
|
|
9
9
|
* SYNC: When Switch.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 {Switch} from './Switch';
|
|
16
|
+
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
__resetLiveRegionsForTest();
|
|
20
|
+
});
|
|
16
21
|
|
|
17
22
|
// Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
|
|
18
23
|
// reflects its open state via a `popover-open` attribute the tests can assert.
|
|
@@ -324,6 +329,32 @@ describe('Switch', () => {
|
|
|
324
329
|
expect(describedBy).toBeTruthy();
|
|
325
330
|
});
|
|
326
331
|
|
|
332
|
+
// Regression: the status is conditionally mounted, so it must be announced
|
|
333
|
+
// through the persistent useAnnounce live region — a live region born
|
|
334
|
+
// together with its content is not reliably announced.
|
|
335
|
+
it('announces a status message that appears after mount', async () => {
|
|
336
|
+
const {rerender} = render(
|
|
337
|
+
<Switch label="Enable notifications" value={false} onChange={() => {}} />,
|
|
338
|
+
);
|
|
339
|
+
expect(
|
|
340
|
+
document.querySelector('[data-astryx-live-region="assertive"]'),
|
|
341
|
+
).toBeNull();
|
|
342
|
+
|
|
343
|
+
rerender(
|
|
344
|
+
<Switch
|
|
345
|
+
label="Enable notifications"
|
|
346
|
+
value={false}
|
|
347
|
+
onChange={() => {}}
|
|
348
|
+
status={{type: 'error', message: 'Failed to save setting'}}
|
|
349
|
+
/>,
|
|
350
|
+
);
|
|
351
|
+
await waitFor(() => {
|
|
352
|
+
expect(
|
|
353
|
+
document.querySelector('[data-astryx-live-region="assertive"]'),
|
|
354
|
+
).toHaveTextContent('Failed to save setting');
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
|
|
327
358
|
it('calls onFocus and onBlur callbacks', async () => {
|
|
328
359
|
const user = userEvent.setup();
|
|
329
360
|
const handleFocus = vi.fn();
|
package/src/TabList/Tab.tsx
CHANGED
|
@@ -138,7 +138,11 @@ const styles = stylex.create({
|
|
|
138
138
|
},
|
|
139
139
|
indicator: {
|
|
140
140
|
position: 'absolute',
|
|
141
|
-
bottom
|
|
141
|
+
// Sits on the tab's bottom edge by default (-1px). When the tab strip
|
|
142
|
+
// reserves space for a divider rail — TabList `hasDivider` or a Toolbar
|
|
143
|
+
// with a bottom divider — that ancestor sets `--_tab-indicator-bottom`
|
|
144
|
+
// to drop the indicator onto the rail beneath the reserved gap.
|
|
145
|
+
bottom: 'var(--_tab-indicator-bottom, -1px)',
|
|
142
146
|
left: spacingVars['--spacing-3'],
|
|
143
147
|
right: spacingVars['--spacing-3'],
|
|
144
148
|
height: '2px',
|
|
@@ -92,7 +92,7 @@ export const docs = {
|
|
|
92
92
|
bestPractices: [
|
|
93
93
|
{ guidance: true, description: 'Keep tab labels short and descriptive so users can quickly scan available sections.' },
|
|
94
94
|
{ guidance: true, description: 'Use TabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.' },
|
|
95
|
-
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs,
|
|
95
|
+
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, match the Button size to the TabList size (both md, both sm); the divided tab strip reserves space so tabs and same-size buttons align to a shared baseline above the rail.' },
|
|
96
96
|
{ guidance: false, description: 'Use tabs for sequential steps or workflows; use a stepper or wizard pattern instead.' },
|
|
97
97
|
{ guidance: false, description: 'Place more than 6–8 visible tabs before the overflow menu; prioritize the most important categories.' },
|
|
98
98
|
{ guidance: false, description: 'Confuse TabList with SegmentedControl or ToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls: SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.' },
|
|
@@ -113,7 +113,7 @@ export const docsZh = {
|
|
|
113
113
|
bestPractices: [
|
|
114
114
|
{ guidance: true, description: 'Keep tab labels short and descriptive so users can quickly scan available sections.' },
|
|
115
115
|
{ guidance: true, description: 'Use TabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.' },
|
|
116
|
-
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs,
|
|
116
|
+
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, match the Button size to the TabList size (both md, both sm); the divided tab strip reserves space so tabs and same-size buttons align to a shared baseline above the rail.' },
|
|
117
117
|
{ guidance: false, description: 'Use tabs for sequential steps or workflows; use a stepper or wizard pattern instead.' },
|
|
118
118
|
{ guidance: false, description: 'Place more than 6–8 visible tabs before the overflow menu; prioritize the most important categories.' },
|
|
119
119
|
{ guidance: false, description: 'Confuse TabList with SegmentedControl or ToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls: SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.' },
|
|
@@ -135,7 +135,7 @@ export const docsDense = {
|
|
|
135
135
|
bestPractices: [
|
|
136
136
|
{ guidance: true, description: 'Keep tab labels short and descriptive so users can quickly scan available sections.' },
|
|
137
137
|
{ guidance: true, description: 'Use TabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.' },
|
|
138
|
-
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs,
|
|
138
|
+
{ guidance: true, description: 'When using hasDivider with action buttons alongside tabs, match the Button size to the TabList size (both md, both sm); the divided tab strip reserves space so tabs and same-size buttons align to a shared baseline above the rail.' },
|
|
139
139
|
{ guidance: false, description: 'Use tabs for sequential steps or workflows; use a stepper or wizard pattern instead.' },
|
|
140
140
|
{ guidance: false, description: 'Place more than 6–8 visible tabs before the overflow menu; prioritize the most important categories.' },
|
|
141
141
|
{ guidance: false, description: 'Confuse TabList with SegmentedControl or ToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls: SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.' },
|
|
@@ -323,6 +323,72 @@ describe('TabList', () => {
|
|
|
323
323
|
});
|
|
324
324
|
});
|
|
325
325
|
|
|
326
|
+
describe('TabList divider gap', () => {
|
|
327
|
+
// The divider adds the reserved gap + indicator offset via a single class
|
|
328
|
+
// (StyleX applies deterministic classes in the test env). Capture that
|
|
329
|
+
// class set once so the assertions describe intent, not opaque hashes.
|
|
330
|
+
function navClasses(hasDivider: boolean): Set<string> {
|
|
331
|
+
const {unmount} = render(
|
|
332
|
+
<TabList value="home" onChange={() => {}} hasDivider={hasDivider}>
|
|
333
|
+
<Tab value="home" label="Home" />
|
|
334
|
+
</TabList>,
|
|
335
|
+
);
|
|
336
|
+
const nav = screen.getByRole('navigation');
|
|
337
|
+
const classes = new Set(nav.className.split(/\s+/).filter(Boolean));
|
|
338
|
+
unmount();
|
|
339
|
+
return classes;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
it('adds divider-only styling classes when hasDivider is set', () => {
|
|
343
|
+
const withDivider = navClasses(true);
|
|
344
|
+
const withoutDivider = navClasses(false);
|
|
345
|
+
|
|
346
|
+
// Every class the plain nav has must still be present when divided: the
|
|
347
|
+
// divider only adds styling (border + reserved gap + indicator offset),
|
|
348
|
+
// it never removes the base nav styles.
|
|
349
|
+
for (const cls of withoutDivider) {
|
|
350
|
+
expect(withDivider.has(cls)).toBe(true);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// And it must add at least one class the undivided nav does not have.
|
|
354
|
+
const added = [...withDivider].filter(c => !withoutDivider.has(c));
|
|
355
|
+
expect(added.length).toBeGreaterThan(0);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('does not add divider styling to an undivided tab list (default)', () => {
|
|
359
|
+
// Default (no hasDivider) and explicit hasDivider={false} are identical:
|
|
360
|
+
// the non-divided path is untouched by the divider gap change.
|
|
361
|
+
expect(navClasses(false)).toEqual(
|
|
362
|
+
(() => {
|
|
363
|
+
const {unmount} = render(
|
|
364
|
+
<TabList value="home" onChange={() => {}}>
|
|
365
|
+
<Tab value="home" label="Home" />
|
|
366
|
+
</TabList>,
|
|
367
|
+
);
|
|
368
|
+
const nav = screen.getByRole('navigation');
|
|
369
|
+
const classes = new Set(nav.className.split(/\s+/).filter(Boolean));
|
|
370
|
+
unmount();
|
|
371
|
+
return classes;
|
|
372
|
+
})(),
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('keeps the selected indicator rendered under a divider', () => {
|
|
377
|
+
render(
|
|
378
|
+
<TabList value="home" onChange={() => {}} hasDivider>
|
|
379
|
+
<Tab value="home" label="Home" />
|
|
380
|
+
<Tab value="away" label="Away" />
|
|
381
|
+
</TabList>,
|
|
382
|
+
);
|
|
383
|
+
const selected = screen.getByRole('button', {name: 'Home'});
|
|
384
|
+
// The indicator span carries the selected marker; the divider must not
|
|
385
|
+
// drop it (it is repositioned onto the rail, not removed).
|
|
386
|
+
expect(
|
|
387
|
+
selected.querySelector('[data-selected="selected"]'),
|
|
388
|
+
).toBeInTheDocument();
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
326
392
|
describe('TabList keyboard navigation (roving tabindex)', () => {
|
|
327
393
|
it('exposes the tab strip as a single Tab stop (only selected tab is tabbable)', () => {
|
|
328
394
|
render(
|
package/src/TabList/TabList.tsx
CHANGED
|
@@ -96,6 +96,14 @@ const styles = stylex.create({
|
|
|
96
96
|
borderBottomWidth: borderVars['--border-width'],
|
|
97
97
|
borderBottomStyle: 'solid',
|
|
98
98
|
borderBottomColor: colorVars['--color-border'],
|
|
99
|
+
// Reserve a gap between the tabs and the divider rail so the hover pill
|
|
100
|
+
// (which fills the tab height) no longer touches the underline, and an
|
|
101
|
+
// adjacent same-size Button aligns to the tabs rather than butting the
|
|
102
|
+
// rail. The tabs keep their element-size height; this padding grows the
|
|
103
|
+
// strip. `--_tab-indicator-bottom` drops the selected indicator through
|
|
104
|
+
// the reserved gap (+ the 1px border) so it still sits on the rail.
|
|
105
|
+
paddingBlockEnd: spacingVars['--spacing-1'],
|
|
106
|
+
'--_tab-indicator-bottom': `calc(-1 * (${spacingVars['--spacing-1']} + ${borderVars['--border-width']}))`,
|
|
99
107
|
},
|
|
100
108
|
});
|
|
101
109
|
|
package/src/TabList/TabMenu.tsx
CHANGED
|
@@ -122,7 +122,10 @@ const styles = stylex.create({
|
|
|
122
122
|
},
|
|
123
123
|
indicator: {
|
|
124
124
|
position: 'absolute',
|
|
125
|
-
|
|
125
|
+
// Mirrors Tab's indicator: sits on the bottom edge by default (-1px), or
|
|
126
|
+
// drops onto the divider rail when an ancestor (TabList `hasDivider` or a
|
|
127
|
+
// Toolbar with a bottom divider) sets `--_tab-indicator-bottom`.
|
|
128
|
+
bottom: 'var(--_tab-indicator-bottom, -1px)',
|
|
126
129
|
left: spacingVars['--spacing-3'],
|
|
127
130
|
right: spacingVars['--spacing-3'],
|
|
128
131
|
height: '2px',
|
package/src/Table/Table.doc.mjs
CHANGED
|
@@ -111,6 +111,8 @@ export const docs = {
|
|
|
111
111
|
{name: 'useTableSelection'},
|
|
112
112
|
{name: 'useTableSelectionState'},
|
|
113
113
|
{name: 'useTableSortable'},
|
|
114
|
+
{name: 'useTableTreeData'},
|
|
115
|
+
{name: 'useTableTreeState'},
|
|
114
116
|
{name: 'useTablePagination'},
|
|
115
117
|
{name: 'useTableColumnSettings'},
|
|
116
118
|
{name: 'useTableFiltering'},
|
package/src/Table/Table.tsx
CHANGED
|
@@ -275,7 +275,7 @@ function TableInner<T extends Record<string, unknown>>({
|
|
|
275
275
|
* columns={[
|
|
276
276
|
* { key: 'name', header: 'Name', width: proportional(1), renderCell: (u) => (
|
|
277
277
|
* <HStack gap={2} align="center">
|
|
278
|
-
* <Avatar name={u.name} size="
|
|
278
|
+
* <Avatar name={u.name} size="md" />
|
|
279
279
|
* <Text weight="semibold">{u.name}</Text>
|
|
280
280
|
* </HStack>
|
|
281
281
|
* )},
|