@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
|
@@ -99,6 +99,38 @@ describe('Timestamp', () => {
|
|
|
99
99
|
expect(el.textContent).not.toContain(':');
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
it('renders date_weekday format with a weekday prefix', () => {
|
|
103
|
+
render(
|
|
104
|
+
<Timestamp
|
|
105
|
+
value="2026-02-19T17:00:00Z"
|
|
106
|
+
format="date_weekday"
|
|
107
|
+
data-testid="ts"
|
|
108
|
+
/>,
|
|
109
|
+
);
|
|
110
|
+
const el = screen.getByTestId('ts');
|
|
111
|
+
// Includes the year and a leading weekday abbreviation, no time portion.
|
|
112
|
+
expect(el.textContent).toContain('2026');
|
|
113
|
+
expect(el.textContent).not.toContain(':');
|
|
114
|
+
// en-US short weekday for 2026-02-19 is "Thu"; assert a weekday word is
|
|
115
|
+
// present without over-fitting the exact locale punctuation.
|
|
116
|
+
expect(el.textContent).toMatch(/Mon|Tue|Wed|Thu|Fri|Sat|Sun/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('renders date_long format with a full month name', () => {
|
|
120
|
+
render(
|
|
121
|
+
<Timestamp
|
|
122
|
+
value="2026-02-19T17:00:00Z"
|
|
123
|
+
format="date_long"
|
|
124
|
+
data-testid="ts"
|
|
125
|
+
/>,
|
|
126
|
+
);
|
|
127
|
+
const el = screen.getByTestId('ts');
|
|
128
|
+
// Long-month shape: full month name, year, no time portion.
|
|
129
|
+
expect(el.textContent).toContain('February');
|
|
130
|
+
expect(el.textContent).toContain('2026');
|
|
131
|
+
expect(el.textContent).not.toContain(':');
|
|
132
|
+
});
|
|
133
|
+
|
|
102
134
|
it('renders date_time format', () => {
|
|
103
135
|
render(
|
|
104
136
|
<Timestamp
|
|
@@ -21,9 +21,11 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
21
21
|
import {Text} from '../Text';
|
|
22
22
|
import type {TextType, TextSize, TextColor, TextWeight} from '../theme/types';
|
|
23
23
|
import {mergeProps, mergeRefs} from '../utils';
|
|
24
|
+
import {useDevWarning} from '../hooks/useDevWarning';
|
|
24
25
|
import type {BaseProps} from '../BaseProps';
|
|
25
26
|
import {themeProps} from '../utils/themeProps';
|
|
26
27
|
import {colorVars} from '../theme/tokens.stylex';
|
|
28
|
+
import {SHARED_DATE_FORMAT_OPTIONS} from '../utils/plainDate';
|
|
27
29
|
|
|
28
30
|
const LazyXDSTooltip = lazy(async () =>
|
|
29
31
|
import('../Tooltip/Tooltip').then(mod => ({default: mod.Tooltip})),
|
|
@@ -37,6 +39,8 @@ export type TimestampFormat =
|
|
|
37
39
|
| 'relative'
|
|
38
40
|
| 'auto'
|
|
39
41
|
| 'date'
|
|
42
|
+
| 'date_long'
|
|
43
|
+
| 'date_weekday'
|
|
40
44
|
| 'date_time'
|
|
41
45
|
| 'time'
|
|
42
46
|
| 'system_date'
|
|
@@ -53,6 +57,8 @@ export interface TimestampProps extends BaseProps<HTMLTimeElement> {
|
|
|
53
57
|
* - `'relative'`: "2 hours ago", "yesterday", "now"
|
|
54
58
|
* - `'auto'`: Relative for recent times, `date_time` for older
|
|
55
59
|
* - `'date'`: "Mar 21, 2025"
|
|
60
|
+
* - `'date_long'`: "March 21, 2025"
|
|
61
|
+
* - `'date_weekday'`: "Wed, Mar 21, 2025"
|
|
56
62
|
* - `'date_time'`: "Mar 21, 2025, 2:51 PM"
|
|
57
63
|
* - `'time'`: "2:51 PM"
|
|
58
64
|
* - `'system_date'`: "2025-03-21"
|
|
@@ -249,11 +255,22 @@ function formatTimestamp(
|
|
|
249
255
|
): string {
|
|
250
256
|
switch (format) {
|
|
251
257
|
case 'date':
|
|
252
|
-
return new Intl.DateTimeFormat(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
return new Intl.DateTimeFormat(
|
|
259
|
+
undefined,
|
|
260
|
+
SHARED_DATE_FORMAT_OPTIONS.date,
|
|
261
|
+
).format(date);
|
|
262
|
+
|
|
263
|
+
case 'date_long':
|
|
264
|
+
return new Intl.DateTimeFormat(
|
|
265
|
+
undefined,
|
|
266
|
+
SHARED_DATE_FORMAT_OPTIONS.date_long,
|
|
267
|
+
).format(date);
|
|
268
|
+
|
|
269
|
+
case 'date_weekday':
|
|
270
|
+
return new Intl.DateTimeFormat(
|
|
271
|
+
undefined,
|
|
272
|
+
SHARED_DATE_FORMAT_OPTIONS.date_weekday,
|
|
273
|
+
).format(date);
|
|
257
274
|
|
|
258
275
|
case 'date_time':
|
|
259
276
|
return new Intl.DateTimeFormat(undefined, {
|
|
@@ -415,11 +432,14 @@ export function Timestamp({
|
|
|
415
432
|
return () => clearInterval(timer);
|
|
416
433
|
}, [isLive, isValidDate, effectiveFormat, diffSeconds]);
|
|
417
434
|
|
|
435
|
+
useDevWarning(
|
|
436
|
+
'Timestamp',
|
|
437
|
+
`could not parse value ${JSON.stringify(value)} as a date. Rendering nothing.`,
|
|
438
|
+
!isValidDate,
|
|
439
|
+
);
|
|
440
|
+
|
|
418
441
|
// Placed after all hooks so the hook order stays stable across renders.
|
|
419
442
|
if (!isValidDate) {
|
|
420
|
-
console.warn(
|
|
421
|
-
`Timestamp: could not parse value ${JSON.stringify(value)} as a date. Rendering nothing.`,
|
|
422
|
-
);
|
|
423
443
|
return null;
|
|
424
444
|
}
|
|
425
445
|
|
package/src/Toast/useToast.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import {createRoot} from 'react-dom/client';
|
|
|
7
7
|
import {dataAttr} from '../naming';
|
|
8
8
|
import {ToastContext, type ToastContextValue} from './ToastContext';
|
|
9
9
|
import {ToastViewport} from './ToastViewport';
|
|
10
|
+
import {warnOnce} from '../utils/devWarning';
|
|
10
11
|
import type {
|
|
11
12
|
ToastOptions,
|
|
12
13
|
ToastDismissFn,
|
|
@@ -17,7 +18,6 @@ import type {
|
|
|
17
18
|
// Fallback singleton
|
|
18
19
|
let fallbackContext: ToastContextValue | null = null;
|
|
19
20
|
let fallbackRoot: ReturnType<typeof createRoot> | null = null;
|
|
20
|
-
let fallbackWarned = false;
|
|
21
21
|
|
|
22
22
|
const ROOT_THEME_ATTRS = ['data-theme', dataAttr('theme')] as const;
|
|
23
23
|
|
|
@@ -70,13 +70,12 @@ function getFallbackContext(): ToastContextValue {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
73
|
+
warnOnce(
|
|
74
|
+
'toast-fallback-viewport',
|
|
75
|
+
'useToast',
|
|
76
|
+
'No LayerProvider found. Using fallback viewport. ' +
|
|
77
|
+
'Wrap your app with <LayerProvider> or <AppShell> for full control.',
|
|
78
|
+
);
|
|
80
79
|
|
|
81
80
|
const container = document.createElement('div');
|
|
82
81
|
container.setAttribute('data-astryx-toast-fallback', '');
|
|
@@ -41,7 +41,7 @@ interface ToggleButtonGroupContextValue {
|
|
|
41
41
|
isDisabled?: boolean;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const ToggleButtonGroupContext =
|
|
45
45
|
createContext<ToggleButtonGroupContextValue | null>(null);
|
|
46
46
|
ToggleButtonGroupContext.displayName = 'ToggleButtonGroupContext';
|
|
47
47
|
|
|
@@ -31,7 +31,7 @@ export const docs = {
|
|
|
31
31
|
name: 'onChange',
|
|
32
32
|
type: '(items: T[], change: TokenizerChange<T>) => void',
|
|
33
33
|
description:
|
|
34
|
-
"Called when selection changes. The change argument includes the affected item and type ('add' | 'create' | 'remove' | 'reorder').",
|
|
34
|
+
"Called when selection changes. The change argument includes the affected item and type ('add' | 'create' | 'remove' | 'reorder'). Additions and removals (including Backspace on an empty input) are announced to screen readers via a polite live region.",
|
|
35
35
|
required: true,
|
|
36
36
|
},
|
|
37
37
|
{
|
|
@@ -165,6 +165,13 @@ export const docs = {
|
|
|
165
165
|
type: '(query: string) => void',
|
|
166
166
|
description: 'Callback fired when the search query text changes.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
name: 'startIcon',
|
|
170
|
+
type: 'ReactNode | IconType',
|
|
171
|
+
description:
|
|
172
|
+
'Icon to display at the start of the input, before any tokens. Accepts a semantic icon name, an SVG icon component, or a ReactNode directly.',
|
|
173
|
+
slotElements: [{__element: 'Icon', props: {icon: 'search', size: 'sm'}}],
|
|
174
|
+
},
|
|
168
175
|
{
|
|
169
176
|
name: 'endContent',
|
|
170
177
|
type: 'ReactNode',
|
|
@@ -370,6 +377,12 @@ export const docsZh = {
|
|
|
370
377
|
type: '(query: string) => void',
|
|
371
378
|
description: '\u641c\u7d22\u67e5\u8be2\u6587\u672c\u53d8\u66f4\u65f6\u89e6\u53d1\u7684\u56de\u8c03\u3002',
|
|
372
379
|
},
|
|
380
|
+
{
|
|
381
|
+
name: 'startIcon',
|
|
382
|
+
type: 'ReactNode | IconType',
|
|
383
|
+
description:
|
|
384
|
+
'\u5728\u8f93\u5165\u6846\u5f00\u5934\uff08token \u4e4b\u524d\uff09\u663e\u793a\u7684\u56fe\u6807\u3002\u63a5\u53d7\u8bed\u4e49\u56fe\u6807\u540d\u79f0\u3001SVG \u56fe\u6807\u7ec4\u4ef6\u6216\u76f4\u63a5\u4f20\u5165 ReactNode\u3002',
|
|
385
|
+
},
|
|
373
386
|
{
|
|
374
387
|
name: 'endContent',
|
|
375
388
|
type: 'ReactNode',
|
|
@@ -460,6 +473,7 @@ export const docsDense = {
|
|
|
460
473
|
size: 'Input+token size.',
|
|
461
474
|
debounceMs: 'Search debounce delay ms. 0 for sync sources.',
|
|
462
475
|
onChangeQuery: 'Fired on search query text change.',
|
|
476
|
+
startIcon: 'Icon at input start, before tokens. Icon name, SVG component, or ReactNode.',
|
|
463
477
|
endContent: 'Content at input row end. For buttons, counts, controls.',
|
|
464
478
|
handleRef: 'Imperative handle for focus() and blur() control.',
|
|
465
479
|
xstyle: 'StyleX layout styles (margins, positioning). Must be stylex.create() value.',
|
|
@@ -9,11 +9,17 @@
|
|
|
9
9
|
* SYNC: When Tokenizer.tsx changes, update tests to match
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
|
|
12
|
+
import {describe, it, expect, vi, beforeAll, afterAll, afterEach} from 'vitest';
|
|
13
13
|
import {render, screen, fireEvent, act, waitFor} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {Tokenizer} from './Tokenizer';
|
|
16
|
+
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
16
17
|
import type {SearchSource, SearchableItem} from '../Typeahead/types';
|
|
18
|
+
import {TestIcon} from '../__tests__/TestIcon';
|
|
19
|
+
|
|
20
|
+
function politeRegion(): HTMLElement | null {
|
|
21
|
+
return document.querySelector('[data-astryx-live-region="polite"]');
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
// Store original matches to restore later
|
|
19
25
|
const originalMatches = HTMLElement.prototype.matches;
|
|
@@ -60,6 +66,10 @@ afterAll(() => {
|
|
|
60
66
|
(HTMLElement.prototype as any).matches = originalMatches;
|
|
61
67
|
});
|
|
62
68
|
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
__resetLiveRegionsForTest();
|
|
71
|
+
});
|
|
72
|
+
|
|
63
73
|
// Test data
|
|
64
74
|
const users: SearchableItem[] = [
|
|
65
75
|
{id: '1', label: 'Alice'},
|
|
@@ -818,6 +828,51 @@ describe('Tokenizer', () => {
|
|
|
818
828
|
});
|
|
819
829
|
});
|
|
820
830
|
|
|
831
|
+
describe('startIcon', () => {
|
|
832
|
+
it('does not render a start icon when omitted', () => {
|
|
833
|
+
render(
|
|
834
|
+
<Tokenizer
|
|
835
|
+
label="Members"
|
|
836
|
+
searchSource={userSource}
|
|
837
|
+
value={[]}
|
|
838
|
+
onChange={() => {}}
|
|
839
|
+
/>,
|
|
840
|
+
);
|
|
841
|
+
expect(document.querySelector('svg')).not.toBeInTheDocument();
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('renders a ReactNode start icon before the tokens', () => {
|
|
845
|
+
render(
|
|
846
|
+
<Tokenizer
|
|
847
|
+
label="Members"
|
|
848
|
+
searchSource={userSource}
|
|
849
|
+
value={[{id: '1', label: 'Alice'}]}
|
|
850
|
+
onChange={() => {}}
|
|
851
|
+
startIcon={<TestIcon data-testid="start-icon" />}
|
|
852
|
+
/>,
|
|
853
|
+
);
|
|
854
|
+
const icon = screen.getByTestId('start-icon');
|
|
855
|
+
const token = screen.getByText('Alice');
|
|
856
|
+
expect(icon).toBeInTheDocument();
|
|
857
|
+
expect(
|
|
858
|
+
icon.compareDocumentPosition(token) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
859
|
+
).toBeTruthy();
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it('renders an IconType (SVG component) start icon', () => {
|
|
863
|
+
render(
|
|
864
|
+
<Tokenizer
|
|
865
|
+
label="Members"
|
|
866
|
+
searchSource={userSource}
|
|
867
|
+
value={[]}
|
|
868
|
+
onChange={() => {}}
|
|
869
|
+
startIcon={TestIcon}
|
|
870
|
+
/>,
|
|
871
|
+
);
|
|
872
|
+
expect(document.querySelector('svg')).toBeInTheDocument();
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
|
|
821
876
|
describe('disabledMessage', () => {
|
|
822
877
|
const h = {hidden: true} as const;
|
|
823
878
|
const isOpen = (el: Element) => el.matches(':popover-open');
|
|
@@ -921,6 +976,130 @@ describe('Tokenizer', () => {
|
|
|
921
976
|
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
922
977
|
});
|
|
923
978
|
});
|
|
979
|
+
describe('announcements', () => {
|
|
980
|
+
it('announces removal politely on Backspace with an empty input', async () => {
|
|
981
|
+
const onChange = vi.fn();
|
|
982
|
+
render(
|
|
983
|
+
<Tokenizer
|
|
984
|
+
label="Members"
|
|
985
|
+
searchSource={userSource}
|
|
986
|
+
value={[users[0], users[1]]}
|
|
987
|
+
onChange={onChange}
|
|
988
|
+
/>,
|
|
989
|
+
);
|
|
990
|
+
const input = screen.getByRole('combobox');
|
|
991
|
+
fireEvent.keyDown(input, {key: 'Backspace'});
|
|
992
|
+
expect(onChange).toHaveBeenCalledWith([users[0]], {
|
|
993
|
+
item: users[1],
|
|
994
|
+
type: 'remove',
|
|
995
|
+
});
|
|
996
|
+
await waitFor(() => {
|
|
997
|
+
expect(politeRegion()).toHaveTextContent('Removed Bob');
|
|
998
|
+
});
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
it("announces removal politely when clicking a token's remove button", async () => {
|
|
1002
|
+
render(
|
|
1003
|
+
<Tokenizer
|
|
1004
|
+
label="Members"
|
|
1005
|
+
searchSource={userSource}
|
|
1006
|
+
value={[users[0], users[1]]}
|
|
1007
|
+
onChange={() => {}}
|
|
1008
|
+
/>,
|
|
1009
|
+
);
|
|
1010
|
+
fireEvent.click(screen.getByRole('button', {name: 'Remove Alice'}));
|
|
1011
|
+
await waitFor(() => {
|
|
1012
|
+
expect(politeRegion()).toHaveTextContent('Removed Alice');
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
it('announces addition politely when selecting a search result', async () => {
|
|
1017
|
+
render(
|
|
1018
|
+
<Tokenizer
|
|
1019
|
+
label="Members"
|
|
1020
|
+
searchSource={userSource}
|
|
1021
|
+
value={[]}
|
|
1022
|
+
onChange={() => {}}
|
|
1023
|
+
hasEntriesOnFocus
|
|
1024
|
+
debounceMs={0}
|
|
1025
|
+
/>,
|
|
1026
|
+
);
|
|
1027
|
+
const input = screen.getByRole('combobox');
|
|
1028
|
+
fireEvent.focus(input);
|
|
1029
|
+
await act(async () => {
|
|
1030
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1031
|
+
});
|
|
1032
|
+
fireEvent.click(screen.getByText('Alice'));
|
|
1033
|
+
await waitFor(() => {
|
|
1034
|
+
expect(politeRegion()).toHaveTextContent('Added Alice');
|
|
1035
|
+
});
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
it('announces addition politely when creating a token with hasCreate', async () => {
|
|
1039
|
+
const emptySource: SearchSource = {
|
|
1040
|
+
search: () => [],
|
|
1041
|
+
bootstrap: () => [],
|
|
1042
|
+
};
|
|
1043
|
+
render(
|
|
1044
|
+
<Tokenizer
|
|
1045
|
+
label="Tags"
|
|
1046
|
+
searchSource={emptySource}
|
|
1047
|
+
value={[]}
|
|
1048
|
+
onChange={() => {}}
|
|
1049
|
+
hasCreate
|
|
1050
|
+
debounceMs={0}
|
|
1051
|
+
/>,
|
|
1052
|
+
);
|
|
1053
|
+
const input = screen.getByRole('combobox');
|
|
1054
|
+
await act(async () => {
|
|
1055
|
+
fireEvent.change(input, {target: {value: 'new-tag'}});
|
|
1056
|
+
});
|
|
1057
|
+
await act(async () => {
|
|
1058
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1059
|
+
});
|
|
1060
|
+
fireEvent.click(screen.getByText('Create "new-tag"'));
|
|
1061
|
+
await waitFor(() => {
|
|
1062
|
+
expect(politeRegion()).toHaveTextContent('Added new-tag');
|
|
1063
|
+
});
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
it('does not announce on mount', () => {
|
|
1067
|
+
render(
|
|
1068
|
+
<Tokenizer
|
|
1069
|
+
label="Members"
|
|
1070
|
+
searchSource={userSource}
|
|
1071
|
+
value={[users[0]]}
|
|
1072
|
+
onChange={() => {}}
|
|
1073
|
+
/>,
|
|
1074
|
+
);
|
|
1075
|
+
// The live regions are created lazily on first announce, so a mount
|
|
1076
|
+
// with pre-selected tokens must not create (or speak through) one.
|
|
1077
|
+
expect(politeRegion()).toBeNull();
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it('does not announce add/remove while typing', async () => {
|
|
1081
|
+
render(
|
|
1082
|
+
<Tokenizer
|
|
1083
|
+
label="Members"
|
|
1084
|
+
searchSource={userSource}
|
|
1085
|
+
value={[]}
|
|
1086
|
+
onChange={() => {}}
|
|
1087
|
+
debounceMs={0}
|
|
1088
|
+
/>,
|
|
1089
|
+
);
|
|
1090
|
+
const input = screen.getByRole('combobox');
|
|
1091
|
+
await act(async () => {
|
|
1092
|
+
fireEvent.change(input, {target: {value: 'Ali'}});
|
|
1093
|
+
});
|
|
1094
|
+
await act(async () => {
|
|
1095
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1096
|
+
});
|
|
1097
|
+
// BaseTypeahead announces result counts while typing (existing
|
|
1098
|
+
// behavior); typing alone must not produce add/remove announcements.
|
|
1099
|
+
expect(politeRegion()?.textContent ?? '').not.toMatch(/Added|Removed/);
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
|
|
924
1103
|
describe('form participation', () => {
|
|
925
1104
|
it('submits one entry per token id under htmlName', () => {
|
|
926
1105
|
const {container} = render(
|
|
@@ -951,7 +1130,9 @@ describe('Tokenizer', () => {
|
|
|
951
1130
|
/>
|
|
952
1131
|
</form>,
|
|
953
1132
|
);
|
|
954
|
-
expect([
|
|
1133
|
+
expect([
|
|
1134
|
+
...new FormData(container.querySelector('form')!).keys(),
|
|
1135
|
+
]).toEqual([]);
|
|
955
1136
|
});
|
|
956
1137
|
});
|
|
957
1138
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Tokenizer.tsx
|
|
7
|
-
* @input Uses React, BaseTypeahead, Field, Token
|
|
7
|
+
* @input Uses React, BaseTypeahead, Field, Token, useAnnounce
|
|
8
8
|
* @output Exports Tokenizer multi-select typeahead component
|
|
9
9
|
* @position Composed component; forwards DOM ref and exposes focus control via
|
|
10
10
|
* handleRef
|
|
@@ -43,6 +43,7 @@ import {renderIconSlot, type IconType} from '../Icon';
|
|
|
43
43
|
import {OverflowList} from '../OverflowList';
|
|
44
44
|
import {useLayer} from '../Layer/useLayer';
|
|
45
45
|
import {useTooltip} from '../Tooltip';
|
|
46
|
+
import {useAnnounce} from '../hooks/useAnnounce';
|
|
46
47
|
import {
|
|
47
48
|
colorVars,
|
|
48
49
|
spacingVars,
|
|
@@ -560,6 +561,12 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
560
561
|
[],
|
|
561
562
|
);
|
|
562
563
|
|
|
564
|
+
// Announce token add/remove politely via the persistent live region.
|
|
565
|
+
// Tokens previously appeared and disappeared silently — Backspace on an
|
|
566
|
+
// empty input removes the trailing token, and the per-token remove buttons
|
|
567
|
+
// gave no audible feedback either.
|
|
568
|
+
const announce = useAnnounce();
|
|
569
|
+
|
|
563
570
|
// Handle adding an item — detect creatable synthetic items
|
|
564
571
|
const handleAdd = useCallback(
|
|
565
572
|
(item: T | null) => {
|
|
@@ -584,6 +591,7 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
584
591
|
const realItem = base as T;
|
|
585
592
|
const newItems = [...value, realItem];
|
|
586
593
|
onChange(newItems, {item: realItem, type: 'create'});
|
|
594
|
+
announce(`Added ${createdValue}`);
|
|
587
595
|
return;
|
|
588
596
|
}
|
|
589
597
|
|
|
@@ -592,18 +600,22 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
592
600
|
}
|
|
593
601
|
const newItems = [...value, item];
|
|
594
602
|
onChange(newItems, {item, type: 'add'});
|
|
603
|
+
announce(`Added ${item.label}`);
|
|
595
604
|
},
|
|
596
|
-
[value, onChange, isAtMax, selectedIds, hasCreate],
|
|
605
|
+
[value, onChange, isAtMax, selectedIds, hasCreate, announce],
|
|
597
606
|
);
|
|
598
607
|
|
|
599
|
-
// Handle removing an item
|
|
608
|
+
// Handle removing an item. Single removal path: both Backspace on an empty
|
|
609
|
+
// input and the per-token remove buttons route through here, so the
|
|
610
|
+
// announcement covers both.
|
|
600
611
|
const handleRemove = useCallback(
|
|
601
612
|
(item: T) => {
|
|
602
613
|
const newItems = value.filter(v => v.id !== item.id);
|
|
603
614
|
onChange(newItems, {item, type: 'remove'});
|
|
615
|
+
announce(`Removed ${item.label}`);
|
|
604
616
|
inputRef.current?.focus();
|
|
605
617
|
},
|
|
606
|
-
[value, onChange],
|
|
618
|
+
[value, onChange, announce],
|
|
607
619
|
);
|
|
608
620
|
|
|
609
621
|
// Handle clearing all items
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi} from 'vitest';
|
|
13
|
-
import {render, screen} from '@testing-library/react';
|
|
13
|
+
import {fireEvent, render, screen} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {TopNav} from './TopNav';
|
|
16
16
|
import {TopNavRenderContext} from './TopNavRenderContext';
|
|
@@ -297,6 +297,85 @@ describe('TopNavHeading', () => {
|
|
|
297
297
|
render(<TopNavHeading heading="Test" ref={ref} />);
|
|
298
298
|
expect(ref).toHaveBeenCalledWith(expect.any(HTMLElement));
|
|
299
299
|
});
|
|
300
|
+
|
|
301
|
+
// ===========================================================================
|
|
302
|
+
// Menu popover semantics — the popup must not be announced as a modal
|
|
303
|
+
// dialog, and role="menu" must be scoped to the actual menu items so the
|
|
304
|
+
// heading button is not an invalid child of the menu.
|
|
305
|
+
// ===========================================================================
|
|
306
|
+
|
|
307
|
+
// The popover layer keeps `popover` content display:none in jsdom even
|
|
308
|
+
// when open, hiding it from role queries — so these tests assert the
|
|
309
|
+
// popup's ARIA semantics at the DOM level instead.
|
|
310
|
+
describe('menu popover semantics', () => {
|
|
311
|
+
const menuItems = (
|
|
312
|
+
<>
|
|
313
|
+
<div role="menuitem">Alpha</div>
|
|
314
|
+
<div role="menuitem">Beta</div>
|
|
315
|
+
</>
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
it('does not wrap the heading menu popup in a modal dialog', async () => {
|
|
319
|
+
const user = userEvent.setup();
|
|
320
|
+
render(<TopNavHeading heading="My App" menu={menuItems} />);
|
|
321
|
+
await user.click(screen.getByRole('button', {name: 'Open menu'}));
|
|
322
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
323
|
+
expect(document.querySelector('[aria-modal="true"]')).toBeNull();
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('scopes role="menu" to only menuitem children with an accessible name', async () => {
|
|
327
|
+
const user = userEvent.setup();
|
|
328
|
+
render(<TopNavHeading heading="My App" menu={menuItems} />);
|
|
329
|
+
await user.click(screen.getByRole('button', {name: 'Open menu'}));
|
|
330
|
+
const menu = document.querySelector('[role="menu"]');
|
|
331
|
+
expect(menu).not.toBeNull();
|
|
332
|
+
expect(menu).toHaveAttribute('aria-label', 'My App');
|
|
333
|
+
const children = Array.from(menu!.children);
|
|
334
|
+
expect(children.length).toBeGreaterThan(0);
|
|
335
|
+
for (const child of children) {
|
|
336
|
+
expect(child).toHaveAttribute('role', 'menuitem');
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('keeps the popup heading button outside the menu and closes on click', async () => {
|
|
341
|
+
const user = userEvent.setup();
|
|
342
|
+
render(<TopNavHeading heading="My App" menu={menuItems} />);
|
|
343
|
+
const trigger = screen.getByRole('button', {name: 'Open menu'});
|
|
344
|
+
await user.click(trigger);
|
|
345
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
346
|
+
const menu = document.querySelector('[role="menu"]');
|
|
347
|
+
expect(menu).not.toBeNull();
|
|
348
|
+
// The heading replica button in the popup is a sibling of the menu,
|
|
349
|
+
// not an invalid menu child.
|
|
350
|
+
const headingButton = Array.from(
|
|
351
|
+
document.querySelectorAll('button'),
|
|
352
|
+
).find(b => b !== trigger && b.textContent?.includes('My App'));
|
|
353
|
+
expect(headingButton).toBeDefined();
|
|
354
|
+
expect(menu!.contains(headingButton!)).toBe(false);
|
|
355
|
+
// Clicking it still closes the popup.
|
|
356
|
+
fireEvent.click(headingButton!);
|
|
357
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('applies the same semantics in mixed mode (menu + hrefs)', async () => {
|
|
361
|
+
const user = userEvent.setup();
|
|
362
|
+
render(
|
|
363
|
+
<TopNavHeading
|
|
364
|
+
heading="Product"
|
|
365
|
+
headingHref="/product"
|
|
366
|
+
menu={menuItems}
|
|
367
|
+
/>,
|
|
368
|
+
);
|
|
369
|
+
await user.click(screen.getByRole('button', {name: 'Open menu'}));
|
|
370
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
371
|
+
const menu = document.querySelector('[role="menu"]');
|
|
372
|
+
expect(menu).not.toBeNull();
|
|
373
|
+
expect(menu).toHaveAttribute('aria-label', 'Product');
|
|
374
|
+
for (const child of Array.from(menu!.children)) {
|
|
375
|
+
expect(child).toHaveAttribute('role', 'menuitem');
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
});
|
|
300
379
|
});
|
|
301
380
|
|
|
302
381
|
describe('NavIcon', () => {
|
|
@@ -444,4 +523,49 @@ describe('TopNavItem', () => {
|
|
|
444
523
|
expect(card).toHaveAttribute('aria-label', 'Featured');
|
|
445
524
|
});
|
|
446
525
|
});
|
|
526
|
+
|
|
527
|
+
describe('TopNavMegaMenuFeaturedCard image alt handling', () => {
|
|
528
|
+
it('marks the image as explicitly decorative when imageAlt is omitted', () => {
|
|
529
|
+
render(
|
|
530
|
+
<TopNavMegaMenuFeaturedCard
|
|
531
|
+
title="What's new"
|
|
532
|
+
image="/promo.jpg"
|
|
533
|
+
data-testid="featured-card"
|
|
534
|
+
/>,
|
|
535
|
+
);
|
|
536
|
+
const img = screen.getByTestId('featured-card').querySelector('img');
|
|
537
|
+
expect(img).toHaveAttribute('alt', '');
|
|
538
|
+
expect(img).toHaveAttribute('role', 'presentation');
|
|
539
|
+
expect(img).toHaveAttribute('aria-hidden', 'true');
|
|
540
|
+
// The image must not be exposed to assistive technology as a nameless img.
|
|
541
|
+
expect(screen.queryByRole('img')).toBeNull();
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('exposes the image with normal img semantics when imageAlt is provided', () => {
|
|
545
|
+
render(
|
|
546
|
+
<TopNavMegaMenuFeaturedCard
|
|
547
|
+
title="What's new"
|
|
548
|
+
image="/promo.jpg"
|
|
549
|
+
imageAlt="Team collaboration"
|
|
550
|
+
/>,
|
|
551
|
+
);
|
|
552
|
+
const img = screen.getByRole('img', {name: 'Team collaboration'});
|
|
553
|
+
expect(img).not.toHaveAttribute('role');
|
|
554
|
+
expect(img).not.toHaveAttribute('aria-hidden');
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('keeps title and link semantics unchanged with a decorative image', () => {
|
|
558
|
+
render(
|
|
559
|
+
<TopNavMegaMenuFeaturedCard
|
|
560
|
+
title="What's new"
|
|
561
|
+
image="/promo.jpg"
|
|
562
|
+
linkLabel="Read the announcement"
|
|
563
|
+
linkHref="/blog/v4"
|
|
564
|
+
/>,
|
|
565
|
+
);
|
|
566
|
+
expect(screen.getByText("What's new")).toBeInTheDocument();
|
|
567
|
+
const link = screen.getByRole('link', {name: /Read the announcement/});
|
|
568
|
+
expect(link).toHaveAttribute('href', '/blog/v4');
|
|
569
|
+
});
|
|
570
|
+
});
|
|
447
571
|
});
|