@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
package/src/Item/Item.test.tsx
CHANGED
|
@@ -31,18 +31,13 @@ describe('Item', () => {
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
it('renders marker', () => {
|
|
34
|
-
render(
|
|
35
|
-
<Item label="Item" marker={<span data-testid="marker">•</span>} />,
|
|
36
|
-
);
|
|
34
|
+
render(<Item label="Item" marker={<span data-testid="marker">•</span>} />);
|
|
37
35
|
expect(screen.getByTestId('marker')).toBeInTheDocument();
|
|
38
36
|
});
|
|
39
37
|
|
|
40
38
|
it('renders startContent', () => {
|
|
41
39
|
render(
|
|
42
|
-
<Item
|
|
43
|
-
label="Item"
|
|
44
|
-
startContent={<span data-testid="avatar">A</span>}
|
|
45
|
-
/>,
|
|
40
|
+
<Item label="Item" startContent={<span data-testid="avatar">A</span>} />,
|
|
46
41
|
);
|
|
47
42
|
expect(screen.getByTestId('avatar')).toBeInTheDocument();
|
|
48
43
|
});
|
|
@@ -255,12 +250,7 @@ describe('Item', () => {
|
|
|
255
250
|
it('does not fire onClick when disabled item is clicked', async () => {
|
|
256
251
|
const onClick = vi.fn();
|
|
257
252
|
render(
|
|
258
|
-
<Item
|
|
259
|
-
label="Disabled"
|
|
260
|
-
onClick={onClick}
|
|
261
|
-
isDisabled
|
|
262
|
-
data-testid="item"
|
|
263
|
-
/>,
|
|
253
|
+
<Item label="Disabled" onClick={onClick} isDisabled data-testid="item" />,
|
|
264
254
|
);
|
|
265
255
|
const item = screen.getByTestId('item');
|
|
266
256
|
item.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
|
@@ -276,14 +266,46 @@ describe('Item', () => {
|
|
|
276
266
|
// Selected state
|
|
277
267
|
// ===========================================================================
|
|
278
268
|
|
|
279
|
-
it('
|
|
269
|
+
it('conveys selection via aria-current on the default div root', () => {
|
|
270
|
+
// aria-selected is invalid ARIA on a generic div (axe: aria-allowed-attr),
|
|
271
|
+
// so selection is exposed via aria-current, which is valid on any element.
|
|
280
272
|
render(<Item label="Selected" isSelected data-testid="item" />);
|
|
281
|
-
|
|
273
|
+
const item = screen.getByTestId('item');
|
|
274
|
+
expect(item).not.toHaveAttribute('aria-selected');
|
|
275
|
+
expect(item).toHaveAttribute('aria-current', 'true');
|
|
282
276
|
});
|
|
283
277
|
|
|
284
|
-
it('
|
|
285
|
-
render(
|
|
286
|
-
|
|
278
|
+
it('applies aria-selected (not aria-current) when the role permits it', () => {
|
|
279
|
+
render(
|
|
280
|
+
<Item label="Selected" isSelected role="option" data-testid="item" />,
|
|
281
|
+
);
|
|
282
|
+
const item = screen.getByTestId('item');
|
|
283
|
+
expect(item).toHaveAttribute('aria-selected', 'true');
|
|
284
|
+
// A permitted role uses aria-selected; aria-current would be redundant.
|
|
285
|
+
expect(item).not.toHaveAttribute('aria-current');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('falls back to aria-current when the role does not permit aria-selected', () => {
|
|
289
|
+
render(
|
|
290
|
+
<Item label="Selected" isSelected role="menuitem" data-testid="item" />,
|
|
291
|
+
);
|
|
292
|
+
const item = screen.getByTestId('item');
|
|
293
|
+
expect(item).not.toHaveAttribute('aria-selected');
|
|
294
|
+
expect(item).toHaveAttribute('aria-current', 'true');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('applies neither aria-selected nor aria-current when not selected', () => {
|
|
298
|
+
render(<Item label="Not Selected" role="option" data-testid="item" />);
|
|
299
|
+
const item = screen.getByTestId('item');
|
|
300
|
+
expect(item).not.toHaveAttribute('aria-selected');
|
|
301
|
+
expect(item).not.toHaveAttribute('aria-current');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('lets a consumer-provided aria-current win over the selection default', () => {
|
|
305
|
+
render(
|
|
306
|
+
<Item label="Step" isSelected aria-current="step" data-testid="item" />,
|
|
307
|
+
);
|
|
308
|
+
expect(screen.getByTestId('item')).toHaveAttribute('aria-current', 'step');
|
|
287
309
|
});
|
|
288
310
|
|
|
289
311
|
// ===========================================================================
|
package/src/Item/Item.tsx
CHANGED
|
@@ -131,7 +131,12 @@ export interface ItemProps extends BaseProps<HTMLElement> {
|
|
|
131
131
|
isHighlighted?: boolean;
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Selected state.
|
|
134
|
+
* Selected state. Always applies the selected visual styling. When `role`
|
|
135
|
+
* permits it (option, tab, row, gridcell, columnheader, rowheader, treeitem)
|
|
136
|
+
* the state is exposed as `aria-selected`; otherwise (e.g. a listitem or a
|
|
137
|
+
* bare div, where `aria-selected` is invalid ARIA) it falls back to
|
|
138
|
+
* `aria-current="true"` so assistive tech is still told which item is
|
|
139
|
+
* selected. A consumer-provided `aria-current` always wins.
|
|
135
140
|
* @default false
|
|
136
141
|
*/
|
|
137
142
|
isSelected?: boolean;
|
|
@@ -148,6 +153,24 @@ export interface ItemProps extends BaseProps<HTMLElement> {
|
|
|
148
153
|
'data-testid'?: string;
|
|
149
154
|
}
|
|
150
155
|
|
|
156
|
+
// =============================================================================
|
|
157
|
+
// Constants
|
|
158
|
+
// =============================================================================
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Roles on which WAI-ARIA permits the aria-selected attribute.
|
|
162
|
+
* https://www.w3.org/TR/wai-aria-1.2/#aria-selected
|
|
163
|
+
*/
|
|
164
|
+
const ARIA_SELECTED_ROLES = new Set([
|
|
165
|
+
'option',
|
|
166
|
+
'tab',
|
|
167
|
+
'row',
|
|
168
|
+
'gridcell',
|
|
169
|
+
'columnheader',
|
|
170
|
+
'rowheader',
|
|
171
|
+
'treeitem',
|
|
172
|
+
]);
|
|
173
|
+
|
|
151
174
|
// =============================================================================
|
|
152
175
|
// Styles
|
|
153
176
|
// =============================================================================
|
|
@@ -346,6 +369,11 @@ export function Item({
|
|
|
346
369
|
// handles keyboard access. Skip the invisible button/anchor and put
|
|
347
370
|
// onClick directly on the root element instead.
|
|
348
371
|
const hasParentRole = role != null;
|
|
372
|
+
// aria-selected is only valid on selectable roles (option, tab, treeitem,
|
|
373
|
+
// grid cells). On the default div/li root the attribute is invalid ARIA
|
|
374
|
+
// (axe: aria-allowed-attr), so selection stays visual-only there — callers
|
|
375
|
+
// that need selection semantics pass a permitted role.
|
|
376
|
+
const allowsAriaSelected = role != null && ARIA_SELECTED_ROLES.has(role);
|
|
349
377
|
|
|
350
378
|
const isStringLabel = typeof label === 'string';
|
|
351
379
|
const isStringDescription = typeof description === 'string';
|
|
@@ -471,7 +499,15 @@ export function Item({
|
|
|
471
499
|
<Component
|
|
472
500
|
ref={ref as React.Ref<never>}
|
|
473
501
|
{...restProps}
|
|
474
|
-
aria-selected={isSelected || undefined}
|
|
502
|
+
aria-selected={(allowsAriaSelected && isSelected) || undefined}
|
|
503
|
+
// aria-selected is invalid on roles that don't permit it (listitem, a
|
|
504
|
+
// bare div, etc.). For those, convey selection via aria-current — valid
|
|
505
|
+
// on any element — so the state still reaches AT. Written after
|
|
506
|
+
// {...restProps} so it must defer to a consumer-provided aria-current.
|
|
507
|
+
aria-current={
|
|
508
|
+
restProps['aria-current'] ??
|
|
509
|
+
(isSelected && !allowsAriaSelected ? true : undefined)
|
|
510
|
+
}
|
|
475
511
|
aria-disabled={isDisabled || undefined}
|
|
476
512
|
{...mergeProps(
|
|
477
513
|
themeProps('item', {density, align}),
|
package/src/Layer/useLayer.tsx
CHANGED
|
@@ -100,6 +100,12 @@ export interface ContextRenderProps {
|
|
|
100
100
|
* non-hashed selector for the layer.
|
|
101
101
|
*/
|
|
102
102
|
role?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Accessible name applied to the popover container via `aria-label`.
|
|
105
|
+
* Pair with `role` so layers with a named role (e.g. `'dialog'`) expose a
|
|
106
|
+
* proper name to assistive technology.
|
|
107
|
+
*/
|
|
108
|
+
'aria-label'?: string;
|
|
103
109
|
/**
|
|
104
110
|
* StyleX styles for the popover container.
|
|
105
111
|
*/
|
|
@@ -525,6 +531,7 @@ export function useLayer(
|
|
|
525
531
|
alignment = 'center',
|
|
526
532
|
positioning = 'anchor',
|
|
527
533
|
role,
|
|
534
|
+
'aria-label': ariaLabel,
|
|
528
535
|
xstyle,
|
|
529
536
|
className: extraClassName,
|
|
530
537
|
style: extraStyle,
|
|
@@ -561,6 +568,7 @@ export function useLayer(
|
|
|
561
568
|
ref={popoverRefCallback}
|
|
562
569
|
id={id}
|
|
563
570
|
role={role}
|
|
571
|
+
aria-label={ariaLabel}
|
|
564
572
|
popover={lightDismiss ? 'auto' : 'manual'}
|
|
565
573
|
className={combinedClassName}
|
|
566
574
|
style={{...stylexResult.style, ...anchorStyle, ...extraStyle}}
|
package/src/List/List.test.tsx
CHANGED
|
@@ -436,11 +436,7 @@ describe('List', () => {
|
|
|
436
436
|
it('sets target on anchor when provided', () => {
|
|
437
437
|
const {container} = render(
|
|
438
438
|
<List>
|
|
439
|
-
<ListItem
|
|
440
|
-
label="External"
|
|
441
|
-
href="https://example.com"
|
|
442
|
-
target="_blank"
|
|
443
|
-
/>
|
|
439
|
+
<ListItem label="External" href="https://example.com" target="_blank" />
|
|
444
440
|
</List>,
|
|
445
441
|
);
|
|
446
442
|
const anchor = container.querySelector('a');
|
|
@@ -500,17 +496,21 @@ describe('List', () => {
|
|
|
500
496
|
// Selected state
|
|
501
497
|
// ===========================================================================
|
|
502
498
|
|
|
503
|
-
it('
|
|
499
|
+
it('conveys selection via aria-current when isSelected', () => {
|
|
500
|
+
// aria-selected is not permitted on an li (role listitem, axe:
|
|
501
|
+
// aria-allowed-attr), so selection is exposed via aria-current — valid on
|
|
502
|
+
// any element — so screen-reader users are still told which item is chosen.
|
|
504
503
|
const {container} = render(
|
|
505
504
|
<List>
|
|
506
505
|
<ListItem label="Selected" isSelected onClick={() => {}} />
|
|
507
506
|
</List>,
|
|
508
507
|
);
|
|
509
508
|
const item = container.querySelector('.astryx-item');
|
|
510
|
-
expect(item).toHaveAttribute('aria-selected'
|
|
509
|
+
expect(item).not.toHaveAttribute('aria-selected');
|
|
510
|
+
expect(item).toHaveAttribute('aria-current', 'true');
|
|
511
511
|
});
|
|
512
512
|
|
|
513
|
-
it('
|
|
513
|
+
it('applies neither aria-selected nor aria-current when not selected', () => {
|
|
514
514
|
const {container} = render(
|
|
515
515
|
<List>
|
|
516
516
|
<ListItem label="Not Selected" />
|
|
@@ -518,6 +518,7 @@ describe('List', () => {
|
|
|
518
518
|
);
|
|
519
519
|
const li = container.querySelector('li');
|
|
520
520
|
expect(li).not.toHaveAttribute('aria-selected');
|
|
521
|
+
expect(li).not.toHaveAttribute('aria-current');
|
|
521
522
|
});
|
|
522
523
|
|
|
523
524
|
// ===========================================================================
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
import {
|
|
30
30
|
useCallback,
|
|
31
31
|
useEffect,
|
|
32
|
+
useId,
|
|
32
33
|
useMemo,
|
|
33
34
|
useRef,
|
|
34
35
|
useState,
|
|
@@ -295,6 +296,10 @@ export function MobileNav({
|
|
|
295
296
|
// Read from AppShell context as fallback
|
|
296
297
|
const appShellMobile = useAppShellMobile();
|
|
297
298
|
const isOpen = isOpenProp ?? appShellMobile.isMobileNavOpen;
|
|
299
|
+
// Share the id from AppShell context so the toggle's aria-controls resolves to
|
|
300
|
+
// this drawer; fall back to a locally generated id when used standalone.
|
|
301
|
+
const fallbackId = useId();
|
|
302
|
+
const dialogId = appShellMobile.mobileNavId || fallbackId;
|
|
298
303
|
const onOpenChange = useMemo(
|
|
299
304
|
() =>
|
|
300
305
|
onOpenChangeProp ??
|
|
@@ -411,6 +416,7 @@ export function MobileNav({
|
|
|
411
416
|
return (
|
|
412
417
|
<dialog
|
|
413
418
|
ref={mergeRefs(ref, dialogRef)}
|
|
419
|
+
id={dialogId}
|
|
414
420
|
{...mergeProps(
|
|
415
421
|
themeProps('mobile-nav', {side: resolvedSide}),
|
|
416
422
|
stylex.props(
|
|
@@ -425,7 +431,12 @@ export function MobileNav({
|
|
|
425
431
|
)}
|
|
426
432
|
{...rest}
|
|
427
433
|
data-testid={testId}
|
|
428
|
-
aria-label={
|
|
434
|
+
aria-label={
|
|
435
|
+
label ??
|
|
436
|
+
(typeof header === 'string'
|
|
437
|
+
? header
|
|
438
|
+
: t('@astryx.mobileNav.navigation'))
|
|
439
|
+
}
|
|
429
440
|
onClick={composeEventHandlers(onClickProp, handleDialogClick)}
|
|
430
441
|
onCancel={handleCancel}>
|
|
431
442
|
{/* Drawer panel — tabIndex so showModal() focuses the drawer, not the close button */}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file MobileNavToggle.test.tsx
|
|
5
|
+
* @input Uses vitest, @testing-library/react, AppShell + SideNav
|
|
6
|
+
* @output Unit tests for MobileNavToggle ARIA wiring
|
|
7
|
+
* @position Testing; validates MobileNavToggle.tsx exposes aria-expanded and
|
|
8
|
+
* aria-controls so screen-reader users know the drawer state and target.
|
|
9
|
+
*
|
|
10
|
+
* SYNC: When MobileNavToggle.tsx changes, update tests to match new behavior
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
describe,
|
|
15
|
+
it,
|
|
16
|
+
expect,
|
|
17
|
+
vi,
|
|
18
|
+
beforeAll,
|
|
19
|
+
beforeEach,
|
|
20
|
+
afterEach,
|
|
21
|
+
} from 'vitest';
|
|
22
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
23
|
+
import {AppShell} from '../AppShell/AppShell';
|
|
24
|
+
import {SideNav, SideNavItem, SideNavSection} from '../SideNav';
|
|
25
|
+
|
|
26
|
+
// jsdom doesn't implement showModal/close on <dialog>, so we mock them
|
|
27
|
+
beforeAll(() => {
|
|
28
|
+
HTMLDialogElement.prototype.showModal =
|
|
29
|
+
HTMLDialogElement.prototype.showModal ||
|
|
30
|
+
function (this: HTMLDialogElement) {
|
|
31
|
+
this.setAttribute('open', '');
|
|
32
|
+
};
|
|
33
|
+
HTMLDialogElement.prototype.close =
|
|
34
|
+
HTMLDialogElement.prototype.close ||
|
|
35
|
+
function (this: HTMLDialogElement) {
|
|
36
|
+
this.removeAttribute('open');
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
class MockResizeObserver {
|
|
41
|
+
observe() {}
|
|
42
|
+
unobserve() {}
|
|
43
|
+
disconnect() {}
|
|
44
|
+
}
|
|
45
|
+
vi.stubGlobal('ResizeObserver', MockResizeObserver);
|
|
46
|
+
|
|
47
|
+
function createMockMatchMedia(matches: boolean) {
|
|
48
|
+
return {
|
|
49
|
+
matches,
|
|
50
|
+
media: '',
|
|
51
|
+
onchange: null,
|
|
52
|
+
addEventListener: vi.fn(),
|
|
53
|
+
removeEventListener: vi.fn(),
|
|
54
|
+
addListener: vi.fn(),
|
|
55
|
+
removeListener: vi.fn(),
|
|
56
|
+
dispatchEvent: vi.fn(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
// Below the breakpoint so the mobile nav (and its toggle) render.
|
|
62
|
+
vi.stubGlobal(
|
|
63
|
+
'matchMedia',
|
|
64
|
+
vi.fn().mockReturnValue(createMockMatchMedia(true)),
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
vi.restoreAllMocks();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
function TestShell() {
|
|
73
|
+
return (
|
|
74
|
+
<AppShell
|
|
75
|
+
sideNav={
|
|
76
|
+
<SideNav>
|
|
77
|
+
<SideNavSection title="Test" isHeaderHidden>
|
|
78
|
+
<SideNavItem label="Home" />
|
|
79
|
+
</SideNavSection>
|
|
80
|
+
</SideNav>
|
|
81
|
+
}
|
|
82
|
+
mobileNav={{breakpoint: 'md'}}>
|
|
83
|
+
<div>Content</div>
|
|
84
|
+
</AppShell>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe('MobileNavToggle ARIA wiring', () => {
|
|
89
|
+
it('exposes aria-expanded="false" when the drawer is closed', () => {
|
|
90
|
+
render(<TestShell />);
|
|
91
|
+
|
|
92
|
+
const toggle = screen.getByRole('button', {name: /open navigation/i});
|
|
93
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('exposes aria-expanded="true" after opening the drawer', () => {
|
|
97
|
+
render(<TestShell />);
|
|
98
|
+
|
|
99
|
+
const toggle = screen.getByRole('button', {name: /open navigation/i});
|
|
100
|
+
fireEvent.click(toggle);
|
|
101
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('references the nav drawer via aria-controls that resolves to the dialog', () => {
|
|
105
|
+
render(<TestShell />);
|
|
106
|
+
|
|
107
|
+
const toggle = screen.getByRole('button', {name: /open navigation/i});
|
|
108
|
+
const controls = toggle.getAttribute('aria-controls');
|
|
109
|
+
expect(controls).toBeTruthy();
|
|
110
|
+
|
|
111
|
+
const target = document.getElementById(controls!);
|
|
112
|
+
expect(target).not.toBeNull();
|
|
113
|
+
expect(target).toBe(screen.getAllByRole('dialog', {hidden: true})[0]);
|
|
114
|
+
expect(target?.tagName).toBe('DIALOG');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -69,7 +69,13 @@ export function MobileNavToggle({
|
|
|
69
69
|
}: MobileNavToggleProps) {
|
|
70
70
|
const t = useTranslator();
|
|
71
71
|
const label = labelFromProps ?? t('@astryx.mobileNav.toggle.open');
|
|
72
|
-
const {
|
|
72
|
+
const {
|
|
73
|
+
isMobile,
|
|
74
|
+
isMobileNavEnabled,
|
|
75
|
+
isMobileNavOpen,
|
|
76
|
+
mobileNavId,
|
|
77
|
+
toggleMobileNav,
|
|
78
|
+
} = useAppShellMobile();
|
|
73
79
|
|
|
74
80
|
// Don't render above the breakpoint or when mobile nav is disabled
|
|
75
81
|
if (!isMobile || !isMobileNavEnabled) {
|
|
@@ -83,6 +89,8 @@ export function MobileNavToggle({
|
|
|
83
89
|
label={label}
|
|
84
90
|
icon={children ?? <Icon icon="menu" color="inherit" />}
|
|
85
91
|
onClick={toggleMobileNav}
|
|
92
|
+
aria-expanded={isMobileNavOpen}
|
|
93
|
+
aria-controls={mobileNavId || undefined}
|
|
86
94
|
data-testid={testId ?? 'mobile-nav-toggle'}
|
|
87
95
|
xstyle={xstyle}
|
|
88
96
|
className={className}
|
|
@@ -99,7 +99,8 @@ export const docs = {
|
|
|
99
99
|
{
|
|
100
100
|
name: 'hasSearch',
|
|
101
101
|
type: 'boolean',
|
|
102
|
-
description:
|
|
102
|
+
description:
|
|
103
|
+
'Whether to show a search input for filtering options. As the user types, the match count (or "No results found") is announced to screen readers via a polite live region.',
|
|
103
104
|
},
|
|
104
105
|
{
|
|
105
106
|
name: 'searchPlaceholder',
|
|
@@ -239,7 +240,8 @@ export const docsZh = {
|
|
|
239
240
|
hasSearch: '是否显示用于过滤选项的搜索输入。',
|
|
240
241
|
searchPlaceholder: '搜索输入的占位文本。',
|
|
241
242
|
isDisabled: '禁用选择器。',
|
|
242
|
-
htmlName:
|
|
243
|
+
htmlName:
|
|
244
|
+
'用于表单提交的 HTML name 属性。为每个已选值渲染一个隐藏输入,类似原生多选。',
|
|
243
245
|
disabledMessage:
|
|
244
246
|
'解释选择器被禁用的原因。与 isDisabled 一起使用时,悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持触发器可聚焦(仍无法激活)。请使用此属性,而不是用 Tooltip 包裹被禁用的选择器。',
|
|
245
247
|
isLabelHidden: '视觉上隐藏标签同时保持其可访问性。',
|
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
render,
|
|
15
|
+
screen,
|
|
16
|
+
fireEvent,
|
|
17
|
+
waitFor,
|
|
18
|
+
within,
|
|
19
|
+
} from '@testing-library/react';
|
|
14
20
|
import userEvent from '@testing-library/user-event';
|
|
15
21
|
import {MultiSelector} from './MultiSelector';
|
|
16
22
|
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
@@ -360,6 +366,30 @@ describe('MultiSelector', () => {
|
|
|
360
366
|
expect(activeId).toBeTruthy();
|
|
361
367
|
});
|
|
362
368
|
|
|
369
|
+
it('End/Home jump the highlight to the last/first option (non-search)', async () => {
|
|
370
|
+
const user = userEvent.setup();
|
|
371
|
+
render(
|
|
372
|
+
<MultiSelector
|
|
373
|
+
label="Fruit"
|
|
374
|
+
options={defaultOptions}
|
|
375
|
+
value={[]}
|
|
376
|
+
onChange={() => {}}
|
|
377
|
+
/>,
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const trigger = screen.getByRole('combobox');
|
|
381
|
+
await user.click(trigger);
|
|
382
|
+
const options = screen.getAllByRole('option', h);
|
|
383
|
+
|
|
384
|
+
await user.keyboard('{End}');
|
|
385
|
+
expect(trigger).toHaveAttribute(
|
|
386
|
+
'aria-activedescendant',
|
|
387
|
+
options[options.length - 1].id,
|
|
388
|
+
);
|
|
389
|
+
await user.keyboard('{Home}');
|
|
390
|
+
expect(trigger).toHaveAttribute('aria-activedescendant', options[0].id);
|
|
391
|
+
});
|
|
392
|
+
|
|
363
393
|
it('toggles item with Enter key', async () => {
|
|
364
394
|
const user = userEvent.setup();
|
|
365
395
|
const onChange = vi.fn();
|
|
@@ -535,6 +565,65 @@ describe('MultiSelector', () => {
|
|
|
535
565
|
expect(options).toHaveLength(1);
|
|
536
566
|
});
|
|
537
567
|
|
|
568
|
+
it('PageDown/PageUp jump the highlight to the last/first filtered option', async () => {
|
|
569
|
+
const user = userEvent.setup();
|
|
570
|
+
render(
|
|
571
|
+
<MultiSelector
|
|
572
|
+
label="Fruit"
|
|
573
|
+
options={defaultOptions}
|
|
574
|
+
value={[]}
|
|
575
|
+
onChange={() => {}}
|
|
576
|
+
hasSearch
|
|
577
|
+
/>,
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
581
|
+
const searchInput = screen.getByRole('combobox', h);
|
|
582
|
+
// Filter to Banana and Orange so "last" means last *visible* option.
|
|
583
|
+
await user.type(searchInput, 'an');
|
|
584
|
+
const options = screen.getAllByRole('option', h);
|
|
585
|
+
expect(options).toHaveLength(2);
|
|
586
|
+
|
|
587
|
+
await user.keyboard('{PageDown}');
|
|
588
|
+
expect(searchInput).toHaveAttribute(
|
|
589
|
+
'aria-activedescendant',
|
|
590
|
+
options[options.length - 1].id,
|
|
591
|
+
);
|
|
592
|
+
await user.keyboard('{PageUp}');
|
|
593
|
+
expect(searchInput).toHaveAttribute('aria-activedescendant', options[0].id);
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
it('Home/End move the search caret, not the option highlight', async () => {
|
|
597
|
+
const user = userEvent.setup();
|
|
598
|
+
render(
|
|
599
|
+
<MultiSelector
|
|
600
|
+
label="Fruit"
|
|
601
|
+
options={defaultOptions}
|
|
602
|
+
value={[]}
|
|
603
|
+
onChange={() => {}}
|
|
604
|
+
hasSearch
|
|
605
|
+
/>,
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
609
|
+
const searchInput = screen.getByRole<HTMLInputElement>('combobox', h);
|
|
610
|
+
await user.type(searchInput, 'an');
|
|
611
|
+
expect(searchInput.selectionStart).toBe(2);
|
|
612
|
+
const activeBefore = searchInput.getAttribute('aria-activedescendant');
|
|
613
|
+
// Home/End stay on the input for caret movement (APG editable combobox);
|
|
614
|
+
// the option highlight must not move.
|
|
615
|
+
await user.keyboard('{Home}');
|
|
616
|
+
expect(searchInput.selectionStart).toBe(0);
|
|
617
|
+
expect(searchInput.getAttribute('aria-activedescendant')).toBe(
|
|
618
|
+
activeBefore,
|
|
619
|
+
);
|
|
620
|
+
await user.keyboard('{End}');
|
|
621
|
+
expect(searchInput.selectionStart).toBe(2);
|
|
622
|
+
expect(searchInput.getAttribute('aria-activedescendant')).toBe(
|
|
623
|
+
activeBefore,
|
|
624
|
+
);
|
|
625
|
+
});
|
|
626
|
+
|
|
538
627
|
it('shows empty state when search has no results', async () => {
|
|
539
628
|
const user = userEvent.setup();
|
|
540
629
|
render(
|
|
@@ -551,7 +640,85 @@ describe('MultiSelector', () => {
|
|
|
551
640
|
const searchInput = screen.getByRole('combobox', h);
|
|
552
641
|
await user.type(searchInput, 'xyz');
|
|
553
642
|
|
|
554
|
-
expect(
|
|
643
|
+
expect(
|
|
644
|
+
within(screen.getByRole('listbox', h)).getByText('No results found'),
|
|
645
|
+
).toBeInTheDocument();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
describe('result announcements', () => {
|
|
649
|
+
it('announces the match count politely while searching', async () => {
|
|
650
|
+
const user = userEvent.setup();
|
|
651
|
+
render(
|
|
652
|
+
<MultiSelector
|
|
653
|
+
label="Fruit"
|
|
654
|
+
options={defaultOptions}
|
|
655
|
+
value={EMPTY_VALUE}
|
|
656
|
+
onChange={() => {}}
|
|
657
|
+
hasSearch
|
|
658
|
+
/>,
|
|
659
|
+
);
|
|
660
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
661
|
+
// "an" matches Banana and Orange.
|
|
662
|
+
await user.type(screen.getByRole('combobox', h), 'an');
|
|
663
|
+
await waitFor(() => {
|
|
664
|
+
expect(politeRegion()).toHaveTextContent('2 results');
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('announces the singular form when one option matches', async () => {
|
|
669
|
+
const user = userEvent.setup();
|
|
670
|
+
render(
|
|
671
|
+
<MultiSelector
|
|
672
|
+
label="Fruit"
|
|
673
|
+
options={defaultOptions}
|
|
674
|
+
value={EMPTY_VALUE}
|
|
675
|
+
onChange={() => {}}
|
|
676
|
+
hasSearch
|
|
677
|
+
/>,
|
|
678
|
+
);
|
|
679
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
680
|
+
// "app" matches only Apple. Anchored so it cannot pass on "1 results".
|
|
681
|
+
await user.type(screen.getByRole('combobox', h), 'app');
|
|
682
|
+
await waitFor(() => {
|
|
683
|
+
expect(politeRegion()).toHaveTextContent(/^1 result$/);
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it('announces "No results found" when nothing matches', async () => {
|
|
688
|
+
const user = userEvent.setup();
|
|
689
|
+
render(
|
|
690
|
+
<MultiSelector
|
|
691
|
+
label="Fruit"
|
|
692
|
+
options={defaultOptions}
|
|
693
|
+
value={EMPTY_VALUE}
|
|
694
|
+
onChange={() => {}}
|
|
695
|
+
hasSearch
|
|
696
|
+
/>,
|
|
697
|
+
);
|
|
698
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
699
|
+
await user.type(screen.getByRole('combobox', h), 'xyz');
|
|
700
|
+
await waitFor(() => {
|
|
701
|
+
expect(politeRegion()).toHaveTextContent('No results found');
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it('does not announce results until the user searches', async () => {
|
|
706
|
+
const user = userEvent.setup();
|
|
707
|
+
render(
|
|
708
|
+
<MultiSelector
|
|
709
|
+
label="Fruit"
|
|
710
|
+
options={defaultOptions}
|
|
711
|
+
value={EMPTY_VALUE}
|
|
712
|
+
onChange={() => {}}
|
|
713
|
+
hasSearch
|
|
714
|
+
/>,
|
|
715
|
+
);
|
|
716
|
+
// Popover closed: nothing announced.
|
|
717
|
+
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
718
|
+
// Open with an empty query: still nothing announced.
|
|
719
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
720
|
+
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
721
|
+
});
|
|
555
722
|
});
|
|
556
723
|
|
|
557
724
|
it('renders with description', () => {
|
|
@@ -1072,7 +1239,9 @@ describe('MultiSelector', () => {
|
|
|
1072
1239
|
/>
|
|
1073
1240
|
</form>,
|
|
1074
1241
|
);
|
|
1075
|
-
expect([
|
|
1242
|
+
expect([
|
|
1243
|
+
...new FormData(container.querySelector('form')!).keys(),
|
|
1244
|
+
]).toEqual([]);
|
|
1076
1245
|
});
|
|
1077
1246
|
});
|
|
1078
1247
|
});
|