@astryxdesign/core 0.3.0 → 0.4.0
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 +156 -0
- package/dist/AlertDialog/AlertDialog.d.ts +10 -2
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +22 -3
- package/dist/AppShell/AppShell.d.ts +2 -2
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +40 -9
- package/dist/AspectRatio/AspectRatio.d.ts +13 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +9 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +33 -20
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +15 -14
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
- package/dist/Breadcrumbs/index.d.ts +2 -1
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +1 -0
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -15
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +2 -1
- package/dist/Calendar/styles.d.ts +0 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +0 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +8 -12
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +2 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +3 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +4 -3
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +17 -17
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +2 -6
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +36 -125
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -11
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +84 -67
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +35 -15
- package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +6 -3
- package/dist/CommandPalette/CommandPaletteList.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +35 -33
- package/dist/ContextMenu/ContextMenu.d.ts +2 -2
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +1 -1
- package/dist/ContextMenu/index.d.ts +2 -1
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +1 -0
- package/dist/DateInput/DateInput.d.ts +9 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +24 -23
- package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +11 -16
- package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +22 -17
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +4 -4
- package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +62 -20
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
- package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
- package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
- package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +4 -0
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +35 -27
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +9 -5
- package/dist/Field/InputClearButton.d.ts +16 -3
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +15 -4
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +3 -19
- package/dist/Indicator/CheckIndicator.d.ts +25 -0
- package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
- package/dist/Indicator/CheckIndicator.js +155 -0
- package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
- package/dist/Indicator/CheckboxIndicator.js +173 -0
- package/dist/Indicator/RadioIndicator.d.ts +23 -0
- package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
- package/dist/Indicator/RadioIndicator.js +137 -0
- package/dist/Indicator/index.d.ts +24 -0
- package/dist/Indicator/index.d.ts.map +1 -0
- package/dist/Indicator/index.js +26 -0
- package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
- package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
- package/dist/Indicator/indicator.markers.stylex.js +21 -0
- package/dist/Indicator/indicatorRegistry.d.ts +71 -0
- package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
- package/dist/Indicator/indicatorRegistry.js +93 -0
- package/dist/Indicator/types.d.ts +167 -0
- package/dist/Indicator/types.d.ts.map +1 -0
- package/dist/Indicator/types.js +1 -0
- package/dist/Indicator/useIndicator.d.ts +18 -0
- package/dist/Indicator/useIndicator.d.ts.map +1 -0
- package/dist/Indicator/useIndicator.js +30 -0
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -8
- package/dist/Layer/useLayer.d.ts +14 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +25 -2
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +56 -53
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +4 -5
- package/dist/MobileNav/MobileNav.js +8 -8
- package/dist/MoreMenu/MoreMenu.d.ts +14 -1
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +4 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +78 -75
- package/dist/NumberInput/NumberInput.d.ts +27 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +230 -42
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +2 -3
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +61 -63
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +6 -8
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
- package/dist/ProgressBar/ProgressBar.d.ts +11 -4
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +96 -6
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +29 -84
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +2 -3
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +3 -11
- package/dist/Selector/Selector.d.ts +2 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +164 -94
- package/dist/Selector/hooks.d.ts +18 -6
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +57 -40
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +29 -14
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +56 -20
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +28 -22
- package/dist/Stack/stack.stylex.d.ts +3 -3
- package/dist/Switch/Switch.js +11 -10
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -3
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +41 -19
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +8 -0
- package/dist/Table/index.d.ts +1 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
- package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
- package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/index.js +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
- package/dist/Table/tableContextMenu.d.ts.map +1 -1
- package/dist/Table/tableContextMenu.js +2 -1
- package/dist/Table/types.d.ts +11 -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 +16 -6
- package/dist/TextInput/TextInput.d.ts +10 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +13 -19
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +32 -26
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +7 -15
- package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
- package/dist/Timestamp/TimestampHoverCard.js +16 -34
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +20 -11
- 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 +3 -19
- package/dist/TopNav/TopNav.d.ts.map +1 -1
- package/dist/TopNav/TopNav.js +6 -5
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +54 -16
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +3 -9
- package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +146 -60
- package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +83 -38
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +13 -0
- package/dist/TreeList/TreeListBranches.d.ts +1 -1
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +14 -4
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +57 -30
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -8
- package/dist/astryx.css +74 -85
- package/dist/astryx.umd.js +56 -56
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/containerReveal.stylex.d.ts +61 -0
- package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
- package/dist/hooks/containerReveal.stylex.js +63 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useClipboard.d.ts +59 -0
- package/dist/hooks/useClipboard.d.ts.map +1 -0
- package/dist/hooks/useClipboard.js +106 -0
- package/dist/hooks/useContainerReveal.d.ts +14 -8
- package/dist/hooks/useContainerReveal.d.ts.map +1 -1
- package/dist/hooks/useContainerReveal.js +28 -69
- package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
- package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
- package/dist/hooks/useIndicatorFocusRing.js +96 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -1
- package/dist/hooks/useLongPress.js +12 -2
- package/dist/hooks/useScrollLock.d.ts.map +1 -1
- package/dist/hooks/useScrollLock.js +33 -18
- package/dist/hooks/useStreamingText.d.ts +18 -3
- package/dist/hooks/useStreamingText.d.ts.map +1 -1
- package/dist/hooks/useStreamingText.js +47 -7
- package/dist/hooks/useTreeFocus.d.ts.map +1 -1
- package/dist/hooks/useTreeFocus.js +9 -3
- package/dist/hooks/useTypeahead.d.ts +3 -2
- package/dist/hooks/useTypeahead.d.ts.map +1 -1
- package/dist/hooks/useTypeahead.js +19 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/defineTheme.d.ts +16 -0
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +8 -0
- package/dist/theme/derivedVarRegistry.d.ts +8 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +14 -13
- package/dist/theme/expandColorScale.d.ts +3 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +17 -7
- package/dist/theme/expandTypeScale.d.ts.map +1 -1
- package/dist/theme/expandTypeScale.js +17 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +11 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.d.ts +107 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
- package/dist/utils/focusOutline.stylex.js +130 -0
- package/dist/utils/focusReturn.d.ts +24 -0
- package/dist/utils/focusReturn.d.ts.map +1 -0
- package/dist/utils/focusReturn.js +29 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/mergeRefs.d.ts.map +1 -1
- package/dist/utils/mergeRefs.js +7 -5
- package/dist/utils/rtlStyles.d.ts +2 -2
- package/dist/utils/rtlStyles.js +2 -2
- package/dist/utils/themeProps.d.ts +20 -1
- package/dist/utils/themeProps.d.ts.map +1 -1
- package/dist/utils/themeProps.js +8 -2
- package/locales/en.json +12 -0
- package/locales/pseudo.json +9 -0
- package/package.json +7 -2
- package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
- package/src/AlertDialog/AlertDialog.test.tsx +208 -1
- package/src/AlertDialog/AlertDialog.tsx +21 -3
- package/src/AppShell/AppShell.doc.mjs +11 -0
- package/src/AppShell/AppShell.test.tsx +139 -0
- package/src/AppShell/AppShell.tsx +32 -22
- package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
- package/src/AspectRatio/AspectRatio.test.tsx +30 -0
- package/src/AspectRatio/AspectRatio.tsx +13 -0
- package/src/Avatar/Avatar.doc.mjs +2 -13
- package/src/Avatar/Avatar.test.tsx +33 -8
- package/src/Avatar/Avatar.tsx +52 -50
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
- package/src/Banner/Banner.tsx +9 -6
- package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
- package/src/Breadcrumbs/index.ts +5 -1
- package/src/Button/Button.tsx +21 -41
- package/src/Calendar/Calendar.tsx +2 -1
- package/src/Calendar/styles.ts +0 -8
- package/src/Carousel/Carousel.doc.mjs +3 -0
- package/src/Carousel/Carousel.tsx +10 -6
- package/src/Chat/Chat.doc.mjs +2 -0
- package/src/Chat/ChatDictationButton.tsx +7 -1
- package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
- package/src/Chat/ChatSendButton.tsx +2 -3
- package/src/Chat/ChatToolCalls.test.tsx +35 -0
- package/src/Chat/ChatToolCalls.tsx +36 -16
- package/src/Chat/useTriggerMenu.tsx +2 -5
- package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
- package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
- package/src/CheckboxInput/CheckboxInput.tsx +45 -202
- package/src/ClickableCard/ClickableCard.tsx +6 -11
- package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
- package/src/CodeBlock/CodeBlock.test.tsx +91 -0
- package/src/CodeBlock/CodeBlock.tsx +57 -80
- package/src/Collapsible/Collapsible.tsx +22 -18
- package/src/CommandPalette/CommandPalette.test.tsx +29 -0
- package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
- package/src/CommandPalette/CommandPaletteList.tsx +2 -2
- package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
- package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
- package/src/ComplexSelector/ComplexSelector.tsx +46 -29
- package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
- package/src/ContextMenu/ContextMenu.test.tsx +41 -0
- package/src/ContextMenu/ContextMenu.tsx +3 -3
- package/src/ContextMenu/index.ts +5 -1
- package/src/DateInput/DateInput.doc.mjs +18 -3
- package/src/DateInput/DateInput.test.tsx +93 -11
- package/src/DateInput/DateInput.tsx +41 -19
- package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
- package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
- package/src/DateRangeInput/DateRangeInput.tsx +24 -12
- package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
- package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
- package/src/DateTimeInput/DateTimeInput.tsx +35 -8
- package/src/Dialog/Dialog.tsx +2 -9
- package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
- package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
- package/src/DropdownMenu/DropdownMenu.tsx +113 -52
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
- package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
- package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
- package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
- package/src/DropdownMenu/index.ts +8 -1
- package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
- package/src/EmptyState/EmptyState.doc.mjs +4 -0
- package/src/EmptyState/EmptyState.test.tsx +62 -0
- package/src/EmptyState/EmptyState.tsx +16 -6
- package/src/Field/Field.doc.mjs +1 -0
- package/src/Field/InputClearButton.test.tsx +117 -0
- package/src/Field/InputClearButton.tsx +30 -3
- package/src/HoverCard/useHoverCard.tsx +2 -23
- package/src/Icon/Icon.doc.mjs +2 -1
- package/src/Indicator/CheckIndicator.tsx +174 -0
- package/src/Indicator/CheckboxIndicator.tsx +257 -0
- package/src/Indicator/Indicator.doc.mjs +344 -0
- package/src/Indicator/Indicator.test.tsx +393 -0
- package/src/Indicator/RadioIndicator.tsx +206 -0
- package/src/Indicator/index.ts +45 -0
- package/src/Indicator/indicator.markers.stylex.ts +19 -0
- package/src/Indicator/indicatorRegistry.test.tsx +106 -0
- package/src/Indicator/indicatorRegistry.ts +120 -0
- package/src/Indicator/types.ts +186 -0
- package/src/Indicator/useIndicator.ts +39 -0
- package/src/Item/Item.doc.mjs +1 -1
- package/src/Item/Item.tsx +7 -14
- package/src/Layer/useLayer.doc.mjs +2 -2
- package/src/Layer/useLayer.test.tsx +85 -0
- package/src/Layer/useLayer.tsx +39 -1
- package/src/Lightbox/Lightbox.doc.mjs +1 -1
- package/src/Lightbox/Lightbox.tsx +43 -38
- package/src/Link/Link.tsx +4 -11
- package/src/MobileNav/MobileNav.tsx +3 -3
- package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
- package/src/MoreMenu/MoreMenu.test.tsx +68 -1
- package/src/MoreMenu/MoreMenu.tsx +19 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
- package/src/MultiSelector/MultiSelector.test.tsx +255 -16
- package/src/MultiSelector/MultiSelector.tsx +103 -99
- package/src/NumberInput/NumberInput.doc.mjs +265 -50
- package/src/NumberInput/NumberInput.test.tsx +726 -21
- package/src/NumberInput/NumberInput.tsx +353 -59
- package/src/Outline/Outline.tsx +2 -5
- package/src/Pagination/Pagination.doc.mjs +1 -1
- package/src/Pagination/Pagination.test.tsx +22 -7
- package/src/Pagination/Pagination.tsx +38 -35
- package/src/Popover/Popover.tsx +6 -7
- package/src/PowerSearch/PowerSearch.tsx +1 -1
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
- package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
- package/src/ProgressBar/ProgressBar.test.tsx +224 -1
- package/src/ProgressBar/ProgressBar.tsx +124 -22
- package/src/RadioList/RadioList.doc.mjs +4 -2
- package/src/RadioList/RadioListItem.tsx +31 -130
- package/src/Resizable/ResizeHandle.tsx +2 -9
- package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
- package/src/SelectableCard/SelectableCard.tsx +4 -9
- package/src/Selector/Selector.doc.mjs +7 -4
- package/src/Selector/Selector.test.tsx +935 -25
- package/src/Selector/Selector.tsx +189 -106
- package/src/Selector/hooks.ts +77 -50
- package/src/SideNav/SideNav.test.tsx +46 -0
- package/src/SideNav/SideNav.tsx +1 -0
- package/src/SideNav/SideNavCollapseButton.tsx +15 -10
- package/src/SideNav/SideNavHeading.tsx +48 -10
- package/src/SideNav/SideNavItem.tsx +20 -12
- package/src/Stack/Stack.doc.mjs +110 -0
- package/src/StatusDot/StatusDot.doc.mjs +12 -3
- package/src/Switch/Switch.test.tsx +32 -0
- package/src/Switch/Switch.tsx +8 -8
- package/src/TabList/Tab.tsx +2 -9
- package/src/TabList/TabMenu.tsx +14 -21
- package/src/Table/BaseTable.tsx +11 -0
- package/src/Table/index.ts +1 -4
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
- package/src/Table/plugins/rowExpansion/index.ts +1 -4
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
- package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
- package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
- package/src/Table/tableContextMenu.test.tsx +27 -0
- package/src/Table/tableContextMenu.tsx +1 -0
- package/src/Table/types.ts +11 -0
- package/src/Table/useTableGroupedRows.doc.mjs +1 -1
- package/src/Table/useTableRowExpansion.doc.mjs +43 -66
- package/src/TextArea/TextArea.doc.mjs +42 -2
- package/src/TextArea/TextArea.test.tsx +219 -0
- package/src/TextArea/TextArea.tsx +34 -7
- package/src/TextInput/TextInput.doc.mjs +18 -2
- package/src/TextInput/TextInput.test.tsx +189 -0
- package/src/TextInput/TextInput.tsx +25 -31
- package/src/Thumbnail/Thumbnail.tsx +2 -9
- package/src/TimeInput/TimeInput.doc.mjs +2 -2
- package/src/TimeInput/TimeInput.test.tsx +30 -3
- package/src/TimeInput/TimeInput.tsx +9 -26
- package/src/Timestamp/Timestamp.doc.mjs +4 -3
- package/src/Timestamp/TimestampHoverCard.tsx +13 -35
- package/src/Token/Token.tsx +5 -28
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
- package/src/Tokenizer/Tokenizer.test.tsx +45 -3
- package/src/Tokenizer/Tokenizer.tsx +10 -2
- package/src/Tooltip/useTooltip.tsx +2 -23
- package/src/TopNav/TopNav.tsx +1 -3
- package/src/TopNav/TopNavHeading.tsx +42 -13
- package/src/TopNav/TopNavItem.tsx +3 -21
- package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
- package/src/TopNav/TopNavMegaMenu.tsx +118 -53
- package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
- package/src/TopNav/TopNavMenu.tsx +34 -37
- package/src/TreeList/TreeList.doc.mjs +5 -0
- package/src/TreeList/TreeList.test.tsx +206 -7
- package/src/TreeList/TreeList.tsx +22 -0
- package/src/TreeList/TreeListBranches.tsx +25 -2
- package/src/TreeList/TreeListItem.tsx +89 -52
- package/src/Typeahead/BaseTypeahead.tsx +19 -6
- package/src/Typeahead/Typeahead.doc.mjs +1 -0
- package/src/Typeahead/Typeahead.test.tsx +76 -3
- package/src/hooks/containerReveal.stylex.ts +102 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useClipboard.doc.mjs +87 -0
- package/src/hooks/useClipboard.test.tsx +151 -0
- package/src/hooks/useClipboard.ts +135 -0
- package/src/hooks/useContainerReveal.doc.mjs +7 -7
- package/src/hooks/useContainerReveal.test.tsx +42 -62
- package/src/hooks/useContainerReveal.ts +36 -91
- package/src/hooks/useImageMode.test.ts +1 -1
- package/src/hooks/useIndicatorFocusRing.tsx +120 -0
- package/src/hooks/useLongPress.test.tsx +18 -0
- package/src/hooks/useLongPress.ts +12 -2
- package/src/hooks/useScrollLock.test.ts +72 -0
- package/src/hooks/useScrollLock.ts +51 -19
- package/src/hooks/useStreamingText.test.ts +46 -1
- package/src/hooks/useStreamingText.ts +49 -7
- package/src/hooks/useTreeFocus.test.tsx +50 -0
- package/src/hooks/useTreeFocus.ts +14 -3
- package/src/hooks/useTypeahead.test.tsx +150 -0
- package/src/hooks/useTypeahead.ts +24 -9
- package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
- package/src/index.ts +1 -0
- package/src/theme/defineTheme.ts +24 -0
- package/src/theme/derivedVarRegistry.test.ts +21 -1
- package/src/theme/derivedVarRegistry.ts +19 -6
- package/src/theme/expandColorScale.test.ts +14 -0
- package/src/theme/expandColorScale.ts +24 -9
- package/src/theme/expandTypeScale.test.ts +43 -0
- package/src/theme/expandTypeScale.ts +17 -0
- package/src/theme/generateThemeRules.test.ts +61 -20
- package/src/theme/generateThemeRules.ts +11 -0
- package/src/theme/index.ts +13 -0
- package/src/utils/focusOutline.stylex.ts +163 -0
- package/src/utils/focusReturn.test.ts +42 -0
- package/src/utils/focusReturn.ts +31 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/mergeRefs.test.ts +48 -0
- package/src/utils/mergeRefs.ts +12 -6
- package/src/utils/rtlStyles.ts +2 -2
- package/src/utils/themeProps.ts +26 -4
- package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
- package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
- package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
- package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
- package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
- package/dist/RadioList/radio.markers.stylex.js +0 -12
- package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
- package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
- package/dist/hooks/containerReveal.pool.stylex.js +0 -292
- package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
- package/src/RadioList/radio.markers.stylex.ts +0 -10
- package/src/hooks/containerReveal.pool.stylex.ts +0 -571
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
import {render, renderHook, screen} from '@testing-library/react';
|
|
5
|
+
import type {PropsWithChildren, ReactNode} from 'react';
|
|
6
|
+
import {Theme} from '../theme/Theme';
|
|
7
|
+
import {defineTheme} from '../theme/defineTheme';
|
|
8
|
+
import {CheckboxIndicator} from './CheckboxIndicator';
|
|
9
|
+
import {CheckIndicator} from './CheckIndicator';
|
|
10
|
+
import {RadioIndicator} from './RadioIndicator';
|
|
11
|
+
import {getIndicator} from './indicatorRegistry';
|
|
12
|
+
import {useIndicator} from './useIndicator';
|
|
13
|
+
import type {IndicatorProps} from './types';
|
|
14
|
+
|
|
15
|
+
function createThemeWrapper(theme: ReturnType<typeof defineTheme>) {
|
|
16
|
+
function ThemeWrapper({children}: PropsWithChildren): ReactNode {
|
|
17
|
+
return <Theme theme={theme}>{children}</Theme>;
|
|
18
|
+
}
|
|
19
|
+
return ThemeWrapper;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('default indicators', () => {
|
|
23
|
+
it('renders the checkbox theme target with state and size', () => {
|
|
24
|
+
render(<CheckboxIndicator state="indeterminate" size="sm" />);
|
|
25
|
+
|
|
26
|
+
const box = document.querySelector('.astryx-checkbox');
|
|
27
|
+
expect(box).toBeInTheDocument();
|
|
28
|
+
expect(box).toHaveAttribute('data-checked', 'indeterminate');
|
|
29
|
+
expect(box).toHaveAttribute('data-size', 'sm');
|
|
30
|
+
// Decorative: the owning control keeps the role and accessible name.
|
|
31
|
+
expect(box).toHaveAttribute('aria-hidden', 'true');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders the radio dot only when checked, in both states', () => {
|
|
35
|
+
const {rerender} = render(<RadioIndicator state="unchecked" />);
|
|
36
|
+
|
|
37
|
+
// The circle draws in the unchecked state — this is what lets a radio act
|
|
38
|
+
// as a selection indicator where an icon would render nothing.
|
|
39
|
+
expect(document.querySelector('.astryx-radio')).toBeInTheDocument();
|
|
40
|
+
expect(document.querySelector('.astryx-radio-dot')).not.toBeInTheDocument();
|
|
41
|
+
|
|
42
|
+
rerender(<RadioIndicator state="checked" />);
|
|
43
|
+
expect(document.querySelector('.astryx-radio-dot')).toBeInTheDocument();
|
|
44
|
+
expect(document.querySelector('.astryx-radio')).toHaveAttribute(
|
|
45
|
+
'data-checked',
|
|
46
|
+
'checked',
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('renders children instead of the state mark', () => {
|
|
51
|
+
render(
|
|
52
|
+
<CheckboxIndicator state="checked">
|
|
53
|
+
<span data-testid="busy" />
|
|
54
|
+
</CheckboxIndicator>,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(screen.getByTestId('busy')).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('reflects the disabled state for theme targeting', () => {
|
|
61
|
+
render(<RadioIndicator state="checked" isDisabled />);
|
|
62
|
+
|
|
63
|
+
expect(document.querySelector('.astryx-radio')).toHaveAttribute(
|
|
64
|
+
'data-disabled',
|
|
65
|
+
'disabled',
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* CheckIndicator is the one indicator that draws nothing in a state, which is
|
|
72
|
+
* what makes it the default selection mark — and what made its two escape
|
|
73
|
+
* hatches (props and children) easy to get wrong. Both are pinned here.
|
|
74
|
+
*/
|
|
75
|
+
describe('CheckIndicator', () => {
|
|
76
|
+
it('draws the mark when checked and nothing when not', () => {
|
|
77
|
+
const {container, rerender} = render(<CheckIndicator state="checked" />);
|
|
78
|
+
|
|
79
|
+
const mark = container.querySelector('.astryx-icon');
|
|
80
|
+
expect(mark).toBeInTheDocument();
|
|
81
|
+
expect(mark).toHaveAttribute('aria-hidden', 'true');
|
|
82
|
+
|
|
83
|
+
rerender(<CheckIndicator state="unchecked" />);
|
|
84
|
+
// No empty box beside an unchosen row — the reason a check is the default.
|
|
85
|
+
expect(container).toBeEmptyDOMElement();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('renders children INSTEAD of the mark, in both states', () => {
|
|
89
|
+
// A host shows a pending Spinner through `children` in whatever state the
|
|
90
|
+
// row is in, and an unchosen row is the common one. The unchecked case
|
|
91
|
+
// used to render nothing at all.
|
|
92
|
+
for (const state of ['checked', 'unchecked'] as const) {
|
|
93
|
+
const {unmount} = render(
|
|
94
|
+
<CheckIndicator state={state}>
|
|
95
|
+
<span data-testid="busy" />
|
|
96
|
+
</CheckIndicator>,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(screen.getByTestId('busy'), `state=${state}`).toBeInTheDocument();
|
|
100
|
+
// The mark is replaced, not accompanied.
|
|
101
|
+
expect(document.querySelector('.astryx-icon')).not.toBeInTheDocument();
|
|
102
|
+
unmount();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('forwards caller props and styling on both render paths', () => {
|
|
107
|
+
// Same contract whether or not children are present: a data-testid, an id
|
|
108
|
+
// and an xstyle-driven class must survive either branch.
|
|
109
|
+
const {container, rerender} = render(
|
|
110
|
+
<CheckIndicator state="checked" data-testid="mark" id="pinned" />,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(screen.getByTestId('mark')).toHaveAttribute('id', 'pinned');
|
|
114
|
+
|
|
115
|
+
rerender(
|
|
116
|
+
<CheckIndicator
|
|
117
|
+
state="checked"
|
|
118
|
+
data-testid="mark"
|
|
119
|
+
id="pinned"
|
|
120
|
+
className="host-target">
|
|
121
|
+
<span data-testid="busy" />
|
|
122
|
+
</CheckIndicator>,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const withChildren = screen.getByTestId('mark');
|
|
126
|
+
expect(withChildren).toHaveAttribute('id', 'pinned');
|
|
127
|
+
expect(withChildren).toHaveClass('host-target');
|
|
128
|
+
expect(withChildren).toHaveAttribute('aria-hidden', 'true');
|
|
129
|
+
expect(container.querySelector('[data-testid="busy"]')).toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The busy idiom a host actually writes is `children={isBusy && <Spinner/>}`,
|
|
135
|
+
* which passes `false` when it is not busy. `false` is non-null and is not
|
|
136
|
+
* caught by `??`, so both a `!= null` guard and a `children ?? mark` fallback
|
|
137
|
+
* take the children path, render nothing in it, and delete the state mark
|
|
138
|
+
* (#4893). Every indicator gets the same case, because all three had the bug.
|
|
139
|
+
*/
|
|
140
|
+
describe('falsy children never suppress the state mark (#4893)', () => {
|
|
141
|
+
const cases = [
|
|
142
|
+
{
|
|
143
|
+
name: 'CheckIndicator',
|
|
144
|
+
render: (children: ReactNode) =>
|
|
145
|
+
render(<CheckIndicator state="checked">{children}</CheckIndicator>),
|
|
146
|
+
markSelector: '.astryx-icon',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'CheckboxIndicator',
|
|
150
|
+
render: (children: ReactNode) =>
|
|
151
|
+
render(
|
|
152
|
+
<CheckboxIndicator state="checked">{children}</CheckboxIndicator>,
|
|
153
|
+
),
|
|
154
|
+
markSelector: 'svg',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'RadioIndicator',
|
|
158
|
+
render: (children: ReactNode) =>
|
|
159
|
+
render(<RadioIndicator state="checked">{children}</RadioIndicator>),
|
|
160
|
+
markSelector: '.astryx-radio-indicator-dot',
|
|
161
|
+
},
|
|
162
|
+
] as const;
|
|
163
|
+
|
|
164
|
+
// Everything React renders as nothing. `0` is deliberately absent: it
|
|
165
|
+
// renders the visible text "0", so it IS content and must replace the mark.
|
|
166
|
+
const emptyValues = [
|
|
167
|
+
['false — the `isBusy && …` idiom', false],
|
|
168
|
+
['null', null],
|
|
169
|
+
['undefined', undefined],
|
|
170
|
+
['empty string', ''],
|
|
171
|
+
] as const;
|
|
172
|
+
|
|
173
|
+
for (const {name, render: renderCase, markSelector} of cases) {
|
|
174
|
+
for (const [label, child] of emptyValues) {
|
|
175
|
+
it(`${name} keeps its mark when children is ${label}`, () => {
|
|
176
|
+
const {container} = renderCase(child);
|
|
177
|
+
|
|
178
|
+
expect(
|
|
179
|
+
container.querySelector(markSelector),
|
|
180
|
+
`${name} lost its mark to a falsy child`,
|
|
181
|
+
).toBeInTheDocument();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
it(`${name} still lets real children replace the mark`, () => {
|
|
186
|
+
// The negative control: without this, "always render the mark" would
|
|
187
|
+
// pass every case above and break the busy state instead.
|
|
188
|
+
const {container} = renderCase(<span data-testid="busy" />);
|
|
189
|
+
|
|
190
|
+
expect(
|
|
191
|
+
container.querySelector('[data-testid="busy"]'),
|
|
192
|
+
).toBeInTheDocument();
|
|
193
|
+
expect(container.querySelector(markSelector)).not.toBeInTheDocument();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
it('treats 0 as content, not as empty', () => {
|
|
198
|
+
// isRenderable's documented edge: 0 renders the character "0".
|
|
199
|
+
const {container} = render(
|
|
200
|
+
<CheckIndicator state="checked">{0}</CheckIndicator>,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
expect(container.textContent).toBe('0');
|
|
204
|
+
expect(container.querySelector('.astryx-icon')).not.toBeInTheDocument();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* An indicator is decorative BY CONTRACT: the owning control supplies the role
|
|
210
|
+
* and the accessible name, so an indicator that is also announced says the same
|
|
211
|
+
* thing twice (#4918).
|
|
212
|
+
*
|
|
213
|
+
* The contract is held in two places, because neither covers the whole thing:
|
|
214
|
+
*
|
|
215
|
+
* - `IndicatorProps` omits the a11y props, which makes `role` a compile
|
|
216
|
+
* error. It does NOT make `aria-hidden` one: TypeScript exempts JSX
|
|
217
|
+
* attribute names that are not valid JS identifiers from excess-property
|
|
218
|
+
* checking, so anything hyphenated slips through. That is a language rule,
|
|
219
|
+
* not a gap in our types — the second test below is the proof, and it will
|
|
220
|
+
* start failing the day TS changes its mind, which is when we can drop the
|
|
221
|
+
* ordering.
|
|
222
|
+
* - So each component emits its own `aria-hidden` AFTER `{...rest}`, which is
|
|
223
|
+
* what actually keeps a caller from un-hiding it. Nothing is stripped.
|
|
224
|
+
*/
|
|
225
|
+
describe('the decorative contract (#4918)', () => {
|
|
226
|
+
it('rejects `role` and `tabIndex` at compile time', () => {
|
|
227
|
+
// Both are valid JS identifiers, so excess-property checking applies and
|
|
228
|
+
// the type alone is enough — no runtime guard needed. `tabIndex` matters
|
|
229
|
+
// because the element is unconditionally aria-hidden: a tab stop on it is
|
|
230
|
+
// a focusable node in a hidden subtree (axe `aria-hidden-focus`).
|
|
231
|
+
const rejected = [
|
|
232
|
+
// @ts-expect-error — the owning control holds the role.
|
|
233
|
+
<CheckIndicator key="a" state="checked" role="checkbox" />,
|
|
234
|
+
// @ts-expect-error — the owning control holds the focus.
|
|
235
|
+
<CheckboxIndicator key="b" state="checked" tabIndex={0} />,
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
expect(rejected).toHaveLength(2);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('cannot reject a hyphenated a11y attribute — TS exempts those', () => {
|
|
242
|
+
// NO @ts-expect-error here, deliberately: this compiles, and the comment
|
|
243
|
+
// above explains why. Runtime order is what makes it harmless.
|
|
244
|
+
const accepted = <CheckIndicator state="checked" aria-label="inert" />;
|
|
245
|
+
|
|
246
|
+
expect(accepted).toBeTruthy();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('cannot reject a SPREAD either — which is the ordinary host idiom', () => {
|
|
250
|
+
// Also no @ts-expect-error, also deliberate. A spread bypasses
|
|
251
|
+
// excess-property checking for every member, so even `role` and `tabIndex`
|
|
252
|
+
// — the two the type catches as literals — get through this way.
|
|
253
|
+
//
|
|
254
|
+
// Left alone on purpose. Nothing in the repo spreads a hostile object at an
|
|
255
|
+
// indicator, `aria-hidden` is settled by attribute order regardless, and a
|
|
256
|
+
// forwarded `role`/`aria-label` is inert inside a hidden subtree. Guarding
|
|
257
|
+
// it at runtime would cost a module to defend a case no call site reaches.
|
|
258
|
+
const hostile = {role: 'checkbox', tabIndex: 0};
|
|
259
|
+
const accepted = <CheckIndicator state="checked" {...hostile} />;
|
|
260
|
+
|
|
261
|
+
expect(accepted).toBeTruthy();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const cases = [
|
|
265
|
+
{
|
|
266
|
+
name: 'CheckIndicator (glyph path)',
|
|
267
|
+
render: (p: Record<string, unknown>) => (
|
|
268
|
+
<CheckIndicator state="checked" {...p} />
|
|
269
|
+
),
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'CheckIndicator (children path)',
|
|
273
|
+
render: (p: Record<string, unknown>) => (
|
|
274
|
+
<CheckIndicator state="checked" {...p}>
|
|
275
|
+
<b />
|
|
276
|
+
</CheckIndicator>
|
|
277
|
+
),
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: 'CheckboxIndicator',
|
|
281
|
+
render: (p: Record<string, unknown>) => (
|
|
282
|
+
<CheckboxIndicator state="checked" {...p} />
|
|
283
|
+
),
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: 'RadioIndicator',
|
|
287
|
+
render: (p: Record<string, unknown>) => (
|
|
288
|
+
<RadioIndicator state="checked" {...p} />
|
|
289
|
+
),
|
|
290
|
+
},
|
|
291
|
+
] as const;
|
|
292
|
+
|
|
293
|
+
for (const {name, render: renderCase} of cases) {
|
|
294
|
+
it(`${name} stays aria-hidden even when a caller passes false`, () => {
|
|
295
|
+
const {container} = render(renderCase({'aria-hidden': 'false'}));
|
|
296
|
+
|
|
297
|
+
expect(container.firstElementChild).toHaveAttribute(
|
|
298
|
+
'aria-hidden',
|
|
299
|
+
'true',
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it(`${name} still forwards ordinary props`, () => {
|
|
304
|
+
// The negative control: order must not turn into "drop everything".
|
|
305
|
+
const {container} = render(
|
|
306
|
+
renderCase({'data-testid': 'ind', id: 'pinned', dir: 'rtl'}),
|
|
307
|
+
);
|
|
308
|
+
const el = container.firstElementChild;
|
|
309
|
+
|
|
310
|
+
expect(el).toHaveAttribute('data-testid', 'ind');
|
|
311
|
+
expect(el).toHaveAttribute('id', 'pinned');
|
|
312
|
+
expect(el).toHaveAttribute('dir', 'rtl');
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe('useIndicator', () => {
|
|
318
|
+
it('returns the built-in indicator without a theme override', () => {
|
|
319
|
+
const {result} = renderHook(() => useIndicator('checkbox'));
|
|
320
|
+
|
|
321
|
+
expect(result.current).toBe(CheckboxIndicator);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('resolves an indicator component from the nearest theme', () => {
|
|
325
|
+
function BrandCheckbox({state}: IndicatorProps) {
|
|
326
|
+
return <span data-testid="brand">{state}</span>;
|
|
327
|
+
}
|
|
328
|
+
const theme = defineTheme({
|
|
329
|
+
name: 'brand-indicators',
|
|
330
|
+
indicators: {checkbox: BrandCheckbox},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const {result} = renderHook(() => useIndicator('checkbox'), {
|
|
334
|
+
wrapper: createThemeWrapper(theme),
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(result.current).toBe(BrandCheckbox);
|
|
338
|
+
// Unmapped indicators keep the built-in.
|
|
339
|
+
expect(getIndicator('radio', theme)).toBe(RadioIndicator);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* A theme target is public API. Renaming one to follow the
|
|
345
|
+
* `<component>-kebab` convention (`checkbox` → `checkbox-indicator`) would
|
|
346
|
+
* silently break every theme styling the old name — the CSS still compiles, it
|
|
347
|
+
* just stops matching. So both names are emitted for a deprecation window, and
|
|
348
|
+
* these tests pin that promise from both ends: the new name exists, and the
|
|
349
|
+
* old one has not quietly disappeared.
|
|
350
|
+
*/
|
|
351
|
+
describe('renamed theme targets stay non-breaking', () => {
|
|
352
|
+
const cases = [
|
|
353
|
+
{
|
|
354
|
+
name: 'CheckboxIndicator',
|
|
355
|
+
render: () => <CheckboxIndicator state="checked" />,
|
|
356
|
+
current: 'astryx-checkbox-indicator',
|
|
357
|
+
legacy: 'astryx-checkbox',
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'RadioIndicator',
|
|
361
|
+
render: () => <RadioIndicator state="checked" />,
|
|
362
|
+
current: 'astryx-radio-indicator',
|
|
363
|
+
legacy: 'astryx-radio',
|
|
364
|
+
},
|
|
365
|
+
] as const;
|
|
366
|
+
|
|
367
|
+
for (const {name, render: renderCase, current, legacy} of cases) {
|
|
368
|
+
it(`${name} emits both the current and the legacy target`, () => {
|
|
369
|
+
const {container} = render(renderCase());
|
|
370
|
+
const el = container.querySelector(`.${current}`);
|
|
371
|
+
expect(el, `${name} should render ${current}`).toBeInTheDocument();
|
|
372
|
+
expect(el, `${name} must keep emitting ${legacy}`).toHaveClass(legacy);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
it('keeps the legacy dot target on the radio mark', () => {
|
|
377
|
+
const {container} = render(<RadioIndicator state="checked" />);
|
|
378
|
+
const dot = container.querySelector('.astryx-radio-indicator-dot');
|
|
379
|
+
expect(dot).toBeInTheDocument();
|
|
380
|
+
expect(dot).toHaveClass('astryx-radio-dot');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('puts both names on ONE element, so either selector wins equally', () => {
|
|
384
|
+
// If the legacy class were moved to a wrapper instead, an old theme's
|
|
385
|
+
// rules would land on a different box than a new theme's — same-element
|
|
386
|
+
// is what makes the two names interchangeable.
|
|
387
|
+
const {container} = render(<CheckboxIndicator state="unchecked" />);
|
|
388
|
+
expect(container.querySelectorAll('.astryx-checkbox')).toHaveLength(1);
|
|
389
|
+
expect(container.querySelector('.astryx-checkbox')).toBe(
|
|
390
|
+
container.querySelector('.astryx-checkbox-indicator'),
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file RadioIndicator.tsx
|
|
5
|
+
* @input Indicator state props
|
|
6
|
+
* @output Exports RadioIndicator — the default radio selection visual
|
|
7
|
+
* @position Decorative radio visual shared by RadioList, decorative menu
|
|
8
|
+
* markers, and any selection slot themed to use a radio
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as stylex from '@stylexjs/stylex';
|
|
12
|
+
import {
|
|
13
|
+
borderVars,
|
|
14
|
+
colorVars,
|
|
15
|
+
durationVars,
|
|
16
|
+
easeVars,
|
|
17
|
+
radiusVars,
|
|
18
|
+
} from '../theme/tokens.stylex';
|
|
19
|
+
import {isRenderable, mergeProps, themeProps} from '../utils';
|
|
20
|
+
import {indicatorScope} from './indicator.markers.stylex';
|
|
21
|
+
import type {IndicatorProps} from './types';
|
|
22
|
+
|
|
23
|
+
const styles = stylex.create({
|
|
24
|
+
circle: {
|
|
25
|
+
boxSizing: 'border-box',
|
|
26
|
+
display: 'flex',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
flexShrink: 0,
|
|
30
|
+
borderWidth: borderVars['--border-width'],
|
|
31
|
+
borderStyle: 'solid',
|
|
32
|
+
// A circle, in tokens: --radius-full is the house "fully rounded" value
|
|
33
|
+
// (9999px) and renders pixel-identical to 50% on a square box, which the
|
|
34
|
+
// size styles below guarantee. Themeable through --radius-full, unlike a
|
|
35
|
+
// raw 50%.
|
|
36
|
+
borderRadius: radiusVars['--radius-full'],
|
|
37
|
+
transitionProperty: 'background-color, border-color',
|
|
38
|
+
transitionDuration: {
|
|
39
|
+
default: durationVars['--duration-fast'],
|
|
40
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
41
|
+
},
|
|
42
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
43
|
+
},
|
|
44
|
+
unchecked: {
|
|
45
|
+
borderColor: {
|
|
46
|
+
default: colorVars['--color-border-emphasized'],
|
|
47
|
+
[stylex.when.ancestor(':hover', indicatorScope)]: {
|
|
48
|
+
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-border-emphasized']}, ${colorVars['--color-tint-hover']} 20%)`,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
backgroundColor: {
|
|
52
|
+
default: colorVars['--color-background-surface'],
|
|
53
|
+
[stylex.when.ancestor(':hover', indicatorScope)]: {
|
|
54
|
+
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-background-surface']}, ${colorVars['--color-tint-hover']} 5%)`,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
checked: {
|
|
59
|
+
borderColor: {
|
|
60
|
+
default: colorVars['--color-accent'],
|
|
61
|
+
[stylex.when.ancestor(':hover', indicatorScope)]: {
|
|
62
|
+
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-accent']}, ${colorVars['--color-tint-hover']} 15%)`,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
backgroundColor: {
|
|
66
|
+
default: colorVars['--color-accent'],
|
|
67
|
+
[stylex.when.ancestor(':hover', indicatorScope)]: {
|
|
68
|
+
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-accent']}, ${colorVars['--color-tint-hover']} 15%)`,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
disabled: {
|
|
73
|
+
opacity: 0.5,
|
|
74
|
+
borderColor: {
|
|
75
|
+
default: colorVars['--color-border'],
|
|
76
|
+
[stylex.when.ancestor(':hover', indicatorScope)]: {
|
|
77
|
+
'@media (hover: hover)': colorVars['--color-border'],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
disabledUnchecked: {
|
|
82
|
+
backgroundColor: {
|
|
83
|
+
default: colorVars['--color-background-muted'],
|
|
84
|
+
[stylex.when.ancestor(':hover', indicatorScope)]: {
|
|
85
|
+
'@media (hover: hover)': colorVars['--color-background-muted'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
dot: {
|
|
90
|
+
borderRadius: radiusVars['--radius-full'],
|
|
91
|
+
backgroundColor: {
|
|
92
|
+
default: colorVars['--color-on-accent'],
|
|
93
|
+
// Forced colors (Windows High Contrast) strips painted backgrounds,
|
|
94
|
+
// which would make the selected dot invisible — checked and unchecked
|
|
95
|
+
// radios would look identical. CanvasText keeps the dot perceivable on
|
|
96
|
+
// the Canvas circle fill (WCAG 1.4.11).
|
|
97
|
+
'@media (forced-colors: active)': 'CanvasText',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const circleSizeStyles = stylex.create({
|
|
103
|
+
sm: {
|
|
104
|
+
width: 20,
|
|
105
|
+
height: 20,
|
|
106
|
+
},
|
|
107
|
+
md: {
|
|
108
|
+
width: 24,
|
|
109
|
+
height: 24,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const dotSizeStyles = stylex.create({
|
|
114
|
+
sm: {
|
|
115
|
+
width: 8,
|
|
116
|
+
height: 8,
|
|
117
|
+
},
|
|
118
|
+
md: {
|
|
119
|
+
width: 10,
|
|
120
|
+
height: 10,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The default radio visual: a circle with a filled inner dot when selected.
|
|
126
|
+
*
|
|
127
|
+
* Decorative and non-interactive — it renders `aria-hidden` and owns no input,
|
|
128
|
+
* role, or focus behavior. Themes replace it wholesale through
|
|
129
|
+
* `defineTheme({indicators: {radio: MyRadio}})`, or restyle it through the
|
|
130
|
+
* `radio` / `radio-dot` theme targets like any other component.
|
|
131
|
+
*
|
|
132
|
+
* Unlike an icon, a radio draws in *both* states — an empty circle when
|
|
133
|
+
* unchecked. That is what makes it usable as a selection indicator in
|
|
134
|
+
* components whose default is "a checkmark when selected, nothing otherwise".
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```
|
|
138
|
+
* <RadioIndicator state="checked" size="md" />
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export function RadioIndicator({
|
|
142
|
+
state,
|
|
143
|
+
size = 'md',
|
|
144
|
+
isDisabled = false,
|
|
145
|
+
children,
|
|
146
|
+
ref,
|
|
147
|
+
className,
|
|
148
|
+
style,
|
|
149
|
+
xstyle,
|
|
150
|
+
...rest
|
|
151
|
+
}: IndicatorProps<'singleSelection'>) {
|
|
152
|
+
// A radio has no partial state; anything other than unchecked reads as
|
|
153
|
+
// selected.
|
|
154
|
+
const isChecked = state !== 'unchecked';
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<span
|
|
158
|
+
// `{...rest}` first, own contract after. TypeScript cannot reject a
|
|
159
|
+
// hyphenated JSX attribute (see IndicatorProps), so attribute order is
|
|
160
|
+
// what actually keeps a caller from un-hiding a decorative element —
|
|
161
|
+
// rubric P3, "owned aria-* set after {...rest}".
|
|
162
|
+
{...rest}
|
|
163
|
+
ref={ref}
|
|
164
|
+
aria-hidden="true"
|
|
165
|
+
{...mergeProps(
|
|
166
|
+
themeProps(
|
|
167
|
+
'radio-indicator',
|
|
168
|
+
{
|
|
169
|
+
size,
|
|
170
|
+
checked: isChecked ? 'checked' : null,
|
|
171
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
172
|
+
},
|
|
173
|
+
// `radio` was the target before indicators existed; themes styling
|
|
174
|
+
// it keep working until the next major.
|
|
175
|
+
{legacyNames: ['radio']},
|
|
176
|
+
),
|
|
177
|
+
stylex.props(
|
|
178
|
+
styles.circle,
|
|
179
|
+
circleSizeStyles[size],
|
|
180
|
+
isChecked ? styles.checked : styles.unchecked,
|
|
181
|
+
isDisabled && styles.disabled,
|
|
182
|
+
isDisabled && !isChecked && styles.disabledUnchecked,
|
|
183
|
+
xstyle,
|
|
184
|
+
),
|
|
185
|
+
className,
|
|
186
|
+
style,
|
|
187
|
+
)}>
|
|
188
|
+
{isRenderable(children)
|
|
189
|
+
? children
|
|
190
|
+
: isChecked && (
|
|
191
|
+
<span
|
|
192
|
+
{...mergeProps(
|
|
193
|
+
themeProps(
|
|
194
|
+
'radio-indicator-dot',
|
|
195
|
+
{size},
|
|
196
|
+
{legacyNames: ['radio-dot']},
|
|
197
|
+
),
|
|
198
|
+
stylex.props(styles.dot, dotSizeStyles[size]),
|
|
199
|
+
)}
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
</span>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
RadioIndicator.displayName = 'RadioIndicator';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file index.ts
|
|
5
|
+
* @input None (re-export barrel)
|
|
6
|
+
* @output Public indicator surface: components, registry, hook, types
|
|
7
|
+
* @position Entry point for the stateful control-visual layer
|
|
8
|
+
*
|
|
9
|
+
* Indicators are the componentized form of stateful control visuals — the
|
|
10
|
+
* checkbox box, the radio circle, the mark on a chosen option. They are
|
|
11
|
+
* decorative: the owning component keeps the role, state, and focus behavior,
|
|
12
|
+
* while the indicator turns state into a picture.
|
|
13
|
+
*
|
|
14
|
+
* That makes them themeable through CSS targets like any other component, and
|
|
15
|
+
* replaceable wholesale by name through `defineTheme({indicators})` — replace
|
|
16
|
+
* `check` and every single-selection mark in the app follows.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export {CheckboxIndicator} from './CheckboxIndicator';
|
|
20
|
+
export {CheckIndicator} from './CheckIndicator';
|
|
21
|
+
export {RadioIndicator} from './RadioIndicator';
|
|
22
|
+
|
|
23
|
+
// Registry (RSC-compatible, no 'use client')
|
|
24
|
+
export {defaultIndicators, getIndicator} from './indicatorRegistry';
|
|
25
|
+
export type {
|
|
26
|
+
CoreIndicatorName,
|
|
27
|
+
IndicatorRegistrySource,
|
|
28
|
+
} from './indicatorRegistry';
|
|
29
|
+
|
|
30
|
+
export {useIndicator} from './useIndicator';
|
|
31
|
+
|
|
32
|
+
export {indicatorScope} from './indicator.markers.stylex';
|
|
33
|
+
|
|
34
|
+
export type {
|
|
35
|
+
IndicatorComponent,
|
|
36
|
+
IndicatorFamily,
|
|
37
|
+
IndicatorFamilyMap,
|
|
38
|
+
IndicatorMap,
|
|
39
|
+
IndicatorName,
|
|
40
|
+
IndicatorNameOfFamily,
|
|
41
|
+
IndicatorProps,
|
|
42
|
+
IndicatorRegistry,
|
|
43
|
+
IndicatorSize,
|
|
44
|
+
IndicatorState,
|
|
45
|
+
} from './types';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import * as stylex from '@stylexjs/stylex';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scoped marker for indicator ancestor selectors.
|
|
7
|
+
*
|
|
8
|
+
* An owner (CheckboxInput's row, RadioListItem's row) applies this marker to
|
|
9
|
+
* the element whose hover and focus should drive the indicator's appearance.
|
|
10
|
+
* The indicator reads it through `stylex.when.ancestor()`, so interaction
|
|
11
|
+
* state stays in CSS instead of being threaded through React props — and
|
|
12
|
+
* owners that should *not* tint their indicator on hover (decorative menu
|
|
13
|
+
* markers, listbox options) simply don't apply the marker.
|
|
14
|
+
*
|
|
15
|
+
* Owners apply it only while enabled, so disabled controls get no hover
|
|
16
|
+
* feedback.
|
|
17
|
+
*/
|
|
18
|
+
export const indicatorScope: ReturnType<typeof stylex.defineMarker> =
|
|
19
|
+
stylex.defineMarker();
|