@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
|
@@ -119,6 +119,15 @@ export interface HoverCardOptions {
|
|
|
119
119
|
*/
|
|
120
120
|
isEnabled?: boolean;
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Accessible name for the hover card popup.
|
|
124
|
+
*
|
|
125
|
+
* When provided, the popup is exposed to assistive technology as a named
|
|
126
|
+
* `role="dialog"`. When omitted, the popup falls back to `role="group"` —
|
|
127
|
+
* a group may validly be unnamed, an unnamed dialog may not.
|
|
128
|
+
*/
|
|
129
|
+
label?: string;
|
|
130
|
+
|
|
122
131
|
/**
|
|
123
132
|
* Controlled open state. When provided, overrides hover/focus triggers:
|
|
124
133
|
* - `true`: force-show the hover card (hover/focus hide is suppressed)
|
|
@@ -250,6 +259,7 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
250
259
|
hideDelay = 200,
|
|
251
260
|
focusTrigger = 'auto',
|
|
252
261
|
isEnabled = true,
|
|
262
|
+
label,
|
|
253
263
|
isOpen,
|
|
254
264
|
isDefaultOpen = false,
|
|
255
265
|
onShow,
|
|
@@ -462,7 +472,11 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
462
472
|
const renderProps = {
|
|
463
473
|
placement: renderPlacement,
|
|
464
474
|
alignment: props?.alignment ?? alignment,
|
|
465
|
-
|
|
475
|
+
// A named dialog when a label is provided; otherwise a group. A group
|
|
476
|
+
// may validly be unnamed, an unnamed dialog may not — and hover cards
|
|
477
|
+
// are non-modal, so group is honest semantics without a name.
|
|
478
|
+
role: label ? 'dialog' : 'group',
|
|
479
|
+
'aria-label': label || undefined,
|
|
466
480
|
xstyle: [popoverXstyle, layerAnimations[renderPlacement]],
|
|
467
481
|
// Render the layer as inline-safe phrasing markup so HoverCard stays
|
|
468
482
|
// valid (and hydration-stable) inside inline contexts like a `<p>`.
|
|
@@ -516,7 +530,15 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
516
530
|
renderProps,
|
|
517
531
|
);
|
|
518
532
|
},
|
|
519
|
-
[
|
|
533
|
+
[
|
|
534
|
+
layer,
|
|
535
|
+
placement,
|
|
536
|
+
alignment,
|
|
537
|
+
label,
|
|
538
|
+
clearTimeouts,
|
|
539
|
+
scheduleHide,
|
|
540
|
+
popoverXstyle,
|
|
541
|
+
],
|
|
520
542
|
);
|
|
521
543
|
|
|
522
544
|
return {
|
package/src/Icon/Icon.doc.mjs
CHANGED
|
@@ -34,6 +34,11 @@ export const docs = {
|
|
|
34
34
|
description: 'Icon size.',
|
|
35
35
|
default: "'md'",
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
name: 'label',
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'Accessible name for a MEANINGFUL, standalone icon (a status glyph or icon-only indicator with no adjacent text). Setting it exposes the icon to screen readers as role="img" with this text as the accessible name (aria-label) and removes the default aria-hidden. Omit it (default) for decorative icons and the icon stays hidden from assistive tech (aria-hidden="true"). This is the accessible-name / alt-text prop for icons — one prop instead of manually setting aria-label + role + aria-hidden. An empty string is treated as decorative. Do not set it when an interactive parent (Button, IconButton, link) already names the control.',
|
|
41
|
+
},
|
|
37
42
|
],
|
|
38
43
|
theming: {
|
|
39
44
|
targets: [
|
|
@@ -45,6 +50,7 @@ export const docs = {
|
|
|
45
50
|
bestPractices: [
|
|
46
51
|
{ guidance: true, description: 'Use semantic icon names when available; they adapt to theme changes automatically.' },
|
|
47
52
|
{ guidance: true, description: 'Pair icons with text labels for accessibility; icon-only elements need an accessible label.' },
|
|
53
|
+
{ guidance: true, description: 'For a meaningful standalone icon (no adjacent text), give it an accessible name via the `label` prop — it sets role="img" + aria-label and unhides the icon.' },
|
|
48
54
|
{ guidance: true, description: 'Use color tokens for icon colors, not hardcoded hex values.' },
|
|
49
55
|
{ guidance: true, description: 'Be mindful of context; decorative icons in compact components can distract rather than help.' },
|
|
50
56
|
{ guidance: false, description: 'Use icons as the sole means of conveying meaning; always provide a text alternative.' },
|
|
@@ -79,6 +85,11 @@ export const docsZh = {
|
|
|
79
85
|
description: '图标尺寸。',
|
|
80
86
|
default: "'md'",
|
|
81
87
|
},
|
|
88
|
+
{
|
|
89
|
+
name: 'label',
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: '有含义的独立图标的可访问名称(无相邻文字的状态图标或纯图标指示器)。设置后会将图标以 role="img" 暴露给辅助技术,并以该文本作为可访问名称(aria-label),同时移除默认的 aria-hidden。省略(默认)用于装饰性图标,图标对辅助技术保持隐藏(aria-hidden="true")。空字符串按装饰性处理。当交互式父元素(Button、IconButton、链接)已命名该控件时请勿设置。',
|
|
92
|
+
},
|
|
82
93
|
],
|
|
83
94
|
theming: {
|
|
84
95
|
targets: [
|
|
@@ -123,5 +134,6 @@ export const docsDense = {
|
|
|
123
134
|
icon: 'Semantic icon name or SVG component. Valid names: close, chevronDown, chevronLeft, chevronRight, check, success, error, warning, info, calendar, clock, externalLink, menu, moreHorizontal, search, arrowUp, arrowDown, arrowsUpDown, funnel, eyeSlash, viewColumns, copy, checkDouble, wrench, stop, microphone. For others, pass an SVG component.',
|
|
124
135
|
color: 'Color variant mapped to Astryx icon color tokens.',
|
|
125
136
|
size: 'Icon size.',
|
|
137
|
+
label: 'Accessible name for a meaningful, standalone icon. Sets role="img" + aria-label and drops the default aria-hidden. Omit (default) for decorative icons (stays aria-hidden). Empty string = decorative. The accessible-name/alt-text prop for icons.',
|
|
126
138
|
},
|
|
127
139
|
};
|
package/src/Icon/Icon.test.tsx
CHANGED
|
@@ -96,6 +96,52 @@ describe('Icon', () => {
|
|
|
96
96
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('sizes component-mode icons in rem so they scale with root font-size', () => {
|
|
100
|
+
const sizes = {
|
|
101
|
+
xsm: '0.75rem',
|
|
102
|
+
sm: '1rem',
|
|
103
|
+
md: '1.25rem',
|
|
104
|
+
lg: '1.5rem',
|
|
105
|
+
} as const;
|
|
106
|
+
for (const [size, expected] of Object.entries(sizes)) {
|
|
107
|
+
const {unmount} = render(
|
|
108
|
+
<Icon
|
|
109
|
+
icon={TestIcon}
|
|
110
|
+
size={size as keyof typeof sizes}
|
|
111
|
+
data-testid="icon"
|
|
112
|
+
/>,
|
|
113
|
+
);
|
|
114
|
+
const style = getComputedStyle(screen.getByTestId('icon'));
|
|
115
|
+
expect(style.width).toBe(expected);
|
|
116
|
+
expect(style.height).toBe(expected);
|
|
117
|
+
unmount();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('sizes registry (string-mode) icons in rem, including fontSize', () => {
|
|
122
|
+
const sizes = {
|
|
123
|
+
xsm: '0.75rem',
|
|
124
|
+
sm: '1rem',
|
|
125
|
+
md: '1.25rem',
|
|
126
|
+
lg: '1.5rem',
|
|
127
|
+
} as const;
|
|
128
|
+
for (const [size, expected] of Object.entries(sizes)) {
|
|
129
|
+
const {unmount} = render(
|
|
130
|
+
<Icon
|
|
131
|
+
icon="check"
|
|
132
|
+
size={size as keyof typeof sizes}
|
|
133
|
+
data-testid="icon"
|
|
134
|
+
/>,
|
|
135
|
+
);
|
|
136
|
+
const style = getComputedStyle(screen.getByTestId('icon'));
|
|
137
|
+
expect(style.width).toBe(expected);
|
|
138
|
+
expect(style.height).toBe(expected);
|
|
139
|
+
// fontSize is expressed in rem so 1em-based registry icons scale too.
|
|
140
|
+
expect(style.fontSize).toBe(expected);
|
|
141
|
+
unmount();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
99
145
|
it('forwards ref correctly', () => {
|
|
100
146
|
const ref = vi.fn();
|
|
101
147
|
render(<Icon icon={TestIcon} ref={ref} />);
|
|
@@ -137,4 +183,100 @@ describe('Icon', () => {
|
|
|
137
183
|
expect(icon).toHaveAttribute('aria-label', 'Done');
|
|
138
184
|
expect(icon).toHaveAttribute('aria-hidden', 'false');
|
|
139
185
|
});
|
|
186
|
+
|
|
187
|
+
describe('label (accessible name)', () => {
|
|
188
|
+
it('makes a component-mode icon meaningful: role="img" + aria-label, no aria-hidden', () => {
|
|
189
|
+
render(<Icon icon={TestIcon} label="Completed" data-testid="icon" />);
|
|
190
|
+
const icon = screen.getByTestId('icon');
|
|
191
|
+
expect(icon).toHaveAttribute('role', 'img');
|
|
192
|
+
expect(icon).toHaveAttribute('aria-label', 'Completed');
|
|
193
|
+
expect(icon).not.toHaveAttribute('aria-hidden');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('makes a string-mode (registry) icon meaningful: role="img" + aria-label, no aria-hidden', () => {
|
|
197
|
+
render(<Icon icon="check" label="Completed" data-testid="icon" />);
|
|
198
|
+
const icon = screen.getByTestId('icon');
|
|
199
|
+
expect(icon).toHaveAttribute('role', 'img');
|
|
200
|
+
expect(icon).toHaveAttribute('aria-label', 'Completed');
|
|
201
|
+
expect(icon).not.toHaveAttribute('aria-hidden');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('keeps the decorative default when label is omitted (component mode)', () => {
|
|
205
|
+
render(<Icon icon={TestIcon} data-testid="icon" />);
|
|
206
|
+
const icon = screen.getByTestId('icon');
|
|
207
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
208
|
+
expect(icon).not.toHaveAttribute('role');
|
|
209
|
+
expect(icon).not.toHaveAttribute('aria-label');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('keeps the decorative default when label is omitted (string mode)', () => {
|
|
213
|
+
render(<Icon icon="check" data-testid="icon" />);
|
|
214
|
+
const icon = screen.getByTestId('icon');
|
|
215
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
216
|
+
expect(icon).not.toHaveAttribute('role');
|
|
217
|
+
expect(icon).not.toHaveAttribute('aria-label');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('treats an empty string label as decorative (component mode)', () => {
|
|
221
|
+
render(<Icon icon={TestIcon} label="" data-testid="icon" />);
|
|
222
|
+
const icon = screen.getByTestId('icon');
|
|
223
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
224
|
+
expect(icon).not.toHaveAttribute('role');
|
|
225
|
+
expect(icon).not.toHaveAttribute('aria-label');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('treats an empty string label as decorative (string mode)', () => {
|
|
229
|
+
render(<Icon icon="check" label="" data-testid="icon" />);
|
|
230
|
+
const icon = screen.getByTestId('icon');
|
|
231
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
232
|
+
expect(icon).not.toHaveAttribute('role');
|
|
233
|
+
expect(icon).not.toHaveAttribute('aria-label');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('lets an explicit aria-hidden win over label (component mode)', () => {
|
|
237
|
+
render(
|
|
238
|
+
<Icon icon={TestIcon} label="Close" aria-hidden data-testid="icon" />,
|
|
239
|
+
);
|
|
240
|
+
const icon = screen.getByTestId('icon');
|
|
241
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('lets an explicit aria-hidden win over label (string mode)', () => {
|
|
245
|
+
render(
|
|
246
|
+
<Icon icon="check" label="Close" aria-hidden data-testid="icon" />,
|
|
247
|
+
);
|
|
248
|
+
const icon = screen.getByTestId('icon');
|
|
249
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('lets an explicit aria-label override the label-derived name (component mode)', () => {
|
|
253
|
+
render(
|
|
254
|
+
<Icon
|
|
255
|
+
icon={TestIcon}
|
|
256
|
+
label="Close"
|
|
257
|
+
aria-label="Dismiss"
|
|
258
|
+
data-testid="icon"
|
|
259
|
+
/>,
|
|
260
|
+
);
|
|
261
|
+
expect(screen.getByTestId('icon')).toHaveAttribute(
|
|
262
|
+
'aria-label',
|
|
263
|
+
'Dismiss',
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('lets an explicit role override the label-derived role (string mode)', () => {
|
|
268
|
+
render(
|
|
269
|
+
<Icon
|
|
270
|
+
icon="check"
|
|
271
|
+
label="Close"
|
|
272
|
+
role="presentation"
|
|
273
|
+
data-testid="icon"
|
|
274
|
+
/>,
|
|
275
|
+
);
|
|
276
|
+
expect(screen.getByTestId('icon')).toHaveAttribute(
|
|
277
|
+
'role',
|
|
278
|
+
'presentation',
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
140
282
|
});
|
package/src/Icon/Icon.tsx
CHANGED
|
@@ -111,51 +111,64 @@ const colorStyles = stylex.create({
|
|
|
111
111
|
/**
|
|
112
112
|
* Size styles for direct SVG icon components.
|
|
113
113
|
* Uses width/height only — SVG components handle their own viewBox scaling.
|
|
114
|
+
*
|
|
115
|
+
* Sizes are expressed in `rem` (relative to the root font-size) so icons scale
|
|
116
|
+
* in step with text when the document font-size changes, matching the rest of
|
|
117
|
+
* the design system's rem-based type scale. Values are the px-equivalents at a
|
|
118
|
+
* 16px root: 12px → 0.75rem, 16px → 1rem, 20px → 1.25rem, 24px → 1.5rem.
|
|
114
119
|
*/
|
|
115
120
|
const sizeStyles = stylex.create({
|
|
116
121
|
xsm: {
|
|
117
|
-
width:
|
|
118
|
-
height:
|
|
122
|
+
width: '0.75rem',
|
|
123
|
+
height: '0.75rem',
|
|
119
124
|
},
|
|
120
125
|
sm: {
|
|
121
|
-
width:
|
|
122
|
-
height:
|
|
126
|
+
width: '1rem',
|
|
127
|
+
height: '1rem',
|
|
123
128
|
},
|
|
124
129
|
md: {
|
|
125
|
-
width:
|
|
126
|
-
height:
|
|
130
|
+
width: '1.25rem',
|
|
131
|
+
height: '1.25rem',
|
|
127
132
|
},
|
|
128
133
|
lg: {
|
|
129
|
-
width:
|
|
130
|
-
height:
|
|
134
|
+
width: '1.5rem',
|
|
135
|
+
height: '1.5rem',
|
|
131
136
|
},
|
|
132
137
|
});
|
|
133
138
|
|
|
134
139
|
/**
|
|
135
140
|
* Size styles for string-based (registry) icons.
|
|
136
141
|
* Includes fontSize so that 1em-based icons from the registry scale correctly.
|
|
142
|
+
*
|
|
143
|
+
* Expressed in `rem` for the same reason as {@link sizeStyles} — icons track the
|
|
144
|
+
* root font-size instead of being locked to absolute pixels.
|
|
137
145
|
*/
|
|
138
146
|
const spanSizeStyles = stylex.create({
|
|
147
|
+
/* eslint-disable @astryx/no-hardcoded-styles -- fontSize here sizes 1em-based
|
|
148
|
+
registry SVGs to the icon box; icons use their own 12/16/20/24 scale, not
|
|
149
|
+
the 14px-anchored textSizeVars type scale. Values are rem so icons track
|
|
150
|
+
the root font-size. */
|
|
139
151
|
xsm: {
|
|
140
|
-
width:
|
|
141
|
-
height:
|
|
142
|
-
fontSize:
|
|
152
|
+
width: '0.75rem',
|
|
153
|
+
height: '0.75rem',
|
|
154
|
+
fontSize: '0.75rem',
|
|
143
155
|
},
|
|
144
156
|
sm: {
|
|
145
|
-
width:
|
|
146
|
-
height:
|
|
147
|
-
fontSize:
|
|
157
|
+
width: '1rem',
|
|
158
|
+
height: '1rem',
|
|
159
|
+
fontSize: '1rem',
|
|
148
160
|
},
|
|
149
161
|
md: {
|
|
150
|
-
width:
|
|
151
|
-
height:
|
|
152
|
-
fontSize:
|
|
162
|
+
width: '1.25rem',
|
|
163
|
+
height: '1.25rem',
|
|
164
|
+
fontSize: '1.25rem',
|
|
153
165
|
},
|
|
154
166
|
lg: {
|
|
155
|
-
width:
|
|
156
|
-
height:
|
|
157
|
-
fontSize:
|
|
167
|
+
width: '1.5rem',
|
|
168
|
+
height: '1.5rem',
|
|
169
|
+
fontSize: '1.5rem',
|
|
158
170
|
},
|
|
171
|
+
/* eslint-enable @astryx/no-hardcoded-styles */
|
|
159
172
|
});
|
|
160
173
|
|
|
161
174
|
// =============================================================================
|
|
@@ -194,13 +207,57 @@ export interface IconProps extends Omit<
|
|
|
194
207
|
color?: IconColor;
|
|
195
208
|
/**
|
|
196
209
|
* The size of the icon.
|
|
197
|
-
* - 'xsm': 12px
|
|
198
|
-
* - 'sm': 16px
|
|
199
|
-
* - 'md': 20px
|
|
200
|
-
* - 'lg': 24px
|
|
210
|
+
* - 'xsm': 0.75rem (12px at a 16px root)
|
|
211
|
+
* - 'sm': 1rem (16px at a 16px root)
|
|
212
|
+
* - 'md': 1.25rem (20px at a 16px root)
|
|
213
|
+
* - 'lg': 1.5rem (24px at a 16px root)
|
|
201
214
|
* @default 'md'
|
|
202
215
|
*/
|
|
203
216
|
size?: IconSize;
|
|
217
|
+
/**
|
|
218
|
+
* Accessible name for the icon. Set this only when the icon is MEANINGFUL on
|
|
219
|
+
* its own — a standalone status glyph or an icon-only indicator with no
|
|
220
|
+
* adjacent text conveying the same information. Providing it exposes the icon
|
|
221
|
+
* to assistive tech as `role="img"` with this string as the accessible name
|
|
222
|
+
* (via `aria-label`) and drops the default `aria-hidden="true"`.
|
|
223
|
+
*
|
|
224
|
+
* Omit it (the default) for decorative icons — the common case, e.g. an icon
|
|
225
|
+
* beside a text label — and the icon stays hidden from assistive tech
|
|
226
|
+
* (`aria-hidden="true"`). An empty string (`''`) is treated the same as
|
|
227
|
+
* omitting it (decorative), since an empty accessible name is meaningless.
|
|
228
|
+
*
|
|
229
|
+
* Don't set `label` when an interactive parent (Button, IconButton, link)
|
|
230
|
+
* already names the control — that produces a duplicate announcement.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```
|
|
234
|
+
* // Meaningful, standalone icon
|
|
235
|
+
* <Icon icon="success" label="Completed" />
|
|
236
|
+
*
|
|
237
|
+
* // Decorative icon (default) — omit label
|
|
238
|
+
* <Icon icon="search" />
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
label?: string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Derives the ARIA attributes for an icon from its `label` prop.
|
|
246
|
+
*
|
|
247
|
+
* - Non-empty `label` → meaningful image: `role="img"` + `aria-label`, and no
|
|
248
|
+
* `aria-hidden` (an `aria-hidden` element is removed from the accessibility
|
|
249
|
+
* tree, so its accessible name would be ignored).
|
|
250
|
+
* - Omitted or empty `label` → decorative default: `aria-hidden="true"`.
|
|
251
|
+
*
|
|
252
|
+
* The result is spread BEFORE `{...props}` in both render modes so an explicit
|
|
253
|
+
* `aria-hidden` / `role` / `aria-label` from the consumer always wins.
|
|
254
|
+
*/
|
|
255
|
+
function getIconA11yProps(
|
|
256
|
+
label: string | undefined,
|
|
257
|
+
): {role: 'img'; 'aria-label': string} | {'aria-hidden': 'true'} {
|
|
258
|
+
return label != null && label !== ''
|
|
259
|
+
? {role: 'img', 'aria-label': label}
|
|
260
|
+
: {'aria-hidden': 'true'};
|
|
204
261
|
}
|
|
205
262
|
|
|
206
263
|
// =============================================================================
|
|
@@ -219,9 +276,14 @@ export function Icon({
|
|
|
219
276
|
icon,
|
|
220
277
|
color = 'inherit',
|
|
221
278
|
size = 'md',
|
|
279
|
+
label,
|
|
222
280
|
ref,
|
|
223
281
|
...props
|
|
224
282
|
}: IconProps) {
|
|
283
|
+
// Derive ARIA from `label`: decorative (aria-hidden) by default, or a
|
|
284
|
+
// meaningful image (role="img" + aria-label) when `label` is non-empty.
|
|
285
|
+
const a11yProps = getIconA11yProps(label);
|
|
286
|
+
|
|
225
287
|
// String mode: resolve from icon registry, wrap in styled span
|
|
226
288
|
if (typeof icon === 'string') {
|
|
227
289
|
return (
|
|
@@ -229,6 +291,7 @@ export function Icon({
|
|
|
229
291
|
name={icon}
|
|
230
292
|
color={color}
|
|
231
293
|
size={size}
|
|
294
|
+
a11yProps={a11yProps}
|
|
232
295
|
spanProps={props}
|
|
233
296
|
/>
|
|
234
297
|
);
|
|
@@ -239,7 +302,10 @@ export function Icon({
|
|
|
239
302
|
return (
|
|
240
303
|
<IconComponent
|
|
241
304
|
ref={ref}
|
|
242
|
-
|
|
305
|
+
// Derived a11y (decorative default or meaningful `label`) is spread
|
|
306
|
+
// BEFORE {...props} so an explicit aria-hidden/role/aria-label from the
|
|
307
|
+
// consumer still wins as an escape hatch.
|
|
308
|
+
{...a11yProps}
|
|
243
309
|
{...mergeProps(
|
|
244
310
|
themeProps('icon', {size, color}),
|
|
245
311
|
stylex.props(styles.root, colorStyles[color], sizeStyles[size]),
|
|
@@ -266,11 +332,13 @@ function IconFromRegistry({
|
|
|
266
332
|
name,
|
|
267
333
|
color,
|
|
268
334
|
size,
|
|
335
|
+
a11yProps,
|
|
269
336
|
spanProps,
|
|
270
337
|
}: {
|
|
271
338
|
name: IconName;
|
|
272
339
|
color: IconColor;
|
|
273
340
|
size: IconSize;
|
|
341
|
+
a11yProps: {role: 'img'; 'aria-label': string} | {'aria-hidden': 'true'};
|
|
274
342
|
spanProps?: Omit<SVGProps<SVGSVGElement>, 'ref' | 'color'>;
|
|
275
343
|
}) {
|
|
276
344
|
const resolvedIcon = getIcon(name);
|
|
@@ -281,11 +349,11 @@ function IconFromRegistry({
|
|
|
281
349
|
|
|
282
350
|
return (
|
|
283
351
|
<span
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
|
|
352
|
+
// Derived a11y — decorative (aria-hidden) by default, or a meaningful
|
|
353
|
+
// image (role="img" + aria-label) when `label` is set. Placed BEFORE the
|
|
354
|
+
// prop spread so consumers can still override it with explicit
|
|
355
|
+
// aria-hidden/role/aria-label. This mirrors component-mode Icon.
|
|
356
|
+
{...a11yProps}
|
|
289
357
|
{...(spanProps as React.HTMLAttributes<HTMLSpanElement>)}
|
|
290
358
|
{...mergeProps(
|
|
291
359
|
themeProps('icon', {size, color}),
|
|
@@ -36,6 +36,13 @@ export const docs = {
|
|
|
36
36
|
description: 'Size variant.',
|
|
37
37
|
default: "\'md\'",
|
|
38
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: 'elevation',
|
|
41
|
+
type: "'none' | 'low' | 'med' | 'high'",
|
|
42
|
+
description:
|
|
43
|
+
'Resting shadow depth. The most common FAB shape is an icon-only button, so raise it with `low`/`med`/`high` for a floating action button. `none` is the default flat button.',
|
|
44
|
+
default: "'none'",
|
|
45
|
+
},
|
|
39
46
|
{
|
|
40
47
|
name: 'isLoading',
|
|
41
48
|
type: 'boolean',
|
|
@@ -95,6 +102,7 @@ export const docsDense = {
|
|
|
95
102
|
icon: 'icon element rendered inside button',
|
|
96
103
|
variant: 'visual style variant',
|
|
97
104
|
size: 'size variant',
|
|
105
|
+
elevation: 'resting shadow depth: none|low|med|high; raise for a floating action button (FAB)',
|
|
98
106
|
isLoading: 'shows loading spinner + disables interaction',
|
|
99
107
|
isDisabled: 'disables button',
|
|
100
108
|
tooltip: 'tooltip text shown on hover',
|
|
@@ -17,10 +17,7 @@ import {IconButton} from './IconButton';
|
|
|
17
17
|
describe('IconButton', () => {
|
|
18
18
|
it('renders as an icon-only button with aria-label', () => {
|
|
19
19
|
render(
|
|
20
|
-
<IconButton
|
|
21
|
-
label="Settings"
|
|
22
|
-
icon={<span data-testid="icon">⚙</span>}
|
|
23
|
-
/>,
|
|
20
|
+
<IconButton label="Settings" icon={<span data-testid="icon">⚙</span>} />,
|
|
24
21
|
);
|
|
25
22
|
const button = screen.getByRole('button', {name: 'Settings'});
|
|
26
23
|
expect(button).toHaveAttribute('aria-label', 'Settings');
|
|
@@ -29,10 +26,7 @@ describe('IconButton', () => {
|
|
|
29
26
|
|
|
30
27
|
it('does not render label as visible text', () => {
|
|
31
28
|
render(
|
|
32
|
-
<IconButton
|
|
33
|
-
label="Settings"
|
|
34
|
-
icon={<span data-testid="icon">⚙</span>}
|
|
35
|
-
/>,
|
|
29
|
+
<IconButton label="Settings" icon={<span data-testid="icon">⚙</span>} />,
|
|
36
30
|
);
|
|
37
31
|
const button = screen.getByRole('button');
|
|
38
32
|
// The label text should not appear as visible content
|
|
@@ -42,11 +36,7 @@ describe('IconButton', () => {
|
|
|
42
36
|
|
|
43
37
|
it('forwards variant prop', () => {
|
|
44
38
|
render(
|
|
45
|
-
<IconButton
|
|
46
|
-
label="Delete"
|
|
47
|
-
icon={<span>🗑</span>}
|
|
48
|
-
variant="destructive"
|
|
49
|
-
/>,
|
|
39
|
+
<IconButton label="Delete" icon={<span>🗑</span>} variant="destructive" />,
|
|
50
40
|
);
|
|
51
41
|
expect(screen.getByRole('button', {name: 'Delete'})).toBeInTheDocument();
|
|
52
42
|
});
|
|
@@ -60,11 +50,7 @@ describe('IconButton', () => {
|
|
|
60
50
|
const user = userEvent.setup();
|
|
61
51
|
const handleClick = vi.fn();
|
|
62
52
|
render(
|
|
63
|
-
<IconButton
|
|
64
|
-
label="Close"
|
|
65
|
-
icon={<span>✕</span>}
|
|
66
|
-
onClick={handleClick}
|
|
67
|
-
/>,
|
|
53
|
+
<IconButton label="Close" icon={<span>✕</span>} onClick={handleClick} />,
|
|
68
54
|
);
|
|
69
55
|
|
|
70
56
|
await user.click(screen.getByRole('button'));
|
|
@@ -105,4 +91,15 @@ describe('IconButton', () => {
|
|
|
105
91
|
it('has displayName set', () => {
|
|
106
92
|
expect(IconButton.displayName).toBe('IconButton');
|
|
107
93
|
});
|
|
94
|
+
|
|
95
|
+
it('forwards the elevation prop through to the underlying button', () => {
|
|
96
|
+
const classFor = (elevation: 'none' | 'med') => {
|
|
97
|
+
const {container} = render(
|
|
98
|
+
<IconButton label="Add" icon={<span>+</span>} elevation={elevation} />,
|
|
99
|
+
);
|
|
100
|
+
return container.querySelector('button')!.className;
|
|
101
|
+
};
|
|
102
|
+
// A raised FAB must render differently from the default flat icon button.
|
|
103
|
+
expect(classFor('med')).not.toBe(classFor('none'));
|
|
104
|
+
});
|
|
108
105
|
});
|
package/src/Item/Item.doc.mjs
CHANGED
|
@@ -28,7 +28,7 @@ export const docs = {
|
|
|
28
28
|
props: [
|
|
29
29
|
{name: 'label', type: 'ReactNode', description: 'Primary text identifying this item. Accepts string (auto-truncated) or ReactNode (for rich content).', required: true},
|
|
30
30
|
{name: 'marker', type: 'ReactNode', description: 'Marker rendered before startContent as a direct flex child. Use for list bullets/counters that need custom baseline alignment.'},
|
|
31
|
-
{name: 'startContent', type: 'ReactNode', description: 'Content rendered before the label/description area, such as an icon, avatar, or checkbox.', slotElements: [{__element: 'Avatar', props: {name: 'Ada Lovelace', size: '
|
|
31
|
+
{name: 'startContent', type: 'ReactNode', description: 'Content rendered before the label/description area, such as an icon, avatar, or checkbox.', slotElements: [{__element: 'Avatar', props: {name: 'Ada Lovelace', size: 'sm'}}, {__element: 'Icon', props: {icon: 'info', size: 'sm', color: 'secondary'}}]},
|
|
32
32
|
{name: 'description', type: 'ReactNode', description: 'Secondary text: subtitle, description, or supporting info.'},
|
|
33
33
|
{name: 'endContent', type: 'ReactNode', description: 'Content rendered after the label/description area, such as badges, metadata, timestamps, or action buttons.', slotElements: [{__element: 'Badge', props: {label: 3}}, {__element: 'Text', props: {color: 'secondary'}, children: '2h ago'}]},
|
|
34
34
|
{name: 'as', type: "'div' | 'li' | 'span'", description: 'HTML element to render as the root.', default: "'div'"},
|
package/src/Layer/useLayer.tsx
CHANGED
|
@@ -100,6 +100,12 @@ export interface ContextRenderProps {
|
|
|
100
100
|
* non-hashed selector for the layer.
|
|
101
101
|
*/
|
|
102
102
|
role?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Accessible name applied to the popover container via `aria-label`.
|
|
105
|
+
* Pair with `role` so layers with a named role (e.g. `'dialog'`) expose a
|
|
106
|
+
* proper name to assistive technology.
|
|
107
|
+
*/
|
|
108
|
+
'aria-label'?: string;
|
|
103
109
|
/**
|
|
104
110
|
* StyleX styles for the popover container.
|
|
105
111
|
*/
|
|
@@ -525,6 +531,7 @@ export function useLayer(
|
|
|
525
531
|
alignment = 'center',
|
|
526
532
|
positioning = 'anchor',
|
|
527
533
|
role,
|
|
534
|
+
'aria-label': ariaLabel,
|
|
528
535
|
xstyle,
|
|
529
536
|
className: extraClassName,
|
|
530
537
|
style: extraStyle,
|
|
@@ -561,6 +568,7 @@ export function useLayer(
|
|
|
561
568
|
ref={popoverRefCallback}
|
|
562
569
|
id={id}
|
|
563
570
|
role={role}
|
|
571
|
+
aria-label={ariaLabel}
|
|
564
572
|
popover={lightDismiss ? 'auto' : 'manual'}
|
|
565
573
|
className={combinedClassName}
|
|
566
574
|
style={{...stylexResult.style, ...anchorStyle, ...extraStyle}}
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
import {
|
|
30
30
|
useCallback,
|
|
31
31
|
useEffect,
|
|
32
|
+
useId,
|
|
32
33
|
useMemo,
|
|
33
34
|
useRef,
|
|
34
35
|
useState,
|
|
@@ -295,6 +296,10 @@ export function MobileNav({
|
|
|
295
296
|
// Read from AppShell context as fallback
|
|
296
297
|
const appShellMobile = useAppShellMobile();
|
|
297
298
|
const isOpen = isOpenProp ?? appShellMobile.isMobileNavOpen;
|
|
299
|
+
// Share the id from AppShell context so the toggle's aria-controls resolves to
|
|
300
|
+
// this drawer; fall back to a locally generated id when used standalone.
|
|
301
|
+
const fallbackId = useId();
|
|
302
|
+
const dialogId = appShellMobile.mobileNavId || fallbackId;
|
|
298
303
|
const onOpenChange = useMemo(
|
|
299
304
|
() =>
|
|
300
305
|
onOpenChangeProp ??
|
|
@@ -411,6 +416,7 @@ export function MobileNav({
|
|
|
411
416
|
return (
|
|
412
417
|
<dialog
|
|
413
418
|
ref={mergeRefs(ref, dialogRef)}
|
|
419
|
+
id={dialogId}
|
|
414
420
|
{...mergeProps(
|
|
415
421
|
themeProps('mobile-nav', {side: resolvedSide}),
|
|
416
422
|
stylex.props(
|
|
@@ -425,7 +431,12 @@ export function MobileNav({
|
|
|
425
431
|
)}
|
|
426
432
|
{...rest}
|
|
427
433
|
data-testid={testId}
|
|
428
|
-
aria-label={
|
|
434
|
+
aria-label={
|
|
435
|
+
label ??
|
|
436
|
+
(typeof header === 'string'
|
|
437
|
+
? header
|
|
438
|
+
: t('@astryx.mobileNav.navigation'))
|
|
439
|
+
}
|
|
429
440
|
onClick={composeEventHandlers(onClickProp, handleDialogClick)}
|
|
430
441
|
onCancel={handleCancel}>
|
|
431
442
|
{/* Drawer panel — tabIndex so showModal() focuses the drawer, not the close button */}
|