@astryxdesign/core 0.1.8 → 0.1.9-canary.1887ee2
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 +53 -3
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +203 -49
- 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/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.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +1 -0
- package/dist/Banner/Banner.d.ts +9 -1
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +33 -4
- 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 +33 -4
- 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 +36 -4
- 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/ChatLayout.js +4 -4
- 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/Chat/useTriggerMenu.js +2 -2
- 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/Citation/Citation.d.ts +22 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +20 -6
- 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 +9 -2
- 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/CommandPalette/CommandPaletteItem.js +1 -1
- package/dist/DateInput/DateInput.d.ts +55 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +28 -2
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +3 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +18 -3
- 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/DropdownMenuItem.js +1 -1
- 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 +43 -16
- package/dist/FieldStatus/FieldStatus.d.ts +15 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +58 -6
- package/dist/FileInput/FileInput.d.ts +9 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +42 -20
- 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/Lightbox/Lightbox.js +4 -4
- 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 +9 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +41 -12
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +16 -0
- package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
- package/dist/NumberInput/NumberInput.d.ts +9 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +8 -1
- package/dist/Outline/Outline.d.ts +0 -1
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +0 -1
- package/dist/OverflowList/OverflowList.d.ts +20 -3
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +29 -4
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +11 -3
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +3 -9
- package/dist/PowerSearch/PowerSearch.d.ts +9 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +2 -0
- 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 +8 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +7 -3
- 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/TabList/Tab.js +2 -2
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +3 -2
- package/dist/Table/TableCell.js +3 -3
- package/dist/Table/TableHeaderCell.js +3 -3
- 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/stickyColumns/useTableStickyColumns.js +1 -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/TextArea/TextArea.d.ts +9 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +2 -0
- package/dist/TextInput/TextInput.d.ts +9 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +6 -1
- 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 +9 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +17 -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 +10 -3
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +18 -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 +28 -8
- 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/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +20 -11
- package/dist/Typeahead/Typeahead.d.ts +9 -2
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +2 -0
- package/dist/astryx.css +41 -31
- 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/isRtlElement.d.ts +18 -0
- package/dist/hooks/isRtlElement.d.ts.map +1 -0
- package/dist/hooks/isRtlElement.js +27 -0
- 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/useGridFocus.d.ts +6 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +12 -9
- package/dist/hooks/useListFocus.d.ts +6 -1
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +12 -4
- package/dist/hooks/useOverflow.d.ts +19 -0
- package/dist/hooks/useOverflow.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +36 -25
- package/dist/i18n/InternationalizationContext.d.ts +1 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -0
- package/dist/i18n/InternationalizationProvider.d.ts +8 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +5 -1
- package/dist/i18n/getLocaleDirection.d.ts +18 -0
- package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
- package/dist/i18n/getLocaleDirection.js +29 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +7 -1
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +7 -18
- package/dist/i18n/useDirection.d.ts +2 -0
- package/dist/i18n/useDirection.d.ts.map +1 -0
- package/dist/i18n/useDirection.js +27 -0
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +2 -7
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +13 -0
- 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 +32 -0
- package/locales/fr-FR.json +14 -0
- package/locales/pseudo.json +24 -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 +60 -3
- package/src/Avatar/Avatar.test.tsx +281 -1
- package/src/Avatar/Avatar.tsx +324 -64
- 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/AvatarGroup/AvatarGroup.doc.mjs +2 -0
- package/src/AvatarGroup/AvatarGroup.test.tsx +125 -0
- package/src/AvatarGroup/AvatarGroup.tsx +58 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +1 -0
- package/src/Banner/Banner.doc.mjs +9 -0
- package/src/Banner/Banner.test.tsx +30 -0
- package/src/Banner/Banner.tsx +53 -9
- 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 +27 -3
- 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 +51 -5
- package/src/Chat/ChatContext.tsx +37 -10
- package/src/Chat/ChatDictationButton.tsx +4 -1
- package/src/Chat/ChatLayout.tsx +7 -5
- 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 +4 -2
- 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/Citation/Citation.doc.mjs +4 -3
- package/src/Citation/Citation.test.tsx +97 -0
- package/src/Citation/Citation.tsx +43 -11
- 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 +10 -2
- 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/CommandPalette/CommandPaletteItem.tsx +1 -1
- package/src/ContextMenu/ContextMenu.test.tsx +193 -0
- package/src/DateInput/DateInput.doc.mjs +31 -0
- package/src/DateInput/DateInput.test.tsx +131 -0
- package/src/DateInput/DateInput.tsx +78 -3
- package/src/DateInput/index.ts +1 -0
- package/src/DateRangeInput/DateRangeInput.doc.mjs +8 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +23 -0
- package/src/DateRangeInput/DateRangeInput.tsx +13 -3
- package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -1
- package/src/DateTimeInput/DateTimeInput.test.tsx +147 -0
- package/src/DateTimeInput/DateTimeInput.tsx +42 -18
- 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/DropdownMenuItem.tsx +1 -1
- 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 +9 -1
- package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
- package/src/FieldStatus/FieldStatus.test.tsx +150 -40
- package/src/FieldStatus/FieldStatus.tsx +63 -5
- package/src/FileInput/FileInput.doc.mjs +17 -2
- package/src/FileInput/FileInput.test.tsx +135 -12
- package/src/FileInput/FileInput.tsx +53 -28
- 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/Lightbox/Lightbox.tsx +4 -4
- 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 +13 -2
- package/src/MultiSelector/MultiSelector.test.tsx +195 -3
- package/src/MultiSelector/MultiSelector.tsx +65 -12
- package/src/MultiSelector/hooks.ts +17 -0
- package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +15 -0
- package/src/NumberInput/NumberInput.test.tsx +82 -7
- package/src/NumberInput/NumberInput.tsx +21 -4
- package/src/Outline/Outline.tsx +0 -1
- package/src/OverflowList/OverflowList.doc.mjs +39 -6
- package/src/OverflowList/OverflowList.test.tsx +111 -0
- package/src/OverflowList/OverflowList.tsx +48 -8
- package/src/Pagination/Pagination.tsx +11 -3
- package/src/Popover/Popover.tsx +7 -4
- package/src/Popover/usePopover.tsx +9 -13
- package/src/PowerSearch/PowerSearch.doc.mjs +29 -0
- package/src/PowerSearch/PowerSearch.test.tsx +43 -0
- package/src/PowerSearch/PowerSearch.tsx +10 -1
- 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.doc.mjs +7 -0
- package/src/Selector/Selector.test.tsx +92 -0
- package/src/Selector/Selector.tsx +16 -2
- 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/TabList/Tab.tsx +2 -2
- package/src/TabList/TabMenu.tsx +2 -2
- package/src/Table/BaseTable.tsx +3 -5
- package/src/Table/TableCell.tsx +3 -3
- package/src/Table/TableHeaderCell.tsx +3 -3
- 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/stickyColumns/useTableStickyColumns.tsx +1 -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.doc.mjs +15 -0
- package/src/TextArea/TextArea.test.tsx +32 -1
- package/src/TextArea/TextArea.tsx +10 -0
- package/src/TextInput/TextInput.doc.mjs +15 -0
- package/src/TextInput/TextInput.test.tsx +44 -0
- package/src/TextInput/TextInput.tsx +13 -1
- package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
- package/src/Thumbnail/Thumbnail.test.tsx +98 -7
- package/src/Thumbnail/Thumbnail.tsx +142 -42
- package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
- package/src/TimeInput/TimeInput.doc.mjs +15 -0
- package/src/TimeInput/TimeInput.test.tsx +99 -1
- package/src/TimeInput/TimeInput.tsx +29 -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 +30 -1
- package/src/Tokenizer/Tokenizer.test.tsx +206 -2
- package/src/Tokenizer/Tokenizer.tsx +26 -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 +52 -12
- package/src/TreeList/TreeListTypes.ts +27 -1
- package/src/TreeList/index.ts +2 -2
- package/src/Typeahead/BaseTypeahead.tsx +30 -15
- package/src/Typeahead/Typeahead.doc.mjs +7 -0
- package/src/Typeahead/Typeahead.test.tsx +50 -0
- package/src/Typeahead/Typeahead.tsx +10 -0
- 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/isRtlElement.ts +27 -0
- package/src/hooks/useDevWarning.test.tsx +45 -0
- package/src/hooks/useDevWarning.ts +54 -0
- 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/useListFocus.doc.mjs +3 -3
- package/src/hooks/useListFocus.test.tsx +67 -1
- package/src/hooks/useListFocus.ts +21 -5
- package/src/hooks/useOverflow.test.ts +96 -4
- package/src/hooks/useOverflow.ts +74 -33
- package/src/i18n/InternationalizationContext.ts +4 -0
- package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
- package/src/i18n/InternationalizationProvider.tsx +12 -2
- package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
- package/src/i18n/__tests__/useDirection.test.tsx +66 -0
- package/src/i18n/getLocaleDirection.ts +29 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/resolve.ts +9 -21
- package/src/i18n/useDirection.ts +29 -0
- package/src/reset.css +0 -26
- package/src/reset.test.ts +60 -0
- package/src/theme/Theme.tsx +11 -14
- package/src/theme/derivedVarRegistry.test.ts +1 -0
- package/src/theme/derivedVarRegistry.ts +6 -0
- package/src/theme/generateThemeRules.test.ts +107 -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
|
@@ -147,16 +147,33 @@ describe('FileInput', () => {
|
|
|
147
147
|
expect(screen.getByText('Upload')).toBeInTheDocument();
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
it('
|
|
150
|
+
it('conveys required state through the accessible description', () => {
|
|
151
151
|
render(
|
|
152
152
|
<FileInput label="Resume" isRequired value={null} onChange={() => {}} />,
|
|
153
153
|
);
|
|
154
|
-
|
|
154
|
+
const trigger = screen.getByRole('button', {name: 'Resume'});
|
|
155
|
+
// Required is conveyed via a visually hidden "Required" node referenced
|
|
156
|
+
// from aria-describedby on the focusable role="button" trigger — not the
|
|
155
157
|
// hidden file input (forms-6).
|
|
156
|
-
expect(
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
expect(trigger).toHaveAccessibleDescription(/Required/);
|
|
159
|
+
// aria-required is not a supported property of role="button" in
|
|
160
|
+
// WAI-ARIA 1.2 (AT does not announce it), so it must never appear.
|
|
161
|
+
expect(trigger).not.toHaveAttribute('aria-required');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('combines required with the description in the accessible description', () => {
|
|
165
|
+
render(
|
|
166
|
+
<FileInput
|
|
167
|
+
label="Resume"
|
|
168
|
+
description="PDF only"
|
|
169
|
+
isRequired
|
|
170
|
+
value={null}
|
|
171
|
+
onChange={() => {}}
|
|
172
|
+
/>,
|
|
159
173
|
);
|
|
174
|
+
const trigger = screen.getByRole('button', {name: 'Resume'});
|
|
175
|
+
expect(trigger).toHaveAccessibleDescription(/PDF only/);
|
|
176
|
+
expect(trigger).toHaveAccessibleDescription(/Required/);
|
|
160
177
|
});
|
|
161
178
|
|
|
162
179
|
it('places aria-describedby on the focusable button, not the hidden input (forms-6)', () => {
|
|
@@ -176,11 +193,11 @@ describe('FileInput', () => {
|
|
|
176
193
|
expect(input).toHaveAttribute('aria-hidden', 'true');
|
|
177
194
|
});
|
|
178
195
|
|
|
179
|
-
it('does not
|
|
196
|
+
it('does not mention required without isRequired', () => {
|
|
180
197
|
render(<FileInput label="Resume" value={null} onChange={() => {}} />);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
);
|
|
198
|
+
const trigger = screen.getByRole('button', {name: 'Resume'});
|
|
199
|
+
expect(trigger).not.toHaveAccessibleDescription(/Required/);
|
|
200
|
+
expect(trigger).not.toHaveAttribute('aria-required');
|
|
184
201
|
});
|
|
185
202
|
|
|
186
203
|
it('sets disabled attribute when isDisabled is true', () => {
|
|
@@ -348,9 +365,44 @@ describe('FileInput', () => {
|
|
|
348
365
|
fireEvent.change(fileInputEl(), {
|
|
349
366
|
target: {files: [createFile('note.txt', 100)]},
|
|
350
367
|
});
|
|
351
|
-
// A rejected selection
|
|
352
|
-
// the
|
|
353
|
-
|
|
368
|
+
// A rejected selection announces nothing politely. (The region pair may
|
|
369
|
+
// exist because the validation error itself is announced assertively via
|
|
370
|
+
// FieldStatus, but the polite channel must stay empty.)
|
|
371
|
+
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('announces a validation error exactly once across live regions', async () => {
|
|
375
|
+
render(
|
|
376
|
+
<FileInput
|
|
377
|
+
label="Upload"
|
|
378
|
+
value={null}
|
|
379
|
+
onChange={() => {}}
|
|
380
|
+
accept=".pdf"
|
|
381
|
+
/>,
|
|
382
|
+
);
|
|
383
|
+
fireEvent.change(fileInputEl(), {
|
|
384
|
+
target: {files: [createFile('note.txt', 100)]},
|
|
385
|
+
});
|
|
386
|
+
const errorText = '"note.txt" is not an accepted file type';
|
|
387
|
+
// The error lands in the assertive announce region (via the FieldStatus
|
|
388
|
+
// the derived error status mounts)…
|
|
389
|
+
await waitFor(() => {
|
|
390
|
+
expect(
|
|
391
|
+
document.querySelector('[data-astryx-live-region="assertive"]'),
|
|
392
|
+
).toHaveTextContent(errorText);
|
|
393
|
+
});
|
|
394
|
+
// …and in exactly one live region overall. FileInput's own role="status"
|
|
395
|
+
// region used to duplicate the FieldStatus announcement, so the same
|
|
396
|
+
// error was read twice (assertively, then politely).
|
|
397
|
+
const liveRegions = Array.from(
|
|
398
|
+
document.querySelectorAll(
|
|
399
|
+
'[role="status"], [role="alert"], [aria-live]',
|
|
400
|
+
),
|
|
401
|
+
);
|
|
402
|
+
const regionsWithError = liveRegions.filter(el =>
|
|
403
|
+
(el.textContent ?? '').includes(errorText),
|
|
404
|
+
);
|
|
405
|
+
expect(regionsWithError).toHaveLength(1);
|
|
354
406
|
});
|
|
355
407
|
});
|
|
356
408
|
|
|
@@ -641,6 +693,54 @@ describe('FileInput', () => {
|
|
|
641
693
|
});
|
|
642
694
|
});
|
|
643
695
|
|
|
696
|
+
describe('trigger accessible name', () => {
|
|
697
|
+
it('includes the selected file name in the trigger name', () => {
|
|
698
|
+
const file = createFile('report.pdf', 1024, 'application/pdf');
|
|
699
|
+
render(<FileInput label="Document" value={file} onChange={() => {}} />);
|
|
700
|
+
expect(
|
|
701
|
+
screen.getByRole('button', {name: 'Document, report.pdf'}),
|
|
702
|
+
).toBeInTheDocument();
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it('includes all selected file names when multiple files are selected', () => {
|
|
706
|
+
const files = [createFile('a.txt', 100), createFile('b.txt', 200)];
|
|
707
|
+
render(
|
|
708
|
+
<FileInput
|
|
709
|
+
label="Files"
|
|
710
|
+
value={files}
|
|
711
|
+
onChange={() => {}}
|
|
712
|
+
isMultiple
|
|
713
|
+
/>,
|
|
714
|
+
);
|
|
715
|
+
expect(
|
|
716
|
+
screen.getByRole('button', {name: 'Files, a.txt, b.txt'}),
|
|
717
|
+
).toBeInTheDocument();
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('uses exactly the label when no files are selected', () => {
|
|
721
|
+
render(<FileInput label="Document" value={null} onChange={() => {}} />);
|
|
722
|
+
expect(screen.getByRole('button', {name: 'Document'})).toHaveAttribute(
|
|
723
|
+
'aria-label',
|
|
724
|
+
'Document',
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('includes the selected file name in dropzone mode', () => {
|
|
729
|
+
const file = createFile('doc.pdf', 100, 'application/pdf');
|
|
730
|
+
render(
|
|
731
|
+
<FileInput
|
|
732
|
+
label="Upload"
|
|
733
|
+
value={file}
|
|
734
|
+
onChange={() => {}}
|
|
735
|
+
mode="dropzone"
|
|
736
|
+
/>,
|
|
737
|
+
);
|
|
738
|
+
expect(
|
|
739
|
+
screen.getByRole('button', {name: 'Upload, doc.pdf'}),
|
|
740
|
+
).toBeInTheDocument();
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
|
|
644
744
|
describe('data-testid', () => {
|
|
645
745
|
it('passes data-testid to native input', () => {
|
|
646
746
|
render(
|
|
@@ -800,3 +900,26 @@ describe('FileInput', () => {
|
|
|
800
900
|
});
|
|
801
901
|
});
|
|
802
902
|
});
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
describe('FileInput statusVariant forwarding', () => {
|
|
906
|
+
it('defaults to attached (status renders with data-variant="attached")', () => {
|
|
907
|
+
const {container} = render(
|
|
908
|
+
<FileInput label="Upload" value={null} onChange={() => {}} status={{type: 'error', message: 'Something went wrong'}} />,
|
|
909
|
+
);
|
|
910
|
+
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
911
|
+
'data-variant',
|
|
912
|
+
'attached',
|
|
913
|
+
);
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
it('forwards statusVariant="detached" to the underlying Field status', () => {
|
|
917
|
+
const {container} = render(
|
|
918
|
+
<FileInput label="Upload" value={null} onChange={() => {}} status={{type: 'error', message: 'Something went wrong'}} statusVariant="detached" />,
|
|
919
|
+
);
|
|
920
|
+
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
921
|
+
'data-variant',
|
|
922
|
+
'detached',
|
|
923
|
+
);
|
|
924
|
+
});
|
|
925
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file FileInput.tsx
|
|
7
|
-
* @input Uses React, useId, Field, Icon, Spinner
|
|
7
|
+
* @input Uses React, useId, Field, Icon, Spinner, VisuallyHidden
|
|
8
8
|
* @output Exports FileInput component, FileInputProps, FileInputStatus
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by FileInput.test.tsx
|
|
10
10
|
*
|
|
@@ -41,9 +41,11 @@ import {
|
|
|
41
41
|
InputClearButton,
|
|
42
42
|
type InputStatus,
|
|
43
43
|
type InputStatusType,
|
|
44
|
+
type FieldStatusVariant,
|
|
44
45
|
} from '../Field';
|
|
45
46
|
import {Icon, type IconName} from '../Icon';
|
|
46
47
|
import {Spinner} from '../Spinner';
|
|
48
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
47
49
|
import {useAnnounce} from '../hooks/useAnnounce';
|
|
48
50
|
import {useTooltip} from '../Tooltip';
|
|
49
51
|
|
|
@@ -246,17 +248,6 @@ const styles = stylex.create({
|
|
|
246
248
|
flex: 1,
|
|
247
249
|
minWidth: 0,
|
|
248
250
|
},
|
|
249
|
-
liveRegion: {
|
|
250
|
-
position: 'absolute',
|
|
251
|
-
width: 1,
|
|
252
|
-
height: 1,
|
|
253
|
-
padding: 0,
|
|
254
|
-
margin: -1,
|
|
255
|
-
overflow: 'hidden',
|
|
256
|
-
clip: 'rect(0, 0, 0, 0)',
|
|
257
|
-
whiteSpace: 'nowrap',
|
|
258
|
-
borderWidth: 0,
|
|
259
|
-
},
|
|
260
251
|
});
|
|
261
252
|
|
|
262
253
|
const statusBorderStyles = stylex.create({
|
|
@@ -358,6 +349,13 @@ export interface FileInputProps extends Omit<
|
|
|
358
349
|
* Validation status for the input.
|
|
359
350
|
*/
|
|
360
351
|
status?: InputStatus;
|
|
352
|
+
/**
|
|
353
|
+
* How the status message is placed relative to the input.
|
|
354
|
+
* - 'attached': message overlaps directly below the input (bordered treatment)
|
|
355
|
+
* - 'detached': message floats below as a separate element with spacing
|
|
356
|
+
* @default 'attached'
|
|
357
|
+
*/
|
|
358
|
+
statusVariant?: FieldStatusVariant;
|
|
361
359
|
/**
|
|
362
360
|
* Description text displayed below the label.
|
|
363
361
|
*/
|
|
@@ -414,6 +412,7 @@ export function FileInput({
|
|
|
414
412
|
isRequired = false,
|
|
415
413
|
isLoading = false,
|
|
416
414
|
status: statusProp,
|
|
415
|
+
statusVariant = 'attached',
|
|
417
416
|
description,
|
|
418
417
|
placeholder,
|
|
419
418
|
mode = 'input',
|
|
@@ -430,15 +429,16 @@ export function FileInput({
|
|
|
430
429
|
const id = useId();
|
|
431
430
|
const descriptionID = useId();
|
|
432
431
|
const statusMessageID = useId();
|
|
433
|
-
const
|
|
432
|
+
const requiredID = useId();
|
|
434
433
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
435
434
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
436
435
|
const [validationError, setValidationError] = useState<string | null>(null);
|
|
437
436
|
const [, startTransition] = useTransition();
|
|
438
437
|
|
|
439
438
|
// Announce successful file selection to screen readers via a persistent
|
|
440
|
-
// live region (forms-17).
|
|
441
|
-
//
|
|
439
|
+
// live region (forms-17). Validation errors are announced (assertively) by
|
|
440
|
+
// the FieldStatus that the derived error status mounts, so only the
|
|
441
|
+
// successful attach needs an explicit announcement here.
|
|
442
442
|
const announce = useAnnounce();
|
|
443
443
|
|
|
444
444
|
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
@@ -474,10 +474,18 @@ export function FileInput({
|
|
|
474
474
|
success: 'success',
|
|
475
475
|
};
|
|
476
476
|
|
|
477
|
+
// Required state. `aria-required` is not a supported property of
|
|
478
|
+
// role="button" in WAI-ARIA 1.2 (AT does not announce it there), so the
|
|
479
|
+
// trigger instead points its aria-describedby at a visually hidden
|
|
480
|
+
// "Required" span — mirroring the Field label's visible indicator (where
|
|
481
|
+
// isOptional takes precedence) and the pattern Slider uses for its thumbs.
|
|
482
|
+
const conveysRequired = isRequired && !isOptional;
|
|
483
|
+
|
|
477
484
|
const ariaDescribedBy =
|
|
478
485
|
[
|
|
479
486
|
description ? descriptionID : null,
|
|
480
487
|
status?.message ? statusMessageID : null,
|
|
488
|
+
conveysRequired ? requiredID : null,
|
|
481
489
|
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
482
490
|
]
|
|
483
491
|
.filter(Boolean)
|
|
@@ -515,8 +523,9 @@ export function FileInput({
|
|
|
515
523
|
onChange(result);
|
|
516
524
|
|
|
517
525
|
// Announce the successful selection politely. Validation errors are
|
|
518
|
-
//
|
|
519
|
-
// attach here (do not
|
|
526
|
+
// announced assertively by the FieldStatus that the derived error
|
|
527
|
+
// status mounts, so only announce the attach here (do not
|
|
528
|
+
// double-announce errors).
|
|
520
529
|
if (errors.length === 0) {
|
|
521
530
|
announce(
|
|
522
531
|
valid.length === 1
|
|
@@ -728,6 +737,7 @@ export function FileInput({
|
|
|
728
737
|
}
|
|
729
738
|
: undefined
|
|
730
739
|
}
|
|
740
|
+
statusVariant={statusVariant}
|
|
731
741
|
labelTooltip={labelTooltip}
|
|
732
742
|
width={width}>
|
|
733
743
|
<div
|
|
@@ -745,13 +755,27 @@ export function FileInput({
|
|
|
745
755
|
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
746
756
|
onClick={handleClick}
|
|
747
757
|
onKeyDown={handleKeyDown}
|
|
748
|
-
aria-label
|
|
758
|
+
// aria-label suppresses child content from the accessible name, so
|
|
759
|
+
// compose the selected filenames into it — otherwise a screen-reader
|
|
760
|
+
// user refocusing the control hears only the field label and cannot
|
|
761
|
+
// tell what (if anything) is attached.
|
|
762
|
+
aria-label={
|
|
763
|
+
hasFiles && fileNames
|
|
764
|
+
? t('@astryx.fileInput.triggerWithFiles', {label, fileNames})
|
|
765
|
+
: label
|
|
766
|
+
}
|
|
749
767
|
aria-busy={isLoading || undefined}
|
|
750
768
|
// These describe the operable control, so they belong on the focusable
|
|
751
769
|
// role="button" wrapper — not the hidden tabIndex={-1} file input the
|
|
752
|
-
// user never focuses (forms-6).
|
|
770
|
+
// user never focuses (forms-6). Required state is conveyed through
|
|
771
|
+
// the visually hidden "Required" node in aria-describedby, not
|
|
772
|
+
// aria-required, which role="button" does not support in ARIA 1.2.
|
|
753
773
|
aria-describedby={ariaDescribedBy}
|
|
754
|
-
aria-
|
|
774
|
+
// aria-invalid is likewise unsupported on role="button", but it stays:
|
|
775
|
+
// a message-less error status (`status={{type: 'error'}}`) has no
|
|
776
|
+
// described-by replacement, and some AT still honor the attribute, so
|
|
777
|
+
// removing it would not be strictly better. Errors *with* a message
|
|
778
|
+
// are described via statusMessageID above.
|
|
755
779
|
aria-invalid={status?.type === 'error' ? 'true' : undefined}
|
|
756
780
|
{...dragDropProps}
|
|
757
781
|
{...mergeProps(
|
|
@@ -785,16 +809,17 @@ export function FileInput({
|
|
|
785
809
|
/>
|
|
786
810
|
{isDropzone ? renderDropzoneContent() : renderCompactContent()}
|
|
787
811
|
{hasFiles && !isDisabled && !isLoading && (
|
|
788
|
-
<InputClearButton
|
|
812
|
+
<InputClearButton
|
|
813
|
+
label={t('@astryx.fileInput.clearLabel', {label})}
|
|
814
|
+
onClick={handleClear}
|
|
815
|
+
/>
|
|
789
816
|
)}
|
|
790
817
|
</div>
|
|
791
|
-
|
|
792
|
-
id={
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
{validationError}
|
|
797
|
-
</div>
|
|
818
|
+
{conveysRequired && (
|
|
819
|
+
<VisuallyHidden id={requiredID}>
|
|
820
|
+
{t('@astryx.fileInput.required')}
|
|
821
|
+
</VisuallyHidden>
|
|
822
|
+
)}
|
|
798
823
|
{showsDisabledMessage &&
|
|
799
824
|
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
800
825
|
</Field>
|
|
@@ -79,6 +79,12 @@ export const docs = {
|
|
|
79
79
|
description: 'Enables or disables the hover and focus triggers.',
|
|
80
80
|
default: 'true',
|
|
81
81
|
},
|
|
82
|
+
{
|
|
83
|
+
name: 'label',
|
|
84
|
+
type: 'string',
|
|
85
|
+
description:
|
|
86
|
+
'Accessible name for the hover card popup. When provided, the popup is exposed as a named role="dialog"; when omitted, it falls back to role="group" (a group may validly be unnamed).',
|
|
87
|
+
},
|
|
82
88
|
{
|
|
83
89
|
name: 'onOpenChange',
|
|
84
90
|
type: '(isOpen: boolean) => void',
|
|
@@ -188,6 +194,12 @@ export const docsZh = {
|
|
|
188
194
|
description: '启用或禁用悬停和聚焦触发器。',
|
|
189
195
|
default: 'true',
|
|
190
196
|
},
|
|
197
|
+
{
|
|
198
|
+
name: 'label',
|
|
199
|
+
type: 'string',
|
|
200
|
+
description:
|
|
201
|
+
'悬浮卡片弹出层的无障碍名称。提供时,弹出层以具名的 role="dialog" 暴露;省略时,回退为 role="group"(group 可以合法地没有名称)。',
|
|
202
|
+
},
|
|
191
203
|
{
|
|
192
204
|
name: 'onOpenChange',
|
|
193
205
|
type: '(isOpen: boolean) => void',
|
|
@@ -252,6 +264,7 @@ export const docsDense = {
|
|
|
252
264
|
hideDelay: 'Hide delay in ms.',
|
|
253
265
|
focusTrigger: 'Controls when focus events trigger hover card.',
|
|
254
266
|
isEnabled: 'Enable/disable hover + focus triggers.',
|
|
267
|
+
label: 'Accessible name for the popup. With label: named role="dialog"; without: role="group".',
|
|
255
268
|
onOpenChange: 'Callback when visibility changes; true=shown, false=hidden.',
|
|
256
269
|
hasHoverIndication: 'Dashed underline on trigger element.',
|
|
257
270
|
isDefaultOpen: 'Show hover card on mount. Still dismissible.',
|
|
@@ -58,15 +58,34 @@ describe('HoverCard', () => {
|
|
|
58
58
|
expect(screen.getByRole('button', {name: 'Trigger'})).toBeInTheDocument();
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
it('
|
|
61
|
+
it('exposes the floating layer as role="group" when no label is provided', () => {
|
|
62
62
|
render(
|
|
63
63
|
<HoverCard content={<span>Card content</span>}>
|
|
64
64
|
<button type="button">Trigger</button>
|
|
65
65
|
</HoverCard>,
|
|
66
66
|
);
|
|
67
|
-
|
|
67
|
+
// A group may validly be unnamed; an unnamed dialog may not. Without a
|
|
68
|
+
// label the layer must not claim the dialog role.
|
|
69
|
+
expect(screen.getByRole('group', {hidden: true})).toHaveTextContent(
|
|
68
70
|
'Card content',
|
|
69
71
|
);
|
|
72
|
+
expect(screen.queryByRole('dialog', {hidden: true})).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('exposes the floating layer as a named dialog when label is provided', () => {
|
|
76
|
+
render(
|
|
77
|
+
<HoverCard content={<span>Card content</span>} label="Profile preview">
|
|
78
|
+
<button type="button">Trigger</button>
|
|
79
|
+
</HoverCard>,
|
|
80
|
+
);
|
|
81
|
+
// The layer is hidden while closed, so assert the accessible name via the
|
|
82
|
+
// aria-label attribute on the role-carrying element (same pattern as the
|
|
83
|
+
// Popover dialogLabel test) — accname computation returns '' for hidden
|
|
84
|
+
// elements.
|
|
85
|
+
const dialog = screen.getByRole('dialog', {hidden: true});
|
|
86
|
+
expect(dialog).toHaveAttribute('aria-label', 'Profile preview');
|
|
87
|
+
expect(dialog).toHaveTextContent('Card content');
|
|
88
|
+
expect(screen.queryByRole('group', {hidden: true})).toBeNull();
|
|
70
89
|
});
|
|
71
90
|
|
|
72
91
|
it('wraps element children in an inline-safe span', () => {
|
|
@@ -96,6 +96,15 @@ export interface HoverCardProps extends Pick<
|
|
|
96
96
|
*/
|
|
97
97
|
isEnabled?: boolean;
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Accessible name for the hover card popup.
|
|
101
|
+
*
|
|
102
|
+
* When provided, the popup is exposed to assistive technology as a named
|
|
103
|
+
* `role="dialog"`. When omitted, the popup falls back to `role="group"` —
|
|
104
|
+
* a group may validly be unnamed, an unnamed dialog may not.
|
|
105
|
+
*/
|
|
106
|
+
label?: string;
|
|
107
|
+
|
|
99
108
|
/**
|
|
100
109
|
* Callback fired when hover card visibility changes.
|
|
101
110
|
* Called with `true` when shown and `false` when hidden.
|
|
@@ -166,6 +175,7 @@ export function HoverCard({
|
|
|
166
175
|
hideDelay = 200,
|
|
167
176
|
focusTrigger = 'auto',
|
|
168
177
|
isEnabled = true,
|
|
178
|
+
label,
|
|
169
179
|
onOpenChange,
|
|
170
180
|
hasHoverIndication = 'auto',
|
|
171
181
|
isOpen,
|
|
@@ -197,6 +207,7 @@ export function HoverCard({
|
|
|
197
207
|
hideDelay,
|
|
198
208
|
focusTrigger,
|
|
199
209
|
isEnabled,
|
|
210
|
+
label,
|
|
200
211
|
isOpen,
|
|
201
212
|
isDefaultOpen,
|
|
202
213
|
onShow: handleShow,
|
|
@@ -13,6 +13,7 @@ export const docs = {
|
|
|
13
13
|
{name: 'hideDelay', type: 'number', description: 'Delay before hiding after mouse or focus leaves, in milliseconds.', default: '200'},
|
|
14
14
|
{name: 'focusTrigger', type: "'auto' | 'always' | 'never'", description: 'When focus should open the hover card. auto only attaches focus listeners to naturally focusable elements.', default: "'auto'"},
|
|
15
15
|
{name: 'isEnabled', type: 'boolean', description: 'Whether hover and focus triggers are enabled.', default: 'true'},
|
|
16
|
+
{name: 'label', type: 'string', description: 'Accessible name for the hover card popup. When provided, the popup is exposed as a named role="dialog"; when omitted, it falls back to role="group" (a group may validly be unnamed).'},
|
|
16
17
|
{name: 'isOpen', type: 'boolean', description: 'Controlled open state. true force-shows, false force-hides, undefined lets hover/focus manage visibility.'},
|
|
17
18
|
{name: 'isDefaultOpen', type: 'boolean', description: 'Whether the hover card should be shown on mount.', default: 'false'},
|
|
18
19
|
{name: 'onShow', type: '() => void', description: 'Callback fired when the hover card becomes visible.'},
|
|
@@ -52,6 +53,7 @@ export const docsDense = {
|
|
|
52
53
|
hideDelay: 'hide delay after mouse/focus leave in ms.',
|
|
53
54
|
focusTrigger: 'when focus opens hover card; auto = naturally focusable elements only.',
|
|
54
55
|
isEnabled: 'whether hover/focus triggers are enabled.',
|
|
56
|
+
label: 'accessible name for popup. with label: named role="dialog"; without: role="group".',
|
|
55
57
|
isOpen: 'controlled open state: true force-show, false force-hide, undefined unmanaged.',
|
|
56
58
|
isDefaultOpen: 'show on mount.',
|
|
57
59
|
onShow: 'fires when hover card becomes visible.',
|
|
@@ -119,6 +119,15 @@ export interface HoverCardOptions {
|
|
|
119
119
|
*/
|
|
120
120
|
isEnabled?: boolean;
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Accessible name for the hover card popup.
|
|
124
|
+
*
|
|
125
|
+
* When provided, the popup is exposed to assistive technology as a named
|
|
126
|
+
* `role="dialog"`. When omitted, the popup falls back to `role="group"` —
|
|
127
|
+
* a group may validly be unnamed, an unnamed dialog may not.
|
|
128
|
+
*/
|
|
129
|
+
label?: string;
|
|
130
|
+
|
|
122
131
|
/**
|
|
123
132
|
* Controlled open state. When provided, overrides hover/focus triggers:
|
|
124
133
|
* - `true`: force-show the hover card (hover/focus hide is suppressed)
|
|
@@ -250,6 +259,7 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
250
259
|
hideDelay = 200,
|
|
251
260
|
focusTrigger = 'auto',
|
|
252
261
|
isEnabled = true,
|
|
262
|
+
label,
|
|
253
263
|
isOpen,
|
|
254
264
|
isDefaultOpen = false,
|
|
255
265
|
onShow,
|
|
@@ -462,7 +472,11 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
462
472
|
const renderProps = {
|
|
463
473
|
placement: renderPlacement,
|
|
464
474
|
alignment: props?.alignment ?? alignment,
|
|
465
|
-
|
|
475
|
+
// A named dialog when a label is provided; otherwise a group. A group
|
|
476
|
+
// may validly be unnamed, an unnamed dialog may not — and hover cards
|
|
477
|
+
// are non-modal, so group is honest semantics without a name.
|
|
478
|
+
role: label ? 'dialog' : 'group',
|
|
479
|
+
'aria-label': label || undefined,
|
|
466
480
|
xstyle: [popoverXstyle, layerAnimations[renderPlacement]],
|
|
467
481
|
// Render the layer as inline-safe phrasing markup so HoverCard stays
|
|
468
482
|
// valid (and hydration-stable) inside inline contexts like a `<p>`.
|
|
@@ -516,7 +530,15 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
516
530
|
renderProps,
|
|
517
531
|
);
|
|
518
532
|
},
|
|
519
|
-
[
|
|
533
|
+
[
|
|
534
|
+
layer,
|
|
535
|
+
placement,
|
|
536
|
+
alignment,
|
|
537
|
+
label,
|
|
538
|
+
clearTimeouts,
|
|
539
|
+
scheduleHide,
|
|
540
|
+
popoverXstyle,
|
|
541
|
+
],
|
|
520
542
|
);
|
|
521
543
|
|
|
522
544
|
return {
|
package/src/Icon/Icon.doc.mjs
CHANGED
|
@@ -37,7 +37,7 @@ export const docs = {
|
|
|
37
37
|
{
|
|
38
38
|
name: 'label',
|
|
39
39
|
type: 'string',
|
|
40
|
-
description: 'Accessible name for a MEANINGFUL, standalone icon (a status glyph or icon-only indicator with no adjacent text). Setting it exposes the icon to screen readers as role="img" with this text as the accessible name (aria-label) and removes the default aria-hidden. Omit it (default) for decorative icons and the icon stays hidden from assistive tech (aria-hidden="true"). This is the accessible-name / alt-text prop for icons
|
|
40
|
+
description: 'Accessible name for a MEANINGFUL, standalone icon (a status glyph or icon-only indicator with no adjacent text). Setting it exposes the icon to screen readers as role="img" with this text as the accessible name (aria-label) and removes the default aria-hidden. Omit it (default) for decorative icons and the icon stays hidden from assistive tech (aria-hidden="true"). This is the accessible-name / alt-text prop for icons: one prop instead of manually setting aria-label + role + aria-hidden. An empty string is treated as decorative. Do not set it when an interactive parent (Button, IconButton, link) already names the control.',
|
|
41
41
|
},
|
|
42
42
|
],
|
|
43
43
|
theming: {
|
|
@@ -50,7 +50,7 @@ export const docs = {
|
|
|
50
50
|
bestPractices: [
|
|
51
51
|
{ guidance: true, description: 'Use semantic icon names when available; they adapt to theme changes automatically.' },
|
|
52
52
|
{ guidance: true, description: 'Pair icons with text labels for accessibility; icon-only elements need an accessible label.' },
|
|
53
|
-
{ guidance: true, description: 'For a meaningful standalone icon (no adjacent text), give it an accessible name via the `label` prop
|
|
53
|
+
{ guidance: true, description: 'For a meaningful standalone icon (no adjacent text), give it an accessible name via the `label` prop: it sets role="img" + aria-label and unhides the icon.' },
|
|
54
54
|
{ guidance: true, description: 'Use color tokens for icon colors, not hardcoded hex values.' },
|
|
55
55
|
{ guidance: true, description: 'Be mindful of context; decorative icons in compact components can distract rather than help.' },
|
|
56
56
|
{ guidance: false, description: 'Use icons as the sole means of conveying meaning; always provide a text alternative.' },
|
|
@@ -121,6 +121,7 @@ export const docsDense = {
|
|
|
121
121
|
bestPractices: [
|
|
122
122
|
{ guidance: true, description: 'Use semantic icon names when available; they adapt to theme changes automatically.' },
|
|
123
123
|
{ guidance: true, description: 'Pair icons with text labels for accessibility; icon-only elements need an accessible label.' },
|
|
124
|
+
{ guidance: true, description: 'For a meaningful standalone icon (no adjacent text), set an accessible name via `label`: sets role="img" + aria-label, unhides icon.' },
|
|
124
125
|
{ guidance: true, description: 'Use color tokens for icon colors, not hardcoded hex values.' },
|
|
125
126
|
{ guidance: true, description: 'Be mindful of context; decorative icons in compact components can distract rather than help.' },
|
|
126
127
|
{ guidance: false, description: 'Use icons as the sole means of conveying meaning; always provide a text alternative.' },
|
package/src/Icon/Icon.tsx
CHANGED
|
@@ -229,12 +229,17 @@ export interface IconProps extends Omit<
|
|
|
229
229
|
* Don't set `label` when an interactive parent (Button, IconButton, link)
|
|
230
230
|
* already names the control — that produces a duplicate announcement.
|
|
231
231
|
*
|
|
232
|
+
* Meaningful, standalone icon: give it a label.
|
|
233
|
+
*
|
|
232
234
|
* @example
|
|
233
235
|
* ```
|
|
234
|
-
* // Meaningful, standalone icon
|
|
235
236
|
* <Icon icon="success" label="Completed" />
|
|
237
|
+
* ```
|
|
238
|
+
*
|
|
239
|
+
* Decorative icon (the default): omit label.
|
|
236
240
|
*
|
|
237
|
-
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```
|
|
238
243
|
* <Icon icon="search" />
|
|
239
244
|
* ```
|
|
240
245
|
*/
|
|
@@ -36,6 +36,13 @@ export const docs = {
|
|
|
36
36
|
description: 'Size variant.',
|
|
37
37
|
default: "\'md\'",
|
|
38
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: 'elevation',
|
|
41
|
+
type: "'none' | 'low' | 'med' | 'high'",
|
|
42
|
+
description:
|
|
43
|
+
'Resting shadow depth. The most common FAB shape is an icon-only button, so raise it with `low`/`med`/`high` for a floating action button. `none` is the default flat button.',
|
|
44
|
+
default: "'none'",
|
|
45
|
+
},
|
|
39
46
|
{
|
|
40
47
|
name: 'isLoading',
|
|
41
48
|
type: 'boolean',
|
|
@@ -95,6 +102,7 @@ export const docsDense = {
|
|
|
95
102
|
icon: 'icon element rendered inside button',
|
|
96
103
|
variant: 'visual style variant',
|
|
97
104
|
size: 'size variant',
|
|
105
|
+
elevation: 'resting shadow depth: none|low|med|high; raise for a floating action button (FAB)',
|
|
98
106
|
isLoading: 'shows loading spinner + disables interaction',
|
|
99
107
|
isDisabled: 'disables button',
|
|
100
108
|
tooltip: 'tooltip text shown on hover',
|
|
@@ -17,10 +17,7 @@ import {IconButton} from './IconButton';
|
|
|
17
17
|
describe('IconButton', () => {
|
|
18
18
|
it('renders as an icon-only button with aria-label', () => {
|
|
19
19
|
render(
|
|
20
|
-
<IconButton
|
|
21
|
-
label="Settings"
|
|
22
|
-
icon={<span data-testid="icon">⚙</span>}
|
|
23
|
-
/>,
|
|
20
|
+
<IconButton label="Settings" icon={<span data-testid="icon">⚙</span>} />,
|
|
24
21
|
);
|
|
25
22
|
const button = screen.getByRole('button', {name: 'Settings'});
|
|
26
23
|
expect(button).toHaveAttribute('aria-label', 'Settings');
|
|
@@ -29,10 +26,7 @@ describe('IconButton', () => {
|
|
|
29
26
|
|
|
30
27
|
it('does not render label as visible text', () => {
|
|
31
28
|
render(
|
|
32
|
-
<IconButton
|
|
33
|
-
label="Settings"
|
|
34
|
-
icon={<span data-testid="icon">⚙</span>}
|
|
35
|
-
/>,
|
|
29
|
+
<IconButton label="Settings" icon={<span data-testid="icon">⚙</span>} />,
|
|
36
30
|
);
|
|
37
31
|
const button = screen.getByRole('button');
|
|
38
32
|
// The label text should not appear as visible content
|
|
@@ -42,11 +36,7 @@ describe('IconButton', () => {
|
|
|
42
36
|
|
|
43
37
|
it('forwards variant prop', () => {
|
|
44
38
|
render(
|
|
45
|
-
<IconButton
|
|
46
|
-
label="Delete"
|
|
47
|
-
icon={<span>🗑</span>}
|
|
48
|
-
variant="destructive"
|
|
49
|
-
/>,
|
|
39
|
+
<IconButton label="Delete" icon={<span>🗑</span>} variant="destructive" />,
|
|
50
40
|
);
|
|
51
41
|
expect(screen.getByRole('button', {name: 'Delete'})).toBeInTheDocument();
|
|
52
42
|
});
|
|
@@ -60,11 +50,7 @@ describe('IconButton', () => {
|
|
|
60
50
|
const user = userEvent.setup();
|
|
61
51
|
const handleClick = vi.fn();
|
|
62
52
|
render(
|
|
63
|
-
<IconButton
|
|
64
|
-
label="Close"
|
|
65
|
-
icon={<span>✕</span>}
|
|
66
|
-
onClick={handleClick}
|
|
67
|
-
/>,
|
|
53
|
+
<IconButton label="Close" icon={<span>✕</span>} onClick={handleClick} />,
|
|
68
54
|
);
|
|
69
55
|
|
|
70
56
|
await user.click(screen.getByRole('button'));
|
|
@@ -105,4 +91,15 @@ describe('IconButton', () => {
|
|
|
105
91
|
it('has displayName set', () => {
|
|
106
92
|
expect(IconButton.displayName).toBe('IconButton');
|
|
107
93
|
});
|
|
94
|
+
|
|
95
|
+
it('forwards the elevation prop through to the underlying button', () => {
|
|
96
|
+
const classFor = (elevation: 'none' | 'med') => {
|
|
97
|
+
const {container} = render(
|
|
98
|
+
<IconButton label="Add" icon={<span>+</span>} elevation={elevation} />,
|
|
99
|
+
);
|
|
100
|
+
return container.querySelector('button')!.className;
|
|
101
|
+
};
|
|
102
|
+
// A raised FAB must render differently from the default flat icon button.
|
|
103
|
+
expect(classFor('med')).not.toBe(classFor('none'));
|
|
104
|
+
});
|
|
108
105
|
});
|