@astryxdesign/core 0.1.8 → 0.1.9-canary.75d2d03
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/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +11 -3
- 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/InternationalizationContext.d.ts +1 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -0
- package/dist/i18n/InternationalizationProvider.d.ts +8 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +5 -1
- package/dist/i18n/getLocaleDirection.d.ts +18 -0
- package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
- package/dist/i18n/getLocaleDirection.js +29 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +7 -1
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +7 -18
- package/dist/i18n/useDirection.d.ts +2 -0
- package/dist/i18n/useDirection.d.ts.map +1 -0
- package/dist/i18n/useDirection.js +27 -0
- 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/Pagination/Pagination.tsx +11 -3
- 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/InternationalizationContext.ts +4 -0
- package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
- package/src/i18n/InternationalizationProvider.tsx +12 -2
- package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
- package/src/i18n/__tests__/useDirection.test.tsx +66 -0
- package/src/i18n/getLocaleDirection.ts +29 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/resolve.ts +9 -21
- package/src/i18n/useDirection.ts +29 -0
- 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
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
|
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
import {render, screen} from '@testing-library/react';
|
|
5
|
+
import * as stylex from '@stylexjs/stylex';
|
|
6
|
+
import {colorVars} from '../theme/tokens.stylex';
|
|
7
|
+
import {Avatar} from './Avatar';
|
|
8
|
+
import {AvatarStatusDot, type AvatarStatusDotVariant} from './AvatarStatusDot';
|
|
9
|
+
|
|
10
|
+
const GLYPH_SELECTOR = '.astryx-avatar-status-dot-glyph';
|
|
11
|
+
const DOT_SELECTOR = '.astryx-avatar-status-dot';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Renders an AvatarStatusDot inside an Avatar of the given numeric size and
|
|
15
|
+
* returns the dot root element. Size tiers: <=36 -> 10px dot (no icons),
|
|
16
|
+
* 40-72 -> 20px dot, >=96 -> 32px dot.
|
|
17
|
+
*/
|
|
18
|
+
function renderDot(
|
|
19
|
+
dotProps: React.ComponentProps<typeof AvatarStatusDot>,
|
|
20
|
+
avatarSize: 36 | 48 | 128 = 48,
|
|
21
|
+
) {
|
|
22
|
+
const {container} = render(
|
|
23
|
+
<Avatar
|
|
24
|
+
name="Ada Lovelace"
|
|
25
|
+
size={avatarSize}
|
|
26
|
+
status={<AvatarStatusDot {...dotProps} />}
|
|
27
|
+
/>,
|
|
28
|
+
);
|
|
29
|
+
const dot = container.querySelector(DOT_SELECTOR);
|
|
30
|
+
expect(dot).not.toBeNull();
|
|
31
|
+
return dot as HTMLElement;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('AvatarStatusDot', () => {
|
|
35
|
+
describe('shape glyphs (WCAG 1.4.1 — colour is not the only signal)', () => {
|
|
36
|
+
it('renders no glyph for success: the plain filled dot is the reference shape', () => {
|
|
37
|
+
const dot = renderDot({variant: 'success'});
|
|
38
|
+
expect(dot.querySelector(GLYPH_SELECTOR)).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('renders a ring glyph for neutral', () => {
|
|
42
|
+
const dot = renderDot({variant: 'neutral'});
|
|
43
|
+
const glyph = dot.querySelector(GLYPH_SELECTOR);
|
|
44
|
+
expect(glyph).not.toBeNull();
|
|
45
|
+
expect(glyph).toHaveAttribute('data-shape', 'ring');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('renders a minus glyph for error', () => {
|
|
49
|
+
const dot = renderDot({variant: 'error'});
|
|
50
|
+
const glyph = dot.querySelector(GLYPH_SELECTOR);
|
|
51
|
+
expect(glyph).not.toBeNull();
|
|
52
|
+
expect(glyph).toHaveAttribute('data-shape', 'minus');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('hides the glyph from assistive tech: it is a visual redundancy of the status', () => {
|
|
56
|
+
const dot = renderDot({variant: 'error'});
|
|
57
|
+
expect(dot.querySelector(GLYPH_SELECTOR)).toHaveAttribute(
|
|
58
|
+
'aria-hidden',
|
|
59
|
+
'true',
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('renders glyphs at every size tier', () => {
|
|
64
|
+
for (const size of [36, 48, 128] as const) {
|
|
65
|
+
const dot = renderDot({variant: 'neutral'}, size);
|
|
66
|
+
expect(dot.querySelector(GLYPH_SELECTOR)).not.toBeNull();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('draws the glyph as an inline svg sized to the dot inner field', () => {
|
|
71
|
+
// The glyph is a stroked <svg>, matching how the rest of the system
|
|
72
|
+
// draws marks (Icon's defaultIcons, the CheckboxInput checkmark).
|
|
73
|
+
// Its viewBox is 1 user unit per px of the dot's inner field
|
|
74
|
+
// (dot minus both borders), so stroke widths below are literal px.
|
|
75
|
+
const cases = [
|
|
76
|
+
[36, 8],
|
|
77
|
+
[48, 16],
|
|
78
|
+
[128, 24],
|
|
79
|
+
] as const;
|
|
80
|
+
for (const [avatarSize, field] of cases) {
|
|
81
|
+
const glyph = renderDot({variant: 'neutral'}, avatarSize).querySelector(
|
|
82
|
+
GLYPH_SELECTOR,
|
|
83
|
+
) as SVGElement;
|
|
84
|
+
expect(glyph.tagName.toLowerCase(), `avatar ${avatarSize}`).toBe('svg');
|
|
85
|
+
expect(glyph.getAttribute('viewBox'), `avatar ${avatarSize}`).toBe(
|
|
86
|
+
`0 0 ${field} ${field}`,
|
|
87
|
+
);
|
|
88
|
+
expect(glyph.getAttribute('width'), `avatar ${avatarSize}`).toBe(
|
|
89
|
+
String(field),
|
|
90
|
+
);
|
|
91
|
+
expect(glyph.getAttribute('height'), `avatar ${avatarSize}`).toBe(
|
|
92
|
+
String(field),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('strokes the ring at the system glyph weight, not a quarter of the field', () => {
|
|
98
|
+
// stroke ~= field / 12, floored at 1px for the smallest tier, which
|
|
99
|
+
// puts these on the icon family's 1 / 1.5 / 2 ladder (~6-12% of the
|
|
100
|
+
// field) instead of the 25% band a CSS box cutout produced.
|
|
101
|
+
const cases = [
|
|
102
|
+
// avatar size, field, stroke, radius = (field - stroke) / 2
|
|
103
|
+
[36, 8, 1, 3.5],
|
|
104
|
+
[48, 16, 1.5, 7.25],
|
|
105
|
+
[128, 24, 2, 11],
|
|
106
|
+
] as const;
|
|
107
|
+
for (const [avatarSize, field, stroke, radius] of cases) {
|
|
108
|
+
const circle = renderDot(
|
|
109
|
+
{variant: 'neutral'},
|
|
110
|
+
avatarSize,
|
|
111
|
+
).querySelector(`${GLYPH_SELECTOR} circle`) as SVGCircleElement;
|
|
112
|
+
expect(circle, `avatar ${avatarSize}`).not.toBeNull();
|
|
113
|
+
expect(
|
|
114
|
+
circle.getAttribute('stroke-width'),
|
|
115
|
+
`avatar ${avatarSize}`,
|
|
116
|
+
).toBe(String(stroke));
|
|
117
|
+
// Radius is to the stroke centreline, so the ring's outer edge lands
|
|
118
|
+
// exactly on the inner field and never clips against the border.
|
|
119
|
+
expect(circle.getAttribute('r'), `avatar ${avatarSize}`).toBe(
|
|
120
|
+
String(radius),
|
|
121
|
+
);
|
|
122
|
+
expect(circle.getAttribute('cx'), `avatar ${avatarSize}`).toBe(
|
|
123
|
+
String(field / 2),
|
|
124
|
+
);
|
|
125
|
+
expect(circle.getAttribute('cy'), `avatar ${avatarSize}`).toBe(
|
|
126
|
+
String(field / 2),
|
|
127
|
+
);
|
|
128
|
+
expect(circle.getAttribute('fill'), `avatar ${avatarSize}`).toBe(
|
|
129
|
+
'none',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('strokes the minus bar at the same weight, spanning 75% of the field with round caps', () => {
|
|
135
|
+
// Ends are inset by half the stroke so the round caps land inside the
|
|
136
|
+
// 75% span rather than overhanging it.
|
|
137
|
+
const cases = [
|
|
138
|
+
// avatar size, field, stroke, x1, x2
|
|
139
|
+
[36, 8, 1, 1.5, 6.5],
|
|
140
|
+
[48, 16, 1.5, 2.75, 13.25],
|
|
141
|
+
[128, 24, 2, 4, 20],
|
|
142
|
+
] as const;
|
|
143
|
+
for (const [avatarSize, field, stroke, x1, x2] of cases) {
|
|
144
|
+
const line = renderDot({variant: 'error'}, avatarSize).querySelector(
|
|
145
|
+
`${GLYPH_SELECTOR} line`,
|
|
146
|
+
) as SVGLineElement;
|
|
147
|
+
expect(line, `avatar ${avatarSize}`).not.toBeNull();
|
|
148
|
+
expect(line.getAttribute('stroke-width'), `avatar ${avatarSize}`).toBe(
|
|
149
|
+
String(stroke),
|
|
150
|
+
);
|
|
151
|
+
expect(line.getAttribute('x1'), `avatar ${avatarSize}`).toBe(
|
|
152
|
+
String(x1),
|
|
153
|
+
);
|
|
154
|
+
expect(line.getAttribute('x2'), `avatar ${avatarSize}`).toBe(
|
|
155
|
+
String(x2),
|
|
156
|
+
);
|
|
157
|
+
expect(line.getAttribute('y1'), `avatar ${avatarSize}`).toBe(
|
|
158
|
+
String(field / 2),
|
|
159
|
+
);
|
|
160
|
+
expect(line.getAttribute('y2'), `avatar ${avatarSize}`).toBe(
|
|
161
|
+
String(field / 2),
|
|
162
|
+
);
|
|
163
|
+
expect(
|
|
164
|
+
line.getAttribute('stroke-linecap'),
|
|
165
|
+
`avatar ${avatarSize}`,
|
|
166
|
+
).toBe('round');
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('paints both glyphs from currentColor so the dot owns the ink colour', () => {
|
|
171
|
+
for (const variant of ['neutral', 'error'] as const) {
|
|
172
|
+
const mark = renderDot({variant}, 48).querySelector(
|
|
173
|
+
`${GLYPH_SELECTOR} circle, ${GLYPH_SELECTOR} line`,
|
|
174
|
+
) as SVGElement;
|
|
175
|
+
expect(mark.getAttribute('stroke'), variant).toBe('currentColor');
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('fills the ring variant with surface and inks it with the variant colour', () => {
|
|
180
|
+
// A ring only reads as hollow if its interior is not the variant
|
|
181
|
+
// colour, so `neutral` inverts: surface fill, coloured stroke.
|
|
182
|
+
// StyleX atomic classes are deterministic, so a local probe pins it.
|
|
183
|
+
const probe = stylex.create({
|
|
184
|
+
ring: {
|
|
185
|
+
backgroundColor: colorVars['--color-background-surface'],
|
|
186
|
+
color: colorVars['--color-text-secondary'],
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
const dot = renderDot({variant: 'neutral'}, 48);
|
|
190
|
+
const atomicClasses = (stylex.props(probe.ring).className ?? '')
|
|
191
|
+
.split(' ')
|
|
192
|
+
// Dev mode prepends a per-file `File__style.key` debug class that
|
|
193
|
+
// legitimately differs between the probe and the component.
|
|
194
|
+
.filter(cls => !cls.includes('__'));
|
|
195
|
+
expect(atomicClasses.length).toBeGreaterThan(0);
|
|
196
|
+
for (const cls of atomicClasses) {
|
|
197
|
+
expect(dot.className).toContain(cls);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('lets a user icon inherit the dot ink instead of hard-coding surface', () => {
|
|
202
|
+
// Surface ink on the surface-filled ring variant would be invisible.
|
|
203
|
+
const probe = stylex.create({
|
|
204
|
+
surfaceInk: {color: colorVars['--color-background-surface']},
|
|
205
|
+
});
|
|
206
|
+
renderDot(
|
|
207
|
+
{variant: 'neutral', icon: <svg data-testid="user-icon" />},
|
|
208
|
+
48,
|
|
209
|
+
);
|
|
210
|
+
const iconWrapper = screen.getByTestId('user-icon')
|
|
211
|
+
.parentElement as HTMLElement;
|
|
212
|
+
const surfaceInkClasses = (stylex.props(probe.surfaceInk).className ?? '')
|
|
213
|
+
.split(' ')
|
|
214
|
+
.filter(cls => !cls.includes('__'));
|
|
215
|
+
expect(surfaceInkClasses.length).toBeGreaterThan(0);
|
|
216
|
+
for (const cls of surfaceInkClasses) {
|
|
217
|
+
expect(iconWrapper.className).not.toContain(cls);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('renders no glyph for custom augmented variants (documented: they must bring their own non-colour mark)', () => {
|
|
222
|
+
const dot = renderDot({variant: 'away' as AvatarStatusDotVariant});
|
|
223
|
+
expect(dot.querySelector(GLYPH_SELECTOR)).toBeNull();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('glyph and icon interplay', () => {
|
|
228
|
+
it('suppresses the glyph when a user icon renders: the icon is the non-colour mark', () => {
|
|
229
|
+
const dot = renderDot(
|
|
230
|
+
{variant: 'error', icon: <svg data-testid="user-icon" />},
|
|
231
|
+
48,
|
|
232
|
+
);
|
|
233
|
+
expect(screen.getByTestId('user-icon')).toBeInTheDocument();
|
|
234
|
+
expect(dot.querySelector(GLYPH_SELECTOR)).toBeNull();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('keeps the glyph at the smallest tier where icons never render', () => {
|
|
238
|
+
const dot = renderDot(
|
|
239
|
+
{variant: 'error', icon: <svg data-testid="user-icon" />},
|
|
240
|
+
36,
|
|
241
|
+
);
|
|
242
|
+
expect(screen.queryByTestId('user-icon')).not.toBeInTheDocument();
|
|
243
|
+
expect(dot.querySelector(GLYPH_SELECTOR)).not.toBeNull();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('keeps the glyph for non-renderable icons: booleans from `cond && <Icon />` render nothing', () => {
|
|
247
|
+
for (const nonRenderable of [true, false, ''] as const) {
|
|
248
|
+
const dot = renderDot({variant: 'error', icon: nonRenderable}, 48);
|
|
249
|
+
expect(
|
|
250
|
+
dot.querySelector(GLYPH_SELECTOR),
|
|
251
|
+
`icon={${JSON.stringify(nonRenderable)}}`,
|
|
252
|
+
).not.toBeNull();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('existing contract', () => {
|
|
258
|
+
it('exposes role="img" with the label as accessible name when label is provided', () => {
|
|
259
|
+
const dot = renderDot({variant: 'success', label: 'Online'});
|
|
260
|
+
expect(dot).toHaveAttribute('role', 'img');
|
|
261
|
+
expect(dot).toHaveAttribute('aria-label', 'Online');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('has no img role without a label', () => {
|
|
265
|
+
const dot = renderDot({variant: 'success'});
|
|
266
|
+
expect(dot).not.toHaveAttribute('role');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('reflects the variant as a data attribute for theming', () => {
|
|
270
|
+
const dot = renderDot({variant: 'neutral'});
|
|
271
|
+
expect(dot).toHaveAttribute('data-variant', 'neutral');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('renders the user icon at tiers with room for it, hidden from assistive tech', () => {
|
|
275
|
+
renderDot(
|
|
276
|
+
{variant: 'success', icon: <svg data-testid="user-icon" />},
|
|
277
|
+
128,
|
|
278
|
+
);
|
|
279
|
+
const icon = screen.getByTestId('user-icon');
|
|
280
|
+
expect(icon).toBeInTheDocument();
|
|
281
|
+
expect(icon.parentElement).toHaveAttribute('aria-hidden', 'true');
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|