@astryxdesign/core 0.1.9 → 0.2.0-canary.136a00f
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 +107 -0
- package/dist/Avatar/Avatar.d.ts +42 -3
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +190 -59
- package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +58 -6
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
- package/dist/Banner/Banner.js +2 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
- package/dist/Breadcrumbs/index.d.ts +1 -0
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +2 -1
- package/dist/Button/Button.d.ts +1 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +6 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +50 -9
- package/dist/Calendar/dayCellUtils.d.ts +4 -4
- package/dist/Calendar/dayCellUtils.js +2 -2
- package/dist/Calendar/styles.d.ts +30 -31
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +30 -31
- package/dist/Card/Card.js +1 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +22 -12
- package/dist/Chat/ChatComposerInput.js +1 -1
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +3 -1
- package/dist/Chat/ChatLayout.js +4 -4
- package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
- package/dist/Chat/ChatMessageBubble.js +8 -8
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +10 -3
- package/dist/Chat/useTriggerMenu.js +2 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -6
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +19 -4
- package/dist/Citation/Citation.d.ts +22 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +20 -6
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +16 -10
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +3 -1
- package/dist/CommandPalette/CommandPalette.d.ts +1 -1
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +42 -3
- package/dist/CommandPalette/CommandPaletteItem.js +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +23 -5
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +3 -1
- package/dist/DateInput/DateInput.d.ts +10 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +33 -19
- package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +23 -20
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -18
- package/dist/Divider/Divider.d.ts +2 -2
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +13 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +22 -5
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
- package/dist/DropdownMenu/index.d.ts +10 -0
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +13 -0
- package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
- package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemHover.js +33 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
- package/dist/DropdownMenu/menuItemRoles.js +10 -1
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -6
- package/dist/Field/Field.d.ts +3 -1
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -1
- package/dist/Field/FieldLabel.d.ts +2 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -5
- package/dist/Field/inputStyles.stylex.d.ts +1 -0
- package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
- package/dist/Field/inputStyles.stylex.js +1 -0
- package/dist/FieldStatus/FieldStatus.d.ts +10 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +39 -3
- package/dist/FileInput/FileInput.d.ts +10 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +93 -65
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +10 -4
- package/dist/Icon/Icon.d.ts +15 -1
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +19 -3
- package/dist/Item/Item.d.ts +16 -3
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +31 -6
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +10 -2
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +19 -13
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +6 -3
- package/dist/List/ListItem.d.ts +11 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +2 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MetadataList/MetadataList.d.ts +1 -1
- package/dist/MetadataList/MetadataList.d.ts.map +1 -1
- package/dist/MetadataList/MetadataList.js +4 -2
- package/dist/MultiSelector/MultiSelector.d.ts +9 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +80 -42
- package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
- package/dist/NumberInput/NumberInput.d.ts +10 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +22 -17
- package/dist/Outline/Outline.js +2 -2
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +11 -3
- package/dist/PowerSearch/PowerSearch.d.ts +9 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +2 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +6 -1
- package/dist/RadioList/RadioList.d.ts +2 -2
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +5 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +49 -19
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
- package/dist/Selector/Selector.d.ts +8 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +66 -17
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +5 -5
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +13 -10
- package/dist/SideNav/SideNavItem.d.ts +1 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +12 -10
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +2 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +87 -34
- package/dist/Spinner/Spinner.d.ts +3 -2
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +11 -2
- package/dist/Switch/Switch.d.ts +8 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +171 -91
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts +2 -11
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +5 -9
- package/dist/TabList/TabListContext.d.ts +0 -5
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +0 -5
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +30 -4
- package/dist/Table/TableCell.js +3 -3
- package/dist/Table/TableHeaderCell.js +3 -3
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +3 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
- package/dist/Table/types.d.ts +32 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +10 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +18 -19
- package/dist/TextInput/TextInput.d.ts +10 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +20 -17
- package/dist/Thumbnail/Thumbnail.js +2 -2
- package/dist/TimeInput/TimeInput.d.ts +10 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +17 -17
- package/dist/Timestamp/Timestamp.d.ts +31 -3
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +47 -76
- package/dist/Timestamp/formatInstant.d.ts +41 -0
- package/dist/Timestamp/formatInstant.d.ts.map +1 -0
- package/dist/Timestamp/formatInstant.js +177 -0
- package/dist/Timestamp/index.d.ts +1 -0
- package/dist/Timestamp/index.d.ts.map +1 -1
- package/dist/Timestamp/tooltipEntries.d.ts +56 -0
- package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
- package/dist/Timestamp/tooltipEntries.js +163 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +61 -9
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +3 -1
- package/dist/Token/Token.d.ts +34 -4
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +85 -45
- package/dist/Token/TokenLink.d.ts +62 -0
- package/dist/Token/TokenLink.d.ts.map +1 -0
- package/dist/Token/TokenLink.js +76 -0
- package/dist/Token/index.d.ts +1 -1
- package/dist/Token/index.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts +9 -2
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +4 -2
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +17 -1
- package/dist/TopNav/TopNavItem.d.ts +3 -1
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +30 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +1 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +8 -6
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +32 -14
- package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +22 -11
- package/dist/Typeahead/Typeahead.d.ts +9 -2
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +11 -2
- package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/Typeahead/TypeaheadItem.js +20 -10
- package/dist/astryx.css +70 -30
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/isRtlElement.d.ts +18 -0
- package/dist/hooks/isRtlElement.d.ts.map +1 -0
- package/dist/hooks/isRtlElement.js +27 -0
- package/dist/hooks/useAnnounce.d.ts +3 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +39 -1
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +52 -11
- package/dist/hooks/useGridFocus.d.ts +6 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +12 -9
- package/dist/hooks/useHotkeys.d.ts +9 -0
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +9 -0
- package/dist/hooks/useInputStatusIcon.d.ts +38 -0
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
- package/dist/hooks/useInputStatusIcon.js +158 -0
- package/dist/hooks/useKeyboardHint.d.ts +1 -1
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
- package/dist/hooks/useKeyboardHint.js +4 -2
- package/dist/hooks/useListFocus.d.ts +39 -1
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +54 -9
- package/dist/i18n/InternationalizationContext.d.ts +1 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -0
- package/dist/i18n/InternationalizationProvider.d.ts +8 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +5 -1
- package/dist/i18n/getLocaleDirection.d.ts +18 -0
- package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
- package/dist/i18n/getLocaleDirection.js +29 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +7 -1
- package/dist/i18n/useDirection.d.ts +2 -0
- package/dist/i18n/useDirection.d.ts.map +1 -0
- package/dist/i18n/useDirection.js +27 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +13 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/plainDate.d.ts +17 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +23 -4
- package/dist/utils/rtlStyles.d.ts +24 -0
- package/dist/utils/rtlStyles.d.ts.map +1 -0
- package/dist/utils/rtlStyles.js +27 -0
- package/locales/en.json +112 -0
- package/locales/pseudo.json +84 -0
- package/package.json +6 -1
- package/src/Avatar/Avatar.doc.mjs +50 -3
- package/src/Avatar/Avatar.test.tsx +218 -0
- package/src/Avatar/Avatar.tsx +285 -79
- package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
- package/src/Avatar/AvatarStatusDot.tsx +6 -4
- package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
- package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
- package/src/AvatarGroup/AvatarGroup.tsx +58 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
- package/src/Banner/Banner.tsx +6 -6
- package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
- package/src/Breadcrumbs/index.ts +4 -0
- package/src/Button/Button.doc.mjs +0 -4
- package/src/Button/Button.test.tsx +16 -0
- package/src/Button/Button.tsx +6 -4
- package/src/Calendar/Calendar.test.tsx +170 -20
- package/src/Calendar/Calendar.tsx +73 -16
- package/src/Calendar/dayCellUtils.test.ts +18 -18
- package/src/Calendar/dayCellUtils.ts +2 -2
- package/src/Calendar/styles.ts +33 -39
- package/src/Card/Card.tsx +1 -1
- package/src/Carousel/Carousel.tsx +25 -6
- package/src/Chat/ChatComposer.doc.mjs +2 -2
- package/src/Chat/ChatComposerInput.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.tsx +2 -2
- package/src/Chat/ChatDictationButton.tsx +7 -1
- package/src/Chat/ChatLayout.tsx +4 -4
- package/src/Chat/ChatMessageBubble.tsx +15 -10
- package/src/Chat/ChatSystemMessage.test.tsx +5 -0
- package/src/Chat/ChatToolCalls.tsx +9 -2
- package/src/Chat/useTriggerMenu.tsx +1 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
- package/src/CheckboxList/CheckboxList.test.tsx +94 -3
- package/src/CheckboxList/CheckboxListItem.tsx +17 -10
- package/src/Citation/Citation.doc.mjs +4 -3
- package/src/Citation/Citation.test.tsx +97 -0
- package/src/Citation/Citation.tsx +43 -11
- package/src/CodeBlock/CodeBlock.test.tsx +17 -0
- package/src/CodeBlock/CodeBlock.tsx +38 -11
- package/src/Collapsible/Collapsible.doc.mjs +1 -0
- package/src/Collapsible/Collapsible.test.tsx +7 -0
- package/src/Collapsible/Collapsible.tsx +16 -6
- package/src/CommandPalette/CommandPalette.test.tsx +95 -1
- package/src/CommandPalette/CommandPalette.tsx +41 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
- package/src/ContextMenu/ContextMenu.test.tsx +59 -2
- package/src/ContextMenu/ContextMenu.tsx +22 -11
- package/src/ContextMenu/index.ts +5 -0
- package/src/DateInput/DateInput.doc.mjs +25 -0
- package/src/DateInput/DateInput.test.tsx +230 -0
- package/src/DateInput/DateInput.tsx +51 -29
- package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
- package/src/DateRangeInput/DateRangeInput.tsx +42 -30
- package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
- package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
- package/src/DateTimeInput/DateTimeInput.tsx +44 -39
- package/src/Divider/Divider.test.tsx +32 -8
- package/src/Divider/Divider.tsx +14 -1
- package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
- package/src/DropdownMenu/DropdownMenu.tsx +31 -12
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
- package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
- package/src/DropdownMenu/index.ts +16 -0
- package/src/DropdownMenu/menuItemHover.ts +39 -0
- package/src/DropdownMenu/menuItemRoles.ts +9 -0
- package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
- package/src/Field/Field.tsx +14 -9
- package/src/Field/FieldLabel.test.tsx +25 -0
- package/src/Field/FieldLabel.tsx +12 -3
- package/src/Field/inputStyles.stylex.ts +2 -0
- package/src/FieldStatus/FieldStatus.test.tsx +72 -0
- package/src/FieldStatus/FieldStatus.tsx +48 -2
- package/src/FileInput/FileInput.doc.mjs +23 -2
- package/src/FileInput/FileInput.test.tsx +104 -13
- package/src/FileInput/FileInput.tsx +106 -79
- package/src/HoverCard/useHoverCard.tsx +15 -3
- package/src/Icon/Icon.doc.mjs +13 -0
- package/src/Icon/Icon.test.tsx +89 -0
- package/src/Icon/Icon.tsx +51 -3
- package/src/Item/Item.doc.mjs +5 -0
- package/src/Item/Item.test.tsx +88 -0
- package/src/Item/Item.tsx +57 -11
- package/src/Layer/layerAnimations.stylex.ts +23 -0
- package/src/Lightbox/Lightbox.tsx +15 -7
- package/src/List/List.test.tsx +17 -29
- package/src/List/List.tsx +6 -1
- package/src/List/ListItem.doc.mjs +12 -0
- package/src/List/ListItem.tsx +13 -0
- package/src/Markdown/Markdown.test.tsx +21 -0
- package/src/Markdown/Markdown.tsx +4 -0
- package/src/MetadataList/MetadataList.test.tsx +24 -0
- package/src/MetadataList/MetadataList.tsx +6 -2
- package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
- package/src/MultiSelector/MultiSelector.test.tsx +402 -0
- package/src/MultiSelector/MultiSelector.tsx +120 -59
- package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +21 -0
- package/src/NumberInput/NumberInput.test.tsx +71 -0
- package/src/NumberInput/NumberInput.tsx +35 -26
- package/src/Outline/Outline.test.tsx +12 -12
- package/src/Outline/Outline.tsx +2 -2
- package/src/Pagination/Pagination.tsx +11 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +23 -0
- package/src/PowerSearch/PowerSearch.tsx +10 -1
- package/src/ProgressBar/ProgressBar.test.tsx +34 -0
- package/src/ProgressBar/ProgressBar.tsx +16 -1
- package/src/RadioList/RadioList.doc.mjs +6 -0
- package/src/RadioList/RadioList.test.tsx +26 -4
- package/src/RadioList/RadioList.tsx +2 -2
- package/src/RadioList/RadioListItem.tsx +12 -5
- package/src/Resizable/ResizeHandle.test.tsx +94 -0
- package/src/Resizable/ResizeHandle.tsx +70 -20
- package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
- package/src/Selector/Selector.doc.mjs +15 -0
- package/src/Selector/Selector.test.tsx +370 -0
- package/src/Selector/Selector.tsx +88 -20
- package/src/SideNav/SideNav.tsx +1 -8
- package/src/SideNav/SideNavCollapseButton.tsx +9 -7
- package/src/SideNav/SideNavItem.tsx +5 -3
- package/src/Skeleton/Skeleton.test.tsx +16 -0
- package/src/Skeleton/Skeleton.tsx +13 -1
- package/src/Slider/Slider.doc.mjs +6 -0
- package/src/Slider/Slider.test.tsx +240 -12
- package/src/Slider/Slider.tsx +96 -23
- package/src/Spinner/Spinner.test.tsx +10 -8
- package/src/Spinner/Spinner.tsx +15 -5
- package/src/Switch/Switch.doc.mjs +142 -42
- package/src/Switch/Switch.test.tsx +111 -0
- package/src/Switch/Switch.tsx +149 -32
- package/src/TabList/Tab.tsx +2 -2
- package/src/TabList/TabList.doc.mjs +0 -6
- package/src/TabList/TabList.test.tsx +3 -15
- package/src/TabList/TabList.tsx +6 -17
- package/src/TabList/TabListContext.ts +0 -6
- package/src/TabList/TabMenu.tsx +2 -2
- package/src/Table/BaseTable.tsx +34 -2
- package/src/Table/Table.doc.mjs +11 -0
- package/src/Table/Table.test.tsx +85 -0
- package/src/Table/TableCell.tsx +3 -3
- package/src/Table/TableHeaderCell.tsx +3 -3
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
- package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
- package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
- package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
- package/src/Table/types.ts +33 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/TextArea/TextArea.doc.mjs +21 -0
- package/src/TextArea/TextArea.test.tsx +23 -0
- package/src/TextArea/TextArea.tsx +27 -26
- package/src/TextInput/TextInput.doc.mjs +21 -0
- package/src/TextInput/TextInput.test.tsx +150 -0
- package/src/TextInput/TextInput.tsx +28 -25
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/Thumbnail/Thumbnail.tsx +1 -1
- package/src/TimeInput/TimeInput.doc.mjs +21 -0
- package/src/TimeInput/TimeInput.test.tsx +23 -0
- package/src/TimeInput/TimeInput.tsx +24 -25
- package/src/Timestamp/Timestamp.doc.mjs +32 -6
- package/src/Timestamp/Timestamp.test.tsx +371 -0
- package/src/Timestamp/Timestamp.tsx +96 -93
- package/src/Timestamp/formatInstant.ts +218 -0
- package/src/Timestamp/index.ts +4 -0
- package/src/Timestamp/tooltipEntries.test.ts +552 -0
- package/src/Timestamp/tooltipEntries.ts +210 -0
- package/src/Toast/ToastViewport.test.tsx +218 -8
- package/src/Toast/ToastViewport.tsx +71 -12
- package/src/ToggleButton/ToggleButton.test.tsx +24 -0
- package/src/ToggleButton/ToggleButton.tsx +19 -1
- package/src/Token/Token.test.tsx +166 -15
- package/src/Token/Token.tsx +104 -47
- package/src/Token/TokenLink.tsx +107 -0
- package/src/Token/index.ts +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
- package/src/Tokenizer/Tokenizer.test.tsx +23 -0
- package/src/Tokenizer/Tokenizer.tsx +14 -2
- package/src/Tooltip/Tooltip.test.tsx +46 -0
- package/src/Tooltip/useTooltip.tsx +20 -0
- package/src/TopNav/TopNav.test.tsx +93 -2
- package/src/TopNav/TopNavItem.doc.mjs +3 -3
- package/src/TopNav/TopNavItem.tsx +35 -6
- package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +6 -0
- package/src/TreeList/TreeList.test.tsx +50 -10
- package/src/TreeList/TreeList.tsx +5 -0
- package/src/TreeList/TreeListBranches.tsx +8 -6
- package/src/TreeList/TreeListItem.tsx +33 -11
- package/src/Typeahead/BaseTypeahead.tsx +42 -15
- package/src/Typeahead/Typeahead.doc.mjs +13 -0
- package/src/Typeahead/Typeahead.test.tsx +112 -0
- package/src/Typeahead/Typeahead.tsx +17 -1
- package/src/Typeahead/TypeaheadItem.tsx +2 -1
- package/src/__tests__/forcedColors.ts +60 -0
- package/src/__tests__/inputWidthContract.test.tsx +62 -0
- package/src/doc-mjs.d.ts +19 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/isRtlElement.ts +27 -0
- package/src/hooks/useAnnounce.test.tsx +75 -2
- package/src/hooks/useAnnounce.ts +47 -1
- package/src/hooks/useFocusTrap.test.tsx +104 -0
- package/src/hooks/useFocusTrap.ts +61 -12
- package/src/hooks/useGridFocus.doc.mjs +3 -3
- package/src/hooks/useGridFocus.test.tsx +41 -0
- package/src/hooks/useGridFocus.ts +21 -10
- package/src/hooks/useHotkeys.ts +9 -0
- package/src/hooks/useInputStatusIcon.test.tsx +162 -0
- package/src/hooks/useInputStatusIcon.tsx +224 -0
- package/src/hooks/useKeyboardHint.test.tsx +22 -0
- package/src/hooks/useKeyboardHint.tsx +6 -2
- package/src/hooks/useListFocus.doc.mjs +25 -3
- package/src/hooks/useListFocus.test.tsx +153 -1
- package/src/hooks/useListFocus.ts +102 -9
- package/src/i18n/InternationalizationContext.ts +4 -0
- package/src/i18n/InternationalizationProvider.tsx +12 -2
- package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
- package/src/i18n/__tests__/useDirection.test.tsx +66 -0
- package/src/i18n/getLocaleDirection.ts +29 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/useDirection.ts +29 -0
- package/src/reset.css +0 -26
- package/src/reset.test.ts +60 -0
- package/src/theme/derivedVarRegistry.test.ts +1 -0
- package/src/theme/derivedVarRegistry.ts +6 -0
- package/src/theme/generateThemeRules.test.ts +42 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/plainDate.test.ts +24 -6
- package/src/utils/plainDate.ts +18 -4
- package/src/utils/rtlStyles.ts +27 -0
|
@@ -169,4 +169,101 @@ describe('Citation', () => {
|
|
|
169
169
|
expect(el.classList.contains(cls)).toBe(true);
|
|
170
170
|
}
|
|
171
171
|
});
|
|
172
|
+
|
|
173
|
+
// --- Source icon: image URL (back-compat) vs ReactNode ---------------------
|
|
174
|
+
|
|
175
|
+
it('renders a legacy string icon as a decorative image (back-compat)', () => {
|
|
176
|
+
// Existing callers pass a favicon URL to `source.icon`. A bare string must
|
|
177
|
+
// still render as <img src>, unchanged from the original behavior.
|
|
178
|
+
const {container} = render(
|
|
179
|
+
<Citation
|
|
180
|
+
source={{
|
|
181
|
+
title: 'GitHub',
|
|
182
|
+
url: 'https://github.com',
|
|
183
|
+
icon: 'https://example.com/favicon.png',
|
|
184
|
+
}}
|
|
185
|
+
number={1}
|
|
186
|
+
data-testid="citation"
|
|
187
|
+
/>,
|
|
188
|
+
);
|
|
189
|
+
const img = container.querySelector('img');
|
|
190
|
+
expect(img).not.toBeNull();
|
|
191
|
+
expect(img).toHaveAttribute('src', 'https://example.com/favicon.png');
|
|
192
|
+
// Decorative: empty alt, and the wrapper is aria-hidden.
|
|
193
|
+
expect(img).toHaveAttribute('alt', '');
|
|
194
|
+
expect(container.querySelector('[aria-hidden="true"] img')).toBe(img);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('renders source.src as a decorative image', () => {
|
|
198
|
+
const {container} = render(
|
|
199
|
+
<Citation
|
|
200
|
+
source={{
|
|
201
|
+
title: 'GitHub',
|
|
202
|
+
url: 'https://github.com',
|
|
203
|
+
src: 'https://example.com/logo.png',
|
|
204
|
+
}}
|
|
205
|
+
number={1}
|
|
206
|
+
data-testid="citation"
|
|
207
|
+
/>,
|
|
208
|
+
);
|
|
209
|
+
const img = container.querySelector('img');
|
|
210
|
+
expect(img).toHaveAttribute('src', 'https://example.com/logo.png');
|
|
211
|
+
expect(img).toHaveAttribute('alt', '');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('renders a ReactNode icon as-is (not an <img>)', () => {
|
|
215
|
+
const {container} = render(
|
|
216
|
+
<Citation
|
|
217
|
+
source={{
|
|
218
|
+
title: 'GitHub',
|
|
219
|
+
url: 'https://github.com',
|
|
220
|
+
icon: <svg data-testid="custom-icon" />,
|
|
221
|
+
}}
|
|
222
|
+
number={1}
|
|
223
|
+
data-testid="citation"
|
|
224
|
+
/>,
|
|
225
|
+
);
|
|
226
|
+
// The node renders directly; no <img> is produced for a node icon.
|
|
227
|
+
expect(container.querySelector('img')).toBeNull();
|
|
228
|
+
expect(screen.getByTestId('custom-icon')).toBeInTheDocument();
|
|
229
|
+
// Still decorative — wrapped in an aria-hidden container.
|
|
230
|
+
expect(
|
|
231
|
+
container.querySelector(
|
|
232
|
+
'[aria-hidden="true"] [data-testid="custom-icon"]',
|
|
233
|
+
),
|
|
234
|
+
).not.toBeNull();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('prefers a node icon over src when both are provided', () => {
|
|
238
|
+
const {container} = render(
|
|
239
|
+
<Citation
|
|
240
|
+
source={{
|
|
241
|
+
title: 'GitHub',
|
|
242
|
+
url: 'https://github.com',
|
|
243
|
+
src: 'https://example.com/logo.png',
|
|
244
|
+
icon: <svg data-testid="custom-icon" />,
|
|
245
|
+
}}
|
|
246
|
+
number={1}
|
|
247
|
+
data-testid="citation"
|
|
248
|
+
/>,
|
|
249
|
+
);
|
|
250
|
+
expect(screen.getByTestId('custom-icon')).toBeInTheDocument();
|
|
251
|
+
expect(container.querySelector('img')).toBeNull();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('keeps aria-label as the sole accessible name when an icon is present', () => {
|
|
255
|
+
render(
|
|
256
|
+
<Citation
|
|
257
|
+
source={{
|
|
258
|
+
title: 'GitHub',
|
|
259
|
+
url: 'https://github.com',
|
|
260
|
+
icon: 'https://example.com/favicon.png',
|
|
261
|
+
}}
|
|
262
|
+
number={2}
|
|
263
|
+
data-testid="citation"
|
|
264
|
+
/>,
|
|
265
|
+
);
|
|
266
|
+
const el = screen.getByTestId('citation');
|
|
267
|
+
expect(el).toHaveAttribute('aria-label', 'Citation 2: GitHub');
|
|
268
|
+
});
|
|
172
269
|
});
|
|
@@ -10,10 +10,14 @@
|
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update these files to stay in sync:
|
|
12
12
|
* - /packages/core/src/Citation/index.ts (exports if types change)
|
|
13
|
+
* - /packages/core/src/Citation/Citation.doc.mjs (props table, features)
|
|
14
|
+
* - /packages/core/src/Citation/Citation.test.tsx (tests for new/changed behavior)
|
|
15
|
+
* - /apps/storybook/stories/Citation.stories.tsx (storybook stories)
|
|
13
16
|
* - /packages/cli/templates/blocks/components/Citation/ (showcase blocks)
|
|
14
17
|
*/
|
|
15
18
|
|
|
16
19
|
import type React from 'react';
|
|
20
|
+
import type {ReactNode} from 'react';
|
|
17
21
|
import * as stylex from '@stylexjs/stylex';
|
|
18
22
|
import {
|
|
19
23
|
colorVars,
|
|
@@ -29,11 +33,29 @@ import {mergeProps} from '../utils';
|
|
|
29
33
|
import type {BaseProps} from '../BaseProps';
|
|
30
34
|
import {themeProps} from '../utils/themeProps';
|
|
31
35
|
import {useTranslator} from '../i18n';
|
|
36
|
+
import {renderIconSlot} from '../Icon';
|
|
32
37
|
|
|
33
38
|
export interface CitationSource {
|
|
34
39
|
title?: string;
|
|
35
40
|
url?: string;
|
|
36
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Image URL for a favicon or source logo, rendered as `<img src>` inside the
|
|
43
|
+
* icon circle. This is the idiomatic field for image sources (mirrors
|
|
44
|
+
* `Avatar`/`Thumbnail` `src`). When both `src` and a non-string `icon` are
|
|
45
|
+
* provided, `icon` wins.
|
|
46
|
+
*/
|
|
47
|
+
src?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Source icon shown before the label text (label variant only). Accepts:
|
|
50
|
+
* - a React node / Astryx `<Icon>` / SVG element — rendered as-is, or
|
|
51
|
+
* - a string image URL — rendered as `<img src>` for backward compatibility
|
|
52
|
+
* with callers that passed a favicon URL here.
|
|
53
|
+
*
|
|
54
|
+
* Note: unlike icon slots elsewhere in the system, a bare string is treated
|
|
55
|
+
* as an image URL (not a semantic icon name) to preserve the original
|
|
56
|
+
* favicon behavior. Pass an Astryx `<Icon>` (or other node) for a real icon.
|
|
57
|
+
*/
|
|
58
|
+
icon?: ReactNode;
|
|
37
59
|
}
|
|
38
60
|
|
|
39
61
|
export interface CitationProps extends BaseProps<HTMLElement> {
|
|
@@ -162,7 +184,18 @@ export function Citation({
|
|
|
162
184
|
const t = useTranslator();
|
|
163
185
|
const title = source.title ?? String(number);
|
|
164
186
|
const href = source.url;
|
|
165
|
-
|
|
187
|
+
|
|
188
|
+
// Resolve the source icon. A non-string `icon` node renders as-is (an Astryx
|
|
189
|
+
// <Icon>, SVG, avatar, etc.). Otherwise fall back to an image URL: `src`, or
|
|
190
|
+
// a legacy string passed to `icon` (back-compat — favicon URLs still render
|
|
191
|
+
// as <img>). The icon is decorative; the accessible name comes solely from
|
|
192
|
+
// the element's aria-label, so nothing is double-announced.
|
|
193
|
+
const iconNode =
|
|
194
|
+
source.icon != null && typeof source.icon !== 'string' ? source.icon : null;
|
|
195
|
+
const imageSrc =
|
|
196
|
+
source.src ?? (typeof source.icon === 'string' ? source.icon : undefined);
|
|
197
|
+
const hasIcon = iconNode != null || imageSrc != null;
|
|
198
|
+
|
|
166
199
|
const Tag = href ? 'a' : 'span';
|
|
167
200
|
// `doc-noteref` is a reference role — only appropriate on the interactive
|
|
168
201
|
// link form. On a plain (unlinked) span it is not a permitted role
|
|
@@ -218,7 +251,7 @@ export function Citation({
|
|
|
218
251
|
themeProps('citation', {variant}),
|
|
219
252
|
stylex.props(
|
|
220
253
|
styles.label,
|
|
221
|
-
|
|
254
|
+
hasIcon && styles.labelWithIcon,
|
|
222
255
|
href != null && styles.labelHover,
|
|
223
256
|
href != null && styles.labelInteractive,
|
|
224
257
|
xstyle,
|
|
@@ -226,14 +259,13 @@ export function Citation({
|
|
|
226
259
|
className,
|
|
227
260
|
style,
|
|
228
261
|
)}>
|
|
229
|
-
{
|
|
230
|
-
<span {...stylex.props(styles.iconWrap)}>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
/>
|
|
262
|
+
{hasIcon && (
|
|
263
|
+
<span aria-hidden="true" {...stylex.props(styles.iconWrap)}>
|
|
264
|
+
{iconNode != null ? (
|
|
265
|
+
renderIconSlot(iconNode, {size: 'sm'})
|
|
266
|
+
) : (
|
|
267
|
+
<img src={imageSrc} alt="" {...stylex.props(styles.icon)} />
|
|
268
|
+
)}
|
|
237
269
|
</span>
|
|
238
270
|
)}
|
|
239
271
|
<span {...stylex.props(styles.labelText)}>{title}</span>
|
|
@@ -14,6 +14,7 @@ import {act, render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
|
14
14
|
import {CodeBlock} from './CodeBlock';
|
|
15
15
|
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
16
16
|
import {dracula} from '../theme/syntax';
|
|
17
|
+
import {InternationalizationProvider} from '../i18n';
|
|
17
18
|
|
|
18
19
|
function politeRegion(): HTMLElement | null {
|
|
19
20
|
return document.querySelector('[data-astryx-live-region="polite"]');
|
|
@@ -72,6 +73,22 @@ describe('CodeBlock', () => {
|
|
|
72
73
|
});
|
|
73
74
|
});
|
|
74
75
|
|
|
76
|
+
it('localizes the copy announcement through the i18n catalog', async () => {
|
|
77
|
+
render(
|
|
78
|
+
<InternationalizationProvider
|
|
79
|
+
locale="fr"
|
|
80
|
+
overrides={{fr: {'@astryx.codeBlock.copied': 'Copié'}}}>
|
|
81
|
+
<CodeBlock code="const x = 1;" language="javascript" />
|
|
82
|
+
</InternationalizationProvider>,
|
|
83
|
+
);
|
|
84
|
+
// The button label and the live-region announcement share the same key.
|
|
85
|
+
fireEvent.click(screen.getByRole('button', {name: 'Copy code'}));
|
|
86
|
+
await waitFor(() => {
|
|
87
|
+
expect(politeRegion()).toHaveTextContent('Copié');
|
|
88
|
+
});
|
|
89
|
+
expect(screen.getByRole('button', {name: 'Copié'})).toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
|
|
75
92
|
it('keeps the copied indicator a full 2s after a rapid re-copy', async () => {
|
|
76
93
|
vi.useFakeTimers();
|
|
77
94
|
try {
|
|
@@ -88,6 +88,23 @@ const dynamicStyles = stylex.create({
|
|
|
88
88
|
}),
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
// Light reveal so the leading chevron eases into view instead of popping in.
|
|
92
|
+
// Growing the chevron's own footprint (width + inline margin) from zero slides
|
|
93
|
+
// the title into place instead of snapping it over, and clipping keeps the
|
|
94
|
+
// glyph from spilling while it's mid-reveal.
|
|
95
|
+
const chevronReveal = stylex.keyframes({
|
|
96
|
+
from: {
|
|
97
|
+
width: 0,
|
|
98
|
+
marginInlineEnd: 0,
|
|
99
|
+
opacity: 0,
|
|
100
|
+
},
|
|
101
|
+
to: {
|
|
102
|
+
width: '14px',
|
|
103
|
+
marginInlineEnd: spacingVars['--spacing-1'],
|
|
104
|
+
opacity: 1,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
91
108
|
const styles = stylex.create({
|
|
92
109
|
root: {
|
|
93
110
|
position: 'relative',
|
|
@@ -133,7 +150,6 @@ const styles = stylex.create({
|
|
|
133
150
|
headerTitle: {
|
|
134
151
|
display: 'flex',
|
|
135
152
|
alignItems: 'center',
|
|
136
|
-
gap: spacingVars['--spacing-1'],
|
|
137
153
|
fontSize: typeScaleVars['--text-supporting-size'],
|
|
138
154
|
fontFamily: typographyVars['--font-family-code'],
|
|
139
155
|
fontWeight: fontWeightVars['--font-weight-medium'],
|
|
@@ -173,13 +189,24 @@ const styles = stylex.create({
|
|
|
173
189
|
flexShrink: 0,
|
|
174
190
|
width: '14px',
|
|
175
191
|
height: '14px',
|
|
192
|
+
marginInlineEnd: spacingVars['--spacing-1'],
|
|
193
|
+
overflow: 'hidden',
|
|
176
194
|
color: 'var(--color-syntax-comment)',
|
|
195
|
+
animationName: {
|
|
196
|
+
default: chevronReveal,
|
|
197
|
+
'@media (prefers-reduced-motion: reduce)': 'none',
|
|
198
|
+
},
|
|
199
|
+
animationDuration: durationVars['--duration-medium'],
|
|
200
|
+
animationTimingFunction: easeVars['--ease-standard'],
|
|
177
201
|
transitionProperty: 'transform',
|
|
178
202
|
transitionDuration: durationVars['--duration-fast'],
|
|
179
203
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
180
204
|
},
|
|
181
|
-
|
|
182
|
-
|
|
205
|
+
collapseChevronExpanded: {
|
|
206
|
+
// Leading disclosure convention (matches TreeList/Table): the resting
|
|
207
|
+
// chevronRight points right (>) when collapsed; rotate it down (v) when
|
|
208
|
+
// expanded.
|
|
209
|
+
transform: 'rotate(90deg)',
|
|
183
210
|
},
|
|
184
211
|
headerCollapsible: {
|
|
185
212
|
cursor: 'pointer',
|
|
@@ -296,7 +323,7 @@ const styles = stylex.create({
|
|
|
296
323
|
copyButtonAbsolute: {
|
|
297
324
|
position: 'absolute',
|
|
298
325
|
top: spacingVars['--spacing-2'],
|
|
299
|
-
|
|
326
|
+
insetInlineEnd: spacingVars['--spacing-2'],
|
|
300
327
|
},
|
|
301
328
|
});
|
|
302
329
|
|
|
@@ -759,7 +786,7 @@ export function CodeBlock({
|
|
|
759
786
|
setCopied(true);
|
|
760
787
|
// Swapping the button's aria-label alone isn't reliably announced by
|
|
761
788
|
// screen readers, so confirm the copy via a polite live region.
|
|
762
|
-
announce('
|
|
789
|
+
announce(t('@astryx.codeBlock.copied'));
|
|
763
790
|
onCopy?.();
|
|
764
791
|
// Restart the reset timer on every copy — otherwise a rapid re-copy
|
|
765
792
|
// is reverted early by the previous click's timer.
|
|
@@ -773,7 +800,7 @@ export function CodeBlock({
|
|
|
773
800
|
} catch {
|
|
774
801
|
// Clipboard failures leave the copied state unchanged.
|
|
775
802
|
}
|
|
776
|
-
}, [code, onCopy, announce]);
|
|
803
|
+
}, [code, onCopy, announce, t]);
|
|
777
804
|
|
|
778
805
|
const sizeStyle = size === 'sm' ? styles.sizeSm : styles.sizeMd;
|
|
779
806
|
// Digits in the largest line number — sizes the gutter column width.
|
|
@@ -845,18 +872,18 @@ export function CodeBlock({
|
|
|
845
872
|
canCollapse && styles.headerCollapsible,
|
|
846
873
|
)}>
|
|
847
874
|
<span {...stylex.props(styles.headerTitle)}>
|
|
848
|
-
{title}
|
|
849
|
-
{title && languageLabel ? ' — ' : ''}
|
|
850
|
-
{languageLabel}
|
|
851
875
|
{canCollapse && (
|
|
852
876
|
<span
|
|
853
877
|
{...stylex.props(
|
|
854
878
|
styles.collapseChevron,
|
|
855
|
-
isCollapsed && styles.
|
|
879
|
+
!isCollapsed && styles.collapseChevronExpanded,
|
|
856
880
|
)}>
|
|
857
|
-
<Icon icon="
|
|
881
|
+
<Icon icon="chevronRight" size="xsm" color="inherit" />
|
|
858
882
|
</span>
|
|
859
883
|
)}
|
|
884
|
+
{title}
|
|
885
|
+
{title && languageLabel ? ' — ' : ''}
|
|
886
|
+
{languageLabel}
|
|
860
887
|
</span>
|
|
861
888
|
</div>
|
|
862
889
|
{copyButtonEl}
|
|
@@ -17,6 +17,7 @@ export const docs = {
|
|
|
17
17
|
theming: {
|
|
18
18
|
targets: [
|
|
19
19
|
{className: 'astryx-collapsible', visualProps: ['density']},
|
|
20
|
+
{className: 'astryx-collapsible-trigger', visualProps: ['density']},
|
|
20
21
|
{className: 'astryx-collapsible-content', visualProps: ['density']},
|
|
21
22
|
{className: 'astryx-collapsible-group', visualProps: ['density']},
|
|
22
23
|
],
|
|
@@ -72,6 +72,13 @@ describe('Collapsible', () => {
|
|
|
72
72
|
expect(screen.getByTestId('root')).toHaveClass('astryx-collapsible');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
it('renders the stable astryx-collapsible-trigger class on the trigger button', () => {
|
|
76
|
+
render(<Collapsible trigger="T">c</Collapsible>);
|
|
77
|
+
expect(screen.getByRole('button')).toHaveClass(
|
|
78
|
+
'astryx-collapsible-trigger',
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
75
82
|
it('renders the stable astryx-collapsible-content class on the content area', () => {
|
|
76
83
|
render(<Collapsible trigger="T">c</Collapsible>);
|
|
77
84
|
const content = contentFor(screen.getByRole('button'));
|
|
@@ -51,7 +51,12 @@ const styles = stylex.create({
|
|
|
51
51
|
root: {
|
|
52
52
|
width: '100%',
|
|
53
53
|
},
|
|
54
|
-
// Trigger button — full width, flex row, no browser button styling
|
|
54
|
+
// Trigger button — full width, flex row, no browser button styling.
|
|
55
|
+
// Anchors heading-adjacent typography (body family, large size, semibold)
|
|
56
|
+
// so the label reads as a section header regardless of where the
|
|
57
|
+
// Collapsible is placed. External themes retarget it independently from the
|
|
58
|
+
// content via the `astryx-collapsible-trigger` target — e.g. a heading font
|
|
59
|
+
// on the trigger while the content stays on the body font.
|
|
55
60
|
trigger: {
|
|
56
61
|
all: 'unset',
|
|
57
62
|
boxSizing: 'border-box',
|
|
@@ -113,7 +118,7 @@ const styles = stylex.create({
|
|
|
113
118
|
// Anchors body typography so revealed text renders at the system's body
|
|
114
119
|
// scale (family/size/weight/leading) instead of inheriting from wherever
|
|
115
120
|
// the Collapsible is placed. External themes override via the
|
|
116
|
-
// `astryx-collapsible-content` target.
|
|
121
|
+
// `astryx-collapsible-content` target, independently from the trigger.
|
|
117
122
|
content: {
|
|
118
123
|
paddingBlockStart: spacingVars['--spacing-1'],
|
|
119
124
|
fontFamily: typographyVars['--font-family-body'],
|
|
@@ -318,10 +323,15 @@ export function Collapsible({
|
|
|
318
323
|
// the system-wide disabled convention (never native `disabled`, which
|
|
319
324
|
// would swallow events like a wrapping tooltip's hover).
|
|
320
325
|
tabIndex={isDisabled ? -1 : undefined}
|
|
321
|
-
{...
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
326
|
+
{...mergeProps(
|
|
327
|
+
themeProps('collapsible-trigger', {
|
|
328
|
+
density: density ?? undefined,
|
|
329
|
+
}),
|
|
330
|
+
stylex.props(
|
|
331
|
+
styles.trigger,
|
|
332
|
+
density != null && triggerDensity[density],
|
|
333
|
+
isDisabled && styles.triggerDisabled,
|
|
334
|
+
),
|
|
325
335
|
)}>
|
|
326
336
|
<span {...stylex.props(styles.triggerLabel)}>{trigger}</span>
|
|
327
337
|
<span
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
11
|
-
import {render, screen, waitFor, fireEvent} from '@testing-library/react';
|
|
11
|
+
import {render, screen, waitFor, fireEvent, act} from '@testing-library/react';
|
|
12
12
|
import {CommandPalette} from './CommandPalette';
|
|
13
13
|
import {createStaticSource} from '@astryxdesign/core/Typeahead';
|
|
14
14
|
import type {SearchSource, SearchableItem} from '@astryxdesign/core/Typeahead';
|
|
15
|
+
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
15
16
|
|
|
16
17
|
const simpleSource = createStaticSource([
|
|
17
18
|
{id: 'home', label: 'Home'},
|
|
@@ -35,6 +36,9 @@ beforeEach(() => {
|
|
|
35
36
|
HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
|
|
36
37
|
this.removeAttribute('open');
|
|
37
38
|
});
|
|
39
|
+
// The live regions are module-level singletons; reset them so
|
|
40
|
+
// announcements from one test don't leak into the next.
|
|
41
|
+
__resetLiveRegionsForTest();
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
describe('CommandPalette', () => {
|
|
@@ -269,4 +273,94 @@ describe('CommandPalette', () => {
|
|
|
269
273
|
expect(screen.getByText('No results')).toBeInTheDocument(),
|
|
270
274
|
);
|
|
271
275
|
});
|
|
276
|
+
|
|
277
|
+
describe('screen reader announcements', () => {
|
|
278
|
+
const politeRegion = () =>
|
|
279
|
+
document.querySelector('[data-astryx-live-region="polite"]');
|
|
280
|
+
|
|
281
|
+
it('announces the result count politely after typing a query', async () => {
|
|
282
|
+
render(
|
|
283
|
+
<CommandPalette
|
|
284
|
+
isOpen={true}
|
|
285
|
+
onOpenChange={() => {}}
|
|
286
|
+
searchSource={simpleSource}
|
|
287
|
+
/>,
|
|
288
|
+
);
|
|
289
|
+
await waitFor(() => expect(screen.getByText('Home')).toBeInTheDocument());
|
|
290
|
+
// "e" matches both Home and Settings.
|
|
291
|
+
fireEvent.change(screen.getByRole('combobox'), {target: {value: 'e'}});
|
|
292
|
+
await waitFor(() => {
|
|
293
|
+
expect(politeRegion()).toHaveTextContent('2 results');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('announces the singular form when one item matches', async () => {
|
|
298
|
+
render(
|
|
299
|
+
<CommandPalette
|
|
300
|
+
isOpen={true}
|
|
301
|
+
onOpenChange={() => {}}
|
|
302
|
+
searchSource={simpleSource}
|
|
303
|
+
/>,
|
|
304
|
+
);
|
|
305
|
+
await waitFor(() => expect(screen.getByText('Home')).toBeInTheDocument());
|
|
306
|
+
// "set" matches only Settings. Anchored so it cannot pass on "1 results".
|
|
307
|
+
fireEvent.change(screen.getByRole('combobox'), {target: {value: 'set'}});
|
|
308
|
+
await waitFor(() => {
|
|
309
|
+
expect(politeRegion()).toHaveTextContent(/^1 result$/);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('announces the empty state with the query when nothing matches', async () => {
|
|
314
|
+
render(
|
|
315
|
+
<CommandPalette
|
|
316
|
+
isOpen={true}
|
|
317
|
+
onOpenChange={() => {}}
|
|
318
|
+
searchSource={simpleSource}
|
|
319
|
+
/>,
|
|
320
|
+
);
|
|
321
|
+
await waitFor(() => expect(screen.getByText('Home')).toBeInTheDocument());
|
|
322
|
+
fireEvent.change(screen.getByRole('combobox'), {target: {value: 'zzz'}});
|
|
323
|
+
await waitFor(() => {
|
|
324
|
+
expect(politeRegion()).toHaveTextContent('No results for zzz');
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('announces loading politely while a search is in flight', async () => {
|
|
329
|
+
// A source whose searches never resolve, so the palette stays busy and
|
|
330
|
+
// only the loading announcement can reach the live region.
|
|
331
|
+
const source: SearchSource = {
|
|
332
|
+
bootstrap: () => [],
|
|
333
|
+
async search(): Promise<SearchableItem[]> {
|
|
334
|
+
return new Promise<SearchableItem[]>(() => {});
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
render(
|
|
338
|
+
<CommandPalette
|
|
339
|
+
isOpen={true}
|
|
340
|
+
onOpenChange={() => {}}
|
|
341
|
+
searchSource={source}
|
|
342
|
+
/>,
|
|
343
|
+
);
|
|
344
|
+
fireEvent.change(screen.getByRole('combobox'), {target: {value: 'z'}});
|
|
345
|
+
await waitFor(() => {
|
|
346
|
+
expect(politeRegion()).toHaveTextContent('Loading');
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('does not announce on initial open (bootstrap)', async () => {
|
|
351
|
+
render(
|
|
352
|
+
<CommandPalette
|
|
353
|
+
isOpen={true}
|
|
354
|
+
onOpenChange={() => {}}
|
|
355
|
+
searchSource={simpleSource}
|
|
356
|
+
/>,
|
|
357
|
+
);
|
|
358
|
+
await waitFor(() => expect(screen.getByText('Home')).toBeInTheDocument());
|
|
359
|
+
// Flush effects and any pending live-region rAF writes.
|
|
360
|
+
await act(async () => {
|
|
361
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
362
|
+
});
|
|
363
|
+
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
364
|
+
});
|
|
365
|
+
});
|
|
272
366
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
'use client';
|
|
4
4
|
/**
|
|
5
5
|
* @file CommandPalette.tsx
|
|
6
|
-
* @input Uses React, Dialog, Layout, CommandPaletteContext, SearchSource, useCombobox
|
|
6
|
+
* @input Uses React, Dialog, Layout, CommandPaletteContext, SearchSource, useCombobox, useAnnounce
|
|
7
7
|
* @output Exports CommandPalette root component and props
|
|
8
8
|
* @position Core root component; dialog shell with searchSource-driven items
|
|
9
9
|
*
|
|
@@ -36,6 +36,7 @@ import {CommandPaletteInput} from './CommandPaletteInput';
|
|
|
36
36
|
import {CommandPaletteFooter} from './CommandPaletteFooter';
|
|
37
37
|
import {CommandPaletteEmpty} from './CommandPaletteEmpty';
|
|
38
38
|
import type {BaseProps} from '../BaseProps';
|
|
39
|
+
import {useAnnounce} from '../hooks/useAnnounce';
|
|
39
40
|
import {useTranslator} from '../i18n';
|
|
40
41
|
|
|
41
42
|
export interface CommandPaletteProps<
|
|
@@ -291,6 +292,13 @@ export function CommandPalette<T extends SearchableItem = SearchableItem>({
|
|
|
291
292
|
const isBusy = isPending;
|
|
292
293
|
const searchVersionRef = useRef(0);
|
|
293
294
|
|
|
295
|
+
// Announce search status to screen readers through the shared polite live
|
|
296
|
+
// region (comboboxes-7 announce path, mirroring Selector / BaseTypeahead).
|
|
297
|
+
// The busy spinner and the empty states are otherwise purely visual, so a
|
|
298
|
+
// screen-reader user typing a query would hear nothing when loading starts
|
|
299
|
+
// or results disappear.
|
|
300
|
+
const announce = useAnnounce();
|
|
301
|
+
|
|
294
302
|
const value = controlledValue ?? internalValue;
|
|
295
303
|
|
|
296
304
|
const setValue = useCallback(
|
|
@@ -318,8 +326,12 @@ export function CommandPalette<T extends SearchableItem = SearchableItem>({
|
|
|
318
326
|
setInternalValue('');
|
|
319
327
|
}
|
|
320
328
|
searchSource.cancel?.();
|
|
329
|
+
// Clear any lingering result / loading announcement when the palette
|
|
330
|
+
// closes so stale status text does not linger in the a11y tree
|
|
331
|
+
// (matching Selector's onHide).
|
|
332
|
+
announce('');
|
|
321
333
|
onOpenChange(false);
|
|
322
|
-
}, [onOpenChange, searchSource, controlledValue]);
|
|
334
|
+
}, [onOpenChange, searchSource, controlledValue, announce]);
|
|
323
335
|
|
|
324
336
|
const selectItem = useCallback(
|
|
325
337
|
(itemValue: string) => {
|
|
@@ -355,6 +367,14 @@ export function CommandPalette<T extends SearchableItem = SearchableItem>({
|
|
|
355
367
|
startTransition(async () => {
|
|
356
368
|
const isBootstrap = query === '';
|
|
357
369
|
|
|
370
|
+
// Loading started for a user query: tell screen-reader users the
|
|
371
|
+
// spinner appeared. The polite region coalesces rapid updates, so for
|
|
372
|
+
// fast sources the result-count announcement below simply replaces
|
|
373
|
+
// this instead of stacking one "Loading" per keystroke.
|
|
374
|
+
if (!isBootstrap) {
|
|
375
|
+
announce(t('@astryx.commandPalette.loading'));
|
|
376
|
+
}
|
|
377
|
+
|
|
358
378
|
// Client-filter previous results for instant narrowing while fetch is in flight
|
|
359
379
|
if (!isBootstrap && searchResults.length > 0) {
|
|
360
380
|
const lower = query.toLowerCase().trim();
|
|
@@ -377,6 +397,23 @@ export function CommandPalette<T extends SearchableItem = SearchableItem>({
|
|
|
377
397
|
setOptimisticResults(items);
|
|
378
398
|
setSearchResults(items);
|
|
379
399
|
|
|
400
|
+
// Announce the outcome from the search commit (not a reactive
|
|
401
|
+
// effect), matching Selector / BaseTypeahead: exactly one
|
|
402
|
+
// announcement per committed query, and the version check above
|
|
403
|
+
// already discards stale keystrokes. Bootstrap stays silent — the
|
|
404
|
+
// same role PowerSearch's mount guard plays — so opening the
|
|
405
|
+
// palette announces nothing; clearing the query only clears any
|
|
406
|
+
// lingering status text.
|
|
407
|
+
if (isBootstrap) {
|
|
408
|
+
announce('');
|
|
409
|
+
} else if (items.length === 0) {
|
|
410
|
+
announce(t('@astryx.commandPalette.noResultsFor', {query}));
|
|
411
|
+
} else {
|
|
412
|
+
announce(
|
|
413
|
+
t('@astryx.commandPalette.resultCount', {count: items.length}),
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
380
417
|
// When opening with a preselected value, highlight it once
|
|
381
418
|
// bootstrap results arrive. No value → highlight stays at -1
|
|
382
419
|
// and ArrowDown naturally moves to the first item.
|
|
@@ -396,6 +433,8 @@ export function CommandPalette<T extends SearchableItem = SearchableItem>({
|
|
|
396
433
|
value,
|
|
397
434
|
combobox,
|
|
398
435
|
setOptimisticResults,
|
|
436
|
+
announce,
|
|
437
|
+
t,
|
|
399
438
|
],
|
|
400
439
|
);
|
|
401
440
|
|