@astryxdesign/core 0.1.8 → 0.1.9
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 +100 -0
- 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 +18 -2
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +63 -7
- package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/Avatar/AvatarStatusDot.js +144 -19
- 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/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
- package/dist/Breadcrumbs/index.d.ts +5 -0
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +9 -1
- 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/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +30 -2
- package/dist/Calendar/dayCellUtils.js +1 -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 +4 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +24 -4
- package/dist/Chat/ChatComposer.d.ts +8 -0
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +46 -7
- 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 +35 -3
- package/dist/Chat/ChatContext.d.ts +33 -0
- package/dist/Chat/ChatContext.d.ts.map +1 -1
- package/dist/Chat/ChatContext.js +16 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +1 -1
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +4 -4
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +7 -1
- package/dist/Chat/ChatToolCalls.d.ts +5 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +18 -9
- package/dist/Chat/index.d.ts +2 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/Chat/index.js +1 -1
- package/dist/Chat/useChatStreamScroll.d.ts +2 -1
- package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
- package/dist/Chat/useChatStreamScroll.js +22 -8
- package/dist/Chat/useSpeechRecognition.d.ts +0 -1
- package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.js +1 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +11 -1
- package/dist/ClickableCard/ClickableCard.d.ts +9 -2
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +5 -2
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +8 -1
- package/dist/CommandPalette/CommandPalette.d.ts +1 -1
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +3 -1
- package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
- 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/DateInput/DateInput.d.ts +47 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +26 -2
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +10 -1
- package/dist/Dialog/Dialog.d.ts +3 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +57 -6
- package/dist/Dialog/DialogContext.d.ts +7 -0
- package/dist/Dialog/DialogContext.d.ts.map +1 -1
- package/dist/Dialog/DialogContext.js +2 -2
- package/dist/Dialog/DialogHeader.d.ts +5 -2
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +6 -1
- package/dist/Divider/Divider.js +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +2 -3
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +42 -15
- 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 +7 -2
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Item/Item.d.ts +6 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +22 -1
- 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 +0 -1
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +0 -1
- 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/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +3 -9
- 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 +16 -13
- 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 +19 -2
- package/dist/Stack/stack.stylex.d.ts +9 -9
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +3 -2
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- 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 +13 -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/tree/useTableTreeData.d.ts +16 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -3
- package/dist/Thumbnail/Thumbnail.d.ts +26 -5
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +97 -54
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +15 -4
- package/dist/Timestamp/Timestamp.d.ts +3 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +8 -6
- package/dist/Toast/useToast.d.ts.map +1 -1
- package/dist/Toast/useToast.js +2 -5
- package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
- package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
- 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/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +18 -8
- package/dist/TopNav/TopNavMegaMenu.js +10 -4
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
- 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/TreeList.d.ts +12 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +2 -0
- package/dist/TreeList/TreeListBranches.d.ts +5 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +10 -4
- package/dist/TreeList/TreeListItem.d.ts +8 -2
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +27 -7
- package/dist/TreeList/TreeListTypes.d.ts +25 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/TreeList/index.d.ts +2 -2
- package/dist/TreeList/index.d.ts.map +1 -1
- package/dist/astryx.css +28 -18
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/authoring/doc.d.ts.map +1 -1
- package/dist/docs-types.d.ts +8 -0
- package/dist/docs-types.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/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/useDevWarning.d.ts +18 -0
- package/dist/hooks/useDevWarning.d.ts.map +1 -0
- package/dist/hooks/useDevWarning.js +49 -0
- package/dist/hooks/useOverflow.d.ts +19 -0
- package/dist/hooks/useOverflow.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +36 -25
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +7 -18
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +2 -7
- 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 +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
- package/dist/utils/devWarning.d.ts +42 -0
- package/dist/utils/devWarning.d.ts.map +1 -0
- package/dist/utils/devWarning.js +88 -0
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/plainDate.d.ts +30 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +53 -0
- package/dist/utils/types.d.ts +13 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/locales/en.json +24 -0
- package/locales/fr-FR.json +14 -0
- package/locales/pseudo.json +18 -0
- package/package.json +1 -1
- 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 +15 -3
- package/src/Avatar/Avatar.test.tsx +156 -1
- package/src/Avatar/Avatar.tsx +96 -4
- package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
- package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
- package/src/Avatar/AvatarStatusDot.tsx +178 -21
- 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/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
- package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
- package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
- package/src/Breadcrumbs/index.ts +27 -0
- 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/Calendar/Calendar.doc.mjs +12 -1
- package/src/Calendar/Calendar.test.tsx +193 -0
- package/src/Calendar/Calendar.tsx +38 -16
- package/src/Calendar/dayCellUtils.test.ts +5 -0
- package/src/Calendar/dayCellUtils.ts +1 -1
- 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 +53 -0
- package/src/Carousel/Carousel.tsx +34 -4
- package/src/Chat/ChatComposer.doc.mjs +12 -3
- package/src/Chat/ChatComposer.test.tsx +115 -0
- package/src/Chat/ChatComposer.tsx +73 -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 +103 -0
- package/src/Chat/ChatComposerInput.tsx +49 -3
- package/src/Chat/ChatContext.tsx +37 -10
- package/src/Chat/ChatDictationButton.tsx +4 -1
- package/src/Chat/ChatLayout.tsx +3 -1
- package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
- package/src/Chat/ChatMessageMetadata.tsx +1 -2
- package/src/Chat/ChatSendButton.tsx +11 -1
- package/src/Chat/ChatToolCalls.test.tsx +64 -0
- package/src/Chat/ChatToolCalls.tsx +25 -4
- package/src/Chat/index.ts +5 -1
- package/src/Chat/useChatStreamScroll.test.tsx +49 -0
- package/src/Chat/useChatStreamScroll.ts +27 -10
- package/src/Chat/useSpeechRecognition.ts +1 -1
- package/src/Chat/useTriggerMenu.tsx +3 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
- package/src/CheckboxInput/CheckboxInput.tsx +1 -1
- package/src/CheckboxList/CheckboxList.test.tsx +81 -3
- package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
- package/src/CheckboxList/CheckboxListItem.tsx +41 -5
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
- package/src/ClickableCard/ClickableCard.test.tsx +34 -0
- package/src/ClickableCard/ClickableCard.tsx +13 -3
- package/src/CodeBlock/CodeBlock.test.tsx +56 -0
- package/src/CodeBlock/CodeBlock.tsx +9 -1
- package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
- package/src/CommandPalette/CommandPalette.tsx +3 -1
- package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
- package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
- 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 +193 -0
- package/src/DateInput/DateInput.doc.mjs +16 -0
- package/src/DateInput/DateInput.test.tsx +108 -0
- package/src/DateInput/DateInput.tsx +68 -3
- package/src/DateInput/index.ts +1 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
- package/src/DateTimeInput/DateTimeInput.tsx +14 -2
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.test.tsx +77 -4
- package/src/Dialog/Dialog.tsx +89 -5
- package/src/Dialog/DialogContext.ts +9 -2
- package/src/Dialog/DialogHeader.doc.mjs +2 -2
- package/src/Dialog/DialogHeader.tsx +9 -2
- package/src/Divider/Divider.tsx +2 -2
- package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
- package/src/Field/Field.test.tsx +57 -12
- package/src/Field/Field.tsx +6 -5
- package/src/Field/FieldLabel.tsx +8 -0
- 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 +3 -2
- package/src/Icon/Icon.tsx +7 -2
- package/src/IconButton/IconButton.doc.mjs +8 -0
- package/src/IconButton/IconButton.test.tsx +15 -18
- package/src/Item/Item.test.tsx +40 -18
- package/src/Item/Item.tsx +38 -2
- package/src/Layer/useLayer.tsx +8 -0
- package/src/List/List.test.tsx +9 -8
- 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 +172 -3
- package/src/MultiSelector/MultiSelector.tsx +55 -12
- package/src/MultiSelector/hooks.ts +17 -0
- package/src/NumberInput/NumberInput.test.tsx +11 -7
- package/src/NumberInput/NumberInput.tsx +1 -2
- package/src/Outline/Outline.tsx +0 -1
- 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/Popover/Popover.tsx +7 -4
- package/src/Popover/usePopover.tsx +9 -13
- package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
- package/src/PowerSearch/PowerSearch.test.tsx +20 -0
- 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 +29 -16
- package/src/Selector/Selector.test.tsx +69 -0
- package/src/Selector/Selector.tsx +5 -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.tsx +5 -1
- package/src/Slider/Slider.test.tsx +75 -2
- package/src/Slider/Slider.tsx +21 -5
- package/src/Switch/Switch.test.tsx +32 -1
- package/src/Table/BaseTable.tsx +3 -5
- package/src/Table/index.ts +5 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
- 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 +19 -1
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
- package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
- package/src/Table/useBaseTablePlugins.ts +10 -6
- package/src/Table/useTableRowStatus.doc.mjs +30 -0
- package/src/Table/useTableSelection.doc.mjs +65 -14
- package/src/Table/useTableTreeData.doc.mjs +29 -0
- package/src/Table/useTableTreeState.doc.mjs +2 -2
- package/src/TextArea/TextArea.test.tsx +9 -1
- package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
- package/src/Thumbnail/Thumbnail.test.tsx +98 -7
- package/src/Thumbnail/Thumbnail.tsx +141 -41
- package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
- package/src/TimeInput/TimeInput.test.tsx +76 -1
- package/src/TimeInput/TimeInput.tsx +19 -6
- package/src/Timestamp/Timestamp.doc.mjs +4 -4
- package/src/Timestamp/Timestamp.test.tsx +32 -0
- package/src/Timestamp/Timestamp.tsx +28 -8
- package/src/Toast/useToast.tsx +7 -8
- package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
- package/src/Tokenizer/Tokenizer.test.tsx +183 -2
- package/src/Tokenizer/Tokenizer.tsx +16 -4
- package/src/TopNav/TopNav.test.tsx +125 -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/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
- package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
- package/src/TopNav/TopNavMenu.test.tsx +118 -4
- package/src/TopNav/TopNavMenu.tsx +83 -5
- package/src/TreeList/TreeList.doc.mjs +22 -0
- package/src/TreeList/TreeList.test.tsx +232 -0
- package/src/TreeList/TreeList.tsx +18 -2
- package/src/TreeList/TreeListBranches.tsx +16 -2
- package/src/TreeList/TreeListItem.tsx +51 -11
- package/src/TreeList/TreeListTypes.ts +27 -1
- package/src/TreeList/index.ts +2 -2
- package/src/authoring/doc.ts +1 -3
- package/src/docs-types.ts +5 -0
- package/src/hooks/computeOverflow.test.ts +422 -0
- package/src/hooks/computeOverflow.ts +290 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useDevWarning.test.tsx +45 -0
- package/src/hooks/useDevWarning.ts +54 -0
- package/src/hooks/useOverflow.test.ts +96 -4
- package/src/hooks/useOverflow.ts +74 -33
- package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
- package/src/i18n/resolve.ts +9 -21
- package/src/theme/Theme.tsx +11 -14
- package/src/theme/generateThemeRules.test.ts +65 -0
- package/src/theme/generateThemeRules.ts +56 -0
- package/src/theme/hct.ts +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
- package/src/utils/devWarning.test.ts +56 -0
- package/src/utils/devWarning.ts +102 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/plainDate.test.ts +22 -0
- package/src/utils/plainDate.ts +61 -0
- package/src/utils/types.ts +14 -0
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
isValidElement,
|
|
25
25
|
useCallback,
|
|
26
26
|
useEffect,
|
|
27
|
+
useId,
|
|
27
28
|
useMemo,
|
|
28
29
|
useRef,
|
|
29
30
|
useState,
|
|
@@ -600,10 +601,14 @@ export function AppShell({
|
|
|
600
601
|
// =========================================================================
|
|
601
602
|
// Mobile context — shared with MobileNavToggle and future TopNav mobile
|
|
602
603
|
// =========================================================================
|
|
604
|
+
// Stable id shared by the drawer (applied as its `id`) and the toggle
|
|
605
|
+
// (referenced via `aria-controls`) so screen readers know what it controls.
|
|
606
|
+
const mobileNavId = useId();
|
|
603
607
|
const mobileContextValue = useMemo<AppShellMobileContextValue>(
|
|
604
608
|
() => ({
|
|
605
609
|
isMobile: isBelowBreakpoint,
|
|
606
610
|
isMobileNavOpen,
|
|
611
|
+
mobileNavId,
|
|
607
612
|
toggleMobileNav: () =>
|
|
608
613
|
mobileNavEnabled && setMobileNavOpen(!isMobileNavOpen),
|
|
609
614
|
openMobileNav: () => mobileNavEnabled && setMobileNavOpen(true),
|
|
@@ -614,6 +619,7 @@ export function AppShell({
|
|
|
614
619
|
[
|
|
615
620
|
isBelowBreakpoint,
|
|
616
621
|
isMobileNavOpen,
|
|
622
|
+
mobileNavId,
|
|
617
623
|
setMobileNavOpen,
|
|
618
624
|
mobileNavEnabled,
|
|
619
625
|
mobileNavHasToggle,
|
|
@@ -20,6 +20,14 @@ export interface AppShellMobileContextValue {
|
|
|
20
20
|
isMobile: boolean;
|
|
21
21
|
/** Whether the mobile nav drawer is currently open */
|
|
22
22
|
isMobileNavOpen: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* DOM id of the mobile nav drawer. `AppShell` sets this so the toggle can
|
|
25
|
+
* point its `aria-controls` at the drawer and the drawer can apply it as its
|
|
26
|
+
* `id`. Optional: callers that construct this context by hand may omit it, in
|
|
27
|
+
* which case `MobileNav` falls back to a locally generated id and the toggle
|
|
28
|
+
* simply drops `aria-controls`.
|
|
29
|
+
*/
|
|
30
|
+
mobileNavId?: string;
|
|
23
31
|
/** Toggle the mobile nav drawer open/closed */
|
|
24
32
|
toggleMobileNav: () => void;
|
|
25
33
|
/** Open the mobile nav drawer */
|
|
@@ -31,6 +31,12 @@ export const docs = {
|
|
|
31
31
|
description:
|
|
32
32
|
'Whether the AppShell-managed mobile navigation drawer is open.',
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
name: 'mobileNavId',
|
|
36
|
+
type: 'string | undefined',
|
|
37
|
+
description:
|
|
38
|
+
'DOM id of the mobile navigation drawer, set by AppShell. Point aria-controls of a custom toggle at this so screen-reader users know which element the toggle expands. Undefined outside an AppShell that manages the drawer.',
|
|
39
|
+
},
|
|
34
40
|
{
|
|
35
41
|
name: 'toggleMobileNav',
|
|
36
42
|
type: '() => void',
|
|
@@ -111,6 +117,8 @@ export const docsDense = {
|
|
|
111
117
|
isMobile:
|
|
112
118
|
'viewport below AppShell mobile nav breakpoint? Use to sync AppShell-adjacent mobile UI',
|
|
113
119
|
isMobileNavOpen: 'AppShell-managed mobile nav drawer open?',
|
|
120
|
+
mobileNavId:
|
|
121
|
+
'DOM id of the drawer; point custom toggle aria-controls at it',
|
|
114
122
|
toggleMobileNav: 'toggle drawer; no-op when mobile nav disabled',
|
|
115
123
|
openMobileNav: 'open drawer; no-op when mobile nav disabled',
|
|
116
124
|
closeMobileNav: 'close drawer',
|
|
@@ -15,6 +15,7 @@ export const docs = {
|
|
|
15
15
|
{guidance: true, description: 'Always pass a name so the avatar can show initials if the photo fails to load, and so screen readers can announce who it represents.'},
|
|
16
16
|
{guidance: true, description: 'Pick a size that matches the context: xsm or sm for inline mentions, md or lg for lists and cards, xl for profile headers.'},
|
|
17
17
|
{guidance: true, description: 'Add a status dot when knowing someone\'s availability matters, like in chat or team views.'},
|
|
18
|
+
{guidance: true, description: 'When wrapping an Avatar in your own Tooltip or HoverCard, set tooltip={false} so the built-in name tooltip does not overlap yours.'},
|
|
18
19
|
{guidance: false, description: 'Use Avatar for logos, product images, or anything that isn\'t a person or team. Use an image or icon instead.'},
|
|
19
20
|
{guidance: false, description: 'Force a square or custom shape. Avatars are always circular to stay consistent across the system.'},
|
|
20
21
|
],
|
|
@@ -22,13 +23,14 @@ export const docs = {
|
|
|
22
23
|
{name: 'Photo', required: false, description: 'The profile image, loaded from the src URL. Shown when available.'},
|
|
23
24
|
{name: 'Initials', required: false, description: 'One or two letters extracted from the name. Shown when no photo is available.'},
|
|
24
25
|
{name: 'Default icon', required: false, description: 'A generic person silhouette. Shown when there is no photo or name.'},
|
|
25
|
-
{name: 'Status dot', required: false, description: 'A small indicator in the bottom-right corner showing availability (online, away, busy).'},
|
|
26
|
+
{name: 'Status dot', required: false, description: 'A small indicator in the bottom-right corner showing availability (online, away, busy). Each variant pairs colour with a distinct shape so status does not rely on colour alone.'},
|
|
26
27
|
],
|
|
27
28
|
},
|
|
28
29
|
theming: {
|
|
29
30
|
targets: [
|
|
30
31
|
{className: 'astryx-avatar', visualProps: ['size']},
|
|
31
32
|
{className: 'astryx-avatar-status-dot', visualProps: ['variant']},
|
|
33
|
+
{className: 'astryx-avatar-status-dot-glyph', visualProps: ['shape']},
|
|
32
34
|
],
|
|
33
35
|
},
|
|
34
36
|
description: 'Displays a user avatar with image, initials fallback, and optional status indicator.',
|
|
@@ -65,13 +67,21 @@ export const docs = {
|
|
|
65
67
|
description: 'Corner content for status indicators.',
|
|
66
68
|
slotElements: [
|
|
67
69
|
{
|
|
68
|
-
__element: '
|
|
70
|
+
__element: 'AvatarStatusDot',
|
|
69
71
|
props: {
|
|
70
|
-
variant: '
|
|
72
|
+
variant: 'success',
|
|
73
|
+
label: 'Online',
|
|
71
74
|
},
|
|
72
75
|
},
|
|
73
76
|
],
|
|
74
77
|
},
|
|
78
|
+
{
|
|
79
|
+
name: 'tooltip',
|
|
80
|
+
type: 'string | boolean',
|
|
81
|
+
description:
|
|
82
|
+
"Tooltip shown on hover and keyboard focus. Omitted or true shows the avatar's name; a string shows that text instead; false shows no tooltip. Not auto-disabled when wrapped in your own Tooltip/HoverCard — set tooltip={false} if you supply your own overlay. No tooltip is shown when tooltip is true/omitted and there is no name.",
|
|
83
|
+
default: 'true',
|
|
84
|
+
},
|
|
75
85
|
],
|
|
76
86
|
components: [
|
|
77
87
|
{name: 'AvatarStatusDot'},
|
|
@@ -113,6 +123,8 @@ export const docsDense = {
|
|
|
113
123
|
alt: 'alt text; falls back to name',
|
|
114
124
|
size: "avatar size. Named ('xsm' 20px, 'sm' 24px, 'md' 36px, 'lg' 48px, 'xl' 128px) or numeric px.",
|
|
115
125
|
status: 'corner content for status indicators',
|
|
126
|
+
tooltip:
|
|
127
|
+
"hover/focus tooltip. true/omitted → name; string → that text; false → none. Owns its tooltip; set false when wrapping in your own Tooltip/HoverCard. Default true.",
|
|
116
128
|
},
|
|
117
129
|
components: [
|
|
118
130
|
{name: 'AvatarStatusDot', description: 'size-aware status indicator rendered in the Avatar corner'},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
-
import {describe, it, expect} from 'vitest';
|
|
3
|
+
import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
|
|
4
4
|
import {render, screen, fireEvent} from '@testing-library/react';
|
|
5
5
|
import {Avatar} from './Avatar';
|
|
6
6
|
|
|
@@ -65,4 +65,159 @@ describe('Avatar', () => {
|
|
|
65
65
|
expect(img).not.toBeNull();
|
|
66
66
|
expect(img).toHaveAttribute('src', 'https://example.com/ada.jpg');
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
// --- Name tooltip (tooltip?: string | boolean) ---
|
|
70
|
+
describe('name tooltip', () => {
|
|
71
|
+
const originalShowPopover = HTMLElement.prototype.showPopover;
|
|
72
|
+
const originalHidePopover = HTMLElement.prototype.hidePopover;
|
|
73
|
+
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
// jsdom does not implement the Popover API the tooltip layer uses.
|
|
76
|
+
HTMLElement.prototype.showPopover = vi.fn();
|
|
77
|
+
HTMLElement.prototype.hidePopover = vi.fn();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
HTMLElement.prototype.showPopover = originalShowPopover;
|
|
82
|
+
HTMLElement.prototype.hidePopover = originalHidePopover;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('shows the name in a tooltip by default', () => {
|
|
86
|
+
render(<Avatar name="Ada Lovelace" />);
|
|
87
|
+
const tooltip = screen.getByRole('tooltip', {hidden: true});
|
|
88
|
+
expect(tooltip).toHaveTextContent('Ada Lovelace');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('shows a custom string tooltip instead of the name', () => {
|
|
92
|
+
render(<Avatar name="alovelace" tooltip="Ada Lovelace, Mathematician" />);
|
|
93
|
+
const tooltip = screen.getByRole('tooltip', {hidden: true});
|
|
94
|
+
expect(tooltip).toHaveTextContent('Ada Lovelace, Mathematician');
|
|
95
|
+
expect(tooltip).not.toHaveTextContent('alovelace');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('renders no tooltip when tooltip={false}', () => {
|
|
99
|
+
render(<Avatar name="Ada Lovelace" tooltip={false} />);
|
|
100
|
+
expect(screen.queryByRole('tooltip', {hidden: true})).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('renders no tooltip for a decorative avatar (no name/alt)', () => {
|
|
104
|
+
render(<Avatar src="https://example.com/x.jpg" />);
|
|
105
|
+
expect(screen.queryByRole('tooltip', {hidden: true})).toBeNull();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('renders no tooltip when tooltip is true/default but name is empty', () => {
|
|
109
|
+
render(<Avatar name=" " alt="Profile photo" data-testid="a" />);
|
|
110
|
+
// A whitespace-only name yields nothing to show, and the default tooltip
|
|
111
|
+
// uses `name` (not `alt`), so there is no tooltip.
|
|
112
|
+
expect(screen.queryByRole('tooltip', {hidden: true})).toBeNull();
|
|
113
|
+
// The accessible name still comes from alt, unaffected.
|
|
114
|
+
expect(
|
|
115
|
+
screen.getByRole('img', {name: 'Profile photo'}),
|
|
116
|
+
).toBeInTheDocument();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('still shows a custom string tooltip when there is no name', () => {
|
|
120
|
+
render(<Avatar tooltip="Anonymous user" data-testid="a" />);
|
|
121
|
+
const tooltip = screen.getByRole('tooltip', {hidden: true});
|
|
122
|
+
expect(tooltip).toHaveTextContent('Anonymous user');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('makes the root focusable while a tooltip is attached', () => {
|
|
126
|
+
render(<Avatar name="Ada Lovelace" data-testid="a" />);
|
|
127
|
+
expect(screen.getByTestId('a')).toHaveAttribute('tabindex', '0');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('does not add a tab stop when the tooltip is disabled', () => {
|
|
131
|
+
render(<Avatar name="Ada Lovelace" tooltip={false} data-testid="a" />);
|
|
132
|
+
expect(screen.getByTestId('a')).not.toHaveAttribute('tabindex');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('keeps the accessible name on the root regardless of the tooltip', () => {
|
|
136
|
+
const {rerender} = render(<Avatar name="Ada Lovelace" />);
|
|
137
|
+
expect(
|
|
138
|
+
screen.getByRole('img', {name: 'Ada Lovelace'}),
|
|
139
|
+
).toBeInTheDocument();
|
|
140
|
+
|
|
141
|
+
rerender(<Avatar name="Ada Lovelace" tooltip={false} />);
|
|
142
|
+
expect(
|
|
143
|
+
screen.getByRole('img', {name: 'Ada Lovelace'}),
|
|
144
|
+
).toBeInTheDocument();
|
|
145
|
+
|
|
146
|
+
rerender(<Avatar name="alovelace" tooltip="Ada Lovelace, Eng" />);
|
|
147
|
+
// alt||name still drives the accessible name — not the custom tooltip.
|
|
148
|
+
expect(screen.getByRole('img', {name: 'alovelace'})).toBeInTheDocument();
|
|
149
|
+
|
|
150
|
+
rerender(<Avatar name="alovelace" alt="Ada's profile photo" />);
|
|
151
|
+
expect(
|
|
152
|
+
screen.getByRole('img', {name: "Ada's profile photo"}),
|
|
153
|
+
).toBeInTheDocument();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('does not describe the root with the default name tooltip (no double-announce)', () => {
|
|
157
|
+
render(<Avatar name="Ada Lovelace" data-testid="a" />);
|
|
158
|
+
// The default name tooltip is visual-only: its text duplicates the root
|
|
159
|
+
// aria-label, so we deliberately do NOT wire aria-describedby (OQ-4).
|
|
160
|
+
expect(screen.getByTestId('a')).not.toHaveAttribute('aria-describedby');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('describes the root with a custom string tooltip (matches Button)', () => {
|
|
164
|
+
render(
|
|
165
|
+
<Avatar
|
|
166
|
+
name="alovelace"
|
|
167
|
+
tooltip="Ada Lovelace, Mathematician"
|
|
168
|
+
data-testid="a"
|
|
169
|
+
/>,
|
|
170
|
+
);
|
|
171
|
+
const root = screen.getByTestId('a');
|
|
172
|
+
const describedBy = root.getAttribute('aria-describedby');
|
|
173
|
+
expect(describedBy).toBeTruthy();
|
|
174
|
+
// aria-describedby points at the rendered tooltip layer.
|
|
175
|
+
const tooltip = screen.getByRole('tooltip', {hidden: true});
|
|
176
|
+
expect(tooltip.id).toBeTruthy();
|
|
177
|
+
expect(describedBy).toContain(tooltip.id);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('composes a consumer aria-describedby with the custom tooltip description', () => {
|
|
181
|
+
render(
|
|
182
|
+
<>
|
|
183
|
+
<span id="extra-desc">extra description</span>
|
|
184
|
+
<Avatar
|
|
185
|
+
name="alovelace"
|
|
186
|
+
tooltip="Ada Lovelace, Eng"
|
|
187
|
+
aria-describedby="extra-desc"
|
|
188
|
+
data-testid="a"
|
|
189
|
+
/>
|
|
190
|
+
</>,
|
|
191
|
+
);
|
|
192
|
+
const describedBy = screen
|
|
193
|
+
.getByTestId('a')
|
|
194
|
+
.getAttribute('aria-describedby');
|
|
195
|
+
expect(describedBy).toContain('extra-desc');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('preserves a consumer aria-describedby with the default name tooltip', () => {
|
|
199
|
+
render(
|
|
200
|
+
<>
|
|
201
|
+
<span id="extra-desc">extra description</span>
|
|
202
|
+
<Avatar
|
|
203
|
+
name="Ada Lovelace"
|
|
204
|
+
aria-describedby="extra-desc"
|
|
205
|
+
data-testid="a"
|
|
206
|
+
/>
|
|
207
|
+
</>,
|
|
208
|
+
);
|
|
209
|
+
// Default name tooltip does not touch aria-describedby, so the consumer
|
|
210
|
+
// value passes through untouched.
|
|
211
|
+
expect(screen.getByTestId('a')).toHaveAttribute(
|
|
212
|
+
'aria-describedby',
|
|
213
|
+
'extra-desc',
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('forwards the consumer ref to the root while a tooltip is attached', () => {
|
|
218
|
+
const ref = {current: null as HTMLDivElement | null};
|
|
219
|
+
render(<Avatar name="Ada Lovelace" ref={ref} data-testid="a" />);
|
|
220
|
+
expect(ref.current).toBe(screen.getByTestId('a'));
|
|
221
|
+
});
|
|
222
|
+
});
|
|
68
223
|
});
|
package/src/Avatar/Avatar.tsx
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Avatar.tsx
|
|
7
|
-
* @input Uses React, HTMLAttributes, ReactNode, useState
|
|
7
|
+
* @input Uses React, HTMLAttributes, ReactNode, useState; useTooltip
|
|
8
|
+
* (Tooltip hook) for the optional name-on-hover tooltip
|
|
8
9
|
* @output Exports Avatar component, AvatarProps, AvatarSize types
|
|
9
10
|
* @position Core implementation; consumed by index.ts
|
|
10
11
|
*
|
|
@@ -26,8 +27,9 @@ import {
|
|
|
26
27
|
} from '../theme/tokens.stylex';
|
|
27
28
|
import {AvatarSizeContext} from './AvatarSizeContext';
|
|
28
29
|
import {useAvatarGroup} from '../AvatarGroup/AvatarGroupContext';
|
|
29
|
-
import {mergeProps} from '../utils';
|
|
30
|
+
import {mergeProps, mergeRefs} from '../utils';
|
|
30
31
|
import {themeProps} from '../utils/themeProps';
|
|
32
|
+
import {useTooltip} from '../Tooltip/useTooltip';
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
35
|
* The offset ratio for positioning elements on a circle's edge at 45°.
|
|
@@ -133,6 +135,19 @@ const styles = stylex.create({
|
|
|
133
135
|
status: {
|
|
134
136
|
position: 'absolute',
|
|
135
137
|
},
|
|
138
|
+
// Visible focus ring for the name-tooltip tab stop, matching the repo-wide
|
|
139
|
+
// focus-visible outline treatment (see Timestamp, Token, Thumbnail). Only
|
|
140
|
+
// applied when a tooltip is active so keyboard users can reveal it.
|
|
141
|
+
focusable: {
|
|
142
|
+
outline: {
|
|
143
|
+
default: null,
|
|
144
|
+
':focus-visible': `2px solid ${colorVars['--color-accent']}`,
|
|
145
|
+
},
|
|
146
|
+
outlineOffset: {
|
|
147
|
+
default: '0',
|
|
148
|
+
':focus-visible': '2px',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
136
151
|
});
|
|
137
152
|
|
|
138
153
|
/**
|
|
@@ -216,6 +231,19 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
|
|
|
216
231
|
* Typically used for status indicators or badges.
|
|
217
232
|
*/
|
|
218
233
|
status?: ReactNode;
|
|
234
|
+
/**
|
|
235
|
+
* Tooltip shown on hover (and keyboard focus).
|
|
236
|
+
* - omitted / `true`: show the avatar's `name`
|
|
237
|
+
* - a string: show that text instead
|
|
238
|
+
* - `false`: no tooltip
|
|
239
|
+
*
|
|
240
|
+
* The avatar owns this tooltip. It is NOT auto-disabled when wrapped in your
|
|
241
|
+
* own Tooltip/HoverCard — set `tooltip={false}` if you provide your own
|
|
242
|
+
* overlay. No tooltip is shown if `tooltip` is `true`/omitted and there is
|
|
243
|
+
* no (non-whitespace) `name`.
|
|
244
|
+
* @default true
|
|
245
|
+
*/
|
|
246
|
+
tooltip?: string | boolean;
|
|
219
247
|
}
|
|
220
248
|
|
|
221
249
|
/**
|
|
@@ -265,6 +293,8 @@ function DefaultIcon({size}: {size: number}) {
|
|
|
265
293
|
* <Avatar src="/user.jpg" name="John Doe" />
|
|
266
294
|
* <Avatar name="Jane Smith" size="xl" />
|
|
267
295
|
* <Avatar src="/user.jpg" status={<OnlineIndicator />} />
|
|
296
|
+
* <Avatar name="jsmith" tooltip="Jane Smith, Staff Engineer" />
|
|
297
|
+
* <Avatar name="Jane" tooltip={false} />
|
|
268
298
|
* ```
|
|
269
299
|
*/
|
|
270
300
|
export function Avatar({
|
|
@@ -275,6 +305,7 @@ export function Avatar({
|
|
|
275
305
|
size = 'md',
|
|
276
306
|
src,
|
|
277
307
|
status,
|
|
308
|
+
tooltip = true,
|
|
278
309
|
xstyle,
|
|
279
310
|
className,
|
|
280
311
|
style,
|
|
@@ -303,19 +334,66 @@ export function Avatar({
|
|
|
303
334
|
const resolvedSize = avatarGroup?.size ?? size;
|
|
304
335
|
const numericSize = useMemo(() => resolveSize(resolvedSize), [resolvedSize]);
|
|
305
336
|
|
|
306
|
-
|
|
337
|
+
// Resolve the tooltip content:
|
|
338
|
+
// - `false` → no tooltip
|
|
339
|
+
// - a string → that string
|
|
340
|
+
// - `true` / omitted → the `name` (a whitespace-only name yields nothing)
|
|
341
|
+
// Note: the *visible* tooltip prefers `name` (not `alt`); the *accessible
|
|
342
|
+
// name* on the root still uses `alt || name` above, independent of this.
|
|
343
|
+
const tooltipContent =
|
|
344
|
+
tooltip === false
|
|
345
|
+
? undefined
|
|
346
|
+
: typeof tooltip === 'string'
|
|
347
|
+
? tooltip
|
|
348
|
+
: name;
|
|
349
|
+
const trimmedTooltip = tooltipContent?.trim();
|
|
350
|
+
const showTooltip = trimmedTooltip != null && trimmedTooltip !== '';
|
|
351
|
+
// Whether the tooltip text is a consumer-authored override (a custom string)
|
|
352
|
+
// rather than the default name. A custom description is worth wiring to
|
|
353
|
+
// `aria-describedby` (it adds information, matching Button); the default name
|
|
354
|
+
// tooltip is visual-only — its text duplicates the root `aria-label`, so
|
|
355
|
+
// describing it too would double-announce the same name (OQ-4).
|
|
356
|
+
const isCustomTooltip = typeof tooltip === 'string';
|
|
357
|
+
|
|
358
|
+
// Own the name tooltip via the Tooltip hook (the Button pattern), which
|
|
359
|
+
// returns `describedBy` as a value we choose whether to apply — the only way
|
|
360
|
+
// to satisfy the per-case aria-describedby rule (default name: none; custom
|
|
361
|
+
// string: describe) without editing Tooltip. `focusTrigger: 'auto'` shows the
|
|
362
|
+
// tooltip on keyboard focus once the root is made focusable (below).
|
|
363
|
+
const tooltipHook = useTooltip({
|
|
364
|
+
placement: 'above',
|
|
365
|
+
isEnabled: showTooltip,
|
|
366
|
+
});
|
|
367
|
+
const rootRef = mergeRefs(ref, showTooltip ? tooltipHook.ref : undefined);
|
|
368
|
+
const describedByProp =
|
|
369
|
+
showTooltip && isCustomTooltip
|
|
370
|
+
? {
|
|
371
|
+
'aria-describedby':
|
|
372
|
+
[props['aria-describedby'], tooltipHook.describedBy]
|
|
373
|
+
.filter(Boolean)
|
|
374
|
+
.join(' ') || undefined,
|
|
375
|
+
}
|
|
376
|
+
: null;
|
|
377
|
+
|
|
378
|
+
const avatarElement = (
|
|
307
379
|
<AvatarSizeContext value={numericSize}>
|
|
308
380
|
<div
|
|
309
381
|
{...props}
|
|
310
|
-
ref={
|
|
382
|
+
ref={rootRef}
|
|
311
383
|
role={isDecorative ? 'presentation' : 'img'}
|
|
312
384
|
aria-label={isDecorative ? undefined : accessibleName}
|
|
313
385
|
aria-hidden={isDecorative || undefined}
|
|
386
|
+
// The root is a div[role="img"], not natively focusable. When a name
|
|
387
|
+
// tooltip is active, add a tab stop so keyboard users can reveal it
|
|
388
|
+
// (WCAG 1.4.13 / 2.1.1) — matching Timestamp/Button. Only while active.
|
|
389
|
+
tabIndex={showTooltip ? 0 : undefined}
|
|
314
390
|
data-testid={testId}
|
|
391
|
+
{...describedByProp}
|
|
315
392
|
{...mergeProps(
|
|
316
393
|
themeProps('avatar', {size: resolvedSize}),
|
|
317
394
|
stylex.props(
|
|
318
395
|
styles.wrapper,
|
|
396
|
+
showTooltip && styles.focusable,
|
|
319
397
|
avatarGroup && groupStyles.ring,
|
|
320
398
|
avatarGroup && groupStyles.overlap,
|
|
321
399
|
avatarGroup && groupDynamicStyles.overlap(-avatarGroup.overlap),
|
|
@@ -368,6 +446,20 @@ export function Avatar({
|
|
|
368
446
|
</div>
|
|
369
447
|
</AvatarSizeContext>
|
|
370
448
|
);
|
|
449
|
+
|
|
450
|
+
if (!showTooltip) {
|
|
451
|
+
return avatarElement;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Render the tooltip as a sibling (no wrapper DOM) — the hook's ref is already
|
|
455
|
+
// attached to the root via `rootRef` above. `trimmedTooltip` is guaranteed
|
|
456
|
+
// non-empty here.
|
|
457
|
+
return (
|
|
458
|
+
<>
|
|
459
|
+
{avatarElement}
|
|
460
|
+
{tooltipHook.renderTooltip(trimmedTooltip)}
|
|
461
|
+
</>
|
|
462
|
+
);
|
|
371
463
|
}
|
|
372
464
|
|
|
373
465
|
Avatar.displayName = 'Avatar';
|
|
@@ -7,23 +7,27 @@ export const docs = {
|
|
|
7
7
|
subComponentOf: 'Avatar',
|
|
8
8
|
displayName: 'Avatar Status Dot',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
|
-
description:
|
|
10
|
+
description:
|
|
11
|
+
'Size-aware status indicator dot that reads avatar size from context and scales proportionally. Each variant pairs a colour with a distinct built-in shape (filled, ring, minus) so status is never conveyed by colour alone (WCAG 1.4.1).',
|
|
11
12
|
props: [
|
|
12
13
|
{
|
|
13
14
|
name: 'variant',
|
|
14
15
|
type: "'success' | 'neutral' | 'error'",
|
|
15
|
-
description:
|
|
16
|
+
description:
|
|
17
|
+
'Semantic variant pairing colour with a distinct shape: success = filled green dot, neutral = grey ring, error = red dot with a minus bar.',
|
|
16
18
|
default: "'success'",
|
|
17
19
|
},
|
|
18
20
|
{
|
|
19
21
|
name: 'label',
|
|
20
22
|
type: 'string',
|
|
21
|
-
description:
|
|
23
|
+
description:
|
|
24
|
+
'Accessible label describing the status. Not announced inside an Avatar today (the Avatar root is role="img", which prunes child semantics) — pass it anyway; an Avatar-level name-composition fix is planned.',
|
|
22
25
|
},
|
|
23
26
|
{
|
|
24
27
|
name: 'icon',
|
|
25
28
|
type: 'ReactNode',
|
|
26
|
-
description:
|
|
29
|
+
description:
|
|
30
|
+
'Icon centered inside the dot (hidden at tiny sizes). A rendered icon replaces the built-in shape glyph, so use a different icon per status.',
|
|
27
31
|
slotElements: [
|
|
28
32
|
{
|
|
29
33
|
__element: 'Icon',
|
|
@@ -41,23 +45,27 @@ export const docsZh = {
|
|
|
41
45
|
name: 'AvatarStatusDot',
|
|
42
46
|
isHiddenFromOverview: true,
|
|
43
47
|
displayName: 'Avatar Status Dot',
|
|
44
|
-
description:
|
|
48
|
+
description:
|
|
49
|
+
'尺寸感知的状态指示点,从上下文中读取头像尺寸并等比缩放。每个变体将颜色与独特形状(实心、圆环、横杠)配对,确保状态不仅靠颜色传达(WCAG 1.4.1)。',
|
|
45
50
|
props: [
|
|
46
51
|
{
|
|
47
52
|
name: 'variant',
|
|
48
53
|
type: "'success' | 'neutral' | 'error'",
|
|
49
|
-
description:
|
|
54
|
+
description:
|
|
55
|
+
'语义变体,颜色与形状配对:success = 绿色实心点,neutral = 灰色圆环,error = 带横杠的红色点。',
|
|
50
56
|
default: "'success'",
|
|
51
57
|
},
|
|
52
58
|
{
|
|
53
59
|
name: 'label',
|
|
54
60
|
type: 'string',
|
|
55
|
-
description:
|
|
61
|
+
description:
|
|
62
|
+
'描述状态的无障碍标签。目前在 Avatar 内不会被朗读(Avatar 根元素为 role="img",会裁剪子元素语义)——仍请传入;Avatar 层面的名称合成修复已在计划中。',
|
|
56
63
|
},
|
|
57
64
|
{
|
|
58
65
|
name: 'icon',
|
|
59
66
|
type: 'ReactNode',
|
|
60
|
-
description:
|
|
67
|
+
description:
|
|
68
|
+
'居中显示在状态点内的图标(tiny 尺寸时隐藏)。渲染的图标会替换内置形状标记,请为每个状态使用不同的图标。',
|
|
61
69
|
},
|
|
62
70
|
],
|
|
63
71
|
};
|
|
@@ -66,10 +74,13 @@ export const docsDense = {
|
|
|
66
74
|
name: 'AvatarStatusDot',
|
|
67
75
|
isHiddenFromOverview: true,
|
|
68
76
|
displayName: 'Avatar Status Dot',
|
|
69
|
-
description:
|
|
77
|
+
description:
|
|
78
|
+
'size-aware status dot, reads avatar size from context + scales proportionally; variants pair colour with shape (filled/ring/minus) per WCAG 1.4.1',
|
|
70
79
|
propDescriptions: {
|
|
71
|
-
variant:
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
variant:
|
|
81
|
+
'colour + shape variant: success filled, neutral ring, error minus',
|
|
82
|
+
label:
|
|
83
|
+
'accessible status label (not yet announced inside Avatar — role="img" prunes children)',
|
|
84
|
+
icon: 'icon centered in dot (hidden at tiny sizes); replaces the built-in shape glyph — differ it per status',
|
|
74
85
|
},
|
|
75
86
|
};
|