@astryxdesign/core 0.1.9-canary.d2d84f8 → 0.1.9-canary.e00a241
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/dist/Avatar/Avatar.d.ts +6 -1
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +46 -12
- package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- 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/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/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +3 -1
- 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/CheckboxInput/CheckboxInput.js +4 -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 +13 -7
- 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/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +14 -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 +1 -0
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +31 -19
- package/dist/DateRangeInput/DateRangeInput.d.ts +1 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +20 -19
- 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 +4 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +4 -1
- 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/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 +4 -1
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +4 -2
- package/dist/FileInput/FileInput.d.ts +1 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +91 -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/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +15 -9
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +6 -3
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +65 -40
- package/dist/NumberInput/NumberInput.d.ts +1 -0
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +20 -17
- package/dist/Outline/Outline.js +2 -2
- package/dist/RadioList/RadioList.d.ts +2 -2
- package/dist/RadioList/RadioListItem.js +4 -4
- 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 +2 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +58 -16
- 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/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 +160 -91
- 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/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +30 -4
- 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 +36 -26
- 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 +14 -6
- 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 +1 -0
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +16 -19
- package/dist/TextInput/TextInput.d.ts +1 -0
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +18 -17
- package/dist/TimeInput/TimeInput.d.ts +1 -0
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +15 -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/Token/Token.d.ts +30 -3
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +17 -2
- package/dist/Token/index.d.ts +1 -1
- package/dist/Token/index.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +2 -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.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +19 -10
- package/dist/Typeahead/Typeahead.js +1 -1
- 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 +38 -14
- package/dist/astryx.umd.js +52 -52
- 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/useListFocus.d.ts +39 -1
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +54 -9
- 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 +76 -0
- package/locales/pseudo.json +57 -0
- package/package.json +6 -1
- package/src/Avatar/Avatar.doc.mjs +18 -4
- package/src/Avatar/Avatar.test.tsx +106 -0
- package/src/Avatar/Avatar.tsx +62 -10
- package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
- package/src/Avatar/AvatarStatusDot.tsx +6 -4
- package/src/AvatarGroup/AvatarGroup.test.tsx +16 -1
- package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +51 -0
- package/src/Breadcrumbs/index.ts +4 -0
- package/src/Button/Button.doc.mjs +0 -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/ChatDictationButton.tsx +7 -1
- package/src/Chat/ChatMessageBubble.tsx +15 -10
- package/src/Chat/ChatSystemMessage.test.tsx +5 -0
- package/src/Chat/ChatToolCalls.tsx +9 -2
- package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
- package/src/CheckboxInput/CheckboxInput.tsx +4 -4
- package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
- 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.tsx +35 -8
- 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/ContextMenu/ContextMenu.test.tsx +42 -0
- package/src/ContextMenu/ContextMenu.tsx +13 -11
- package/src/ContextMenu/index.ts +5 -0
- package/src/DateInput/DateInput.doc.mjs +15 -5
- package/src/DateInput/DateInput.test.tsx +210 -3
- package/src/DateInput/DateInput.tsx +41 -29
- package/src/DateRangeInput/DateRangeInput.doc.mjs +11 -3
- package/src/DateRangeInput/DateRangeInput.test.tsx +120 -3
- package/src/DateRangeInput/DateRangeInput.tsx +29 -27
- 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 +8 -2
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +8 -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/inputStyles.stylex.ts +2 -0
- package/src/FieldStatus/FieldStatus.tsx +10 -4
- package/src/FileInput/FileInput.doc.mjs +13 -7
- package/src/FileInput/FileInput.test.tsx +84 -16
- package/src/FileInput/FileInput.tsx +96 -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/Lightbox/Lightbox.tsx +11 -3
- package/src/List/List.test.tsx +17 -29
- package/src/List/List.tsx +6 -1
- package/src/Markdown/Markdown.test.tsx +21 -0
- package/src/Markdown/Markdown.tsx +4 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
- package/src/MultiSelector/MultiSelector.test.tsx +306 -3
- package/src/MultiSelector/MultiSelector.tsx +88 -57
- package/src/NumberInput/NumberInput.doc.mjs +11 -5
- package/src/NumberInput/NumberInput.test.tsx +48 -0
- package/src/NumberInput/NumberInput.tsx +25 -26
- package/src/Outline/Outline.test.tsx +12 -12
- package/src/Outline/Outline.tsx +2 -2
- package/src/RadioList/RadioList.doc.mjs +6 -0
- package/src/RadioList/RadioList.tsx +2 -2
- package/src/RadioList/RadioListItem.tsx +4 -4
- package/src/Resizable/ResizeHandle.test.tsx +94 -0
- package/src/Resizable/ResizeHandle.tsx +70 -20
- package/src/SegmentedControl/SegmentedControlItem.tsx +2 -0
- package/src/Selector/Selector.doc.mjs +8 -0
- package/src/Selector/Selector.test.tsx +328 -3
- package/src/Selector/Selector.tsx +70 -19
- package/src/SideNav/SideNav.tsx +1 -8
- package/src/SideNav/SideNavCollapseButton.tsx +9 -7
- package/src/SideNav/SideNavItem.tsx +5 -3
- 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 +22 -0
- package/src/Switch/Switch.tsx +90 -33
- 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/Table/BaseTable.tsx +34 -2
- package/src/Table/Table.doc.mjs +11 -0
- package/src/Table/Table.test.tsx +85 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +24 -17
- 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 +37 -8
- 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 +11 -5
- package/src/TextArea/TextArea.tsx +17 -26
- package/src/TextInput/TextInput.doc.mjs +11 -5
- package/src/TextInput/TextInput.test.tsx +130 -3
- package/src/TextInput/TextInput.tsx +18 -25
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.doc.mjs +11 -5
- package/src/TimeInput/TimeInput.tsx +14 -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/Token/Token.test.tsx +21 -15
- package/src/Token/Token.tsx +31 -14
- package/src/Token/index.ts +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +8 -1
- package/src/Tokenizer/Tokenizer.tsx +4 -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 +29 -14
- package/src/Typeahead/Typeahead.doc.mjs +6 -0
- package/src/Typeahead/Typeahead.test.tsx +27 -0
- package/src/Typeahead/Typeahead.tsx +1 -1
- package/src/Typeahead/TypeaheadItem.tsx +2 -1
- 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/useListFocus.doc.mjs +25 -3
- package/src/hooks/useListFocus.test.tsx +153 -1
- package/src/hooks/useListFocus.ts +102 -9
- 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
|
@@ -10,6 +10,7 @@ import type {ReactNode} from 'react';
|
|
|
10
10
|
import * as stylex from '@stylexjs/stylex';
|
|
11
11
|
import {Divider} from '../Divider';
|
|
12
12
|
import {DropdownMenuItem} from './DropdownMenuItem';
|
|
13
|
+
import {DropdownMenuSubMenu} from './DropdownMenuSubMenu';
|
|
13
14
|
import {
|
|
14
15
|
spacingVars,
|
|
15
16
|
typographyVars,
|
|
@@ -49,18 +50,14 @@ function getSectionKey(section: DropdownMenuSection, index: number): string {
|
|
|
49
50
|
* Converts data-driven items into DropdownMenuItem components,
|
|
50
51
|
* so both modes share the same rendering and keyboard navigation path.
|
|
51
52
|
*/
|
|
52
|
-
export function renderDropdownItems(
|
|
53
|
-
items: DropdownMenuOption[],
|
|
54
|
-
): ReactNode {
|
|
53
|
+
export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
|
|
55
54
|
const elements: ReactNode[] = [];
|
|
56
55
|
|
|
57
56
|
for (let i = 0; i < items.length; i++) {
|
|
58
57
|
const option = items[i];
|
|
59
58
|
|
|
60
59
|
if ('type' in option && option.type === 'divider') {
|
|
61
|
-
elements.push(
|
|
62
|
-
<Divider key={`divider-${i}`} xstyle={styles.divider} />,
|
|
63
|
-
);
|
|
60
|
+
elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
|
|
64
61
|
} else if ('type' in option && option.type === 'section') {
|
|
65
62
|
elements.push(
|
|
66
63
|
<div
|
|
@@ -84,15 +81,32 @@ export function renderDropdownItems(
|
|
|
84
81
|
</div>,
|
|
85
82
|
);
|
|
86
83
|
} else if (!('type' in option)) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
// A plain item that declares nested `items` becomes a submenu (data-mode
|
|
85
|
+
// parity with the compound DropdownMenuSubMenu API); otherwise it's a
|
|
86
|
+
// leaf. renderDropdownItems owns the recursion and hands the rendered
|
|
87
|
+
// children to DropdownMenuSubMenu — so DropdownMenuSubMenu never imports
|
|
88
|
+
// this module back (no import cycle).
|
|
89
|
+
if (option.items && option.items.length > 0) {
|
|
90
|
+
elements.push(
|
|
91
|
+
<DropdownMenuSubMenu
|
|
92
|
+
key={getItemKey(option)}
|
|
93
|
+
icon={option.icon}
|
|
94
|
+
label={option.label}
|
|
95
|
+
isDisabled={option.isDisabled}>
|
|
96
|
+
{renderDropdownItems(option.items)}
|
|
97
|
+
</DropdownMenuSubMenu>,
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
elements.push(
|
|
101
|
+
<DropdownMenuItem
|
|
102
|
+
key={getItemKey(option)}
|
|
103
|
+
icon={option.icon}
|
|
104
|
+
label={option.label}
|
|
105
|
+
onClick={option.onClick}
|
|
106
|
+
isDisabled={option.isDisabled}
|
|
107
|
+
/>,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
96
110
|
}
|
|
97
111
|
}
|
|
98
112
|
|
package/src/Field/Field.tsx
CHANGED
|
@@ -22,6 +22,7 @@ import type {BaseProps} from '../BaseProps';
|
|
|
22
22
|
import type {SizeValue} from '../utils/types';
|
|
23
23
|
import {FieldLabel} from './FieldLabel';
|
|
24
24
|
import {FieldStatus} from '../FieldStatus/FieldStatus';
|
|
25
|
+
import type {FieldStatusVariant} from '../FieldStatus/FieldStatus';
|
|
25
26
|
import {spacingVars, borderVars} from '../theme/tokens.stylex';
|
|
26
27
|
import type {IconType} from '../Icon';
|
|
27
28
|
import {mergeProps} from '../utils';
|
|
@@ -156,9 +157,10 @@ export interface FieldProps extends Omit<
|
|
|
156
157
|
* How the status message is rendered relative to the input.
|
|
157
158
|
* - 'attached': Status sits directly below the input (default, for bordered inputs)
|
|
158
159
|
* - 'detached': Status is a separate element below the field (for checkboxes, switches, sliders)
|
|
160
|
+
* - 'tooltip': No message box; the input surfaces status through a tooltip on its on-field icon
|
|
159
161
|
* @default 'attached'
|
|
160
162
|
*/
|
|
161
|
-
statusVariant?:
|
|
163
|
+
statusVariant?: FieldStatusVariant;
|
|
162
164
|
/**
|
|
163
165
|
* Width of the field. Numbers are treated as pixels, strings are used as-is
|
|
164
166
|
* (e.g. `'100%'`). Sizes the whole field — label, control, and status — so
|
|
@@ -239,14 +241,17 @@ export function Field({
|
|
|
239
241
|
/>
|
|
240
242
|
);
|
|
241
243
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
244
|
+
// The 'tooltip' variant surfaces status through the input's on-field icon
|
|
245
|
+
// tooltip, so Field renders no message box for it.
|
|
246
|
+
const statusNode =
|
|
247
|
+
status?.message && statusVariant !== 'tooltip' ? (
|
|
248
|
+
<FieldStatus
|
|
249
|
+
type={status.type}
|
|
250
|
+
message={status.message}
|
|
251
|
+
id={resolvedMessageID}
|
|
252
|
+
variant={statusVariant}
|
|
253
|
+
/>
|
|
254
|
+
) : null;
|
|
250
255
|
|
|
251
256
|
// ─── Horizontal-labels mode ───────────────────────────────────────────
|
|
252
257
|
// Use display:contents so the parent grid's `auto 1fr` columns place
|
|
@@ -67,6 +67,8 @@ export const inputWrapperStyles = stylex.create({
|
|
|
67
67
|
cursor: 'not-allowed',
|
|
68
68
|
opacity: 0.5,
|
|
69
69
|
borderColor: colorVars['--color-border-emphasized'],
|
|
70
|
+
// Suppress the base hover ring — a disabled control must not react to hover.
|
|
71
|
+
boxShadow: 'none',
|
|
70
72
|
},
|
|
71
73
|
});
|
|
72
74
|
|
|
@@ -71,9 +71,12 @@ const styles = stylex.create({
|
|
|
71
71
|
gap: spacingVars['--spacing-1'],
|
|
72
72
|
},
|
|
73
73
|
detachedIcon: {
|
|
74
|
-
//
|
|
75
|
-
// (rather than
|
|
76
|
-
|
|
74
|
+
// Center the glyph within the first text-line box so it aligns to the
|
|
75
|
+
// first line of the message (rather than the top of the flex row) when the
|
|
76
|
+
// message wraps to multiple lines.
|
|
77
|
+
display: 'inline-flex',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
height: `calc(${typeScaleVars['--text-supporting-size']} * ${typeScaleVars['--text-supporting-leading']})`,
|
|
77
80
|
flexShrink: 0,
|
|
78
81
|
},
|
|
79
82
|
});
|
|
@@ -109,6 +112,7 @@ const colorStyles = stylex.create({
|
|
|
109
112
|
export interface FieldStatusVariantMap {
|
|
110
113
|
attached: true;
|
|
111
114
|
detached: true;
|
|
115
|
+
tooltip: true;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
/**
|
|
@@ -143,7 +147,9 @@ export interface FieldStatusProps extends BaseProps<HTMLDivElement> {
|
|
|
143
147
|
* decorative for assistive tech (`aria-hidden`): the message text already names
|
|
144
148
|
* the status in words and is announced through the live region. The `attached`
|
|
145
149
|
* variant keeps its status affordance on the bordered input, so it renders no
|
|
146
|
-
* icon here to avoid a duplicate.
|
|
150
|
+
* icon here to avoid a duplicate. The `tooltip` variant renders no message box
|
|
151
|
+
* at all — the input surfaces the status through a tooltip on its on-field
|
|
152
|
+
* icon — so callers skip rendering FieldStatus for it.
|
|
147
153
|
*
|
|
148
154
|
* Screen-reader announcements go through the persistent `useAnnounce` live
|
|
149
155
|
* regions (assertive for errors, polite otherwise) rather than `role`/
|
|
@@ -78,7 +78,7 @@ export const docs = {
|
|
|
78
78
|
name: 'isRequired',
|
|
79
79
|
type: 'boolean',
|
|
80
80
|
description:
|
|
81
|
-
'Displays a "Required" indicator next to the label and
|
|
81
|
+
'Displays a "Required" indicator next to the label and adds a screen-reader-only "Required" description to the trigger (assistive tech does not reliably announce aria-required on the trigger). Mutually exclusive with isOptional.',
|
|
82
82
|
default: 'false',
|
|
83
83
|
},
|
|
84
84
|
{
|
|
@@ -121,9 +121,9 @@ export const docs = {
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
name: 'statusVariant',
|
|
124
|
-
type: "'attached' | 'detached'",
|
|
124
|
+
type: "'attached' | 'detached' | 'tooltip'",
|
|
125
125
|
description:
|
|
126
|
-
'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing.',
|
|
126
|
+
'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing; tooltip hides the message box and surfaces it in a tooltip on the status icon.',
|
|
127
127
|
default: "'attached'",
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -132,6 +132,12 @@ export const docs = {
|
|
|
132
132
|
description:
|
|
133
133
|
'Tooltip text displayed in an info icon at the end of the label.',
|
|
134
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: 'width',
|
|
137
|
+
type: 'SizeValue',
|
|
138
|
+
description:
|
|
139
|
+
'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
|
|
140
|
+
},
|
|
135
141
|
],
|
|
136
142
|
theming: {
|
|
137
143
|
targets: [
|
|
@@ -255,9 +261,9 @@ export const docsZh = {
|
|
|
255
261
|
},
|
|
256
262
|
{
|
|
257
263
|
name: 'statusVariant',
|
|
258
|
-
type: "'attached' | 'detached'",
|
|
264
|
+
type: "'attached' | 'detached' | 'tooltip'",
|
|
259
265
|
description:
|
|
260
|
-
'状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached
|
|
266
|
+
'状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距;tooltip 隐藏消息框,并在状态图标上以提示气泡形式显示。',
|
|
261
267
|
default: "'attached'",
|
|
262
268
|
},
|
|
263
269
|
],
|
|
@@ -322,7 +328,7 @@ export const docsDense = {
|
|
|
322
328
|
isLabelHidden: 'Visually hides label; keeps screen reader access.',
|
|
323
329
|
description: 'Description text between label+input.',
|
|
324
330
|
isOptional: 'Shows "Optional" indicator.',
|
|
325
|
-
isRequired: 'Shows "Required" indicator+
|
|
331
|
+
isRequired: 'Shows "Required" indicator+SR-only trigger description.',
|
|
326
332
|
isDisabled: 'Disables input, prevents interaction.',
|
|
327
333
|
disabledMessage:
|
|
328
334
|
'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps trigger focusable via aria-disabled (opening picker stays blocked). Use instead of wrapping a disabled FileInput in Tooltip.',
|
|
@@ -330,7 +336,7 @@ export const docsDense = {
|
|
|
330
336
|
placeholder: 'Placeholder when no files selected.',
|
|
331
337
|
mode: "Visual mode: 'input' (compact) or 'dropzone' (drag-and-drop).",
|
|
332
338
|
status: 'Validation status; colored border. Message floats below.',
|
|
333
|
-
statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing.',
|
|
339
|
+
statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing; tooltip hides the box and shows it on the status icon.',
|
|
334
340
|
labelTooltip: 'Tooltip in info icon at label end.',
|
|
335
341
|
},
|
|
336
342
|
};
|
|
@@ -147,16 +147,33 @@ describe('FileInput', () => {
|
|
|
147
147
|
expect(screen.getByText('Upload')).toBeInTheDocument();
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
it('
|
|
150
|
+
it('conveys required state through the accessible description', () => {
|
|
151
151
|
render(
|
|
152
152
|
<FileInput label="Resume" isRequired value={null} onChange={() => {}} />,
|
|
153
153
|
);
|
|
154
|
-
|
|
154
|
+
const trigger = screen.getByRole('button', {name: 'Resume'});
|
|
155
|
+
// Required is conveyed via a visually hidden "Required" node referenced
|
|
156
|
+
// from aria-describedby on the focusable role="button" trigger — not the
|
|
155
157
|
// hidden file input (forms-6).
|
|
156
|
-
expect(
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
expect(trigger).toHaveAccessibleDescription(/Required/);
|
|
159
|
+
// aria-required is not a supported property of role="button" in
|
|
160
|
+
// WAI-ARIA 1.2 (AT does not announce it), so it must never appear.
|
|
161
|
+
expect(trigger).not.toHaveAttribute('aria-required');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('combines required with the description in the accessible description', () => {
|
|
165
|
+
render(
|
|
166
|
+
<FileInput
|
|
167
|
+
label="Resume"
|
|
168
|
+
description="PDF only"
|
|
169
|
+
isRequired
|
|
170
|
+
value={null}
|
|
171
|
+
onChange={() => {}}
|
|
172
|
+
/>,
|
|
159
173
|
);
|
|
174
|
+
const trigger = screen.getByRole('button', {name: 'Resume'});
|
|
175
|
+
expect(trigger).toHaveAccessibleDescription(/PDF only/);
|
|
176
|
+
expect(trigger).toHaveAccessibleDescription(/Required/);
|
|
160
177
|
});
|
|
161
178
|
|
|
162
179
|
it('places aria-describedby on the focusable button, not the hidden input (forms-6)', () => {
|
|
@@ -176,11 +193,11 @@ describe('FileInput', () => {
|
|
|
176
193
|
expect(input).toHaveAttribute('aria-hidden', 'true');
|
|
177
194
|
});
|
|
178
195
|
|
|
179
|
-
it('does not
|
|
196
|
+
it('does not mention required without isRequired', () => {
|
|
180
197
|
render(<FileInput label="Resume" value={null} onChange={() => {}} />);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
);
|
|
198
|
+
const trigger = screen.getByRole('button', {name: 'Resume'});
|
|
199
|
+
expect(trigger).not.toHaveAccessibleDescription(/Required/);
|
|
200
|
+
expect(trigger).not.toHaveAttribute('aria-required');
|
|
184
201
|
});
|
|
185
202
|
|
|
186
203
|
it('sets disabled attribute when isDisabled is true', () => {
|
|
@@ -353,6 +370,40 @@ describe('FileInput', () => {
|
|
|
353
370
|
// FieldStatus, but the polite channel must stay empty.)
|
|
354
371
|
expect(politeRegion()?.textContent ?? '').toBe('');
|
|
355
372
|
});
|
|
373
|
+
|
|
374
|
+
it('announces a validation error exactly once across live regions', async () => {
|
|
375
|
+
render(
|
|
376
|
+
<FileInput
|
|
377
|
+
label="Upload"
|
|
378
|
+
value={null}
|
|
379
|
+
onChange={() => {}}
|
|
380
|
+
accept=".pdf"
|
|
381
|
+
/>,
|
|
382
|
+
);
|
|
383
|
+
fireEvent.change(fileInputEl(), {
|
|
384
|
+
target: {files: [createFile('note.txt', 100)]},
|
|
385
|
+
});
|
|
386
|
+
const errorText = '"note.txt" is not an accepted file type';
|
|
387
|
+
// The error lands in the assertive announce region (via the FieldStatus
|
|
388
|
+
// the derived error status mounts)…
|
|
389
|
+
await waitFor(() => {
|
|
390
|
+
expect(
|
|
391
|
+
document.querySelector('[data-astryx-live-region="assertive"]'),
|
|
392
|
+
).toHaveTextContent(errorText);
|
|
393
|
+
});
|
|
394
|
+
// …and in exactly one live region overall. FileInput's own role="status"
|
|
395
|
+
// region used to duplicate the FieldStatus announcement, so the same
|
|
396
|
+
// error was read twice (assertively, then politely).
|
|
397
|
+
const liveRegions = Array.from(
|
|
398
|
+
document.querySelectorAll(
|
|
399
|
+
'[role="status"], [role="alert"], [aria-live]',
|
|
400
|
+
),
|
|
401
|
+
);
|
|
402
|
+
const regionsWithError = liveRegions.filter(el =>
|
|
403
|
+
(el.textContent ?? '').includes(errorText),
|
|
404
|
+
);
|
|
405
|
+
expect(regionsWithError).toHaveLength(1);
|
|
406
|
+
});
|
|
356
407
|
});
|
|
357
408
|
|
|
358
409
|
describe('validation', () => {
|
|
@@ -719,16 +770,20 @@ describe('FileInput', () => {
|
|
|
719
770
|
/>,
|
|
720
771
|
);
|
|
721
772
|
|
|
722
|
-
const trigger = screen.getByRole('button');
|
|
723
773
|
const tooltip = screen.getByRole('tooltip', h);
|
|
724
774
|
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
725
775
|
|
|
726
|
-
|
|
776
|
+
// The trigger button is visually hidden, so the disabled-reason tooltip
|
|
777
|
+
// anchors its hover listeners to the visible container surface.
|
|
778
|
+
const surface = document.querySelector(
|
|
779
|
+
'.astryx-file-input',
|
|
780
|
+
) as HTMLElement;
|
|
781
|
+
fireEvent.mouseEnter(surface);
|
|
727
782
|
await waitFor(() => {
|
|
728
783
|
expect(tooltip).toHaveAttribute('popover-open');
|
|
729
784
|
});
|
|
730
785
|
|
|
731
|
-
fireEvent.mouseLeave(
|
|
786
|
+
fireEvent.mouseLeave(surface);
|
|
732
787
|
await waitFor(() => {
|
|
733
788
|
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
734
789
|
});
|
|
@@ -809,7 +864,10 @@ describe('FileInput', () => {
|
|
|
809
864
|
});
|
|
810
865
|
|
|
811
866
|
it('blocks opening the file picker while focusable-disabled', async () => {
|
|
812
|
-
|
|
867
|
+
// The trigger is a visually-hidden button (pointer-events: none) — real
|
|
868
|
+
// pointer clicks land on the container surface, so bypass the pointer
|
|
869
|
+
// check to exercise the disabled guard directly.
|
|
870
|
+
const user = userEvent.setup({pointerEventsCheck: 0});
|
|
813
871
|
render(
|
|
814
872
|
<FileInput
|
|
815
873
|
label="Resume"
|
|
@@ -850,11 +908,15 @@ describe('FileInput', () => {
|
|
|
850
908
|
});
|
|
851
909
|
});
|
|
852
910
|
|
|
853
|
-
|
|
854
911
|
describe('FileInput statusVariant forwarding', () => {
|
|
855
912
|
it('defaults to attached (status renders with data-variant="attached")', () => {
|
|
856
913
|
const {container} = render(
|
|
857
|
-
<FileInput
|
|
914
|
+
<FileInput
|
|
915
|
+
label="Upload"
|
|
916
|
+
value={null}
|
|
917
|
+
onChange={() => {}}
|
|
918
|
+
status={{type: 'error', message: 'Something went wrong'}}
|
|
919
|
+
/>,
|
|
858
920
|
);
|
|
859
921
|
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
860
922
|
'data-variant',
|
|
@@ -864,7 +926,13 @@ describe('FileInput statusVariant forwarding', () => {
|
|
|
864
926
|
|
|
865
927
|
it('forwards statusVariant="detached" to the underlying Field status', () => {
|
|
866
928
|
const {container} = render(
|
|
867
|
-
<FileInput
|
|
929
|
+
<FileInput
|
|
930
|
+
label="Upload"
|
|
931
|
+
value={null}
|
|
932
|
+
onChange={() => {}}
|
|
933
|
+
status={{type: 'error', message: 'Something went wrong'}}
|
|
934
|
+
statusVariant="detached"
|
|
935
|
+
/>,
|
|
868
936
|
);
|
|
869
937
|
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
870
938
|
'data-variant',
|