@astryxdesign/core 0.1.9 → 0.2.0-canary.136a00f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/dist/Avatar/Avatar.d.ts +42 -3
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +190 -59
- package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +58 -6
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
- package/dist/Banner/Banner.js +2 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
- package/dist/Breadcrumbs/index.d.ts +1 -0
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +2 -1
- package/dist/Button/Button.d.ts +1 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +6 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +50 -9
- package/dist/Calendar/dayCellUtils.d.ts +4 -4
- package/dist/Calendar/dayCellUtils.js +2 -2
- package/dist/Calendar/styles.d.ts +30 -31
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +30 -31
- package/dist/Card/Card.js +1 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +22 -12
- package/dist/Chat/ChatComposerInput.js +1 -1
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +3 -1
- package/dist/Chat/ChatLayout.js +4 -4
- package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
- package/dist/Chat/ChatMessageBubble.js +8 -8
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +10 -3
- package/dist/Chat/useTriggerMenu.js +2 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -6
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +19 -4
- package/dist/Citation/Citation.d.ts +22 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +20 -6
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +16 -10
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +3 -1
- package/dist/CommandPalette/CommandPalette.d.ts +1 -1
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +42 -3
- package/dist/CommandPalette/CommandPaletteItem.js +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +23 -5
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +3 -1
- package/dist/DateInput/DateInput.d.ts +10 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +33 -19
- package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +23 -20
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -18
- package/dist/Divider/Divider.d.ts +2 -2
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +13 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +22 -5
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
- package/dist/DropdownMenu/index.d.ts +10 -0
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +13 -0
- package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
- package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemHover.js +33 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
- package/dist/DropdownMenu/menuItemRoles.js +10 -1
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -6
- package/dist/Field/Field.d.ts +3 -1
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -1
- package/dist/Field/FieldLabel.d.ts +2 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -5
- package/dist/Field/inputStyles.stylex.d.ts +1 -0
- package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
- package/dist/Field/inputStyles.stylex.js +1 -0
- package/dist/FieldStatus/FieldStatus.d.ts +10 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +39 -3
- package/dist/FileInput/FileInput.d.ts +10 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +93 -65
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +10 -4
- package/dist/Icon/Icon.d.ts +15 -1
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +19 -3
- package/dist/Item/Item.d.ts +16 -3
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +31 -6
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +10 -2
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +19 -13
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +6 -3
- package/dist/List/ListItem.d.ts +11 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +2 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MetadataList/MetadataList.d.ts +1 -1
- package/dist/MetadataList/MetadataList.d.ts.map +1 -1
- package/dist/MetadataList/MetadataList.js +4 -2
- package/dist/MultiSelector/MultiSelector.d.ts +9 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +80 -42
- package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
- package/dist/NumberInput/NumberInput.d.ts +10 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +22 -17
- package/dist/Outline/Outline.js +2 -2
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +11 -3
- package/dist/PowerSearch/PowerSearch.d.ts +9 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +2 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +6 -1
- package/dist/RadioList/RadioList.d.ts +2 -2
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +5 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +49 -19
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
- package/dist/Selector/Selector.d.ts +8 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +66 -17
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +5 -5
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +13 -10
- package/dist/SideNav/SideNavItem.d.ts +1 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +12 -10
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +2 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +87 -34
- package/dist/Spinner/Spinner.d.ts +3 -2
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +11 -2
- package/dist/Switch/Switch.d.ts +8 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +171 -91
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts +2 -11
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +5 -9
- package/dist/TabList/TabListContext.d.ts +0 -5
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +0 -5
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +30 -4
- package/dist/Table/TableCell.js +3 -3
- package/dist/Table/TableHeaderCell.js +3 -3
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +3 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
- package/dist/Table/types.d.ts +32 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +10 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +18 -19
- package/dist/TextInput/TextInput.d.ts +10 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +20 -17
- package/dist/Thumbnail/Thumbnail.js +2 -2
- package/dist/TimeInput/TimeInput.d.ts +10 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +17 -17
- package/dist/Timestamp/Timestamp.d.ts +31 -3
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +47 -76
- package/dist/Timestamp/formatInstant.d.ts +41 -0
- package/dist/Timestamp/formatInstant.d.ts.map +1 -0
- package/dist/Timestamp/formatInstant.js +177 -0
- package/dist/Timestamp/index.d.ts +1 -0
- package/dist/Timestamp/index.d.ts.map +1 -1
- package/dist/Timestamp/tooltipEntries.d.ts +56 -0
- package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
- package/dist/Timestamp/tooltipEntries.js +163 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +61 -9
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +3 -1
- package/dist/Token/Token.d.ts +34 -4
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +85 -45
- package/dist/Token/TokenLink.d.ts +62 -0
- package/dist/Token/TokenLink.d.ts.map +1 -0
- package/dist/Token/TokenLink.js +76 -0
- package/dist/Token/index.d.ts +1 -1
- package/dist/Token/index.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts +9 -2
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +4 -2
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +17 -1
- package/dist/TopNav/TopNavItem.d.ts +3 -1
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +30 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +1 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +8 -6
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +32 -14
- package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +22 -11
- package/dist/Typeahead/Typeahead.d.ts +9 -2
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +11 -2
- package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/Typeahead/TypeaheadItem.js +20 -10
- package/dist/astryx.css +70 -30
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/isRtlElement.d.ts +18 -0
- package/dist/hooks/isRtlElement.d.ts.map +1 -0
- package/dist/hooks/isRtlElement.js +27 -0
- package/dist/hooks/useAnnounce.d.ts +3 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +39 -1
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +52 -11
- package/dist/hooks/useGridFocus.d.ts +6 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +12 -9
- package/dist/hooks/useHotkeys.d.ts +9 -0
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +9 -0
- package/dist/hooks/useInputStatusIcon.d.ts +38 -0
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
- package/dist/hooks/useInputStatusIcon.js +158 -0
- package/dist/hooks/useKeyboardHint.d.ts +1 -1
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
- package/dist/hooks/useKeyboardHint.js +4 -2
- package/dist/hooks/useListFocus.d.ts +39 -1
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +54 -9
- package/dist/i18n/InternationalizationContext.d.ts +1 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -0
- package/dist/i18n/InternationalizationProvider.d.ts +8 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +5 -1
- package/dist/i18n/getLocaleDirection.d.ts +18 -0
- package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
- package/dist/i18n/getLocaleDirection.js +29 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +7 -1
- package/dist/i18n/useDirection.d.ts +2 -0
- package/dist/i18n/useDirection.d.ts.map +1 -0
- package/dist/i18n/useDirection.js +27 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +13 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/plainDate.d.ts +17 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +23 -4
- package/dist/utils/rtlStyles.d.ts +24 -0
- package/dist/utils/rtlStyles.d.ts.map +1 -0
- package/dist/utils/rtlStyles.js +27 -0
- package/locales/en.json +112 -0
- package/locales/pseudo.json +84 -0
- package/package.json +6 -1
- package/src/Avatar/Avatar.doc.mjs +50 -3
- package/src/Avatar/Avatar.test.tsx +218 -0
- package/src/Avatar/Avatar.tsx +285 -79
- package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
- package/src/Avatar/AvatarStatusDot.tsx +6 -4
- package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
- package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
- package/src/AvatarGroup/AvatarGroup.tsx +58 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
- package/src/Banner/Banner.tsx +6 -6
- package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
- package/src/Breadcrumbs/index.ts +4 -0
- package/src/Button/Button.doc.mjs +0 -4
- package/src/Button/Button.test.tsx +16 -0
- package/src/Button/Button.tsx +6 -4
- package/src/Calendar/Calendar.test.tsx +170 -20
- package/src/Calendar/Calendar.tsx +73 -16
- package/src/Calendar/dayCellUtils.test.ts +18 -18
- package/src/Calendar/dayCellUtils.ts +2 -2
- package/src/Calendar/styles.ts +33 -39
- package/src/Card/Card.tsx +1 -1
- package/src/Carousel/Carousel.tsx +25 -6
- package/src/Chat/ChatComposer.doc.mjs +2 -2
- package/src/Chat/ChatComposerInput.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.tsx +2 -2
- package/src/Chat/ChatDictationButton.tsx +7 -1
- package/src/Chat/ChatLayout.tsx +4 -4
- package/src/Chat/ChatMessageBubble.tsx +15 -10
- package/src/Chat/ChatSystemMessage.test.tsx +5 -0
- package/src/Chat/ChatToolCalls.tsx +9 -2
- package/src/Chat/useTriggerMenu.tsx +1 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
- package/src/CheckboxList/CheckboxList.test.tsx +94 -3
- package/src/CheckboxList/CheckboxListItem.tsx +17 -10
- package/src/Citation/Citation.doc.mjs +4 -3
- package/src/Citation/Citation.test.tsx +97 -0
- package/src/Citation/Citation.tsx +43 -11
- package/src/CodeBlock/CodeBlock.test.tsx +17 -0
- package/src/CodeBlock/CodeBlock.tsx +38 -11
- package/src/Collapsible/Collapsible.doc.mjs +1 -0
- package/src/Collapsible/Collapsible.test.tsx +7 -0
- package/src/Collapsible/Collapsible.tsx +16 -6
- package/src/CommandPalette/CommandPalette.test.tsx +95 -1
- package/src/CommandPalette/CommandPalette.tsx +41 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
- package/src/ContextMenu/ContextMenu.test.tsx +59 -2
- package/src/ContextMenu/ContextMenu.tsx +22 -11
- package/src/ContextMenu/index.ts +5 -0
- package/src/DateInput/DateInput.doc.mjs +25 -0
- package/src/DateInput/DateInput.test.tsx +230 -0
- package/src/DateInput/DateInput.tsx +51 -29
- package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
- package/src/DateRangeInput/DateRangeInput.tsx +42 -30
- package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
- package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
- package/src/DateTimeInput/DateTimeInput.tsx +44 -39
- package/src/Divider/Divider.test.tsx +32 -8
- package/src/Divider/Divider.tsx +14 -1
- package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
- package/src/DropdownMenu/DropdownMenu.tsx +31 -12
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
- package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
- package/src/DropdownMenu/index.ts +16 -0
- package/src/DropdownMenu/menuItemHover.ts +39 -0
- package/src/DropdownMenu/menuItemRoles.ts +9 -0
- package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
- package/src/Field/Field.tsx +14 -9
- package/src/Field/FieldLabel.test.tsx +25 -0
- package/src/Field/FieldLabel.tsx +12 -3
- package/src/Field/inputStyles.stylex.ts +2 -0
- package/src/FieldStatus/FieldStatus.test.tsx +72 -0
- package/src/FieldStatus/FieldStatus.tsx +48 -2
- package/src/FileInput/FileInput.doc.mjs +23 -2
- package/src/FileInput/FileInput.test.tsx +104 -13
- package/src/FileInput/FileInput.tsx +106 -79
- package/src/HoverCard/useHoverCard.tsx +15 -3
- package/src/Icon/Icon.doc.mjs +13 -0
- package/src/Icon/Icon.test.tsx +89 -0
- package/src/Icon/Icon.tsx +51 -3
- package/src/Item/Item.doc.mjs +5 -0
- package/src/Item/Item.test.tsx +88 -0
- package/src/Item/Item.tsx +57 -11
- package/src/Layer/layerAnimations.stylex.ts +23 -0
- package/src/Lightbox/Lightbox.tsx +15 -7
- package/src/List/List.test.tsx +17 -29
- package/src/List/List.tsx +6 -1
- package/src/List/ListItem.doc.mjs +12 -0
- package/src/List/ListItem.tsx +13 -0
- package/src/Markdown/Markdown.test.tsx +21 -0
- package/src/Markdown/Markdown.tsx +4 -0
- package/src/MetadataList/MetadataList.test.tsx +24 -0
- package/src/MetadataList/MetadataList.tsx +6 -2
- package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
- package/src/MultiSelector/MultiSelector.test.tsx +402 -0
- package/src/MultiSelector/MultiSelector.tsx +120 -59
- package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +21 -0
- package/src/NumberInput/NumberInput.test.tsx +71 -0
- package/src/NumberInput/NumberInput.tsx +35 -26
- package/src/Outline/Outline.test.tsx +12 -12
- package/src/Outline/Outline.tsx +2 -2
- package/src/Pagination/Pagination.tsx +11 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +23 -0
- package/src/PowerSearch/PowerSearch.tsx +10 -1
- package/src/ProgressBar/ProgressBar.test.tsx +34 -0
- package/src/ProgressBar/ProgressBar.tsx +16 -1
- package/src/RadioList/RadioList.doc.mjs +6 -0
- package/src/RadioList/RadioList.test.tsx +26 -4
- package/src/RadioList/RadioList.tsx +2 -2
- package/src/RadioList/RadioListItem.tsx +12 -5
- package/src/Resizable/ResizeHandle.test.tsx +94 -0
- package/src/Resizable/ResizeHandle.tsx +70 -20
- package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
- package/src/Selector/Selector.doc.mjs +15 -0
- package/src/Selector/Selector.test.tsx +370 -0
- package/src/Selector/Selector.tsx +88 -20
- package/src/SideNav/SideNav.tsx +1 -8
- package/src/SideNav/SideNavCollapseButton.tsx +9 -7
- package/src/SideNav/SideNavItem.tsx +5 -3
- package/src/Skeleton/Skeleton.test.tsx +16 -0
- package/src/Skeleton/Skeleton.tsx +13 -1
- package/src/Slider/Slider.doc.mjs +6 -0
- package/src/Slider/Slider.test.tsx +240 -12
- package/src/Slider/Slider.tsx +96 -23
- package/src/Spinner/Spinner.test.tsx +10 -8
- package/src/Spinner/Spinner.tsx +15 -5
- package/src/Switch/Switch.doc.mjs +142 -42
- package/src/Switch/Switch.test.tsx +111 -0
- package/src/Switch/Switch.tsx +149 -32
- package/src/TabList/Tab.tsx +2 -2
- package/src/TabList/TabList.doc.mjs +0 -6
- package/src/TabList/TabList.test.tsx +3 -15
- package/src/TabList/TabList.tsx +6 -17
- package/src/TabList/TabListContext.ts +0 -6
- package/src/TabList/TabMenu.tsx +2 -2
- package/src/Table/BaseTable.tsx +34 -2
- package/src/Table/Table.doc.mjs +11 -0
- package/src/Table/Table.test.tsx +85 -0
- package/src/Table/TableCell.tsx +3 -3
- package/src/Table/TableHeaderCell.tsx +3 -3
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
- package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
- package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
- package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
- package/src/Table/types.ts +33 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/TextArea/TextArea.doc.mjs +21 -0
- package/src/TextArea/TextArea.test.tsx +23 -0
- package/src/TextArea/TextArea.tsx +27 -26
- package/src/TextInput/TextInput.doc.mjs +21 -0
- package/src/TextInput/TextInput.test.tsx +150 -0
- package/src/TextInput/TextInput.tsx +28 -25
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/Thumbnail/Thumbnail.tsx +1 -1
- package/src/TimeInput/TimeInput.doc.mjs +21 -0
- package/src/TimeInput/TimeInput.test.tsx +23 -0
- package/src/TimeInput/TimeInput.tsx +24 -25
- package/src/Timestamp/Timestamp.doc.mjs +32 -6
- package/src/Timestamp/Timestamp.test.tsx +371 -0
- package/src/Timestamp/Timestamp.tsx +96 -93
- package/src/Timestamp/formatInstant.ts +218 -0
- package/src/Timestamp/index.ts +4 -0
- package/src/Timestamp/tooltipEntries.test.ts +552 -0
- package/src/Timestamp/tooltipEntries.ts +210 -0
- package/src/Toast/ToastViewport.test.tsx +218 -8
- package/src/Toast/ToastViewport.tsx +71 -12
- package/src/ToggleButton/ToggleButton.test.tsx +24 -0
- package/src/ToggleButton/ToggleButton.tsx +19 -1
- package/src/Token/Token.test.tsx +166 -15
- package/src/Token/Token.tsx +104 -47
- package/src/Token/TokenLink.tsx +107 -0
- package/src/Token/index.ts +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
- package/src/Tokenizer/Tokenizer.test.tsx +23 -0
- package/src/Tokenizer/Tokenizer.tsx +14 -2
- package/src/Tooltip/Tooltip.test.tsx +46 -0
- package/src/Tooltip/useTooltip.tsx +20 -0
- package/src/TopNav/TopNav.test.tsx +93 -2
- package/src/TopNav/TopNavItem.doc.mjs +3 -3
- package/src/TopNav/TopNavItem.tsx +35 -6
- package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +6 -0
- package/src/TreeList/TreeList.test.tsx +50 -10
- package/src/TreeList/TreeList.tsx +5 -0
- package/src/TreeList/TreeListBranches.tsx +8 -6
- package/src/TreeList/TreeListItem.tsx +33 -11
- package/src/Typeahead/BaseTypeahead.tsx +42 -15
- package/src/Typeahead/Typeahead.doc.mjs +13 -0
- package/src/Typeahead/Typeahead.test.tsx +112 -0
- package/src/Typeahead/Typeahead.tsx +17 -1
- package/src/Typeahead/TypeaheadItem.tsx +2 -1
- package/src/__tests__/forcedColors.ts +60 -0
- package/src/__tests__/inputWidthContract.test.tsx +62 -0
- package/src/doc-mjs.d.ts +19 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/isRtlElement.ts +27 -0
- package/src/hooks/useAnnounce.test.tsx +75 -2
- package/src/hooks/useAnnounce.ts +47 -1
- package/src/hooks/useFocusTrap.test.tsx +104 -0
- package/src/hooks/useFocusTrap.ts +61 -12
- package/src/hooks/useGridFocus.doc.mjs +3 -3
- package/src/hooks/useGridFocus.test.tsx +41 -0
- package/src/hooks/useGridFocus.ts +21 -10
- package/src/hooks/useHotkeys.ts +9 -0
- package/src/hooks/useInputStatusIcon.test.tsx +162 -0
- package/src/hooks/useInputStatusIcon.tsx +224 -0
- package/src/hooks/useKeyboardHint.test.tsx +22 -0
- package/src/hooks/useKeyboardHint.tsx +6 -2
- package/src/hooks/useListFocus.doc.mjs +25 -3
- package/src/hooks/useListFocus.test.tsx +153 -1
- package/src/hooks/useListFocus.ts +102 -9
- package/src/i18n/InternationalizationContext.ts +4 -0
- package/src/i18n/InternationalizationProvider.tsx +12 -2
- package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
- package/src/i18n/__tests__/useDirection.test.tsx +66 -0
- package/src/i18n/getLocaleDirection.ts +29 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/useDirection.ts +29 -0
- package/src/reset.css +0 -26
- package/src/reset.test.ts +60 -0
- package/src/theme/derivedVarRegistry.test.ts +1 -0
- package/src/theme/derivedVarRegistry.ts +6 -0
- package/src/theme/generateThemeRules.test.ts +42 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/plainDate.test.ts +24 -6
- package/src/utils/plainDate.ts +18 -4
- package/src/utils/rtlStyles.ts +27 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file tooltipEntries.ts
|
|
5
|
+
* @input A Date and the consumer's tooltip entry list
|
|
6
|
+
* @output Pure formatting of one tooltip line per entry, plus the entry types
|
|
7
|
+
* @position Leaf module; owns which zone each tooltip line names and whether
|
|
8
|
+
* it needs a zone abbreviation, then defers to formatInstant to render it
|
|
9
|
+
*
|
|
10
|
+
* This module holds only the decisions a tooltip makes that the visible text
|
|
11
|
+
* does not: resolving `timezoneID` (including the `'local'` alias and the
|
|
12
|
+
* fallback for an unknown identifier), and deciding which lines need a zone
|
|
13
|
+
* abbreviation to be told apart. The formatting itself is formatInstant's,
|
|
14
|
+
* shared with the rendered text, so the two surfaces cannot drift.
|
|
15
|
+
*
|
|
16
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
17
|
+
* - /packages/core/src/Timestamp/Timestamp.tsx
|
|
18
|
+
* - /packages/core/src/Timestamp/formatInstant.ts
|
|
19
|
+
* - /packages/core/src/Timestamp/tooltipEntries.test.ts
|
|
20
|
+
* - /packages/core/src/Timestamp/Timestamp.doc.mjs
|
|
21
|
+
* - /packages/core/src/Timestamp/index.ts
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {devWarn} from '../utils/devWarning';
|
|
25
|
+
import {formatInstant} from './formatInstant';
|
|
26
|
+
import type {InstantFormat} from './formatInstant';
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Types
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Formats available to a tooltip line.
|
|
34
|
+
*
|
|
35
|
+
* Every `TimestampFormat` that names a fixed instant, plus `'full'` — the long
|
|
36
|
+
* absolute style the tooltip has always shown ("February 19, 2026 at 5:00:00 PM
|
|
37
|
+
* UTC"). `'relative'` and `'auto'` are excluded: a relative phrase ignores any
|
|
38
|
+
* zone, which would make `timezoneID` silently inert on that line.
|
|
39
|
+
*
|
|
40
|
+
* `'full'` lives only in this vocabulary, never in `TimestampFormat` — nothing
|
|
41
|
+
* asked for it as a visible display format, and keeping it out means new
|
|
42
|
+
* members added to `TimestampFormat` become valid tooltip formats for free.
|
|
43
|
+
*
|
|
44
|
+
* The public spelling of {@link InstantFormat}: consumers reading Timestamp's
|
|
45
|
+
* props should not have to know the name of its internal formatter.
|
|
46
|
+
*/
|
|
47
|
+
export type TimestampTooltipFormat = InstantFormat;
|
|
48
|
+
|
|
49
|
+
/** One line of the Timestamp tooltip. */
|
|
50
|
+
export interface TimestampTooltipEntry {
|
|
51
|
+
/**
|
|
52
|
+
* IANA time zone identifier, e.g. `'UTC'`, `'America/Los_Angeles'`.
|
|
53
|
+
* Omit it — or pass `'local'` — for the viewer's own zone.
|
|
54
|
+
*
|
|
55
|
+
* Prefer region identifiers. Fixed-offset abbreviations such as `'EST'` are
|
|
56
|
+
* accepted by the platform but never observe daylight saving, so they read
|
|
57
|
+
* an hour wrong for half the year — `'America/New_York'` is what people
|
|
58
|
+
* usually mean by "Eastern".
|
|
59
|
+
*
|
|
60
|
+
* An identifier the platform does not recognize falls back to the viewer's
|
|
61
|
+
* zone with a console warning rather than throwing.
|
|
62
|
+
*/
|
|
63
|
+
timezoneID?: string;
|
|
64
|
+
/**
|
|
65
|
+
* How this line renders the instant.
|
|
66
|
+
* @default 'full'
|
|
67
|
+
*/
|
|
68
|
+
format?: TimestampTooltipFormat;
|
|
69
|
+
/**
|
|
70
|
+
* Text shown beside the time, e.g. `'Local'`, `'UTC'`, `'Pacific'`.
|
|
71
|
+
* Supplied already translated; Timestamp never invents or localizes labels.
|
|
72
|
+
*/
|
|
73
|
+
label?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A rendered tooltip line. */
|
|
77
|
+
export interface TimestampTooltipLine {
|
|
78
|
+
label?: string;
|
|
79
|
+
value: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// Zone resolution
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
/** Spelling that means "the viewer's own zone" without naming it. */
|
|
87
|
+
const LOCAL_ZONE_ALIAS = 'local';
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Zone identifiers already reported as unknown.
|
|
91
|
+
*
|
|
92
|
+
* Resolution runs once per entry on every render, so without this a single
|
|
93
|
+
* typo on a live timestamp would emit the same warning every tick, forever.
|
|
94
|
+
* One report per bad identifier is enough to act on.
|
|
95
|
+
*/
|
|
96
|
+
const warnedTimezoneIDs = new Set<string>();
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Resolves an entry's zone to something safe to hand to `Intl`.
|
|
100
|
+
*
|
|
101
|
+
* Returns `undefined` for the viewer's own zone, which is also what `Intl`
|
|
102
|
+
* treats as "use the host zone" — so the local path never constructs a
|
|
103
|
+
* formatter with an explicit `timeZone` and cannot drift from today's output.
|
|
104
|
+
*/
|
|
105
|
+
function resolveTimezoneID(timezoneID: string | undefined): string | undefined {
|
|
106
|
+
if (
|
|
107
|
+
timezoneID === undefined ||
|
|
108
|
+
timezoneID.toLowerCase() === LOCAL_ZONE_ALIAS
|
|
109
|
+
) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// An unrecognized identifier makes every Intl.DateTimeFormat constructor
|
|
114
|
+
// throw a RangeError. Rendering a timestamp must never take down the tree —
|
|
115
|
+
// degrade to the viewer's zone and say so, mirroring how an unparseable
|
|
116
|
+
// `value` is handled in Timestamp.tsx.
|
|
117
|
+
try {
|
|
118
|
+
new Intl.DateTimeFormat(undefined, {timeZone: timezoneID});
|
|
119
|
+
} catch {
|
|
120
|
+
if (!warnedTimezoneIDs.has(timezoneID)) {
|
|
121
|
+
warnedTimezoneIDs.add(timezoneID);
|
|
122
|
+
devWarn(
|
|
123
|
+
'Timestamp',
|
|
124
|
+
`unknown time zone ${JSON.stringify(timezoneID)} in tooltipEntries. Falling back to the viewer's time zone.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return timezoneID;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A stable key for "which zone did the consumer ask for", used to decide
|
|
135
|
+
* whether the tooltip needs zone abbreviations to disambiguate its lines.
|
|
136
|
+
*
|
|
137
|
+
* Deliberately keyed on the *requested* zone, not on the resolved offset: a
|
|
138
|
+
* consumer who asks for two zones gets both labelled even when the viewer
|
|
139
|
+
* happens to be sitting in one of them, so the rendering does not change shape
|
|
140
|
+
* depending on where the reader is. That also keeps this testable under any
|
|
141
|
+
* machine timezone.
|
|
142
|
+
*/
|
|
143
|
+
function zoneKey(resolved: string | undefined): string {
|
|
144
|
+
return resolved === undefined ? LOCAL_ZONE_ALIAS : resolved.toLowerCase();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// =============================================================================
|
|
148
|
+
// Zone marking
|
|
149
|
+
// =============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Whether a format should carry a zone abbreviation.
|
|
153
|
+
*
|
|
154
|
+
* `'full'` always has one (that is the style). `system_*` never does — those
|
|
155
|
+
* are machine shapes and a trailing "PST" would break anything parsing them,
|
|
156
|
+
* which is also why `isTimezoneShown` has never applied to them.
|
|
157
|
+
*
|
|
158
|
+
* The two human formats that can carry one do so when either the reader has
|
|
159
|
+
* to tell several lines apart, or the line shows a zone the consumer named
|
|
160
|
+
* explicitly. That second case matters even for a lone entry: the tooltip is
|
|
161
|
+
* the `<time>` element's `aria-describedby`, and that element's accessible
|
|
162
|
+
* name is the absolute time in the *viewer's* zone — so an unmarked foreign
|
|
163
|
+
* time is announced directly after a local one with nothing to distinguish
|
|
164
|
+
* them. A line the consumer did not steer stays unmarked, matching how the
|
|
165
|
+
* visible text reads with `isTimezoneShown` off.
|
|
166
|
+
*/
|
|
167
|
+
function shouldShowZoneName(
|
|
168
|
+
format: TimestampTooltipFormat,
|
|
169
|
+
hasMultipleZones: boolean,
|
|
170
|
+
isNamedZone: boolean,
|
|
171
|
+
): boolean {
|
|
172
|
+
if (format === 'full') {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (format === 'date_time' || format === 'time') {
|
|
176
|
+
return hasMultipleZones || isNamedZone;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Renders one tooltip line per entry, in the order given.
|
|
183
|
+
*
|
|
184
|
+
* Pure: the same `date` and `entries` always produce the same lines for a given
|
|
185
|
+
* host zone and locale.
|
|
186
|
+
*/
|
|
187
|
+
export function formatTooltipLines(
|
|
188
|
+
date: Date,
|
|
189
|
+
entries: ReadonlyArray<TimestampTooltipEntry>,
|
|
190
|
+
): ReadonlyArray<TimestampTooltipLine> {
|
|
191
|
+
const resolved = entries.map(entry => resolveTimezoneID(entry.timezoneID));
|
|
192
|
+
const hasMultipleZones = new Set(resolved.map(zoneKey)).size > 1;
|
|
193
|
+
|
|
194
|
+
return entries.map((entry, index) => {
|
|
195
|
+
const format = entry.format ?? 'full';
|
|
196
|
+
const timeZone = resolved[index];
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
...(entry.label === undefined ? {} : {label: entry.label}),
|
|
200
|
+
value: formatInstant(date, format, {
|
|
201
|
+
timeZone,
|
|
202
|
+
isTimezoneShown: shouldShowZoneName(
|
|
203
|
+
format,
|
|
204
|
+
hasMultipleZones,
|
|
205
|
+
timeZone !== undefined,
|
|
206
|
+
),
|
|
207
|
+
}),
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -3,19 +3,56 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file ToastViewport.test.tsx
|
|
5
5
|
* @input Uses vitest, @testing-library/react, ToastViewport + useToast
|
|
6
|
-
* @output Unit tests for toast keyboard reach
|
|
7
|
-
*
|
|
6
|
+
* @output Unit tests for toast keyboard reach, focus management, and
|
|
7
|
+
* screen-reader announcements
|
|
8
|
+
* @position Testing; validates ToastViewport.tsx + Toast.tsx focus behavior and
|
|
9
|
+
* the announce-at-dispatch flow through the singleton live regions
|
|
8
10
|
*
|
|
9
|
-
* SYNC: When ToastViewport.tsx
|
|
11
|
+
* SYNC: When ToastViewport.tsx / Toast.tsx focus handling or the toast
|
|
12
|
+
* announcement flow changes, update these tests
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
|
-
import {describe, it, expect, vi, beforeAll} from 'vitest';
|
|
13
|
-
import {
|
|
15
|
+
import {describe, it, expect, vi, beforeAll, afterEach} from 'vitest';
|
|
16
|
+
import {
|
|
17
|
+
render,
|
|
18
|
+
screen,
|
|
19
|
+
fireEvent,
|
|
20
|
+
act,
|
|
21
|
+
waitFor,
|
|
22
|
+
within,
|
|
23
|
+
} from '@testing-library/react';
|
|
14
24
|
import React from 'react';
|
|
25
|
+
import {type AnnounceFn, __resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
15
26
|
import {ToastViewport} from './ToastViewport';
|
|
16
27
|
import {useToast} from './useToast';
|
|
17
28
|
import type {ToastOptions} from './types';
|
|
18
29
|
|
|
30
|
+
// Spy on the announcement sink so tests can prove a toast is announced exactly
|
|
31
|
+
// once. The mock wraps the real useAnnounce, so the singleton live regions are
|
|
32
|
+
// still populated (the region-content assertions below depend on it) while
|
|
33
|
+
// every call is also recorded on announceSpy.
|
|
34
|
+
const {announceSpy} = vi.hoisted(() => ({announceSpy: vi.fn()}));
|
|
35
|
+
vi.mock('../hooks/useAnnounce', async importActual => {
|
|
36
|
+
const actual = await importActual<{
|
|
37
|
+
useAnnounce: () => AnnounceFn;
|
|
38
|
+
__resetLiveRegionsForTest: () => void;
|
|
39
|
+
}>();
|
|
40
|
+
const {useCallback} = await import('react');
|
|
41
|
+
return {
|
|
42
|
+
...actual,
|
|
43
|
+
useAnnounce: (): AnnounceFn => {
|
|
44
|
+
const realAnnounce = actual.useAnnounce();
|
|
45
|
+
return useCallback<AnnounceFn>(
|
|
46
|
+
(message, politeness) => {
|
|
47
|
+
announceSpy(message, politeness);
|
|
48
|
+
realAnnounce(message, politeness);
|
|
49
|
+
},
|
|
50
|
+
[realAnnounce],
|
|
51
|
+
);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
19
56
|
// Popover API is not implemented in jsdom.
|
|
20
57
|
beforeAll(() => {
|
|
21
58
|
if (typeof HTMLElement.prototype.showPopover === 'undefined') {
|
|
@@ -24,6 +61,13 @@ beforeAll(() => {
|
|
|
24
61
|
}
|
|
25
62
|
});
|
|
26
63
|
|
|
64
|
+
// Toast text is mirrored into the singleton live regions, which outlive each
|
|
65
|
+
// render — reset them so text from one test never leaks into the next.
|
|
66
|
+
afterEach(() => {
|
|
67
|
+
__resetLiveRegionsForTest();
|
|
68
|
+
announceSpy.mockClear();
|
|
69
|
+
});
|
|
70
|
+
|
|
27
71
|
// Module-level constant default props (avoids unstable-default-props lint).
|
|
28
72
|
const EMPTY_OPTIONS: ToastOptions = {body: 'placeholder'};
|
|
29
73
|
|
|
@@ -169,7 +213,10 @@ describe('Toast blur timer pause', () => {
|
|
|
169
213
|
act(() => {
|
|
170
214
|
fireEvent.click(screen.getByText('Trigger Auto'));
|
|
171
215
|
});
|
|
172
|
-
|
|
216
|
+
// Scope to the viewport: the toast text is also mirrored into the
|
|
217
|
+
// singleton live region, which lives outside the notifications region.
|
|
218
|
+
const viewport = screen.getByRole('region', {name: 'Notifications'});
|
|
219
|
+
expect(within(viewport).getByText('Auto toast')).toBeInTheDocument();
|
|
173
220
|
|
|
174
221
|
// Window loses focus — timer should pause.
|
|
175
222
|
act(() => {
|
|
@@ -179,7 +226,7 @@ describe('Toast blur timer pause', () => {
|
|
|
179
226
|
vi.advanceTimersByTime(5000);
|
|
180
227
|
});
|
|
181
228
|
// Still present because the timer was paused while blurred.
|
|
182
|
-
expect(
|
|
229
|
+
expect(within(viewport).getByText('Auto toast')).toBeInTheDocument();
|
|
183
230
|
|
|
184
231
|
// Window regains focus — timer resumes and the toast dismisses.
|
|
185
232
|
act(() => {
|
|
@@ -196,7 +243,9 @@ describe('Toast blur timer pause', () => {
|
|
|
196
243
|
completeExit(toastId);
|
|
197
244
|
});
|
|
198
245
|
}
|
|
199
|
-
expect(
|
|
246
|
+
expect(
|
|
247
|
+
within(viewport).queryByText('Auto toast'),
|
|
248
|
+
).not.toBeInTheDocument();
|
|
200
249
|
} finally {
|
|
201
250
|
vi.useRealTimers();
|
|
202
251
|
}
|
|
@@ -213,6 +262,167 @@ describe('ToastViewport region ARIA', () => {
|
|
|
213
262
|
});
|
|
214
263
|
});
|
|
215
264
|
|
|
265
|
+
describe('toast announcements via singleton live regions', () => {
|
|
266
|
+
function politeRegion(): HTMLElement | null {
|
|
267
|
+
return document.querySelector('[data-astryx-live-region="polite"]');
|
|
268
|
+
}
|
|
269
|
+
function assertiveRegion(): HTMLElement | null {
|
|
270
|
+
return document.querySelector('[data-astryx-live-region="assertive"]');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const RICH_INFO: ToastOptions = {
|
|
274
|
+
body: (
|
|
275
|
+
<>
|
|
276
|
+
<strong>Update ready</strong>
|
|
277
|
+
<div>Restart to apply</div>
|
|
278
|
+
</>
|
|
279
|
+
),
|
|
280
|
+
};
|
|
281
|
+
const ERROR_TOAST: ToastOptions = {body: 'Upload failed', type: 'error'};
|
|
282
|
+
const SAVING_V1: ToastOptions = {uniqueID: 'save', body: 'Saving changes'};
|
|
283
|
+
const SAVING_V2: ToastOptions = {uniqueID: 'save', body: 'Changes saved'};
|
|
284
|
+
const IGNORE_KEPT: ToastOptions = {
|
|
285
|
+
uniqueID: 'dup',
|
|
286
|
+
collisionBehavior: 'ignore',
|
|
287
|
+
body: 'Kept',
|
|
288
|
+
};
|
|
289
|
+
const IGNORE_DROPPED: ToastOptions = {
|
|
290
|
+
uniqueID: 'dup',
|
|
291
|
+
collisionBehavior: 'ignore',
|
|
292
|
+
body: 'Dropped',
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
it('announces an info toast politely with its flattened text content', async () => {
|
|
296
|
+
renderViewport(<ShowToastButton options={RICH_INFO} triggerLabel="Show" />);
|
|
297
|
+
act(() => {
|
|
298
|
+
fireEvent.click(screen.getByText('Show'));
|
|
299
|
+
});
|
|
300
|
+
// Announced synchronously at dispatch, exactly once.
|
|
301
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
302
|
+
expect(announceSpy).toHaveBeenCalledWith(
|
|
303
|
+
'Update ready Restart to apply',
|
|
304
|
+
'polite',
|
|
305
|
+
);
|
|
306
|
+
// The flattened text reaches the polite singleton region (the sink).
|
|
307
|
+
await waitFor(() => {
|
|
308
|
+
expect(politeRegion()).toHaveTextContent('Update ready Restart to apply');
|
|
309
|
+
});
|
|
310
|
+
// Status toasts never touch the assertive region.
|
|
311
|
+
expect(assertiveRegion()).toHaveTextContent('');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('announces an error toast assertively', async () => {
|
|
315
|
+
renderViewport(
|
|
316
|
+
<ShowToastButton options={ERROR_TOAST} triggerLabel="Show" />,
|
|
317
|
+
);
|
|
318
|
+
act(() => {
|
|
319
|
+
fireEvent.click(screen.getByText('Show'));
|
|
320
|
+
});
|
|
321
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
322
|
+
expect(announceSpy).toHaveBeenCalledWith('Upload failed', 'assertive');
|
|
323
|
+
await waitFor(() => {
|
|
324
|
+
expect(assertiveRegion()).toHaveTextContent('Upload failed');
|
|
325
|
+
});
|
|
326
|
+
expect(politeRegion()).toHaveTextContent('');
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('announces exactly once at dispatch, even under StrictMode', () => {
|
|
330
|
+
render(
|
|
331
|
+
<React.StrictMode>
|
|
332
|
+
<ToastViewport isTopLayer={false}>
|
|
333
|
+
<ShowToastButton options={INFO_A} triggerLabel="Show" />
|
|
334
|
+
</ToastViewport>
|
|
335
|
+
</React.StrictMode>,
|
|
336
|
+
);
|
|
337
|
+
act(() => {
|
|
338
|
+
fireEvent.click(screen.getByText('Show'));
|
|
339
|
+
});
|
|
340
|
+
// The announcement lives in the imperative dispatch path (addToast), not a
|
|
341
|
+
// render effect, so StrictMode's double render and double-invoked state
|
|
342
|
+
// updater cannot announce the same toast twice.
|
|
343
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
344
|
+
expect(announceSpy).toHaveBeenCalledWith('Toast A', 'polite');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('re-announces a uniqueID toast when its content is overwritten', async () => {
|
|
348
|
+
renderViewport(
|
|
349
|
+
<>
|
|
350
|
+
<ShowToastButton options={SAVING_V1} triggerLabel="Show v1" />
|
|
351
|
+
<ShowToastButton options={SAVING_V2} triggerLabel="Show v2" />
|
|
352
|
+
</>,
|
|
353
|
+
);
|
|
354
|
+
act(() => {
|
|
355
|
+
fireEvent.click(screen.getByText('Show v1'));
|
|
356
|
+
});
|
|
357
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
358
|
+
expect(announceSpy).toHaveBeenLastCalledWith('Saving changes', 'polite');
|
|
359
|
+
// Overwriting via uniqueID replaces the toast in place — the new content is
|
|
360
|
+
// a fresh dispatch and must be announced again.
|
|
361
|
+
act(() => {
|
|
362
|
+
fireEvent.click(screen.getByText('Show v2'));
|
|
363
|
+
});
|
|
364
|
+
expect(announceSpy).toHaveBeenCalledTimes(2);
|
|
365
|
+
expect(announceSpy).toHaveBeenLastCalledWith('Changes saved', 'polite');
|
|
366
|
+
await waitFor(() => {
|
|
367
|
+
expect(politeRegion()).toHaveTextContent('Changes saved');
|
|
368
|
+
});
|
|
369
|
+
// Still a single toast on screen — overwritten in place, not stacked.
|
|
370
|
+
const viewport = screen.getByRole('region', {name: 'Notifications'});
|
|
371
|
+
expect(
|
|
372
|
+
within(viewport).queryAllByText(/Saving changes|Changes saved/),
|
|
373
|
+
).toHaveLength(1);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('does not re-announce an unchanged toast when an unrelated render occurs', () => {
|
|
377
|
+
renderViewport(
|
|
378
|
+
<>
|
|
379
|
+
<ShowToastButton options={INFO_A} triggerLabel="Show A" />
|
|
380
|
+
<ShowToastButton options={ERROR_TOAST} triggerLabel="Show B" />
|
|
381
|
+
</>,
|
|
382
|
+
);
|
|
383
|
+
act(() => {
|
|
384
|
+
fireEvent.click(screen.getByText('Show A'));
|
|
385
|
+
});
|
|
386
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
387
|
+
// A second toast arriving re-renders the viewport with a new toast list.
|
|
388
|
+
// Toast A is not re-dispatched, so it must not be announced again — only
|
|
389
|
+
// the newly dispatched toast produces a call.
|
|
390
|
+
act(() => {
|
|
391
|
+
fireEvent.click(screen.getByText('Show B'));
|
|
392
|
+
});
|
|
393
|
+
expect(announceSpy).toHaveBeenCalledTimes(2);
|
|
394
|
+
expect(announceSpy).toHaveBeenNthCalledWith(1, 'Toast A', 'polite');
|
|
395
|
+
expect(announceSpy).toHaveBeenNthCalledWith(
|
|
396
|
+
2,
|
|
397
|
+
'Upload failed',
|
|
398
|
+
'assertive',
|
|
399
|
+
);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('does not announce a toast whose uniqueID collision is ignored', () => {
|
|
403
|
+
renderViewport(
|
|
404
|
+
<>
|
|
405
|
+
<ShowToastButton options={IGNORE_KEPT} triggerLabel="Show 1" />
|
|
406
|
+
<ShowToastButton options={IGNORE_DROPPED} triggerLabel="Show 2" />
|
|
407
|
+
</>,
|
|
408
|
+
);
|
|
409
|
+
act(() => {
|
|
410
|
+
fireEvent.click(screen.getByText('Show 1'));
|
|
411
|
+
});
|
|
412
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
413
|
+
expect(announceSpy).toHaveBeenLastCalledWith('Kept', 'polite');
|
|
414
|
+
// The colliding toast is suppressed (collisionBehavior: 'ignore'), so it is
|
|
415
|
+
// neither shown nor announced.
|
|
416
|
+
act(() => {
|
|
417
|
+
fireEvent.click(screen.getByText('Show 2'));
|
|
418
|
+
});
|
|
419
|
+
expect(announceSpy).toHaveBeenCalledTimes(1);
|
|
420
|
+
const viewport = screen.getByRole('region', {name: 'Notifications'});
|
|
421
|
+
expect(within(viewport).getByText('Kept')).toBeInTheDocument();
|
|
422
|
+
expect(within(viewport).queryByText('Dropped')).not.toBeInTheDocument();
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
216
426
|
describe('toast timer lifecycle (#3589)', () => {
|
|
217
427
|
it('fires onHide exactly once when dismissed twice during the exit window', () => {
|
|
218
428
|
const onHide = vi.fn();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
isValidElement,
|
|
6
7
|
useCallback,
|
|
7
8
|
useEffect,
|
|
8
9
|
useLayoutEffect,
|
|
@@ -10,10 +11,12 @@ import {
|
|
|
10
11
|
useRef,
|
|
11
12
|
useState,
|
|
12
13
|
} from 'react';
|
|
14
|
+
import type {ReactNode} from 'react';
|
|
13
15
|
import * as stylex from '@stylexjs/stylex';
|
|
14
16
|
import {spacingVars, durationVars, easeVars} from '../theme/tokens.stylex';
|
|
15
17
|
import {mergeProps} from '../utils';
|
|
16
18
|
import {INTERACTIVE_SELECTORS} from '../hooks/useClickableContainer';
|
|
19
|
+
import {useAnnounce} from '../hooks/useAnnounce';
|
|
17
20
|
import {Toast} from './Toast';
|
|
18
21
|
import {ToastContext, type ToastContextValue} from './ToastContext';
|
|
19
22
|
import type {ToastEntry, ToastPosition, ToastDismissReason} from './types';
|
|
@@ -76,6 +79,29 @@ const styles = stylex.create({
|
|
|
76
79
|
},
|
|
77
80
|
});
|
|
78
81
|
|
|
82
|
+
// Flatten a toast's rendered content (title, description, etc.) to the plain
|
|
83
|
+
// text that should be spoken by a screen reader. Only text is announced —
|
|
84
|
+
// interactive endContent is deliberately excluded (it is reachable via F6).
|
|
85
|
+
function getNodeText(node: ReactNode): string {
|
|
86
|
+
if (node == null || typeof node === 'boolean') {
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
if (typeof node === 'string') {
|
|
90
|
+
return node;
|
|
91
|
+
}
|
|
92
|
+
if (typeof node === 'number') {
|
|
93
|
+
return String(node);
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(node)) {
|
|
96
|
+
return node.map(getNodeText).filter(Boolean).join(' ');
|
|
97
|
+
}
|
|
98
|
+
if (isValidElement(node)) {
|
|
99
|
+
const {children} = node.props as {children?: ReactNode};
|
|
100
|
+
return getNodeText(children);
|
|
101
|
+
}
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
|
|
79
105
|
export interface ToastViewportProps {
|
|
80
106
|
position?: ToastPosition;
|
|
81
107
|
maxVisible?: number;
|
|
@@ -159,21 +185,54 @@ export function ToastViewport({
|
|
|
159
185
|
[],
|
|
160
186
|
);
|
|
161
187
|
|
|
162
|
-
|
|
163
|
-
|
|
188
|
+
// Announce toasts through the persistent singleton live regions. Each
|
|
189
|
+
// <Toast> also renders its own role="status"/"alert" region, but that region
|
|
190
|
+
// is "born with content" — mounted together with its text — which many
|
|
191
|
+
// screen readers do not announce (see useAnnounce.ts); the singleton regions
|
|
192
|
+
// are mounted empty and only mutated afterwards, so they are what actually
|
|
193
|
+
// guarantees the announcement (the per-toast markup is kept for browse-mode
|
|
194
|
+
// discoverability). The announcement happens in addToast — the imperative
|
|
195
|
+
// dispatch path invoked once per useToast() call from an event handler,
|
|
196
|
+
// never from render — so each toast is announced exactly once by
|
|
197
|
+
// construction, independent of the React render lifecycle (StrictMode
|
|
198
|
+
// double-render/double-effect, viewport remounts, and unrelated list
|
|
199
|
+
// re-renders never re-announce). It is client-only (addToast never runs
|
|
200
|
+
// during SSR), so it is SSR-safe.
|
|
201
|
+
const announce = useAnnounce();
|
|
202
|
+
|
|
203
|
+
const addToast = useCallback(
|
|
204
|
+
(entry: ToastEntry) => {
|
|
164
205
|
const {uniqueID, collisionBehavior = 'overwrite'} = entry.options;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
206
|
+
// Resolve an ignored collision synchronously against the committed list
|
|
207
|
+
// so a suppressed toast is neither shown nor announced. The remaining
|
|
208
|
+
// announce + setToasts run outside the setToasts updater, which React may
|
|
209
|
+
// invoke more than once — keeping the announcement exactly-once.
|
|
210
|
+
if (
|
|
211
|
+
uniqueID &&
|
|
212
|
+
collisionBehavior === 'ignore' &&
|
|
213
|
+
toastsRef.current.some(t => t.options.uniqueID === uniqueID)
|
|
214
|
+
) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const text = getNodeText(entry.options.body);
|
|
218
|
+
if (text) {
|
|
219
|
+
// Error toasts map to the assertive region (role="alert"); everything
|
|
220
|
+
// else to the polite region (role="status") — mirrors Toast.tsx.
|
|
221
|
+
announce(text, entry.options.type === 'error' ? 'assertive' : 'polite');
|
|
222
|
+
}
|
|
223
|
+
setToasts(prev => {
|
|
224
|
+
if (uniqueID) {
|
|
225
|
+
const existing = prev.find(t => t.options.uniqueID === uniqueID);
|
|
226
|
+
if (existing) {
|
|
227
|
+
// An ignored collision already returned above; overwrite in place.
|
|
228
|
+
return prev.map(t => (t.options.uniqueID === uniqueID ? entry : t));
|
|
170
229
|
}
|
|
171
|
-
return prev.map(t => (t.options.uniqueID === uniqueID ? entry : t));
|
|
172
230
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
231
|
+
return [...prev, entry];
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
[announce],
|
|
235
|
+
);
|
|
177
236
|
|
|
178
237
|
const removeToast = useCallback((id: string, reason: ToastDismissReason) => {
|
|
179
238
|
// An exiting toast stays in toastsRef until its exit transition ends, so
|
|
@@ -14,6 +14,10 @@ import userEvent from '@testing-library/user-event';
|
|
|
14
14
|
import {useState, type MouseEvent} from 'react';
|
|
15
15
|
import {ToggleButton} from './ToggleButton';
|
|
16
16
|
import {ToggleButtonGroup} from './ToggleButtonGroup';
|
|
17
|
+
import {
|
|
18
|
+
getAllInjectedCss,
|
|
19
|
+
getForcedColorsRules,
|
|
20
|
+
} from '../__tests__/forcedColors';
|
|
17
21
|
|
|
18
22
|
// =============================================================================
|
|
19
23
|
// ToggleButton — Standalone
|
|
@@ -517,3 +521,23 @@ describe('ToggleButtonGroup (multiple)', () => {
|
|
|
517
521
|
);
|
|
518
522
|
});
|
|
519
523
|
});
|
|
524
|
+
|
|
525
|
+
// jsdom cannot emulate forced-colors rendering, so these assert that the
|
|
526
|
+
// compiled output includes the forced-colors rules; visual behavior needs
|
|
527
|
+
// manual verification under Windows High Contrast.
|
|
528
|
+
describe('forced colors (WCAG 1.4.11)', () => {
|
|
529
|
+
it('compiles forced-colors overrides so the pressed state survives Windows High Contrast', () => {
|
|
530
|
+
render(<ToggleButton label="Bold" isPressed onPressedChange={() => {}} />);
|
|
531
|
+
const css = getForcedColorsRules();
|
|
532
|
+
// The painted pressed overlay is stripped; Highlight/HighlightText marks
|
|
533
|
+
// the pressed toggle (critical for icon-only toggles, which otherwise
|
|
534
|
+
// lose all pressed indication).
|
|
535
|
+
expect(css).toContain('background-color: highlight;');
|
|
536
|
+
expect(css).toContain('color: highlighttext;');
|
|
537
|
+
// ToggleButton renders a <button>; without opting out of UA remapping it
|
|
538
|
+
// keeps the native ButtonFace surface and ignores the Highlight fill,
|
|
539
|
+
// leaving HighlightText text on a white surface. forced-color-adjust: none
|
|
540
|
+
// makes both render as authored.
|
|
541
|
+
expect(getAllInjectedCss()).toContain('forced-color-adjust: none;');
|
|
542
|
+
});
|
|
543
|
+
});
|
|
@@ -40,7 +40,25 @@ import {themeProps} from '../utils/themeProps';
|
|
|
40
40
|
*/
|
|
41
41
|
const pressedStyles = stylex.create({
|
|
42
42
|
background: {
|
|
43
|
-
|
|
43
|
+
// forced-color-adjust must be `none` here: ToggleButton renders a <button>,
|
|
44
|
+
// and the UA keeps native form-control colors (ButtonFace surface) for it
|
|
45
|
+
// under forced colors, ignoring the authored Highlight fill — the label kept
|
|
46
|
+
// its HighlightText color, giving white text on a white surface. Opting the
|
|
47
|
+
// pressed button out of UA remapping makes both the Highlight surface and
|
|
48
|
+
// the HighlightText label render as authored, restoring figure-ground.
|
|
49
|
+
forcedColorAdjust: 'none',
|
|
50
|
+
backgroundColor: {
|
|
51
|
+
default: colorVars['--color-overlay-pressed'],
|
|
52
|
+
// Forced colors (Windows High Contrast) strips the painted pressed
|
|
53
|
+
// overlay, which would leave icon-only toggles with no pressed
|
|
54
|
+
// indication at all. Highlight/HighlightText is the platform convention
|
|
55
|
+
// for a selected/pressed control (WCAG 1.4.11).
|
|
56
|
+
'@media (forced-colors: active)': 'Highlight',
|
|
57
|
+
},
|
|
58
|
+
color: {
|
|
59
|
+
default: null,
|
|
60
|
+
'@media (forced-colors: active)': 'HighlightText',
|
|
61
|
+
},
|
|
44
62
|
},
|
|
45
63
|
});
|
|
46
64
|
|