@astryxdesign/core 0.7.5 → 0.7.6-canary.0bd3b37
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/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +5 -5
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +183 -5
- package/dist/AppShell/observeMainOverflow.d.ts +9 -0
- package/dist/AppShell/observeMainOverflow.d.ts.map +1 -0
- package/dist/AppShell/observeMainOverflow.js +125 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -3
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +8 -1
- package/dist/BottomSheet/BottomSheet.js +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +2 -1
- package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +30 -3
- package/dist/Button/Button.js +4 -4
- package/dist/Carousel/Carousel.d.ts +3 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +12 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -5
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +37 -4
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +49 -6
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +12 -4
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +108 -23
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +8 -3
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +11 -7
- package/dist/DateInput/usePointerDragScroll.d.ts +10 -1
- package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -1
- package/dist/DateInput/usePointerDragScroll.js +25 -9
- package/dist/DateInput/useScrollSettle.d.ts +3 -1
- package/dist/DateInput/useScrollSettle.d.ts.map +1 -1
- package/dist/DateInput/useScrollSettle.js +37 -8
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +9 -5
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -12
- package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
- package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +34 -9
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +5 -4
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +2 -1
- package/dist/Drawer/Drawer.d.ts +1 -0
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +7 -2
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +35 -22
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.js +2 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -16
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +2 -1
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +31 -6
- package/dist/Field/InputClearButton.js +3 -3
- package/dist/Field/PanelSearchInput.js +2 -2
- package/dist/FieldStatus/FieldStatus.d.ts +3 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +3 -2
- package/dist/FileInput/FileInput.d.ts +3 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +66 -34
- package/dist/Heading/Heading.js +1 -1
- package/dist/Icon/Icon.js +1 -1
- package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckIndicator.js +2 -1
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +3 -1
- package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
- package/dist/Indicator/RadioIndicator.js +3 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +21 -7
- package/dist/Item/ItemSwipeLayer.js +1 -1
- package/dist/Layer/popoverOperations.d.ts +27 -0
- package/dist/Layer/popoverOperations.d.ts.map +1 -0
- package/dist/Layer/popoverOperations.js +159 -0
- package/dist/Layer/useLayer.d.ts +1 -1
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +120 -65
- package/dist/Layer/useTouchTrigger.d.ts +9 -0
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +1 -1
- package/dist/Layout/Layout.js +1 -1
- package/dist/Layout/LayoutContent.js +1 -1
- package/dist/Layout/LayoutFooter.js +1 -1
- package/dist/Layout/LayoutHeader.js +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +5 -0
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +18 -4
- package/dist/List/List.js +1 -1
- package/dist/List/ListItem.js +3 -3
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +7 -2
- package/dist/MultiSelector/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +45 -19
- package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenuItem.js +5 -29
- package/dist/NumberInput/NumberInput.d.ts +3 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +21 -4
- package/dist/NumberInput/numberParser.d.ts +3 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -1
- package/dist/NumberInput/numberParser.js +7 -44
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +27 -0
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +32 -12
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +10 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +37 -30
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +16 -12
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +11 -3
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +81 -12
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +9 -6
- package/dist/Selector/hooks.js +2 -2
- package/dist/Selector/useSelectorPresentation.d.ts +8 -1
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
- package/dist/Selector/useSelectorPresentation.js +20 -4
- package/dist/SideNav/SideNavItem.d.ts +2 -2
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +18 -7
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +100 -12
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +3 -3
- package/dist/Stack/Stack.js +1 -1
- package/dist/Stack/StackItem.js +1 -1
- package/dist/Stepper/Step.d.ts.map +1 -1
- package/dist/Stepper/Step.js +26 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +10 -3
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +16 -1
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +36 -10
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +46 -58
- package/dist/Text/Text.js +1 -1
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +23 -5
- package/dist/TextArea/TextArea.d.ts +1 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +13 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -3
- package/dist/TimeInput/TimeInput.d.ts +6 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +21 -14
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +5 -4
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +2 -2
- package/dist/Timestamp/TimestampHoverCard.js +4 -4
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +60 -20
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +101 -54
- package/dist/ToggleButton/ToggleButtonGroup.d.ts +7 -1
- package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButtonGroup.js +16 -2
- package/dist/Token/Token.d.ts +1 -1
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +52 -5
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +14 -2
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +95 -54
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerBottomSheet.js +11 -17
- package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts +2 -0
- package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts.map +1 -0
- package/dist/Tokenizer/TokenizerClearAllMessageContext.js +17 -0
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +2 -1
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +3 -0
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +1 -0
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +27 -4
- package/dist/Typeahead/BaseTypeahead.d.ts +4 -4
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +52 -9
- package/dist/Typeahead/Typeahead.d.ts +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +43 -9
- package/dist/astryx.css +26 -5
- package/dist/gestures/GestureArena.d.ts +32 -1
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +110 -9
- package/dist/gestures/GestureHost.d.ts +351 -0
- package/dist/gestures/GestureHost.d.ts.map +1 -0
- package/dist/gestures/GestureHost.js +1773 -0
- package/dist/gestures/GestureRecognizer.d.ts +13 -1
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +19 -1
- package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/LongPressGestureRecognizer.js +3 -0
- package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
- package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/PanGestureRecognizer.js +3 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PinchGestureRecognizer.js +128 -0
- package/dist/gestures/PressTracker.d.ts +112 -0
- package/dist/gestures/PressTracker.d.ts.map +1 -0
- package/dist/gestures/PressTracker.js +267 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/SwipeGestureRecognizer.js +148 -0
- package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
- package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/TapGestureRecognizer.js +3 -0
- package/dist/gestures/VelocityTracker.d.ts +6 -2
- package/dist/gestures/VelocityTracker.d.ts.map +1 -1
- package/dist/gestures/VelocityTracker.js +7 -3
- package/dist/gestures/haptics.d.ts +65 -0
- package/dist/gestures/haptics.d.ts.map +1 -0
- package/dist/gestures/haptics.js +182 -0
- package/dist/gestures/index.d.ts +37 -0
- package/dist/gestures/index.d.ts.map +1 -0
- package/dist/gestures/index.js +25 -0
- package/dist/gestures/nativeScroll.d.ts +42 -0
- package/dist/gestures/nativeScroll.d.ts.map +1 -0
- package/dist/gestures/nativeScroll.js +228 -0
- package/dist/gestures/touchAction.d.ts +60 -0
- package/dist/gestures/touchAction.d.ts.map +1 -0
- package/dist/gestures/touchAction.js +157 -0
- package/dist/gestures/touchActionRule.d.ts +34 -0
- package/dist/gestures/touchActionRule.d.ts.map +1 -0
- package/dist/gestures/touchActionRule.js +140 -0
- package/dist/gestures/types.d.ts +18 -7
- package/dist/gestures/types.d.ts.map +1 -1
- package/dist/gestures/types.js +22 -2
- package/dist/gestures/useGestureRecognizer.d.ts +56 -0
- package/dist/gestures/useGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/useGestureRecognizer.js +226 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/modalAnnounce.d.ts.map +1 -1
- package/dist/hooks/modalAnnounce.js +107 -61
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -1
- package/dist/hooks/scrollKeyboardDelegation.js +7 -3
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
- package/dist/hooks/useAdaptivePresentation.js +4 -1
- package/dist/hooks/useAnnounce.d.ts +101 -42
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +161 -75
- package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
- package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
- package/dist/hooks/useAnnounceRenderedText.js +5 -3
- package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
- package/dist/hooks/useBusyAnnouncement.js +6 -12
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +12 -1
- package/dist/hooks/useScrollOverflow.d.ts +2 -1
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +56 -10
- package/dist/i18n/generated-locales/af-ZA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/af-ZA.generated.js +0 -5
- package/dist/i18n/generated-locales/ar-SA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ar-SA.generated.js +0 -5
- package/dist/i18n/generated-locales/ca-ES.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ca-ES.generated.js +0 -5
- package/dist/i18n/generated-locales/cs-CZ.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/cs-CZ.generated.js +0 -5
- package/dist/i18n/generated-locales/da-DK.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/da-DK.generated.js +0 -5
- package/dist/i18n/generated-locales/de-DE.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/de-DE.generated.js +0 -5
- package/dist/i18n/generated-locales/el-GR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/el-GR.generated.js +0 -5
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +8 -5
- package/dist/i18n/generated-locales/es-ES.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/es-ES.generated.js +0 -5
- package/dist/i18n/generated-locales/fi-FI.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/fi-FI.generated.js +0 -5
- package/dist/i18n/generated-locales/fr-FR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/fr-FR.generated.js +0 -5
- package/dist/i18n/generated-locales/he-IL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/he-IL.generated.js +0 -5
- package/dist/i18n/generated-locales/hu-HU.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/hu-HU.generated.js +0 -5
- package/dist/i18n/generated-locales/it-IT.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/it-IT.generated.js +0 -5
- package/dist/i18n/generated-locales/ja-JP.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ja-JP.generated.js +0 -5
- package/dist/i18n/generated-locales/ko-KR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ko-KR.generated.js +0 -5
- package/dist/i18n/generated-locales/nl-NL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/nl-NL.generated.js +0 -5
- package/dist/i18n/generated-locales/no-NO.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/no-NO.generated.js +0 -5
- package/dist/i18n/generated-locales/pl-PL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pl-PL.generated.js +0 -5
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +8 -5
- package/dist/i18n/generated-locales/pt-BR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pt-BR.generated.js +0 -5
- package/dist/i18n/generated-locales/pt-PT.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pt-PT.generated.js +0 -5
- package/dist/i18n/generated-locales/ro-RO.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ro-RO.generated.js +0 -5
- package/dist/i18n/generated-locales/ru-RU.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ru-RU.generated.js +0 -5
- package/dist/i18n/generated-locales/sr-SP.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/sr-SP.generated.js +0 -5
- package/dist/i18n/generated-locales/sv-SE.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/sv-SE.generated.js +0 -5
- package/dist/i18n/generated-locales/tr-TR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/tr-TR.generated.js +0 -5
- package/dist/i18n/generated-locales/uk-UA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/uk-UA.generated.js +0 -5
- package/dist/i18n/generated-locales/vi-VN.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/vi-VN.generated.js +0 -5
- package/dist/i18n/generated-locales/zh-CN.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/zh-CN.generated.js +0 -5
- package/dist/i18n/generated-locales/zh-TW.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/zh-TW.generated.js +0 -5
- package/dist/i18n/useListFormatter.d.ts +9 -0
- package/dist/i18n/useListFormatter.d.ts.map +1 -0
- package/dist/i18n/useListFormatter.js +41 -0
- package/dist/motion/animator.d.ts +83 -8
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +701 -119
- package/dist/motion/coast.d.ts +4 -1
- package/dist/motion/coast.d.ts.map +1 -1
- package/dist/motion/coast.js +4 -2
- package/dist/motion/reducedMotion.d.ts.map +1 -1
- package/dist/motion/reducedMotion.js +6 -2
- package/dist/motion/reducedMotionPlan.d.ts +7 -0
- package/dist/motion/reducedMotionPlan.d.ts.map +1 -1
- package/dist/motion/reducedMotionPlan.js +14 -1
- package/dist/motion/resolveMotion.d.ts +42 -0
- package/dist/motion/resolveMotion.d.ts.map +1 -0
- package/dist/motion/resolveMotion.js +43 -0
- package/dist/motion/roleValue.d.ts +2 -1
- package/dist/motion/roleValue.d.ts.map +1 -1
- package/dist/motion/roleValue.js +2 -1
- package/dist/motion/uikit/timing.d.ts +7 -1
- package/dist/motion/uikit/timing.d.ts.map +1 -1
- package/dist/motion/uikit/timing.js +19 -1
- package/dist/theme/defineTheme.d.ts +19 -5
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +64 -4
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
- package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
- package/dist/theme/domainTokens/index.d.ts +5 -4
- package/dist/theme/domainTokens/index.d.ts.map +1 -1
- package/dist/theme/domainTokens/index.js +5 -9
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/motionKnobs.d.ts +56 -0
- package/dist/theme/motionKnobs.d.ts.map +1 -0
- package/dist/theme/motionKnobs.js +48 -0
- package/dist/theme/resolveThemeValues.d.ts +11 -4
- package/dist/theme/resolveThemeValues.d.ts.map +1 -1
- package/dist/theme/resolveThemeValues.js +40 -4
- package/dist/theme/themeAdaptations.d.ts +7 -3
- package/dist/theme/themeAdaptations.d.ts.map +1 -1
- package/dist/theme/themeAdaptations.js +36 -7
- package/dist/theme/themeMotion.d.ts +96 -0
- package/dist/theme/themeMotion.d.ts.map +1 -0
- package/dist/theme/themeMotion.js +177 -0
- package/dist/theme/tokenReferences.d.ts +31 -0
- package/dist/theme/tokenReferences.d.ts.map +1 -0
- package/dist/theme/tokenReferences.js +132 -0
- package/dist/theme/tokens.d.ts +1 -0
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +9 -129
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +12 -0
- package/dist/utils/focusOutline.stylex.d.ts +6 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +7 -0
- package/dist/utils/focusReturn.d.ts +17 -1
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +11 -4
- package/dist/utils/getNodeText.d.ts +8 -0
- package/dist/utils/getNodeText.d.ts.map +1 -0
- package/dist/utils/getNodeText.js +37 -0
- package/dist/utils/interactionModality.d.ts +11 -2
- package/dist/utils/interactionModality.d.ts.map +1 -1
- package/dist/utils/interactionModality.js +58 -5
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +17 -8
- package/dist/utils/plainDate.d.ts +2 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +46 -3
- package/dist/utils/presentation.d.ts +50 -0
- package/dist/utils/presentation.d.ts.map +1 -0
- package/dist/utils/presentation.js +125 -0
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +20 -1
- package/dist/utils/primaryClick.d.ts +11 -0
- package/dist/utils/primaryClick.d.ts.map +1 -0
- package/dist/utils/primaryClick.js +25 -0
- package/dist/utils/sharedMutationObserver.d.ts +19 -11
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
- package/dist/utils/sharedMutationObserver.js +66 -26
- package/dist/utils/timeFormat.d.ts +47 -0
- package/dist/utils/timeFormat.d.ts.map +1 -0
- package/dist/utils/timeFormat.js +240 -0
- package/dist/utils/touchPresentation.d.ts +2 -2
- package/dist/utils/touchPresentation.d.ts.map +1 -1
- package/dist/utils/touchPresentation.js +13 -8
- package/dist/utils/touchPresentation.stylex.d.ts +15 -0
- package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
- package/dist/utils/touchPresentation.stylex.js +33 -0
- package/dist/utils/touchQueries.d.ts +13 -0
- package/dist/utils/touchQueries.d.ts.map +1 -0
- package/dist/utils/touchQueries.js +15 -0
- package/dist/utils/unicodeDigits.d.ts +20 -0
- package/dist/utils/unicodeDigits.d.ts.map +1 -0
- package/dist/utils/unicodeDigits.js +70 -0
- package/dist/utils/useFocusAcrossSwap.d.ts +19 -0
- package/dist/utils/useFocusAcrossSwap.d.ts.map +1 -0
- package/dist/utils/useFocusAcrossSwap.js +41 -0
- package/locales/af-ZA.json +0 -21
- package/locales/ar-SA.json +0 -21
- package/locales/ca-ES.json +0 -21
- package/locales/cs-CZ.json +0 -21
- package/locales/da-DK.json +0 -21
- package/locales/de-DE.json +0 -21
- package/locales/el-GR.json +0 -21
- package/locales/en.json +35 -23
- package/locales/es-ES.json +0 -21
- package/locales/fi-FI.json +0 -21
- package/locales/fr-FR.json +0 -21
- package/locales/he-IL.json +0 -21
- package/locales/hu-HU.json +0 -21
- package/locales/it-IT.json +0 -21
- package/locales/ja-JP.json +0 -21
- package/locales/ko-KR.json +0 -21
- package/locales/nl-NL.json +0 -21
- package/locales/no-NO.json +0 -21
- package/locales/pl-PL.json +0 -21
- package/locales/pseudo.json +23 -14
- package/locales/pt-BR.json +0 -21
- package/locales/pt-PT.json +0 -21
- package/locales/ro-RO.json +0 -21
- package/locales/ru-RU.json +0 -21
- package/locales/sr-SP.json +0 -21
- package/locales/sv-SE.json +0 -21
- package/locales/tr-TR.json +0 -21
- package/locales/uk-UA.json +0 -21
- package/locales/vi-VN.json +0 -21
- package/locales/zh-CN.json +0 -21
- package/locales/zh-TW.json +0 -21
- package/package.json +19 -5
- package/src/AlertDialog/AlertDialog.tsx +6 -5
- package/src/AppShell/AppShell.doc.mjs +1 -1
- package/src/AppShell/AppShell.spec.md +9 -12
- package/src/AppShell/AppShell.tsx +225 -5
- package/src/AppShell/observeMainOverflow.ts +143 -0
- package/src/Avatar/Avatar.spec.md +22 -22
- package/src/Avatar/Avatar.tsx +18 -1
- package/src/Badge/Badge.doc.mjs +1 -1
- package/src/Badge/Badge.tsx +20 -2
- package/src/BottomSheet/BottomSheet.tsx +1 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
- package/src/BottomSheet/useSheetGestures.ts +37 -6
- package/src/Button/Button.tsx +4 -4
- package/src/Calendar/Calendar.spec.md +87 -48
- package/src/Carousel/Carousel.doc.mjs +1 -1
- package/src/Carousel/Carousel.spec.md +9 -9
- package/src/Carousel/Carousel.tsx +10 -2
- package/src/Chat/ChatComposer.spec.md +75 -59
- package/src/Chat/ChatComposerTokenElement.spec.md +45 -33
- package/src/Chat/ChatDictationButton.spec.md +126 -81
- package/src/Chat/ChatLayout.spec.md +41 -36
- package/src/Chat/ChatLayoutScrollButton.spec.md +87 -103
- package/src/Chat/ChatMessage.spec.md +64 -65
- package/src/Chat/ChatSendButton.spec.md +78 -67
- package/src/CheckboxInput/CheckboxInput.tsx +6 -5
- package/src/Citation/Citation.spec.md +60 -47
- package/src/ClickableCard/ClickableCard.tsx +48 -4
- package/src/Collapsible/Collapsible.spec.md +2 -0
- package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
- package/src/ComplexSelector/ComplexSelector.tsx +60 -6
- package/src/ContextMenu/ContextMenu.spec.md +84 -56
- package/src/ContextMenu/ContextMenuItem.spec.md +132 -0
- package/src/DateInput/DateInput.spec.md +74 -57
- package/src/DateInput/DateInput.tsx +29 -14
- package/src/DateInput/MonthScroller.tsx +108 -24
- package/src/DateInput/TouchDateField.tsx +14 -3
- package/src/DateInput/Wheel.tsx +9 -6
- package/src/DateInput/usePointerDragScroll.ts +34 -10
- package/src/DateInput/useScrollSettle.ts +38 -7
- package/src/DateRangeInput/DateRangeInput.tsx +34 -15
- package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
- package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
- package/src/DateTimeInput/DateTimeInput.tsx +46 -24
- package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
- package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
- package/src/Dialog/Dialog.tsx +42 -9
- package/src/Dialog/DialogHeader.tsx +5 -4
- package/src/Divider/Divider.tsx +2 -1
- package/src/Drawer/Drawer.spec.md +111 -80
- package/src/Drawer/Drawer.tsx +13 -2
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/DropdownMenu/DropdownMenu.spec.md +198 -218
- package/src/DropdownMenu/DropdownMenu.tsx +43 -21
- package/src/DropdownMenu/DropdownMenuCheckboxItem.spec.md +192 -0
- package/src/DropdownMenu/DropdownMenuDivider.spec.md +25 -31
- package/src/DropdownMenu/DropdownMenuGroup.spec.md +145 -0
- package/src/DropdownMenu/DropdownMenuGroup.tsx +2 -1
- package/src/DropdownMenu/DropdownMenuItem.spec.md +48 -42
- package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +34 -37
- package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +54 -53
- package/src/DropdownMenu/DropdownMenuSubMenu.spec.md +47 -41
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
- package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
- package/src/EmptyState/EmptyState.spec.md +35 -29
- package/src/EmptyState/EmptyState.tsx +2 -1
- package/src/Field/Field.doc.mjs +2 -1
- package/src/Field/FieldLabel.doc.mjs +4 -2
- package/src/Field/FieldLabel.spec.md +30 -21
- package/src/Field/FieldLabel.tsx +42 -7
- package/src/Field/InputClearButton.tsx +3 -3
- package/src/Field/PanelSearchInput.tsx +2 -2
- package/src/FieldStatus/FieldStatus.tsx +9 -2
- package/src/FileInput/FileInput.tsx +73 -29
- package/src/FormLayout/FormLayout.spec.md +11 -20
- package/src/Heading/Heading.tsx +1 -1
- package/src/Icon/Icon.tsx +1 -1
- package/src/Indicator/CheckIndicator.spec.md +47 -42
- package/src/Indicator/CheckIndicator.tsx +2 -1
- package/src/Indicator/CheckboxIndicator.spec.md +40 -37
- package/src/Indicator/CheckboxIndicator.tsx +3 -1
- package/src/Indicator/Indicator.spec.md +188 -0
- package/src/Indicator/RadioIndicator.tsx +3 -1
- package/src/Item/Item.tsx +16 -8
- package/src/Item/ItemSwipeLayer.tsx +1 -1
- package/src/Kbd/Kbd.spec.md +81 -38
- package/src/Layer/popoverOperations.ts +172 -0
- package/src/Layer/useLayer.tsx +145 -77
- package/src/Layer/useTouchTrigger.ts +1 -1
- package/src/Layout/Layout.spec.md +127 -131
- package/src/Layout/Layout.tsx +1 -1
- package/src/Layout/LayoutContent.spec.md +205 -0
- package/src/Layout/LayoutContent.tsx +1 -1
- package/src/Layout/LayoutFooter.spec.md +190 -0
- package/src/Layout/LayoutFooter.tsx +1 -1
- package/src/Layout/LayoutHeader.spec.md +193 -0
- package/src/Layout/LayoutHeader.tsx +1 -1
- package/src/Layout/LayoutPanel.spec.md +194 -0
- package/src/Lightbox/Lightbox.spec.md +249 -61
- package/src/Lightbox/Lightbox.tsx +5 -0
- package/src/Link/Link.spec.md +3 -2
- package/src/Link/Link.tsx +20 -2
- package/src/List/List.tsx +1 -1
- package/src/List/ListItem.spec.md +205 -0
- package/src/List/ListItem.tsx +3 -3
- package/src/MetadataList/MetadataList.spec.md +187 -0
- package/src/MetadataList/MetadataListItem.spec.md +165 -0
- package/src/MobileNav/MobileNav.tsx +9 -1
- package/src/MultiSelector/MultiSelector.spec.md +124 -114
- package/src/MultiSelector/MultiSelector.tsx +44 -15
- package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
- package/src/NavMenu/NavHeadingMenuItem.tsx +5 -39
- package/src/NumberInput/NumberInput.doc.mjs +13 -7
- package/src/NumberInput/NumberInput.spec.md +59 -37
- package/src/NumberInput/NumberInput.tsx +37 -4
- package/src/NumberInput/numberParser.ts +7 -45
- package/src/Outline/Outline.spec.md +165 -69
- package/src/Outline/Outline.tsx +23 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +3 -4
- package/src/OverflowList/OverflowList.spec.md +102 -55
- package/src/Pagination/Pagination.spec.md +158 -84
- package/src/Popover/Popover.tsx +34 -14
- package/src/Popover/usePopover.tsx +10 -3
- package/src/PowerSearch/PowerSearch.tsx +42 -31
- package/src/ProgressBar/ProgressBar.spec.md +143 -119
- package/src/ProgressBar/ProgressBar.tsx +58 -10
- package/src/RadioList/RadioList.spec.md +1 -1
- package/src/RadioList/RadioListItem.tsx +11 -2
- package/src/Resizable/Resizable.spec.md +144 -124
- package/src/Resizable/useResizable.ts +82 -16
- package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
- package/src/Selector/Selector.tsx +16 -6
- package/src/Selector/hooks.ts +2 -2
- package/src/Selector/useSelectorPresentation.ts +31 -3
- package/src/SideNav/SideNavItem.spec.md +14 -24
- package/src/SideNav/SideNavItem.tsx +27 -10
- package/src/Skeleton/Skeleton.tsx +3 -1
- package/src/Slider/Slider.spec.md +18 -16
- package/src/Slider/Slider.tsx +117 -12
- package/src/Spinner/Spinner.tsx +5 -3
- package/src/Stack/Stack.tsx +1 -1
- package/src/Stack/StackItem.tsx +1 -1
- package/src/Stepper/Step.spec.md +36 -42
- package/src/Stepper/Step.tsx +33 -6
- package/src/Stepper/Stepper.doc.mjs +1 -1
- package/src/Switch/Switch.spec.md +1 -1
- package/src/Switch/Switch.tsx +11 -3
- package/src/TabList/TabList.tsx +16 -1
- package/src/Table/Table.spec.md +1 -1
- package/src/Table/Table.tsx +43 -11
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
- package/src/Table/plugins/sortable/useTableSortable.tsx +44 -73
- package/src/Text/Text.tsx +1 -1
- package/src/Text/useTruncation.ts +23 -5
- package/src/TextArea/TextArea.spec.md +10 -13
- package/src/TextArea/TextArea.tsx +15 -1
- package/src/TextInput/TextInput.tsx +3 -1
- package/src/TimeInput/TimeInput.doc.mjs +8 -8
- package/src/TimeInput/TimeInput.spec.md +58 -61
- package/src/TimeInput/TimeInput.tsx +36 -18
- package/src/TimeInput/TouchTimeField.tsx +4 -6
- package/src/Timestamp/Timestamp.spec.md +6 -12
- package/src/Timestamp/Timestamp.tsx +6 -2
- package/src/Timestamp/TimestampHoverCard.tsx +4 -4
- package/src/Toast/Toast.spec.md +15 -24
- package/src/Toast/Toast.tsx +104 -36
- package/src/Toast/ToastViewport.tsx +121 -53
- package/src/ToggleButton/ToggleButtonGroup.tsx +20 -1
- package/src/Token/Token.doc.mjs +2 -2
- package/src/Token/Token.spec.md +52 -61
- package/src/Token/Token.tsx +75 -5
- package/src/Token/TokenLink.tsx +21 -7
- package/src/Tokenizer/Tokenizer.spec.md +18 -4
- package/src/Tokenizer/Tokenizer.tsx +102 -60
- package/src/Tokenizer/TokenizerBottomSheet.tsx +11 -17
- package/src/Tokenizer/TokenizerClearAllMessageContext.ts +21 -0
- package/src/Toolbar/Toolbar.spec.md +5 -6
- package/src/Toolbar/Toolbar.tsx +2 -1
- package/src/TopNav/TopNavMegaMenu.tsx +4 -1
- package/src/TopNav/TopNavMenu.tsx +1 -0
- package/src/TreeList/TreeListItem.tsx +26 -4
- package/src/Typeahead/BaseTypeahead.doc.mjs +3 -3
- package/src/Typeahead/BaseTypeahead.spec.md +45 -45
- package/src/Typeahead/BaseTypeahead.tsx +66 -11
- package/src/Typeahead/Typeahead.tsx +51 -9
- package/src/gestures/GestureArena.ts +111 -11
- package/src/gestures/GestureHost.ts +2064 -0
- package/src/gestures/GestureRecognizer.ts +21 -1
- package/src/gestures/LongPressGestureRecognizer.ts +4 -0
- package/src/gestures/PanGestureRecognizer.ts +4 -0
- package/src/gestures/PinchGestureRecognizer.ts +148 -0
- package/src/gestures/PressTracker.ts +386 -0
- package/src/gestures/README.md +420 -0
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
- package/src/gestures/SwipeGestureRecognizer.ts +197 -0
- package/src/gestures/TapGestureRecognizer.ts +4 -0
- package/src/gestures/VelocityTracker.ts +10 -3
- package/src/gestures/haptics.ts +203 -0
- package/src/gestures/index.ts +79 -0
- package/src/gestures/nativeScroll.ts +277 -0
- package/src/gestures/touchAction.ts +191 -0
- package/src/gestures/touchActionRule.ts +157 -0
- package/src/gestures/types.ts +30 -9
- package/src/gestures/useGestureRecognizer.ts +305 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/modalAnnounce.ts +129 -72
- package/src/hooks/scrollKeyboardDelegation.ts +9 -3
- package/src/hooks/useAdaptivePresentation.ts +4 -1
- package/src/hooks/useAnnounce.doc.mjs +35 -3
- package/src/hooks/useAnnounce.ts +209 -102
- package/src/hooks/useAnnounceRenderedText.ts +17 -3
- package/src/hooks/useBusyAnnouncement.ts +7 -10
- package/src/hooks/useMenuHover.ts +15 -1
- package/src/hooks/useScrollOverflow.doc.mjs +1 -1
- package/src/hooks/useScrollOverflow.ts +59 -10
- package/src/i18n/generated-locales/af-ZA.generated.ts +0 -7
- package/src/i18n/generated-locales/ar-SA.generated.ts +0 -6
- package/src/i18n/generated-locales/ca-ES.generated.ts +0 -7
- package/src/i18n/generated-locales/cs-CZ.generated.ts +0 -6
- package/src/i18n/generated-locales/da-DK.generated.ts +0 -6
- package/src/i18n/generated-locales/de-DE.generated.ts +0 -6
- package/src/i18n/generated-locales/el-GR.generated.ts +0 -7
- package/src/i18n/generated-locales/en.generated.ts +9 -6
- package/src/i18n/generated-locales/es-ES.generated.ts +0 -7
- package/src/i18n/generated-locales/fi-FI.generated.ts +0 -6
- package/src/i18n/generated-locales/fr-FR.generated.ts +0 -7
- package/src/i18n/generated-locales/he-IL.generated.ts +0 -6
- package/src/i18n/generated-locales/hu-HU.generated.ts +0 -6
- package/src/i18n/generated-locales/it-IT.generated.ts +0 -7
- package/src/i18n/generated-locales/ja-JP.generated.ts +0 -6
- package/src/i18n/generated-locales/ko-KR.generated.ts +0 -6
- package/src/i18n/generated-locales/nl-NL.generated.ts +0 -6
- package/src/i18n/generated-locales/no-NO.generated.ts +0 -6
- package/src/i18n/generated-locales/pl-PL.generated.ts +0 -7
- package/src/i18n/generated-locales/pseudo.generated.ts +9 -6
- package/src/i18n/generated-locales/pt-BR.generated.ts +0 -7
- package/src/i18n/generated-locales/pt-PT.generated.ts +0 -7
- package/src/i18n/generated-locales/ro-RO.generated.ts +0 -6
- package/src/i18n/generated-locales/ru-RU.generated.ts +0 -7
- package/src/i18n/generated-locales/sr-SP.generated.ts +0 -6
- package/src/i18n/generated-locales/sv-SE.generated.ts +0 -6
- package/src/i18n/generated-locales/tr-TR.generated.ts +0 -7
- package/src/i18n/generated-locales/uk-UA.generated.ts +0 -7
- package/src/i18n/generated-locales/vi-VN.generated.ts +0 -6
- package/src/i18n/generated-locales/zh-CN.generated.ts +0 -6
- package/src/i18n/generated-locales/zh-TW.generated.ts +0 -6
- package/src/i18n/useListFormatter.ts +42 -0
- package/src/motion/MEASUREMENTS.md +2 -1
- package/src/motion/README.md +113 -33
- package/src/motion/animator.ts +932 -130
- package/src/motion/coast.ts +10 -3
- package/src/motion/reducedMotion.ts +12 -2
- package/src/motion/reducedMotionPlan.ts +20 -1
- package/src/motion/resolveMotion.ts +68 -0
- package/src/motion/roleValue.ts +2 -1
- package/src/motion/uikit/timing.ts +15 -1
- package/src/theme/Theme.doc.mjs +1 -1
- package/src/theme/defineTheme.ts +117 -8
- package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
- package/src/theme/domainTokens/index.ts +6 -10
- package/src/theme/index.ts +5 -0
- package/src/theme/motionKnobs.ts +82 -0
- package/src/theme/resolveThemeValues.ts +68 -6
- package/src/theme/themeAdaptations.ts +52 -12
- package/src/theme/themeMotion.ts +221 -0
- package/src/theme/tokenReferences.ts +149 -0
- package/src/theme/tokens.stylex.ts +10 -0
- package/src/theme/tokens.ts +15 -158
- package/src/utils/focusOutline.stylex.ts +28 -0
- package/src/utils/focusReturn.ts +17 -6
- package/src/utils/getNodeText.ts +37 -0
- package/src/utils/interactionModality.ts +64 -5
- package/src/utils/interactionOverlay.stylex.ts +12 -3
- package/src/utils/plainDate.ts +58 -3
- package/src/utils/presentation.ts +180 -0
- package/src/utils/pressFeedback.ts +34 -11
- package/src/utils/primaryClick.ts +28 -0
- package/src/utils/sharedMutationObserver.ts +87 -29
- package/src/utils/timeFormat.ts +314 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.ts +13 -8
- package/src/utils/touchQueries.ts +15 -0
- package/src/utils/unicodeDigits.ts +71 -0
- package/src/utils/useFocusAcrossSwap.ts +55 -0
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.d.ts +0 -2
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.js +0 -132
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.d.ts +0 -2
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.js +0 -78
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.d.ts +0 -2
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +0 -65
- package/dist/List/ListMarkers.a11y.chromium.spec.d.ts +0 -2
- package/dist/List/ListMarkers.a11y.chromium.spec.js +0 -677
- package/dist/Markdown/Markdown.fr23.bench.d.ts +0 -2
- package/dist/Markdown/Markdown.fr23.bench.d.ts.map +0 -1
- package/dist/Markdown/Markdown.fr23.bench.js +0 -152
- package/dist/Markdown/MarkdownTable.a11y.chromium.spec.d.ts +0 -2
- package/dist/Markdown/MarkdownTable.a11y.chromium.spec.js +0 -590
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.d.ts +0 -2
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.js +0 -657
- package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.d.ts +0 -2
- package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.js +0 -153
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.d.ts +0 -2
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +0 -116
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.d.ts +0 -2
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.js +0 -49
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.d.ts +0 -2
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +0 -273
- package/dist/Outline/OutlineFocus.a11y.chromium.spec.d.ts +0 -2
- package/dist/Outline/OutlineFocus.a11y.chromium.spec.js +0 -421
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.d.ts +0 -2
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.js +0 -118
- package/dist/reset.a11y.chromium.spec.d.ts +0 -2
- package/dist/reset.a11y.chromium.spec.js +0 -231
- package/dist/reset.until-found.a11y.browser.spec.d.ts +0 -2
- package/dist/reset.until-found.a11y.browser.spec.js +0 -72
- package/src/AlertDialog/AlertDialog.test.tsx +0 -519
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +0 -116
- package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +0 -52
- package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +0 -68
- package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +0 -144
- package/src/AlertDialog/useImperativeAlertDialog.test.tsx +0 -163
- package/src/AppShell/AppShell.test.tsx +0 -1148
- package/src/AspectRatio/AspectRatio.test.tsx +0 -398
- package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +0 -158
- package/src/Avatar/Avatar.test.tsx +0 -867
- package/src/Avatar/AvatarStatusDot.test.tsx +0 -306
- package/src/AvatarGroup/AvatarGroup.test.tsx +0 -313
- package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +0 -263
- package/src/Badge/Badge.test.tsx +0 -125
- package/src/Banner/Banner.test.tsx +0 -879
- package/src/Blockquote/Blockquote.test.tsx +0 -121
- package/src/BottomSheet/BottomSheet.test.tsx +0 -3108
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +0 -248
- package/src/BottomSheet/BottomSheetKeyboard.test.tsx +0 -195
- package/src/BottomSheet/BottomSheetPanel.test.tsx +0 -549
- package/src/BottomSheet/BottomSheetReplace.test.tsx +0 -1638
- package/src/BottomSheet/BottomSheetStacking.announce.test.tsx +0 -246
- package/src/BottomSheet/BottomSheetStacking.test.tsx +0 -3184
- package/src/BottomSheet/BottomSheetSwitcher.test.tsx +0 -2010
- package/src/BottomSheet/__tests__/BottomSheetDrag.a11y.chromium.spec.ts +0 -129
- package/src/BottomSheet/__tests__/BottomSheetFocusReturn.a11y.browser.spec.ts +0 -97
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +0 -415
- package/src/BottomSheet/__tests__/BottomSheetNativeModal.a11y.browser.spec.ts +0 -462
- package/src/BottomSheet/__tests__/BottomSheetPinchZoom.a11y.chromium.spec.ts +0 -247
- package/src/BottomSheet/__tests__/BottomSheetToastOutlet.a11y.chromium.spec.ts +0 -83
- package/src/BottomSheet/snapOffsets.test.ts +0 -254
- package/src/BottomSheet/useSheetGestures.test.ts +0 -1638
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +0 -921
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +0 -120
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.renders.tsx +0 -45
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.states.ts +0 -81
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.test.tsx +0 -125
- package/src/Button/Button.test.tsx +0 -864
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +0 -355
- package/src/Button/__tests__/Button.a11y.harnesses.tsx +0 -47
- package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -25
- package/src/Button/__tests__/Button.a11y.renders.tsx +0 -139
- package/src/Button/__tests__/Button.a11y.states.ts +0 -317
- package/src/Button/__tests__/Button.a11y.test.tsx +0 -187
- package/src/ButtonGroup/ButtonGroup.test.tsx +0 -1025
- package/src/Calendar/Calendar.test.tsx +0 -2186
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +0 -127
- package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +0 -12
- package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +0 -57
- package/src/Calendar/__tests__/Calendar.a11y.states.ts +0 -139
- package/src/Calendar/__tests__/Calendar.a11y.test.tsx +0 -137
- package/src/Calendar/dayCellUtils.test.ts +0 -520
- package/src/Calendar/getInitialFocusDate.test.ts +0 -94
- package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +0 -54
- package/src/Calendar/hooks/useCalendarConstraints.test.ts +0 -153
- package/src/Calendar/hooks/useCalendarDays.test.ts +0 -265
- package/src/Calendar/hooks/useCalendarNavigation.test.ts +0 -252
- package/src/Card/Card.test.tsx +0 -202
- package/src/Carousel/Carousel.test.tsx +0 -915
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +0 -193
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +0 -27
- package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +0 -39
- package/src/Carousel/__tests__/Carousel.a11y.states.ts +0 -139
- package/src/Carousel/__tests__/Carousel.a11y.test.tsx +0 -127
- package/src/Center/Center.test.tsx +0 -406
- package/src/Chat/ChatComposer.test.tsx +0 -364
- package/src/Chat/ChatComposerDrawer.test.tsx +0 -130
- package/src/Chat/ChatComposerInput.test.tsx +0 -2136
- package/src/Chat/ChatComposerTokenElement.test.tsx +0 -85
- package/src/Chat/ChatDictationButton.test.tsx +0 -108
- package/src/Chat/ChatLayout.test.tsx +0 -311
- package/src/Chat/ChatLayoutScrollButton.test.tsx +0 -99
- package/src/Chat/ChatMessage.test.tsx +0 -344
- package/src/Chat/ChatMessageBubble.test.tsx +0 -294
- package/src/Chat/ChatMessageList.test.tsx +0 -198
- package/src/Chat/ChatMessageMetadata.test.tsx +0 -66
- package/src/Chat/ChatPastedTextToken.test.tsx +0 -71
- package/src/Chat/ChatSendButton.test.tsx +0 -309
- package/src/Chat/ChatSystemMessage.test.tsx +0 -189
- package/src/Chat/ChatTokenizedText.test.tsx +0 -163
- package/src/Chat/ChatToolCalls.test.tsx +0 -413
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +0 -577
- package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +0 -740
- package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +0 -457
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +0 -91
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +0 -91
- package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +0 -465
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +0 -416
- package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +0 -1523
- package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +0 -1004
- package/src/Chat/__tests__/ChatTokenizedText.a11y.chromium.spec.ts +0 -574
- package/src/Chat/__tests__/useChatStreamScroll.a11y.chromium.spec.ts +0 -263
- package/src/Chat/__tests__/useChatStreamScroll.fixture.tsx +0 -61
- package/src/Chat/useChatDictation.test.tsx +0 -369
- package/src/Chat/useChatNewMessages.test.tsx +0 -209
- package/src/Chat/useChatPasteAsToken.test.ts +0 -191
- package/src/Chat/useChatStreamScroll.test.tsx +0 -381
- package/src/Chat/useTriggerMenu.test.tsx +0 -994
- package/src/CheckboxInput/CheckboxInput.test.tsx +0 -926
- package/src/CheckboxInput/__tests__/Checkbox.a11y.chromium.spec.ts +0 -354
- package/src/CheckboxInput/__tests__/Checkbox.a11y.known-failures.ts +0 -54
- package/src/CheckboxInput/__tests__/Checkbox.a11y.renders.tsx +0 -310
- package/src/CheckboxInput/__tests__/Checkbox.a11y.states.ts +0 -404
- package/src/CheckboxInput/__tests__/Checkbox.a11y.test.tsx +0 -128
- package/src/CheckboxList/CheckboxList.fr10.test.tsx +0 -51
- package/src/CheckboxList/CheckboxList.test.tsx +0 -1159
- package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +0 -1998
- package/src/Citation/Citation.test.tsx +0 -361
- package/src/ClickableCard/ClickableCard.test.tsx +0 -286
- package/src/Code/Code.test.tsx +0 -75
- package/src/CodeBlock/CodeBlock.highlightStyles.test.tsx +0 -87
- package/src/CodeBlock/CodeBlock.test.tsx +0 -752
- package/src/CodeBlock/CodeLanguage.test.tsx +0 -173
- package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +0 -290
- package/src/CodeBlock/__tests__/CodeBlockLanguages.a11y.chromium.spec.ts +0 -64
- package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +0 -126
- package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +0 -94
- package/src/CodeBlock/highlightRanges.test.ts +0 -253
- package/src/CodeBlock/languages.test.ts +0 -402
- package/src/CodeBlock/tokenizer.test.ts +0 -280
- package/src/CodeBlock/tokenizerBudget.test.ts +0 -112
- package/src/Collapsible/Collapsible.test.tsx +0 -620
- package/src/Collapsible/CollapsibleGroup.test.tsx +0 -656
- package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +0 -183
- package/src/Collapsible/__tests__/Collapsible.a11y.states.ts +0 -117
- package/src/Collapsible/__tests__/Collapsible.a11y.test.tsx +0 -140
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.chromium.spec.ts +0 -559
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +0 -45
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +0 -139
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +0 -135
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +0 -129
- package/src/Collapsible/useCollapsible.test.tsx +0 -236
- package/src/CommandPalette/CommandPalette.perf.test.ts +0 -100
- package/src/CommandPalette/CommandPalette.test.tsx +0 -750
- package/src/CommandPalette/CommandPaletteEmpty.test.tsx +0 -101
- package/src/CommandPalette/CommandPaletteFooter.test.tsx +0 -122
- package/src/CommandPalette/CommandPaletteGroup.test.tsx +0 -129
- package/src/CommandPalette/CommandPaletteInput.test.tsx +0 -539
- package/src/CommandPalette/CommandPaletteItem.test.tsx +0 -417
- package/src/CommandPalette/CommandPaletteList.test.tsx +0 -184
- package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +0 -600
- package/src/CommandPalette/__tests__/CommandPaletteEmptyAudit.a11y.chromium.spec.ts +0 -715
- package/src/CommandPalette/__tests__/CommandPaletteFooter.a11y.chromium.spec.ts +0 -582
- package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +0 -336
- package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +0 -523
- package/src/ComplexSelector/ComplexSelector.focusableDisabled.test.tsx +0 -76
- package/src/ComplexSelector/ComplexSelector.fr10.test.tsx +0 -262
- package/src/ComplexSelector/ComplexSelector.test.tsx +0 -1023
- package/src/ComplexSelector/__tests__/ComplexSelector.a11y.chromium.spec.ts +0 -965
- package/src/ContextMenu/ContextMenu.test.tsx +0 -1582
- package/src/ContextMenu/ContextMenu.triggerProps.test.tsx +0 -54
- package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +0 -439
- package/src/ContextMenu/__tests__/ContextMenuCursorAnchor.a11y.browser.spec.ts +0 -154
- package/src/ContextMenu/__tests__/ContextMenuLongPress.a11y.chromium.spec.ts +0 -117
- package/src/DateInput/DateInput.fr10.test.tsx +0 -283
- package/src/DateInput/DateInput.test.tsx +0 -1459
- package/src/DateInput/DateInputTouch.test.tsx +0 -2828
- package/src/DateInput/NativeDateField.test.tsx +0 -741
- package/src/DateInput/Presentation.test.tsx +0 -121
- package/src/DateInput/__tests__/DateInputAudit.a11y.chromium.spec.ts +0 -577
- package/src/DateInput/__tests__/WheelPinchZoom.a11y.chromium.spec.ts +0 -171
- package/src/DateRangeInput/DateRangeInput.fr10.test.tsx +0 -165
- package/src/DateRangeInput/DateRangeInput.test.tsx +0 -1108
- package/src/DateTimeInput/DateTimeInput.fr10.test.tsx +0 -319
- package/src/DateTimeInput/DateTimeInput.test.tsx +0 -2575
- package/src/DateTimeInput/DateTimeInputTouch.test.tsx +0 -1071
- package/src/DateTimeInput/NativePickerSegments.test.tsx +0 -657
- package/src/DateTimeInput/Presentation.test.tsx +0 -89
- package/src/Dialog/Dialog.source-build.test.mjs +0 -43
- package/src/Dialog/Dialog.test.tsx +0 -1385
- package/src/Dialog/DialogHeader.test.tsx +0 -477
- package/src/Dialog/__tests__/Dialog.a11y.browser.spec.ts +0 -111
- package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +0 -218
- package/src/Dialog/__tests__/Dialog.a11y.known-failures.ts +0 -28
- package/src/Dialog/__tests__/Dialog.a11y.renders.tsx +0 -107
- package/src/Dialog/__tests__/Dialog.a11y.states.ts +0 -104
- package/src/Dialog/__tests__/Dialog.a11y.test.tsx +0 -102
- package/src/Dialog/__tests__/Dialog.reduced-motion.a11y.browser.spec.ts +0 -252
- package/src/Dialog/useImperativeDialog.test.tsx +0 -78
- package/src/Divider/Divider.test.tsx +0 -155
- package/src/Drawer/Drawer.test.tsx +0 -1300
- package/src/Drawer/DrawerDirection.test.tsx +0 -127
- package/src/Drawer/DrawerHeader.test.tsx +0 -102
- package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +0 -402
- package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +0 -298
- package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +0 -93
- package/src/DropdownMenu/DropdownMenu.test.tsx +0 -4340
- package/src/DropdownMenu/DropdownMenuContext.public.test.ts +0 -22
- package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +0 -126
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +0 -635
- package/src/DropdownMenu/DropdownMenuSheetFirstOpen.test.tsx +0 -72
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +0 -2408
- package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +0 -281
- package/src/DropdownMenu/__tests__/DropdownMenuDrillIn.a11y.chromium.spec.ts +0 -175
- package/src/DropdownMenu/__tests__/DropdownMenuFocusReturn.a11y.browser.spec.ts +0 -74
- package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +0 -335
- package/src/DropdownMenu/__tests__/DropdownMenuItemAudit.a11y.chromium.spec.ts +0 -367
- package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +0 -328
- package/src/DropdownMenu/__tests__/DropdownMenuRadioItemAudit.a11y.chromium.spec.ts +0 -540
- package/src/DropdownMenu/__tests__/DropdownMenuSubMenuAudit.a11y.chromium.spec.ts +0 -671
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +0 -146
- package/src/DropdownMenu/__tests__/Menu.a11y.known-failures.ts +0 -26
- package/src/DropdownMenu/__tests__/Menu.a11y.renders.tsx +0 -65
- package/src/DropdownMenu/__tests__/Menu.a11y.states.ts +0 -138
- package/src/DropdownMenu/__tests__/Menu.a11y.test.tsx +0 -136
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +0 -137
- package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +0 -12
- package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +0 -67
- package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +0 -141
- package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +0 -138
- package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +0 -231
- package/src/DropdownMenu/__tests__/MenuHoverFocus.a11y.chromium.spec.ts +0 -238
- package/src/DropdownMenu/__tests__/MenuPinchZoom.a11y.chromium.spec.ts +0 -173
- package/src/DropdownMenu/__tests__/MenuPress.a11y.chromium.spec.ts +0 -503
- package/src/DropdownMenu/__tests__/SubMenuHeaderFooter.a11y.chromium.spec.ts +0 -268
- package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +0 -43
- package/src/EmptyState/EmptyState.announce.test.tsx +0 -227
- package/src/EmptyState/EmptyState.test.tsx +0 -284
- package/src/Field/Field.test.tsx +0 -800
- package/src/Field/FieldLabel.test.tsx +0 -244
- package/src/Field/InputClearButton.test.tsx +0 -395
- package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +0 -297
- package/src/Field/fieldInputFamily.test.tsx +0 -160
- package/src/FieldStatus/FieldStatus.test.tsx +0 -482
- package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +0 -206
- package/src/FieldStatus/__tests__/StatusMessage.a11y.known-failures.ts +0 -13
- package/src/FieldStatus/__tests__/StatusMessage.a11y.renders.tsx +0 -287
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +0 -302
- package/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx +0 -154
- package/src/FileInput/FileInput.fr10.test.tsx +0 -21
- package/src/FileInput/FileInput.test.tsx +0 -1270
- package/src/FormLayout/FormLayout.test.tsx +0 -444
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -46
- package/src/Grid/Grid.test.tsx +0 -509
- package/src/HStack/HStack.test.tsx +0 -129
- package/src/Heading/Heading.test.tsx +0 -328
- package/src/HoverCard/HoverCard.test.tsx +0 -1339
- package/src/Icon/Icon.test.tsx +0 -990
- package/src/Icon/globalIconRegistry.test.tsx +0 -613
- package/src/IconButton/IconButton.test.tsx +0 -156
- package/src/Indicator/Indicator.test.tsx +0 -389
- package/src/Indicator/indicatorRegistry.test.tsx +0 -106
- package/src/InputGroup/InputGroup.test.tsx +0 -502
- package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +0 -185
- package/src/Item/Item.source-build.test.mjs +0 -88
- package/src/Item/Item.swipeLayer.test.tsx +0 -74
- package/src/Item/Item.test.tsx +0 -1455
- package/src/Item/ItemMarkerColumns.a11y.chromium.spec.ts +0 -211
- package/src/Item/__tests__/ItemSwipeActions.a11y.chromium.spec.ts +0 -337
- package/src/Item/__tests__/ItemSwipePinchZoom.a11y.chromium.spec.ts +0 -186
- package/src/Kbd/Kbd.test.tsx +0 -299
- package/src/Layer/LayerProvider.test.tsx +0 -143
- package/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts +0 -176
- package/src/Layer/__tests__/TouchTrigger.a11y.chromium.spec.ts +0 -200
- package/src/Layer/anchorName.test.ts +0 -146
- package/src/Layer/layerDismissalFamilies.test.tsx +0 -487
- package/src/Layer/layerDismissalInvariants.test.tsx +0 -501
- package/src/Layer/layerHost.test.ts +0 -117
- package/src/Layer/layerScopedContext.test.tsx +0 -499
- package/src/Layer/layerViewportInset.source-build.test.mjs +0 -90
- package/src/Layer/layerViewportInset.test.ts +0 -120
- package/src/Layer/useLayer.test.tsx +0 -2423
- package/src/Layer/useLayerDismissal.test.tsx +0 -580
- package/src/Layer/useTouchTrigger.test.ts +0 -70
- package/src/Layout/Layout.test.tsx +0 -375
- package/src/Layout/LayoutSlots.test.tsx +0 -376
- package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +0 -122
- package/src/Layout/__tests__/Layout.a11y.renders.tsx +0 -110
- package/src/Layout/__tests__/Layout.a11y.states.ts +0 -93
- package/src/Layout/__tests__/Layout.a11y.test.tsx +0 -142
- package/src/Layout/__tests__/childrenAsContent.test.tsx +0 -59
- package/src/Layout/__tests__/contentWidth.test.tsx +0 -303
- package/src/Layout/__tests__/edgeCompensation.test.tsx +0 -191
- package/src/Layout/__tests__/nestedPadding.test.tsx +0 -264
- package/src/Layout/overlayPaddingReset.test.tsx +0 -224
- package/src/Lightbox/Lightbox.test.tsx +0 -819
- package/src/Lightbox/LightboxKeyboard.a11y.browser.spec.ts +0 -78
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +0 -70
- package/src/Lightbox/useLightbox.test.tsx +0 -263
- package/src/Link/Link.test.tsx +0 -457
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +0 -143
- package/src/Link/__tests__/Link.a11y.renders.tsx +0 -97
- package/src/Link/__tests__/Link.a11y.states.ts +0 -104
- package/src/Link/__tests__/Link.a11y.test.tsx +0 -127
- package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +0 -262
- package/src/Link/useLinkComponent.test.tsx +0 -572
- package/src/Link/useLinkify.test.tsx +0 -146
- package/src/List/List.test.tsx +0 -1052
- package/src/List/ListItem.source-build.test.mjs +0 -41
- package/src/List/ListMarkers.a11y.chromium.spec.ts +0 -904
- package/src/Markdown/Markdown.codeLanguages.test.tsx +0 -137
- package/src/Markdown/Markdown.fr23.bench.ts +0 -176
- package/src/Markdown/Markdown.fr23.perf.test.ts +0 -484
- package/src/Markdown/Markdown.helpers.perf.test.ts +0 -495
- package/src/Markdown/Markdown.public.test.ts +0 -309
- package/src/Markdown/Markdown.renderBoundary.test.tsx +0 -72
- package/src/Markdown/Markdown.rerender.test.tsx +0 -143
- package/src/Markdown/Markdown.test.tsx +0 -1383
- package/src/Markdown/MarkdownTable.a11y.chromium.spec.ts +0 -727
- package/src/Markdown/__tests__/MarkdownNesting.a11y.chromium.spec.ts +0 -102
- package/src/Markdown/angleDestinations.test.tsx +0 -104
- package/src/Markdown/ast.test.ts +0 -374
- package/src/Markdown/atxHeadingIndentation.test.tsx +0 -53
- package/src/Markdown/blockNesting.test.tsx +0 -124
- package/src/Markdown/blockquoteMarkers.test.tsx +0 -69
- package/src/Markdown/characterReferences.test.ts +0 -47
- package/src/Markdown/codeSpans.test.tsx +0 -65
- package/src/Markdown/emphasis.test.tsx +0 -214
- package/src/Markdown/fenceInfo.test.tsx +0 -47
- package/src/Markdown/imageAlt.test.tsx +0 -84
- package/src/Markdown/incremental.test.ts +0 -1198
- package/src/Markdown/incrementalIndentation.test.tsx +0 -36
- package/src/Markdown/indentedFences.test.tsx +0 -374
- package/src/Markdown/indentedTables.test.tsx +0 -53
- package/src/Markdown/linkBrackets.test.tsx +0 -116
- package/src/Markdown/linkDestinationLinear.test.tsx +0 -54
- package/src/Markdown/linkDestinations.test.tsx +0 -127
- package/src/Markdown/linkTextCode.test.tsx +0 -65
- package/src/Markdown/listBullets.test.tsx +0 -55
- package/src/Markdown/listContinuation.test.tsx +0 -94
- package/src/Markdown/listThematicBreak.test.tsx +0 -49
- package/src/Markdown/longLinkLabels.test.tsx +0 -49
- package/src/Markdown/openFenceFinalNewline.test.tsx +0 -85
- package/src/Markdown/parser.perf.test.ts +0 -398
- package/src/Markdown/parser.public.test.ts +0 -144
- package/src/Markdown/parser.test.ts +0 -1925
- package/src/Markdown/plugin-renderer/MarkdownPluginNodeRenderer.test.tsx +0 -300
- package/src/Markdown/plugins/frontmatter.test.tsx +0 -150
- package/src/Markdown/plugins/headingLinks.a11y.chromium.spec.ts +0 -856
- package/src/Markdown/plugins/headingLinks.ssr.test.tsx +0 -143
- package/src/Markdown/plugins/headingLinks.test.tsx +0 -637
- package/src/Markdown/plugins/protocol.test.tsx +0 -1083
- package/src/Markdown/plugins/semanticFence.test.tsx +0 -655
- package/src/Markdown/plugins/softBreaks.test.tsx +0 -172
- package/src/Markdown/plugins/sourceDecoration.test.tsx +0 -799
- package/src/Markdown/plugins/sourceLines.test.tsx +0 -715
- package/src/Markdown/plugins/textTransform.test.ts +0 -467
- package/src/Markdown/remark.test.tsx +0 -1877
- package/src/Markdown/streaming.test.ts +0 -148
- package/src/Markdown/streamingListShapes.test.tsx +0 -93
- package/src/Markdown/trailingSpaces.test.tsx +0 -45
- package/src/MetadataList/MetadataList.test.tsx +0 -383
- package/src/MobileNav/MobileNav.test.tsx +0 -281
- package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +0 -584
- package/src/MobileNav/MobileNavCloseTiming.test.ts +0 -58
- package/src/MobileNav/MobileNavCloseVisibility.test.tsx +0 -172
- package/src/MobileNav/MobileNavEntryAnimation.test.tsx +0 -212
- package/src/MobileNav/MobileNavFocus.test.tsx +0 -553
- package/src/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.ts +0 -147
- package/src/MobileNav/MobileNavReopen.test.tsx +0 -118
- package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +0 -109
- package/src/MobileNav/MobileNavToggle.test.tsx +0 -179
- package/src/MobileNav/__tests__/MobileNavWidth.a11y.chromium.spec.ts +0 -103
- package/src/MoreMenu/MoreMenu.test.tsx +0 -356
- package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +0 -149
- package/src/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.ts +0 -72
- package/src/MultiSelector/MultiSelector.focusableDisabled.test.tsx +0 -121
- package/src/MultiSelector/MultiSelector.fr10.test.tsx +0 -221
- package/src/MultiSelector/MultiSelector.test.tsx +0 -4702
- package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +0 -310
- package/src/MultiSelector/__tests__/MultiSelector.a11y.chromium.spec.ts +0 -46
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +0 -128
- package/src/NavIcon/NavIcon.test.tsx +0 -41
- package/src/NavMenu/NavHeadingMenu.test.tsx +0 -714
- package/src/NavMenu/__tests__/NavMenusSlideSelection.a11y.chromium.spec.ts +0 -137
- package/src/NumberInput/NumberInput.fr10.test.tsx +0 -21
- package/src/NumberInput/NumberInput.test.tsx +0 -2636
- package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +0 -124
- package/src/NumberInput/__tests__/NumberInput.a11y.known-failures.ts +0 -26
- package/src/NumberInput/__tests__/NumberInput.a11y.renders.tsx +0 -77
- package/src/NumberInput/__tests__/NumberInput.a11y.states.ts +0 -116
- package/src/NumberInput/__tests__/NumberInput.a11y.test.tsx +0 -154
- package/src/NumberInput/numberInputCommit.test.ts +0 -72
- package/src/NumberInput/numberParser.docblock.test.ts +0 -234
- package/src/NumberInput/numberParser.test.ts +0 -222
- package/src/Outline/Outline.test.tsx +0 -1069
- package/src/Outline/OutlineFocus.a11y.chromium.spec.ts +0 -551
- package/src/Outline/parseOutlineFromMarkdown.test.ts +0 -104
- package/src/OverflowList/OverflowList.test.tsx +0 -1073
- package/src/Overlay/Overlay.test.tsx +0 -424
- package/src/Pagination/Pagination.test.tsx +0 -1754
- package/src/Popover/Popover.test.tsx +0 -1424
- package/src/Popover/PopoverPresentation.test.tsx +0 -952
- package/src/Popover/__tests__/PopoverSecondDocument.a11y.chromium.spec.ts +0 -205
- package/src/PowerSearch/PowerSearch.test.tsx +0 -1003
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +0 -1190
- package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +0 -377
- package/src/PowerSearch/PowerSearchToken.test.tsx +0 -230
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +0 -754
- package/src/PowerSearch/formatFilterValue.test.ts +0 -339
- package/src/PowerSearch/resolveDateTimeRangePart.test.ts +0 -40
- package/src/PowerSearch/usePowerSearchConfig.test.ts +0 -832
- package/src/PowerSearch/usePowerSearchSource.test.ts +0 -621
- package/src/ProgressBar/ProgressBar.test.tsx +0 -852
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +0 -115
- package/src/ProgressBar/__tests__/ProgressBar.a11y.known-failures.ts +0 -12
- package/src/ProgressBar/__tests__/ProgressBar.a11y.renders.tsx +0 -38
- package/src/ProgressBar/__tests__/ProgressBar.a11y.states.ts +0 -123
- package/src/ProgressBar/__tests__/ProgressBar.a11y.test.tsx +0 -107
- package/src/RadioList/RadioList.fr10.test.tsx +0 -67
- package/src/RadioList/RadioList.test.tsx +0 -967
- package/src/RadioList/RadioListItem.press.source-build.test.mjs +0 -80
- package/src/RadioList/__tests__/RadioGroup.a11y.chromium.spec.ts +0 -274
- package/src/RadioList/__tests__/RadioGroup.a11y.known-failures.ts +0 -12
- package/src/RadioList/__tests__/RadioGroup.a11y.renders.tsx +0 -232
- package/src/RadioList/__tests__/RadioGroup.a11y.states.ts +0 -503
- package/src/RadioList/__tests__/RadioGroup.a11y.test.tsx +0 -217
- package/src/Resizable/ResizeHandle.test.tsx +0 -1067
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +0 -120
- package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +0 -39
- package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +0 -80
- package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +0 -197
- package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +0 -123
- package/src/Resizable/shippedDirections.test.ts +0 -462
- package/src/Resizable/useResizable.test.ts +0 -1682
- package/src/Resizable/utils.test.ts +0 -74
- package/src/ScrollableArea/ScrollableArea.test.tsx +0 -304
- package/src/Section/Section.test.tsx +0 -376
- package/src/SegmentedControl/SegmentedControl.test.tsx +0 -897
- package/src/SelectableCard/SelectableCard.test.tsx +0 -216
- package/src/Selector/Selector.focusableDisabled.test.tsx +0 -127
- package/src/Selector/Selector.fr10.test.tsx +0 -149
- package/src/Selector/Selector.sheetActiveDescendant.test.tsx +0 -95
- package/src/Selector/Selector.source-build.test.mjs +0 -79
- package/src/Selector/Selector.test.tsx +0 -5028
- package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +0 -27
- package/src/Selector/__tests__/Combobox.a11y.states.ts +0 -145
- package/src/Selector/__tests__/ComboboxField.a11y.chromium.spec.ts +0 -458
- package/src/Selector/__tests__/EmptyStateAnnouncement.a11y.chromium.spec.ts +0 -91
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +0 -131
- package/src/Selector/__tests__/Listbox.a11y.dom.ts +0 -38
- package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +0 -63
- package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +0 -12
- package/src/Selector/__tests__/Listbox.a11y.states.ts +0 -232
- package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +0 -46
- package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +0 -109
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +0 -126
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +0 -121
- package/src/Selector/useCombobox.public.test.tsx +0 -89
- package/src/Selector/useCombobox.test.tsx +0 -444
- package/src/SideNav/SideNav.test.tsx +0 -4128
- package/src/SideNav/SideNavRailMotion.test.tsx +0 -620
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +0 -417
- package/src/SizeContext/SizeContext.test.tsx +0 -226
- package/src/Skeleton/Skeleton.test.tsx +0 -148
- package/src/Slider/Slider.fr10.test.tsx +0 -93
- package/src/Slider/Slider.test.tsx +0 -1630
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +0 -113
- package/src/Slider/__tests__/Slider.a11y.known-failures.ts +0 -12
- package/src/Slider/__tests__/Slider.a11y.renders.tsx +0 -42
- package/src/Slider/__tests__/Slider.a11y.states.ts +0 -106
- package/src/Slider/__tests__/Slider.a11y.test.tsx +0 -105
- package/src/Spinner/Spinner.test.tsx +0 -484
- package/src/Stack/Stack.test.tsx +0 -473
- package/src/Stack/StackItem.test.tsx +0 -105
- package/src/StatusDot/StatusDot.test.tsx +0 -213
- package/src/Stepper/Stepper.public.test.ts +0 -91
- package/src/Stepper/Stepper.test.tsx +0 -2068
- package/src/Switch/Switch.focusRing.source-build.test.mjs +0 -81
- package/src/Switch/Switch.test.tsx +0 -1071
- package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +0 -211
- package/src/Switch/__tests__/Switch.a11y.known-failures.ts +0 -28
- package/src/Switch/__tests__/Switch.a11y.states.ts +0 -275
- package/src/Switch/__tests__/Switch.a11y.test.tsx +0 -144
- package/src/TabList/TabList.test.tsx +0 -1701
- package/src/TabList/__tests__/TabListOverflow.interaction.browser.spec.ts +0 -99
- package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +0 -199
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +0 -12
- package/src/TabList/__tests__/Tabs.a11y.renders.tsx +0 -92
- package/src/TabList/__tests__/Tabs.a11y.states.ts +0 -247
- package/src/TabList/__tests__/Tabs.a11y.test.tsx +0 -153
- package/src/Table/Table.perf.test.tsx +0 -569
- package/src/Table/Table.test.tsx +0 -1851
- package/src/Table/TableHeaderCell.test.tsx +0 -291
- package/src/Table/TableSelectionToolbar.test.tsx +0 -109
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +0 -129
- package/src/Table/__tests__/Table.a11y.contracts.ts +0 -35
- package/src/Table/__tests__/Table.a11y.renders.tsx +0 -112
- package/src/Table/__tests__/Table.a11y.states.ts +0 -140
- package/src/Table/__tests__/Table.a11y.test.tsx +0 -198
- package/src/Table/__tests__/TableMinWidth.a11y.chromium.spec.ts +0 -75
- package/src/Table/__tests__/TableRowGlide.a11y.browser.spec.ts +0 -382
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +0 -138
- package/src/Table/__tests__/TableStickyDividers.a11y.browser.spec.ts +0 -560
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -919
- package/src/Table/plugins/columnSettings/useTableColumnSettings.test.tsx +0 -175
- package/src/Table/plugins/columnSettings/useTableColumnSettingsState.test.tsx +0 -296
- package/src/Table/plugins/filtering/useTableFilterState.test.tsx +0 -143
- package/src/Table/plugins/filtering/useTableFiltering.test.tsx +0 -357
- package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +0 -112
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +0 -409
- package/src/Table/plugins/pagination/useTablePagination-perf.test.tsx +0 -232
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -666
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.glide.test.tsx +0 -280
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +0 -681
- package/src/Table/plugins/rowIndex/useTableRowIndex.test.tsx +0 -132
- package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +0 -92
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +0 -519
- package/src/Table/plugins/selection/useTableSelection-perf.test.tsx +0 -258
- package/src/Table/plugins/selection/useTableSelection.test.tsx +0 -424
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -451
- package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.test.tsx +0 -174
- package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.ts +0 -86
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +0 -857
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +0 -592
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +0 -334
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +0 -163
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +0 -992
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +0 -740
- package/src/Table/tableContextMenu.test.tsx +0 -208
- package/src/Text/Text.test.tsx +0 -405
- package/src/Text/useTruncation.test.tsx +0 -69
- package/src/TextArea/TextArea.fr10.test.tsx +0 -21
- package/src/TextArea/TextArea.test.tsx +0 -1257
- package/src/TextInput/TextInput.fr10.test.tsx +0 -21
- package/src/TextInput/TextInput.test.tsx +0 -1259
- package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +0 -214
- package/src/TextInput/__tests__/TextInput.a11y.known-failures.ts +0 -40
- package/src/TextInput/__tests__/TextInput.a11y.renders.tsx +0 -202
- package/src/TextInput/__tests__/TextInput.a11y.states.ts +0 -384
- package/src/TextInput/__tests__/TextInput.a11y.test.tsx +0 -130
- package/src/Thumbnail/Thumbnail.test.tsx +0 -384
- package/src/TimeInput/NativeTimeInput.test.tsx +0 -326
- package/src/TimeInput/Presentation.test.tsx +0 -171
- package/src/TimeInput/TimeInput.fr10.test.tsx +0 -312
- package/src/TimeInput/TimeInput.test.tsx +0 -831
- package/src/Timer/Timer.test.tsx +0 -302
- package/src/Timestamp/Timestamp.test.tsx +0 -1636
- package/src/Timestamp/tooltipEntries.test.ts +0 -566
- package/src/Toast/ToastViewport.modalHost.test.tsx +0 -891
- package/src/Toast/ToastViewport.test.tsx +0 -2333
- package/src/Toast/useToast.test.tsx +0 -258
- package/src/ToggleButton/ToggleButton.test.tsx +0 -973
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +0 -177
- package/src/ToggleButton/__tests__/ToggleButton.a11y.renders.tsx +0 -133
- package/src/ToggleButton/__tests__/ToggleButton.a11y.states.ts +0 -167
- package/src/ToggleButton/__tests__/ToggleButton.a11y.test.tsx +0 -103
- package/src/Token/Token.test.tsx +0 -640
- package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +0 -87
- package/src/Tokenizer/Tokenizer.fr10.test.tsx +0 -28
- package/src/Tokenizer/Tokenizer.props-type.test.tsx +0 -126
- package/src/Tokenizer/Tokenizer.ssr.test.tsx +0 -196
- package/src/Tokenizer/Tokenizer.test.tsx +0 -2121
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +0 -2719
- package/src/Tokenizer/__tests__/TokenizerEndLane.a11y.chromium.spec.ts +0 -505
- package/src/Toolbar/Toolbar.test.tsx +0 -570
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +0 -164
- package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +0 -63
- package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +0 -142
- package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +0 -126
- package/src/Tooltip/Tooltip.test.tsx +0 -819
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +0 -130
- package/src/Tooltip/__tests__/Tooltip.a11y.renders.tsx +0 -86
- package/src/Tooltip/__tests__/Tooltip.a11y.states.ts +0 -132
- package/src/Tooltip/__tests__/Tooltip.a11y.test.tsx +0 -126
- package/src/Tooltip/__tests__/TooltipFocusReturn.a11y.chromium.spec.ts +0 -130
- package/src/Tooltip/__tests__/TooltipHoverBridge.a11y.chromium.spec.ts +0 -75
- package/src/Tooltip/__tests__/TooltipTallContent.a11y.browser.spec.ts +0 -673
- package/src/Tooltip/__tests__/TooltipTallContentTracking.a11y.browser.spec.ts +0 -1162
- package/src/Tooltip/useTooltip.test.tsx +0 -483
- package/src/TopNav/TopNav.test.tsx +0 -836
- package/src/TopNav/TopNavMegaMenu.reduced-motion.source-build.test.mjs +0 -83
- package/src/TopNav/TopNavMegaMenu.test.tsx +0 -1132
- package/src/TopNav/TopNavMenu.test.tsx +0 -485
- package/src/TopNav/__tests__/TopNavMobileLongHeading.a11y.chromium.spec.ts +0 -75
- package/src/TreeList/TreeList.test.tsx +0 -1711
- package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +0 -138
- package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +0 -44
- package/src/TreeList/__tests__/TreeList.a11y.states.ts +0 -164
- package/src/TreeList/__tests__/TreeList.a11y.test.tsx +0 -138
- package/src/TreeList/__tests__/TreeListGuideAlignment.interaction.browser.spec.ts +0 -127
- package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +0 -83
- package/src/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.ts +0 -138
- package/src/Typeahead/BaseTypeahead.test.tsx +0 -553
- package/src/Typeahead/Typeahead.fr10.test.tsx +0 -28
- package/src/Typeahead/Typeahead.test.tsx +0 -2824
- package/src/Typeahead/TypeaheadItem.test.tsx +0 -134
- package/src/VStack/VStack.test.tsx +0 -128
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +0 -115
- package/src/__tests__/BusyOwnerAnnouncement.test.tsx +0 -853
- package/src/__tests__/CardPress.a11y.chromium.spec.ts +0 -353
- package/src/__tests__/DisabledTriggerFocus.a11y.chromium.spec.ts +0 -87
- package/src/__tests__/PressModality.a11y.chromium.spec.ts +0 -392
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +0 -814
- package/src/__tests__/TestIcon.tsx +0 -26
- package/src/__tests__/babelPluginAddExtensions.test.ts +0 -190
- package/src/__tests__/fastRoleQueries.ts +0 -66
- package/src/__tests__/forcedColors.ts +0 -60
- package/src/__tests__/pressState.ts +0 -212
- package/src/__tests__/stubMatchMedia.test.ts +0 -79
- package/src/__tests__/stubMatchMedia.ts +0 -95
- package/src/__tests__/stylexDeclarations.ts +0 -51
- package/src/__tests__/unthemedLength.ts +0 -34
- package/src/docPropLiterals.test.ts +0 -964
- package/src/docPropReferences.test.ts +0 -178
- package/src/docs-script.test.mjs +0 -55
- package/src/gestures/__tests__/LongPressGestureRecognizer.test.ts +0 -141
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +0 -327
- package/src/gestures/__tests__/TapGestureRecognizer.test.ts +0 -154
- package/src/gestures/__tests__/VelocityTracker.streams.test.ts +0 -243
- package/src/gestures/__tests__/VelocityTracker.test.ts +0 -261
- package/src/gestures/__tests__/arena.test.ts +0 -824
- package/src/gestures/__tests__/arenaContinuous.test.ts +0 -207
- package/src/gestures/__tests__/arenaTapLongPress.test.ts +0 -211
- package/src/gestures/__tests__/fixtures/pointer-streams/README.md +0 -82
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-fling.json +0 -130
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-slow.json +0 -860
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-stop-release.json +0 -357
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-fling.json +0 -66
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-slow.json +0 -268
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-stop-release.json +0 -128
- package/src/gestures/__tests__/fixtures/pointer-streams/capture.html +0 -123
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-fling.json +0 -64
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-slow.json +0 -268
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-stop-release.json +0 -124
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-fling.json +0 -64
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-slow.json +0 -268
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-stop-release.json +0 -124
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-fling.json +0 -84
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-slow.json +0 -381
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-stop-release.json +0 -171
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-fling.json +0 -45
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-slow.json +0 -101
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-stop-release.json +0 -130
- package/src/gestures/__tests__/harness.ts +0 -204
- package/src/hooks/__tests__/MenuPressShadowRoot.a11y.chromium.spec.ts +0 -144
- package/src/hooks/__tests__/menuLinkClicks.ts +0 -71
- package/src/hooks/actionGroupTracker.exports.test.ts +0 -28
- package/src/hooks/actionGroupTracker.test.ts +0 -510
- package/src/hooks/computeOverflow.test.ts +0 -422
- package/src/hooks/deprecatedApis.public.test.ts +0 -33
- package/src/hooks/menuPressGesture.test.ts +0 -253
- package/src/hooks/scrollKeyboardDelegation.test.ts +0 -155
- package/src/hooks/scrollbarGutter.test.ts +0 -167
- package/src/hooks/useAnnounce.modal.test.tsx +0 -1291
- package/src/hooks/useAnnounce.test.tsx +0 -293
- package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +0 -226
- package/src/hooks/useAutoMediaMode.test.ts +0 -109
- package/src/hooks/useBusyAnnouncement.adopters.test.tsx +0 -69
- package/src/hooks/useBusyAnnouncement.test.tsx +0 -112
- package/src/hooks/useClickableContainer.test.tsx +0 -236
- package/src/hooks/useClipboard.test.tsx +0 -151
- package/src/hooks/useContainerReveal.test.tsx +0 -163
- package/src/hooks/useDevWarning.test.tsx +0 -45
- package/src/hooks/useEntryAnimation.ssr.test.tsx +0 -143
- package/src/hooks/useFocusTrap.test.tsx +0 -635
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +0 -166
- package/src/hooks/useGridFocus.test.tsx +0 -165
- package/src/hooks/useHotkeys.test.ts +0 -265
- package/src/hooks/useImageMode.test.ts +0 -133
- package/src/hooks/useInputContainer.test.tsx +0 -180
- package/src/hooks/useInputStatusIcon.test.tsx +0 -240
- package/src/hooks/useIsHydrating.test.tsx +0 -70
- package/src/hooks/useKeyboardHint.test.tsx +0 -92
- package/src/hooks/useListFocus.test.tsx +0 -664
- package/src/hooks/useLongPress.test.tsx +0 -152
- package/src/hooks/useMediaQuery.test.ts +0 -165
- package/src/hooks/useMenuHover.test.tsx +0 -536
- package/src/hooks/useMenuPress.test.tsx +0 -565
- package/src/hooks/useMergedRefs.test.tsx +0 -62
- package/src/hooks/useOverflow.test.ts +0 -504
- package/src/hooks/useRenamedProp.test.tsx +0 -95
- package/src/hooks/useScrollLock.test.ts +0 -248
- package/src/hooks/useScrollOverflow.test.tsx +0 -222
- package/src/hooks/useScrollableArea.test.tsx +0 -836
- package/src/hooks/useStreamingText.test.ts +0 -237
- package/src/hooks/useTreeFocus.test.tsx +0 -351
- package/src/hooks/useTypeahead.test.tsx +0 -351
- package/src/i18n/__tests__/InternationalizationProvider.test.tsx +0 -128
- package/src/i18n/__tests__/e2e-pagination.test.tsx +0 -172
- package/src/i18n/__tests__/e2e-powersearch.test.tsx +0 -166
- package/src/i18n/__tests__/getLocaleDirection.test.ts +0 -40
- package/src/i18n/__tests__/resolve.test.ts +0 -325
- package/src/i18n/__tests__/useCollator.test.tsx +0 -115
- package/src/i18n/__tests__/useDirection.test.tsx +0 -66
- package/src/i18n/__tests__/useLocale.test.tsx +0 -52
- package/src/i18n/useTranslator.test.tsx +0 -267
- package/src/inputFontFloor.test.ts +0 -106
- package/src/internal/tokenizer-presentation.test.ts +0 -29
- package/src/internal/touch.test.ts +0 -44
- package/src/motion/__tests__/animatedLayout.interaction.browser.spec.ts +0 -1613
- package/src/motion/__tests__/animatedLayoutAdopters.interaction.browser.spec.ts +0 -217
- package/src/motion/__tests__/animatedLayoutBundle.ts +0 -17
- package/src/motion/animatedLayout.boundary.test.ts +0 -155
- package/src/motion/animatedLayout.swap.test.ts +0 -827
- package/src/motion/animatedLayout.test.ts +0 -1154
- package/src/motion/animatedLayoutAdopters.test.tsx +0 -500
- package/src/motion/animator.test.ts +0 -2653
- package/src/motion/bezier.test.ts +0 -52
- package/src/motion/coast.test.ts +0 -99
- package/src/motion/decay.test.ts +0 -120
- package/src/motion/projection.test.ts +0 -107
- package/src/motion/reducedMotion.test.ts +0 -129
- package/src/motion/reducedMotionPlan.test.ts +0 -182
- package/src/motion/resolveSnapPoint.test.ts +0 -189
- package/src/motion/roles.test.ts +0 -256
- package/src/motion/rubberBand.test.ts +0 -88
- package/src/motion/selectPage.test.ts +0 -31
- package/src/motion/spring.test.ts +0 -650
- package/src/motion/uikit/gestures.test.ts +0 -89
- package/src/motion/uikit/springRows.test.ts +0 -71
- package/src/motion/uikit/uikit.test.ts +0 -364
- package/src/motion/uikit/values.test.ts +0 -110
- package/src/naming.test.ts +0 -44
- package/src/postinstall.test.mjs +0 -216
- package/src/reset.a11y.chromium.spec.ts +0 -238
- package/src/reset.test.ts +0 -205
- package/src/reset.until-found.a11y.browser.spec.ts +0 -76
- package/src/reset.until-found.test.ts +0 -32
- package/src/serverSafeComponents.test.ts +0 -675
- package/src/stylexValidation.source-build.test.mjs +0 -42
- package/src/tailwind-theme.test.ts +0 -153
- package/src/theme/MediaTheme.dom.test.tsx +0 -97
- package/src/theme/Theme.test.tsx +0 -337
- package/src/theme/__tests__/SpacingRootFontSize.a11y.chromium.spec.ts +0 -199
- package/src/theme/contrast.test.ts +0 -94
- package/src/theme/declarationBoundary.test.ts +0 -126
- package/src/theme/defineTheme.test.ts +0 -2028
- package/src/theme/derivedVarRegistry.test.ts +0 -588
- package/src/theme/expandColorScale.test.ts +0 -651
- package/src/theme/expandMotionScale.test.ts +0 -145
- package/src/theme/expandRadiusScale.test.ts +0 -59
- package/src/theme/expandTypeScale.test.ts +0 -351
- package/src/theme/extensibleAxes.test.ts +0 -432
- package/src/theme/generateThemeRules.test.ts +0 -1870
- package/src/theme/hct.test.ts +0 -93
- package/src/theme/onMediaTokens.test.ts +0 -431
- package/src/theme/publicThemeHelperContract.test.ts +0 -67
- package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +0 -91
- package/src/theme/syntax/serverSafeSyntax.test.ts +0 -68
- package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +0 -40
- package/src/theme/themeAdaptations.test.ts +0 -1795
- package/src/theme/themeRegistry.test.ts +0 -59
- package/src/theme/themingTargets.test.ts +0 -589
- package/src/theme/tokenValueCompat.test.ts +0 -204
- package/src/theme/tokens.test.ts +0 -441
- package/src/theme/useTheme.test.tsx +0 -322
- package/src/unicodePropertyEscapes.source-build.test.mjs +0 -69
- package/src/utils/chainEventHandlers.test.ts +0 -39
- package/src/utils/characters.test.ts +0 -141
- package/src/utils/color.test.ts +0 -233
- package/src/utils/composeEventHandlers.test.ts +0 -71
- package/src/utils/customPropertyArmLayering.test.ts +0 -131
- package/src/utils/dateParser.test.ts +0 -402
- package/src/utils/dateTypes.test.ts +0 -40
- package/src/utils/devWarning.test.ts +0 -56
- package/src/utils/edgeAutoscroll.test.ts +0 -192
- package/src/utils/focusReturn.test.ts +0 -119
- package/src/utils/getKey.test.ts +0 -25
- package/src/utils/hitArea.test.ts +0 -628
- package/src/utils/ime.test.ts +0 -32
- package/src/utils/inputAria.test.ts +0 -39
- package/src/utils/interactionModality.test.ts +0 -100
- package/src/utils/isApplePlatform.test.ts +0 -61
- package/src/utils/mergeProps.test.ts +0 -166
- package/src/utils/mergeRefs.test.ts +0 -48
- package/src/utils/parseStyleKey.test.ts +0 -56
- package/src/utils/plainDate.test.ts +0 -848
- package/src/utils/pressFeedback.test.ts +0 -874
- package/src/utils/pressGesture.test.ts +0 -323
- package/src/utils/pressPaintSource.test.ts +0 -65
- package/src/utils/pressableCoverage.test.ts +0 -192
- package/src/utils/safeUrl.test.ts +0 -101
- package/src/utils/sharedMutationObserver.test.ts +0 -53
- package/src/utils/sharedResizeObserver.test.ts +0 -283
- package/src/utils/themeProps.test.ts +0 -62
- package/src/utils/timeParser.test.ts +0 -295
- package/src/utils/touchPresentation.test.ts +0 -323
|
@@ -1,2824 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file Typeahead.test.tsx
|
|
5
|
-
* @input Uses vitest, @testing-library/react, Typeahead, BaseTypeahead
|
|
6
|
-
* @output Unit tests for Typeahead components
|
|
7
|
-
* @position Testing; validates Typeahead.tsx and BaseTypeahead.tsx
|
|
8
|
-
*
|
|
9
|
-
* SYNC: When Typeahead components change, update tests to match
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
describe,
|
|
14
|
-
it,
|
|
15
|
-
expect,
|
|
16
|
-
vi,
|
|
17
|
-
beforeAll,
|
|
18
|
-
afterAll,
|
|
19
|
-
beforeEach,
|
|
20
|
-
afterEach,
|
|
21
|
-
} from 'vitest';
|
|
22
|
-
import {render, screen, fireEvent, waitFor, act} from '@testing-library/react';
|
|
23
|
-
import userEvent from '@testing-library/user-event';
|
|
24
|
-
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
25
|
-
import {
|
|
26
|
-
createRef,
|
|
27
|
-
Profiler,
|
|
28
|
-
startTransition,
|
|
29
|
-
StrictMode,
|
|
30
|
-
Suspense,
|
|
31
|
-
useState,
|
|
32
|
-
type ProfilerOnRenderCallback,
|
|
33
|
-
} from 'react';
|
|
34
|
-
import {Typeahead} from './Typeahead';
|
|
35
|
-
import {readFile} from 'node:fs/promises';
|
|
36
|
-
import {resolve} from 'node:path';
|
|
37
|
-
import {BaseTypeahead} from './BaseTypeahead';
|
|
38
|
-
import {
|
|
39
|
-
BusyIndicatorLaneProvider,
|
|
40
|
-
createBusyIndicatorLane,
|
|
41
|
-
} from './busyIndicatorLane';
|
|
42
|
-
import type {SearchSource, SearchableItem} from './types';
|
|
43
|
-
import {InternationalizationProvider} from '../i18n';
|
|
44
|
-
import {InputGroup} from '../InputGroup';
|
|
45
|
-
|
|
46
|
-
// Store original matches to restore later
|
|
47
|
-
const originalMatches = HTMLElement.prototype.matches;
|
|
48
|
-
|
|
49
|
-
// Track popover open state per element
|
|
50
|
-
const popoverOpenState = new WeakMap<HTMLElement, boolean>();
|
|
51
|
-
|
|
52
|
-
// Mock Popover API for jsdom
|
|
53
|
-
beforeAll(() => {
|
|
54
|
-
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
55
|
-
popoverOpenState.set(this, true);
|
|
56
|
-
const event = new Event('toggle');
|
|
57
|
-
Object.defineProperty(event, 'newState', {value: 'open'});
|
|
58
|
-
this.dispatchEvent(event);
|
|
59
|
-
});
|
|
60
|
-
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
61
|
-
popoverOpenState.set(this, false);
|
|
62
|
-
const event = new Event('toggle');
|
|
63
|
-
Object.defineProperty(event, 'newState', {value: 'closed'});
|
|
64
|
-
this.dispatchEvent(event);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
-
(HTMLElement.prototype as any).matches = function (
|
|
69
|
-
selector: string,
|
|
70
|
-
): boolean {
|
|
71
|
-
if (selector === ':popover-open') {
|
|
72
|
-
return popoverOpenState.get(this) ?? false;
|
|
73
|
-
}
|
|
74
|
-
return originalMatches.call(this, selector);
|
|
75
|
-
};
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
afterAll(() => {
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
-
(HTMLElement.prototype as any).matches = originalMatches;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
afterEach(() => {
|
|
84
|
-
__resetLiveRegionsForTest();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Test data
|
|
88
|
-
const fruits: SearchableItem[] = [
|
|
89
|
-
{id: '1', label: 'Apple'},
|
|
90
|
-
{id: '2', label: 'Banana'},
|
|
91
|
-
{id: '3', label: 'Cherry'},
|
|
92
|
-
{id: '4', label: 'Date'},
|
|
93
|
-
{id: '5', label: 'Elderberry'},
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
const fruitSource: SearchSource = {
|
|
97
|
-
search: (query: string) =>
|
|
98
|
-
fruits.filter(f => f.label.toLowerCase().includes(query.toLowerCase())),
|
|
99
|
-
bootstrap: () => fruits.slice(0, 3),
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* The search Spinner: named but not live (family:input-fields FR7,
|
|
104
|
-
* spec:AST-086 FR8), so it is found as an image named "Loading".
|
|
105
|
-
*/
|
|
106
|
-
function queryBusySpinner(): Element | null {
|
|
107
|
-
return screen.queryByRole('img', {name: 'Loading'});
|
|
108
|
-
}
|
|
109
|
-
function getBusySpinner(): Element {
|
|
110
|
-
return screen.getByRole('img', {name: 'Loading'});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
describe('BaseTypeahead', () => {
|
|
114
|
-
it('renders input with combobox role', () => {
|
|
115
|
-
render(
|
|
116
|
-
<BaseTypeahead
|
|
117
|
-
searchSource={fruitSource}
|
|
118
|
-
value={null}
|
|
119
|
-
onChange={() => {}}
|
|
120
|
-
/>,
|
|
121
|
-
);
|
|
122
|
-
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('renders placeholder text', () => {
|
|
126
|
-
render(
|
|
127
|
-
<BaseTypeahead
|
|
128
|
-
searchSource={fruitSource}
|
|
129
|
-
value={null}
|
|
130
|
-
onChange={() => {}}
|
|
131
|
-
placeholder="Pick a fruit..."
|
|
132
|
-
/>,
|
|
133
|
-
);
|
|
134
|
-
expect(screen.getByPlaceholderText('Pick a fruit...')).toBeInTheDocument();
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('sets aria-expanded=false initially', () => {
|
|
138
|
-
render(
|
|
139
|
-
<BaseTypeahead
|
|
140
|
-
searchSource={fruitSource}
|
|
141
|
-
value={null}
|
|
142
|
-
onChange={() => {}}
|
|
143
|
-
/>,
|
|
144
|
-
);
|
|
145
|
-
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
146
|
-
'aria-expanded',
|
|
147
|
-
'false',
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('shows results on input change', async () => {
|
|
152
|
-
render(
|
|
153
|
-
<BaseTypeahead
|
|
154
|
-
searchSource={fruitSource}
|
|
155
|
-
value={null}
|
|
156
|
-
onChange={() => {}}
|
|
157
|
-
/>,
|
|
158
|
-
);
|
|
159
|
-
const input = screen.getByRole('combobox');
|
|
160
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
161
|
-
|
|
162
|
-
await waitFor(() => {
|
|
163
|
-
expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('announces the result count to a live region (comboboxes-6)', async () => {
|
|
168
|
-
render(
|
|
169
|
-
<InternationalizationProvider
|
|
170
|
-
locale="fr"
|
|
171
|
-
overrides={{
|
|
172
|
-
fr: {
|
|
173
|
-
'@astryx.typeahead.resultCount':
|
|
174
|
-
'{count, number} {count, plural, one {résultat} other {résultats}}',
|
|
175
|
-
},
|
|
176
|
-
}}>
|
|
177
|
-
<BaseTypeahead
|
|
178
|
-
searchSource={fruitSource}
|
|
179
|
-
value={null}
|
|
180
|
-
onChange={() => {}}
|
|
181
|
-
debounceMs={0}
|
|
182
|
-
/>
|
|
183
|
-
</InternationalizationProvider>,
|
|
184
|
-
);
|
|
185
|
-
const input = screen.getByRole('combobox');
|
|
186
|
-
// "Ap" matches Apple only — the singular ICU branch.
|
|
187
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
188
|
-
|
|
189
|
-
await waitFor(() => {
|
|
190
|
-
const region = document.querySelector(
|
|
191
|
-
'[data-astryx-live-region="polite"]',
|
|
192
|
-
);
|
|
193
|
-
// Exact, so the plural branch ("1 résultats") would fail.
|
|
194
|
-
expect(region?.textContent).toBe('1 résultat');
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('announces the plural result count', async () => {
|
|
199
|
-
render(
|
|
200
|
-
<InternationalizationProvider
|
|
201
|
-
locale="fr"
|
|
202
|
-
overrides={{
|
|
203
|
-
fr: {
|
|
204
|
-
'@astryx.typeahead.resultCount':
|
|
205
|
-
'{count, number} {count, plural, one {résultat} other {résultats}}',
|
|
206
|
-
},
|
|
207
|
-
}}>
|
|
208
|
-
<BaseTypeahead
|
|
209
|
-
searchSource={fruitSource}
|
|
210
|
-
value={null}
|
|
211
|
-
onChange={() => {}}
|
|
212
|
-
debounceMs={0}
|
|
213
|
-
/>
|
|
214
|
-
</InternationalizationProvider>,
|
|
215
|
-
);
|
|
216
|
-
const input = screen.getByRole('combobox');
|
|
217
|
-
// "err" matches Cherry and Elderberry.
|
|
218
|
-
fireEvent.change(input, {target: {value: 'err'}});
|
|
219
|
-
|
|
220
|
-
await waitFor(() => {
|
|
221
|
-
const region = document.querySelector(
|
|
222
|
-
'[data-astryx-live-region="polite"]',
|
|
223
|
-
);
|
|
224
|
-
expect(region?.textContent).toBe('2 résultats');
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('speaks the result count from a provider catalog', async () => {
|
|
229
|
-
render(
|
|
230
|
-
<InternationalizationProvider
|
|
231
|
-
locale="fr"
|
|
232
|
-
messages={{
|
|
233
|
-
fr: {
|
|
234
|
-
'@astryx.typeahead.resultCount': {
|
|
235
|
-
defaultMessage:
|
|
236
|
-
'{count, number} {count, plural, one {résultat} other {résultats}}',
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
}}>
|
|
240
|
-
<BaseTypeahead
|
|
241
|
-
searchSource={fruitSource}
|
|
242
|
-
value={null}
|
|
243
|
-
onChange={() => {}}
|
|
244
|
-
debounceMs={0}
|
|
245
|
-
/>
|
|
246
|
-
</InternationalizationProvider>,
|
|
247
|
-
);
|
|
248
|
-
const input = screen.getByRole('combobox');
|
|
249
|
-
fireEvent.change(input, {target: {value: 'err'}});
|
|
250
|
-
|
|
251
|
-
await waitFor(() => {
|
|
252
|
-
const region = document.querySelector(
|
|
253
|
-
'[data-astryx-live-region="polite"]',
|
|
254
|
-
);
|
|
255
|
-
// Same key through the catalog path rather than `overrides`.
|
|
256
|
-
expect(region?.textContent).toBe('2 résultats');
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('announces "no results found" when the search is empty (comboboxes-6)', async () => {
|
|
261
|
-
render(
|
|
262
|
-
<BaseTypeahead
|
|
263
|
-
searchSource={fruitSource}
|
|
264
|
-
value={null}
|
|
265
|
-
onChange={() => {}}
|
|
266
|
-
debounceMs={0}
|
|
267
|
-
emptySearchText="No results found"
|
|
268
|
-
/>,
|
|
269
|
-
);
|
|
270
|
-
const input = screen.getByRole('combobox');
|
|
271
|
-
fireEvent.change(input, {target: {value: 'zzzzz'}});
|
|
272
|
-
|
|
273
|
-
await waitFor(() => {
|
|
274
|
-
const region = document.querySelector(
|
|
275
|
-
'[data-astryx-live-region="polite"]',
|
|
276
|
-
);
|
|
277
|
-
expect(region).toHaveTextContent('No results found');
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('exposes the empty state as a themeable target', async () => {
|
|
282
|
-
const {container} = render(
|
|
283
|
-
<BaseTypeahead
|
|
284
|
-
searchSource={fruitSource}
|
|
285
|
-
value={null}
|
|
286
|
-
onChange={() => {}}
|
|
287
|
-
debounceMs={0}
|
|
288
|
-
emptySearchText="No results found"
|
|
289
|
-
/>,
|
|
290
|
-
);
|
|
291
|
-
const input = screen.getByRole('combobox');
|
|
292
|
-
fireEvent.change(input, {target: {value: 'zzzzz'}});
|
|
293
|
-
|
|
294
|
-
await waitFor(() => {
|
|
295
|
-
const emptyState = container.querySelector(
|
|
296
|
-
'.astryx-typeahead-empty-state',
|
|
297
|
-
);
|
|
298
|
-
expect(emptyState).not.toBeNull();
|
|
299
|
-
expect(emptyState).toHaveTextContent('No results found');
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
describe('empty results active descendant (#4059)', () => {
|
|
304
|
-
it('does not set aria-activedescendant when search has 0 results', async () => {
|
|
305
|
-
render(
|
|
306
|
-
<BaseTypeahead
|
|
307
|
-
searchSource={fruitSource}
|
|
308
|
-
value={null}
|
|
309
|
-
onChange={() => {}}
|
|
310
|
-
debounceMs={0}
|
|
311
|
-
/>,
|
|
312
|
-
);
|
|
313
|
-
const input = screen.getByRole('combobox');
|
|
314
|
-
fireEvent.change(input, {target: {value: 'zzzzz'}});
|
|
315
|
-
|
|
316
|
-
await waitFor(() => {
|
|
317
|
-
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// Press ArrowDown — should NOT set aria-activedescendant to option-0
|
|
321
|
-
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
322
|
-
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
323
|
-
|
|
324
|
-
// Press Home — should NOT set aria-activedescendant
|
|
325
|
-
fireEvent.keyDown(input, {key: 'Home'});
|
|
326
|
-
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
describe('IME composition guard (#4828)', () => {
|
|
331
|
-
it('does not select the highlighted result on a composing Enter', async () => {
|
|
332
|
-
const onChange = vi.fn();
|
|
333
|
-
render(
|
|
334
|
-
<BaseTypeahead
|
|
335
|
-
searchSource={fruitSource}
|
|
336
|
-
value={null}
|
|
337
|
-
onChange={onChange}
|
|
338
|
-
debounceMs={0}
|
|
339
|
-
/>,
|
|
340
|
-
);
|
|
341
|
-
const input = screen.getByRole('combobox');
|
|
342
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
343
|
-
await waitFor(() => {
|
|
344
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// The browser fires this composing keydown for the Enter that commits
|
|
348
|
-
// an IME candidate (isComposing: true, or legacy keyCode 229) before
|
|
349
|
-
// compositionend writes the pending syllable into the input. Without
|
|
350
|
-
// the guard this both selects the highlighted result AND clears the
|
|
351
|
-
// input via handleSelect, so the syllable that compositionend then
|
|
352
|
-
// writes lands in an emptied field instead of being part of the word.
|
|
353
|
-
fireEvent.keyDown(input, {key: 'Enter', isComposing: true});
|
|
354
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
355
|
-
expect(input).toHaveValue('App');
|
|
356
|
-
|
|
357
|
-
fireEvent.keyDown(input, {key: 'Enter', keyCode: 229});
|
|
358
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
359
|
-
expect(input).toHaveValue('App');
|
|
360
|
-
|
|
361
|
-
// A real, non-composing Enter still selects normally.
|
|
362
|
-
fireEvent.keyDown(input, {key: 'Enter'});
|
|
363
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
364
|
-
expect.objectContaining({label: 'Apple'}),
|
|
365
|
-
);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('disables input when isDisabled', () => {
|
|
370
|
-
render(
|
|
371
|
-
<BaseTypeahead
|
|
372
|
-
searchSource={fruitSource}
|
|
373
|
-
value={null}
|
|
374
|
-
onChange={() => {}}
|
|
375
|
-
isDisabled
|
|
376
|
-
/>,
|
|
377
|
-
);
|
|
378
|
-
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
it('uses anchorRef for dropdown positioning', () => {
|
|
382
|
-
const anchorRef = {current: document.createElement('div')};
|
|
383
|
-
render(
|
|
384
|
-
<BaseTypeahead
|
|
385
|
-
searchSource={fruitSource}
|
|
386
|
-
value={null}
|
|
387
|
-
onChange={() => {}}
|
|
388
|
-
anchorRef={anchorRef}
|
|
389
|
-
/>,
|
|
390
|
-
);
|
|
391
|
-
// Component renders without error — anchor is wired up internally
|
|
392
|
-
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it('does not select every result when items lack ids', async () => {
|
|
396
|
-
const idlessItems = [
|
|
397
|
-
{label: 'Alpha'},
|
|
398
|
-
{label: 'Beta'},
|
|
399
|
-
] as unknown as SearchableItem[];
|
|
400
|
-
const idlessSource: SearchSource = {
|
|
401
|
-
search: () => idlessItems,
|
|
402
|
-
bootstrap: () => idlessItems,
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
render(
|
|
406
|
-
<BaseTypeahead
|
|
407
|
-
searchSource={idlessSource}
|
|
408
|
-
value={idlessItems[0]}
|
|
409
|
-
onChange={() => {}}
|
|
410
|
-
hasEntriesOnFocus
|
|
411
|
-
debounceMs={0}
|
|
412
|
-
/>,
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
fireEvent.focus(screen.getByRole('combobox'));
|
|
416
|
-
|
|
417
|
-
await waitFor(() => {
|
|
418
|
-
expect(screen.getAllByRole('option', {hidden: true})).toHaveLength(2);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
const options = screen.getAllByRole('option', {hidden: true});
|
|
422
|
-
expect(options[0]).toHaveAttribute('aria-selected', 'true');
|
|
423
|
-
expect(options[1]).toHaveAttribute('aria-selected', 'false');
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
describe('BaseTypeahead focus-out', () => {
|
|
428
|
-
it('closes the dropdown when focus leaves the input', async () => {
|
|
429
|
-
render(
|
|
430
|
-
<>
|
|
431
|
-
<BaseTypeahead
|
|
432
|
-
searchSource={fruitSource}
|
|
433
|
-
value={null}
|
|
434
|
-
onChange={() => {}}
|
|
435
|
-
debounceMs={0}
|
|
436
|
-
/>
|
|
437
|
-
<button type="button">Outside</button>
|
|
438
|
-
</>,
|
|
439
|
-
);
|
|
440
|
-
const input = screen.getByRole('combobox');
|
|
441
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
442
|
-
|
|
443
|
-
await waitFor(() => {
|
|
444
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// Focus moves to an element outside the field/dropdown → menu closes.
|
|
448
|
-
const outside = screen.getByRole('button', {name: 'Outside'});
|
|
449
|
-
fireEvent.blur(input, {relatedTarget: outside});
|
|
450
|
-
|
|
451
|
-
await waitFor(() => {
|
|
452
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it('closes the list on the Tab keydown, before the blur it produces', async () => {
|
|
457
|
-
render(
|
|
458
|
-
<BaseTypeahead
|
|
459
|
-
searchSource={fruitSource}
|
|
460
|
-
value={null}
|
|
461
|
-
onChange={() => {}}
|
|
462
|
-
debounceMs={0}
|
|
463
|
-
/>,
|
|
464
|
-
);
|
|
465
|
-
const input = screen.getByRole('combobox');
|
|
466
|
-
input.focus();
|
|
467
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
468
|
-
await waitFor(() => {
|
|
469
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// No blur is fired here on purpose: dismissing from the blur instead lets
|
|
473
|
-
// the popover close mid-focus-move, which Chrome answers by dropping
|
|
474
|
-
// focus to <body>.
|
|
475
|
-
fireEvent.keyDown(input, {key: 'Tab'});
|
|
476
|
-
await waitFor(() => {
|
|
477
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it('Tab from the input with the list open moves focus to the next control', async () => {
|
|
482
|
-
const user = userEvent.setup();
|
|
483
|
-
render(
|
|
484
|
-
<>
|
|
485
|
-
<BaseTypeahead
|
|
486
|
-
searchSource={fruitSource}
|
|
487
|
-
value={null}
|
|
488
|
-
onChange={() => {}}
|
|
489
|
-
debounceMs={0}
|
|
490
|
-
/>
|
|
491
|
-
<button type="button">Next</button>
|
|
492
|
-
</>,
|
|
493
|
-
);
|
|
494
|
-
const input = screen.getByRole('combobox');
|
|
495
|
-
input.focus();
|
|
496
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
497
|
-
await waitFor(() => {
|
|
498
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
await user.keyboard('{Tab}');
|
|
502
|
-
expect(screen.getByRole('button', {name: 'Next'})).toHaveFocus();
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
it('keeps the dropdown open when focus moves into the anchor wrapper', async () => {
|
|
506
|
-
const anchor = document.createElement('div');
|
|
507
|
-
document.body.appendChild(anchor);
|
|
508
|
-
const anchorRef = {current: anchor};
|
|
509
|
-
render(
|
|
510
|
-
<BaseTypeahead
|
|
511
|
-
searchSource={fruitSource}
|
|
512
|
-
value={null}
|
|
513
|
-
onChange={() => {}}
|
|
514
|
-
anchorRef={anchorRef}
|
|
515
|
-
debounceMs={0}
|
|
516
|
-
/>,
|
|
517
|
-
);
|
|
518
|
-
const input = screen.getByRole('combobox');
|
|
519
|
-
// The input lives inside the wrapper we hand to anchorRef.
|
|
520
|
-
anchor.appendChild(input.closest('div') ?? input);
|
|
521
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
522
|
-
|
|
523
|
-
await waitFor(() => {
|
|
524
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
// A sibling control inside the field (e.g. a clear button) receives focus.
|
|
528
|
-
const sibling = document.createElement('button');
|
|
529
|
-
anchor.appendChild(sibling);
|
|
530
|
-
fireEvent.blur(input, {relatedTarget: sibling});
|
|
531
|
-
|
|
532
|
-
// Menu stays open because focus is still within the field.
|
|
533
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
534
|
-
document.body.removeChild(anchor);
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
it('does not close when a dropdown option receives focus', async () => {
|
|
538
|
-
render(
|
|
539
|
-
<BaseTypeahead
|
|
540
|
-
searchSource={fruitSource}
|
|
541
|
-
value={null}
|
|
542
|
-
onChange={() => {}}
|
|
543
|
-
debounceMs={0}
|
|
544
|
-
/>,
|
|
545
|
-
);
|
|
546
|
-
const input = screen.getByRole('combobox');
|
|
547
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
548
|
-
|
|
549
|
-
await waitFor(() => {
|
|
550
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
const option = screen.getByRole('option', {hidden: true});
|
|
554
|
-
fireEvent.blur(input, {relatedTarget: option});
|
|
555
|
-
|
|
556
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
describe('Typeahead', () => {
|
|
561
|
-
describe('out-of-order async results', () => {
|
|
562
|
-
it('discards a stale response that resolves after a newer query', async () => {
|
|
563
|
-
const resolvers = new Map<string, (items: SearchableItem[]) => void>();
|
|
564
|
-
const rawSource: SearchSource = {
|
|
565
|
-
search: async (query: string) =>
|
|
566
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
567
|
-
resolvers.set(query, resolve);
|
|
568
|
-
}),
|
|
569
|
-
bootstrap: () => [],
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
render(
|
|
573
|
-
<Typeahead
|
|
574
|
-
label="Fruit"
|
|
575
|
-
searchSource={rawSource}
|
|
576
|
-
value={null}
|
|
577
|
-
onChange={() => {}}
|
|
578
|
-
debounceMs={0}
|
|
579
|
-
/>,
|
|
580
|
-
);
|
|
581
|
-
|
|
582
|
-
const input = screen.getByRole('combobox');
|
|
583
|
-
fireEvent.change(input, {target: {value: 'a'}});
|
|
584
|
-
fireEvent.change(input, {target: {value: 'ap'}});
|
|
585
|
-
|
|
586
|
-
// The newer query resolves first…
|
|
587
|
-
await act(async () => {
|
|
588
|
-
resolvers.get('ap')!([{id: 'apple', label: 'Apple'}]);
|
|
589
|
-
});
|
|
590
|
-
expect(screen.getByText('Apple')).toBeInTheDocument();
|
|
591
|
-
|
|
592
|
-
// …then the abandoned query's slow response arrives and must be
|
|
593
|
-
// discarded rather than overwriting the current results.
|
|
594
|
-
await act(async () => {
|
|
595
|
-
resolvers.get('a')!([
|
|
596
|
-
{id: 'avocado', label: 'Avocado'},
|
|
597
|
-
{id: 'apricot', label: 'Apricot'},
|
|
598
|
-
]);
|
|
599
|
-
});
|
|
600
|
-
expect(screen.getByText('Apple')).toBeInTheDocument();
|
|
601
|
-
expect(screen.queryByText('Avocado')).not.toBeInTheDocument();
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
it('discards a response from a source that was replaced while it was in flight', async () => {
|
|
605
|
-
let resolveOld: ((items: SearchableItem[]) => void) | null = null;
|
|
606
|
-
const oldSource: SearchSource = {
|
|
607
|
-
search: async () =>
|
|
608
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
609
|
-
resolveOld = resolve;
|
|
610
|
-
}),
|
|
611
|
-
bootstrap: () => [],
|
|
612
|
-
};
|
|
613
|
-
const newSource: SearchSource = {
|
|
614
|
-
search: () => [{id: 'banana', label: 'Banana'}],
|
|
615
|
-
bootstrap: () => [],
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
const {rerender} = render(
|
|
619
|
-
<Typeahead
|
|
620
|
-
label="Fruit"
|
|
621
|
-
searchSource={oldSource}
|
|
622
|
-
value={null}
|
|
623
|
-
onChange={() => {}}
|
|
624
|
-
debounceMs={0}
|
|
625
|
-
/>,
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
const input = screen.getByRole('combobox');
|
|
629
|
-
fireEvent.change(input, {target: {value: 'a'}});
|
|
630
|
-
await waitFor(() => expect(resolveOld).not.toBeNull());
|
|
631
|
-
|
|
632
|
-
// The source is swapped while the old search is still pending and no
|
|
633
|
-
// new query is typed. The stale response must not populate a menu that
|
|
634
|
-
// now reads from a different source.
|
|
635
|
-
rerender(
|
|
636
|
-
<Typeahead
|
|
637
|
-
label="Fruit"
|
|
638
|
-
searchSource={newSource}
|
|
639
|
-
value={null}
|
|
640
|
-
onChange={() => {}}
|
|
641
|
-
debounceMs={0}
|
|
642
|
-
/>,
|
|
643
|
-
);
|
|
644
|
-
|
|
645
|
-
await act(async () => {
|
|
646
|
-
resolveOld!([{id: 'apple', label: 'Apple'}]);
|
|
647
|
-
});
|
|
648
|
-
expect(screen.queryByText('Apple')).not.toBeInTheDocument();
|
|
649
|
-
|
|
650
|
-
// Work started against the new source still lands normally.
|
|
651
|
-
fireEvent.change(input, {target: {value: 'ba'}});
|
|
652
|
-
await waitFor(() =>
|
|
653
|
-
expect(screen.getByText('Banana')).toBeInTheDocument(),
|
|
654
|
-
);
|
|
655
|
-
});
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
it('renders with label', () => {
|
|
659
|
-
render(
|
|
660
|
-
<Typeahead
|
|
661
|
-
label="Fruit"
|
|
662
|
-
searchSource={fruitSource}
|
|
663
|
-
value={null}
|
|
664
|
-
onChange={() => {}}
|
|
665
|
-
/>,
|
|
666
|
-
);
|
|
667
|
-
expect(screen.getByLabelText('Fruit')).toBeInTheDocument();
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
it('renders description text', () => {
|
|
671
|
-
render(
|
|
672
|
-
<Typeahead
|
|
673
|
-
label="Fruit"
|
|
674
|
-
description="Pick your favorite fruit"
|
|
675
|
-
searchSource={fruitSource}
|
|
676
|
-
value={null}
|
|
677
|
-
onChange={() => {}}
|
|
678
|
-
/>,
|
|
679
|
-
);
|
|
680
|
-
expect(screen.getByText('Pick your favorite fruit')).toBeInTheDocument();
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
it('shows required indicator', () => {
|
|
684
|
-
render(
|
|
685
|
-
<Typeahead
|
|
686
|
-
label="Fruit"
|
|
687
|
-
isRequired
|
|
688
|
-
searchSource={fruitSource}
|
|
689
|
-
value={null}
|
|
690
|
-
onChange={() => {}}
|
|
691
|
-
/>,
|
|
692
|
-
);
|
|
693
|
-
expect(screen.getByText(/Required/)).toBeInTheDocument();
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
it('renders error status message', () => {
|
|
697
|
-
render(
|
|
698
|
-
<Typeahead
|
|
699
|
-
label="Fruit"
|
|
700
|
-
searchSource={fruitSource}
|
|
701
|
-
value={null}
|
|
702
|
-
onChange={() => {}}
|
|
703
|
-
status={{type: 'error', message: 'Selection required'}}
|
|
704
|
-
/>,
|
|
705
|
-
);
|
|
706
|
-
expect(screen.getByText('Selection required')).toBeInTheDocument();
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
it('shows selected value as a token', () => {
|
|
710
|
-
render(
|
|
711
|
-
<Typeahead
|
|
712
|
-
label="Fruit"
|
|
713
|
-
searchSource={fruitSource}
|
|
714
|
-
value={fruits[0]}
|
|
715
|
-
onChange={() => {}}
|
|
716
|
-
/>,
|
|
717
|
-
);
|
|
718
|
-
expect(screen.getByText(fruits[0].label)).toBeInTheDocument();
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
it('shows clear button when hasClear and value is selected', () => {
|
|
722
|
-
render(
|
|
723
|
-
<Typeahead
|
|
724
|
-
label="Fruit"
|
|
725
|
-
searchSource={fruitSource}
|
|
726
|
-
value={fruits[0]}
|
|
727
|
-
onChange={() => {}}
|
|
728
|
-
hasClear
|
|
729
|
-
/>,
|
|
730
|
-
);
|
|
731
|
-
expect(
|
|
732
|
-
screen.getByRole('button', {name: 'Clear selection'}),
|
|
733
|
-
).toBeInTheDocument();
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
it('does not show clear button when hasClear is false', () => {
|
|
737
|
-
render(
|
|
738
|
-
<Typeahead
|
|
739
|
-
label="Fruit"
|
|
740
|
-
searchSource={fruitSource}
|
|
741
|
-
value={fruits[0]}
|
|
742
|
-
onChange={() => {}}
|
|
743
|
-
hasClear={false}
|
|
744
|
-
/>,
|
|
745
|
-
);
|
|
746
|
-
expect(
|
|
747
|
-
screen.queryByRole('button', {name: 'Clear selection'}),
|
|
748
|
-
).not.toBeInTheDocument();
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
it('calls onChange with null when clear button is clicked', () => {
|
|
752
|
-
const onChange = vi.fn();
|
|
753
|
-
render(
|
|
754
|
-
<Typeahead
|
|
755
|
-
label="Fruit"
|
|
756
|
-
searchSource={fruitSource}
|
|
757
|
-
value={fruits[0]}
|
|
758
|
-
onChange={onChange}
|
|
759
|
-
hasClear
|
|
760
|
-
/>,
|
|
761
|
-
);
|
|
762
|
-
fireEvent.click(screen.getByRole('button', {name: 'Clear selection'}));
|
|
763
|
-
expect(onChange).toHaveBeenCalledWith(null);
|
|
764
|
-
});
|
|
765
|
-
|
|
766
|
-
it('forwards DOM props and ref to the combobox input while preserving edit and blur behavior', async () => {
|
|
767
|
-
const ref = createRef<HTMLInputElement>();
|
|
768
|
-
const onClick = vi.fn();
|
|
769
|
-
const onBlur = vi.fn();
|
|
770
|
-
const onChange = vi.fn();
|
|
771
|
-
const {container} = render(
|
|
772
|
-
<Typeahead
|
|
773
|
-
ref={ref}
|
|
774
|
-
id="fruit-field"
|
|
775
|
-
data-tracking="fruit-picker"
|
|
776
|
-
data-testid="fruit-input"
|
|
777
|
-
containerClassName="custom-typeahead"
|
|
778
|
-
containerStyle={{marginTop: 7}}
|
|
779
|
-
label="Fruit"
|
|
780
|
-
searchSource={fruitSource}
|
|
781
|
-
value={fruits[0]}
|
|
782
|
-
onChange={onChange}
|
|
783
|
-
onClick={onClick}
|
|
784
|
-
onBlur={onBlur}
|
|
785
|
-
debounceMs={0}
|
|
786
|
-
/>,
|
|
787
|
-
);
|
|
788
|
-
const surface = container.querySelector<HTMLElement>('.astryx-typeahead')!;
|
|
789
|
-
const input = screen.getByRole('combobox');
|
|
790
|
-
|
|
791
|
-
expect(ref.current).toBe(input);
|
|
792
|
-
expect(screen.getByTestId('fruit-input')).toBe(input);
|
|
793
|
-
expect(input).toHaveAttribute('id', 'fruit-field');
|
|
794
|
-
expect(input).toHaveAttribute('data-tracking', 'fruit-picker');
|
|
795
|
-
expect(input).toHaveAccessibleName('Fruit');
|
|
796
|
-
const fieldRoot = container.querySelector('.custom-typeahead');
|
|
797
|
-
expect(fieldRoot).toHaveClass('astryx-field');
|
|
798
|
-
expect(fieldRoot).toHaveStyle({marginTop: '7px'});
|
|
799
|
-
|
|
800
|
-
fireEvent.click(surface);
|
|
801
|
-
await waitFor(() => expect(input).toHaveFocus());
|
|
802
|
-
expect(input).toHaveValue('Apple');
|
|
803
|
-
fireEvent.click(input);
|
|
804
|
-
expect(onClick).toHaveBeenCalledOnce();
|
|
805
|
-
|
|
806
|
-
fireEvent.blur(input);
|
|
807
|
-
expect(onBlur).toHaveBeenCalledOnce();
|
|
808
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
809
|
-
});
|
|
810
|
-
|
|
811
|
-
it('forwards DOM props and ref to the input inside InputGroup, with control* on the surface', async () => {
|
|
812
|
-
const ref = createRef<HTMLInputElement>();
|
|
813
|
-
const onClick = vi.fn();
|
|
814
|
-
const onBlur = vi.fn();
|
|
815
|
-
const onChange = vi.fn();
|
|
816
|
-
const {container} = render(
|
|
817
|
-
<InputGroup label="Produce">
|
|
818
|
-
<Typeahead
|
|
819
|
-
ref={ref}
|
|
820
|
-
id="fruit-field"
|
|
821
|
-
data-tracking="fruit-picker"
|
|
822
|
-
controlClassName="custom-typeahead"
|
|
823
|
-
controlStyle={{marginTop: 7}}
|
|
824
|
-
label="Fruit"
|
|
825
|
-
searchSource={fruitSource}
|
|
826
|
-
value={fruits[0]}
|
|
827
|
-
onChange={onChange}
|
|
828
|
-
onClick={onClick}
|
|
829
|
-
onBlur={onBlur}
|
|
830
|
-
debounceMs={0}
|
|
831
|
-
/>
|
|
832
|
-
</InputGroup>,
|
|
833
|
-
);
|
|
834
|
-
const surface = container.querySelector<HTMLElement>('.astryx-typeahead')!;
|
|
835
|
-
const input = screen.getByRole('combobox');
|
|
836
|
-
|
|
837
|
-
expect(ref.current).toBe(input);
|
|
838
|
-
expect(input).toHaveAttribute('id', 'fruit-field');
|
|
839
|
-
expect(input).toHaveAttribute('data-tracking', 'fruit-picker');
|
|
840
|
-
expect(surface).toHaveClass('custom-typeahead');
|
|
841
|
-
expect(surface).toHaveStyle({marginTop: '7px'});
|
|
842
|
-
expect(input).toHaveAccessibleName('Produce Fruit');
|
|
843
|
-
|
|
844
|
-
fireEvent.click(surface);
|
|
845
|
-
await waitFor(() => expect(input).toHaveFocus());
|
|
846
|
-
expect(input).toHaveValue('Apple');
|
|
847
|
-
fireEvent.click(input);
|
|
848
|
-
expect(onClick).toHaveBeenCalledOnce();
|
|
849
|
-
|
|
850
|
-
fireEvent.blur(input);
|
|
851
|
-
expect(onBlur).toHaveBeenCalledOnce();
|
|
852
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
853
|
-
});
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
describe('Typeahead size', () => {
|
|
857
|
-
it('renders with size="lg"', () => {
|
|
858
|
-
render(
|
|
859
|
-
<Typeahead
|
|
860
|
-
label="Fruit"
|
|
861
|
-
searchSource={fruitSource}
|
|
862
|
-
value={null}
|
|
863
|
-
onChange={() => {}}
|
|
864
|
-
size="lg"
|
|
865
|
-
/>,
|
|
866
|
-
);
|
|
867
|
-
expect(screen.getByLabelText('Fruit')).toBeInTheDocument();
|
|
868
|
-
});
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
describe('BaseTypeahead hasEntriesOnFocus', () => {
|
|
872
|
-
it('does not commit a loading cycle for an empty synchronous bootstrap', async () => {
|
|
873
|
-
const bootstrap = vi.fn((): SearchableItem[] => []);
|
|
874
|
-
const onRender = vi.fn<ProfilerOnRenderCallback>();
|
|
875
|
-
render(
|
|
876
|
-
<Profiler id="typeahead" onRender={onRender}>
|
|
877
|
-
<BaseTypeahead
|
|
878
|
-
searchSource={{search: () => [], bootstrap}}
|
|
879
|
-
value={null}
|
|
880
|
-
onChange={() => {}}
|
|
881
|
-
hasEntriesOnFocus
|
|
882
|
-
/>
|
|
883
|
-
</Profiler>,
|
|
884
|
-
);
|
|
885
|
-
const input = screen.getByRole('combobox');
|
|
886
|
-
onRender.mockClear();
|
|
887
|
-
|
|
888
|
-
await act(async () => {
|
|
889
|
-
fireEvent.focus(input);
|
|
890
|
-
await Promise.resolve();
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
expect(bootstrap).toHaveBeenCalledOnce();
|
|
894
|
-
expect(onRender).not.toHaveBeenCalled();
|
|
895
|
-
});
|
|
896
|
-
|
|
897
|
-
it('does not report loading for synchronous bootstrap results', async () => {
|
|
898
|
-
const lane = createBusyIndicatorLane();
|
|
899
|
-
const onLoadingChange = vi.fn(lane.onBusyChange);
|
|
900
|
-
render(
|
|
901
|
-
<BusyIndicatorLaneProvider
|
|
902
|
-
value={{...lane, onBusyChange: onLoadingChange}}>
|
|
903
|
-
<BaseTypeahead
|
|
904
|
-
searchSource={fruitSource}
|
|
905
|
-
value={null}
|
|
906
|
-
onChange={() => {}}
|
|
907
|
-
hasEntriesOnFocus
|
|
908
|
-
/>
|
|
909
|
-
</BusyIndicatorLaneProvider>,
|
|
910
|
-
);
|
|
911
|
-
const input = screen.getByRole('combobox');
|
|
912
|
-
|
|
913
|
-
fireEvent.focus(input);
|
|
914
|
-
await waitFor(() => {
|
|
915
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
expect(onLoadingChange).not.toHaveBeenCalled();
|
|
919
|
-
});
|
|
920
|
-
|
|
921
|
-
it('clears a pending search loading state before synchronous bootstrap', async () => {
|
|
922
|
-
let settleSearch: (items: SearchableItem[]) => void = () => {};
|
|
923
|
-
const searchSource: SearchSource = {
|
|
924
|
-
search: async () =>
|
|
925
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
926
|
-
settleSearch = resolve;
|
|
927
|
-
}),
|
|
928
|
-
bootstrap: () => [],
|
|
929
|
-
};
|
|
930
|
-
render(
|
|
931
|
-
<BaseTypeahead
|
|
932
|
-
searchSource={searchSource}
|
|
933
|
-
value={null}
|
|
934
|
-
onChange={() => {}}
|
|
935
|
-
hasEntriesOnFocus
|
|
936
|
-
debounceMs={0}
|
|
937
|
-
/>,
|
|
938
|
-
);
|
|
939
|
-
const input = screen.getByRole('combobox');
|
|
940
|
-
|
|
941
|
-
fireEvent.change(input, {target: {value: 'a'}});
|
|
942
|
-
await waitFor(() => {
|
|
943
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
fireEvent.change(input, {target: {value: ''}});
|
|
947
|
-
await waitFor(() => {
|
|
948
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
949
|
-
});
|
|
950
|
-
|
|
951
|
-
await act(async () => {
|
|
952
|
-
settleSearch(fruits.slice(0, 1));
|
|
953
|
-
await Promise.resolve();
|
|
954
|
-
});
|
|
955
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
956
|
-
});
|
|
957
|
-
|
|
958
|
-
it('keeps the loading state for an asynchronous bootstrap', async () => {
|
|
959
|
-
let settle: (items: SearchableItem[]) => void = () => {};
|
|
960
|
-
const bootstrap = vi.fn(
|
|
961
|
-
async () =>
|
|
962
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
963
|
-
settle = resolve;
|
|
964
|
-
}),
|
|
965
|
-
);
|
|
966
|
-
render(
|
|
967
|
-
<BaseTypeahead
|
|
968
|
-
searchSource={{search: () => [], bootstrap}}
|
|
969
|
-
value={null}
|
|
970
|
-
onChange={() => {}}
|
|
971
|
-
hasEntriesOnFocus
|
|
972
|
-
/>,
|
|
973
|
-
);
|
|
974
|
-
const input = screen.getByRole('combobox');
|
|
975
|
-
|
|
976
|
-
fireEvent.focus(input);
|
|
977
|
-
await waitFor(() => {
|
|
978
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
979
|
-
});
|
|
980
|
-
|
|
981
|
-
await act(async () => {
|
|
982
|
-
settle([]);
|
|
983
|
-
await Promise.resolve();
|
|
984
|
-
});
|
|
985
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
it('clears the loading state when the search source is replaced during an in-flight load', async () => {
|
|
989
|
-
let settle: (items: SearchableItem[]) => void = () => {};
|
|
990
|
-
const bootstrap = vi.fn(
|
|
991
|
-
async () =>
|
|
992
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
993
|
-
settle = resolve;
|
|
994
|
-
}),
|
|
995
|
-
);
|
|
996
|
-
const initialSource: SearchSource = {search: () => [], bootstrap};
|
|
997
|
-
const replacementSource: SearchSource = {
|
|
998
|
-
search: () => [],
|
|
999
|
-
bootstrap: () => [],
|
|
1000
|
-
};
|
|
1001
|
-
|
|
1002
|
-
const {rerender} = render(
|
|
1003
|
-
<BaseTypeahead
|
|
1004
|
-
searchSource={initialSource}
|
|
1005
|
-
value={null}
|
|
1006
|
-
onChange={() => {}}
|
|
1007
|
-
hasEntriesOnFocus
|
|
1008
|
-
/>,
|
|
1009
|
-
);
|
|
1010
|
-
const input = screen.getByRole('combobox');
|
|
1011
|
-
|
|
1012
|
-
fireEvent.focus(input);
|
|
1013
|
-
await waitFor(() => {
|
|
1014
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
rerender(
|
|
1018
|
-
<BaseTypeahead
|
|
1019
|
-
searchSource={replacementSource}
|
|
1020
|
-
value={null}
|
|
1021
|
-
onChange={() => {}}
|
|
1022
|
-
hasEntriesOnFocus
|
|
1023
|
-
/>,
|
|
1024
|
-
);
|
|
1025
|
-
|
|
1026
|
-
await waitFor(() => {
|
|
1027
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
1028
|
-
});
|
|
1029
|
-
|
|
1030
|
-
await act(async () => {
|
|
1031
|
-
settle([]);
|
|
1032
|
-
await Promise.resolve();
|
|
1033
|
-
});
|
|
1034
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
1035
|
-
});
|
|
1036
|
-
|
|
1037
|
-
it('keeps the loading state under StrictMode for a pending autofocus bootstrap', async () => {
|
|
1038
|
-
let settle: (items: SearchableItem[]) => void = () => {};
|
|
1039
|
-
const bootstrap = vi.fn(
|
|
1040
|
-
async () =>
|
|
1041
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
1042
|
-
settle = resolve;
|
|
1043
|
-
}),
|
|
1044
|
-
);
|
|
1045
|
-
render(
|
|
1046
|
-
<StrictMode>
|
|
1047
|
-
<Typeahead
|
|
1048
|
-
label="Test"
|
|
1049
|
-
searchSource={{search: () => [], bootstrap}}
|
|
1050
|
-
value={null}
|
|
1051
|
-
onChange={() => {}}
|
|
1052
|
-
hasAutoFocus
|
|
1053
|
-
hasEntriesOnFocus
|
|
1054
|
-
/>
|
|
1055
|
-
</StrictMode>,
|
|
1056
|
-
);
|
|
1057
|
-
|
|
1058
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
1059
|
-
|
|
1060
|
-
await act(async () => {
|
|
1061
|
-
settle([]);
|
|
1062
|
-
await Promise.resolve();
|
|
1063
|
-
});
|
|
1064
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
1065
|
-
});
|
|
1066
|
-
|
|
1067
|
-
let settlePendingTransitionA: (items: SearchableItem[]) => void = () => {};
|
|
1068
|
-
const stableTransitionSourceA: SearchSource = {
|
|
1069
|
-
search: vi.fn(
|
|
1070
|
-
async () =>
|
|
1071
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
1072
|
-
settlePendingTransitionA = resolve;
|
|
1073
|
-
}),
|
|
1074
|
-
),
|
|
1075
|
-
bootstrap: () => [],
|
|
1076
|
-
};
|
|
1077
|
-
const stableTransitionSourceB: SearchSource = {
|
|
1078
|
-
search: vi.fn(async () => []),
|
|
1079
|
-
bootstrap: () => [],
|
|
1080
|
-
};
|
|
1081
|
-
|
|
1082
|
-
it('clears loading and aria-busy when source is replaced in a transition that suspends and retries', async () => {
|
|
1083
|
-
let isSuspended = false;
|
|
1084
|
-
let resolveSuspension: () => void = () => {};
|
|
1085
|
-
let pendingSuspension: Promise<void> | null = null;
|
|
1086
|
-
|
|
1087
|
-
function SuspendingSibling({shouldSuspend}: {shouldSuspend: boolean}) {
|
|
1088
|
-
if (shouldSuspend && isSuspended && pendingSuspension) {
|
|
1089
|
-
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
1090
|
-
throw pendingSuspension;
|
|
1091
|
-
}
|
|
1092
|
-
return null;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
let updateState!: (source: SearchSource, shouldSuspend: boolean) => void;
|
|
1096
|
-
|
|
1097
|
-
function TransitionContainer() {
|
|
1098
|
-
const [source, setSource] = useState<SearchSource>(
|
|
1099
|
-
stableTransitionSourceA,
|
|
1100
|
-
);
|
|
1101
|
-
const [shouldSuspend, setShouldSuspend] = useState(false);
|
|
1102
|
-
updateState = (newSource, suspend) => {
|
|
1103
|
-
setSource(newSource);
|
|
1104
|
-
setShouldSuspend(suspend);
|
|
1105
|
-
};
|
|
1106
|
-
|
|
1107
|
-
return (
|
|
1108
|
-
<Suspense fallback={<div>Suspended...</div>}>
|
|
1109
|
-
<Typeahead
|
|
1110
|
-
label="Test"
|
|
1111
|
-
searchSource={source}
|
|
1112
|
-
value={null}
|
|
1113
|
-
onChange={() => {}}
|
|
1114
|
-
debounceMs={0}
|
|
1115
|
-
/>
|
|
1116
|
-
<SuspendingSibling shouldSuspend={shouldSuspend} />
|
|
1117
|
-
</Suspense>
|
|
1118
|
-
);
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
render(<TransitionContainer />);
|
|
1122
|
-
const input = screen.getByRole('combobox');
|
|
1123
|
-
|
|
1124
|
-
// Trigger a load so aria-busy="true" is set on the combobox
|
|
1125
|
-
fireEvent.change(input, {target: {value: 'query'}});
|
|
1126
|
-
await waitFor(() => {
|
|
1127
|
-
expect(input).toHaveAttribute('aria-busy', 'true');
|
|
1128
|
-
});
|
|
1129
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
1130
|
-
|
|
1131
|
-
// Create the pending promise for suspension
|
|
1132
|
-
isSuspended = true;
|
|
1133
|
-
pendingSuspension = new Promise<void>(resolve => {
|
|
1134
|
-
resolveSuspension = () => {
|
|
1135
|
-
isSuspended = false;
|
|
1136
|
-
resolve();
|
|
1137
|
-
};
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
|
-
// Inside startTransition, switch from source A to source B while sibling suspends
|
|
1141
|
-
act(() => {
|
|
1142
|
-
startTransition(() => {
|
|
1143
|
-
updateState(stableTransitionSourceB, true);
|
|
1144
|
-
});
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
// The render suspended, so the previous committed state remains
|
|
1148
|
-
expect(input).toHaveAttribute('aria-busy', 'true');
|
|
1149
|
-
|
|
1150
|
-
// Resolve the pending promise and let the transition commit
|
|
1151
|
-
await act(async () => {
|
|
1152
|
-
resolveSuspension();
|
|
1153
|
-
await pendingSuspension;
|
|
1154
|
-
});
|
|
1155
|
-
|
|
1156
|
-
// Settle the abandoned load
|
|
1157
|
-
await act(async () => {
|
|
1158
|
-
settlePendingTransitionA([]);
|
|
1159
|
-
await Promise.resolve();
|
|
1160
|
-
});
|
|
1161
|
-
|
|
1162
|
-
// The combobox must not keep aria-busy and the spinner must be gone
|
|
1163
|
-
expect(input).not.toHaveAttribute('aria-busy');
|
|
1164
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
it('shows bootstrap results on mouse click', async () => {
|
|
1168
|
-
render(
|
|
1169
|
-
<BaseTypeahead
|
|
1170
|
-
searchSource={fruitSource}
|
|
1171
|
-
value={null}
|
|
1172
|
-
onChange={() => {}}
|
|
1173
|
-
hasEntriesOnFocus
|
|
1174
|
-
debounceMs={0}
|
|
1175
|
-
/>,
|
|
1176
|
-
);
|
|
1177
|
-
const input = screen.getByRole('combobox');
|
|
1178
|
-
|
|
1179
|
-
// Simulate full mouse click sequence (pointerdown → focus → pointerup → click)
|
|
1180
|
-
fireEvent.pointerDown(input);
|
|
1181
|
-
fireEvent.focus(input);
|
|
1182
|
-
fireEvent.pointerUp(input);
|
|
1183
|
-
fireEvent.click(input);
|
|
1184
|
-
|
|
1185
|
-
await waitFor(() => {
|
|
1186
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1187
|
-
});
|
|
1188
|
-
});
|
|
1189
|
-
|
|
1190
|
-
it('shows bootstrap results on keyboard focus', async () => {
|
|
1191
|
-
render(
|
|
1192
|
-
<BaseTypeahead
|
|
1193
|
-
searchSource={fruitSource}
|
|
1194
|
-
value={null}
|
|
1195
|
-
onChange={() => {}}
|
|
1196
|
-
hasEntriesOnFocus
|
|
1197
|
-
debounceMs={0}
|
|
1198
|
-
/>,
|
|
1199
|
-
);
|
|
1200
|
-
const input = screen.getByRole('combobox');
|
|
1201
|
-
|
|
1202
|
-
// Keyboard focus — no pointer events
|
|
1203
|
-
fireEvent.focus(input);
|
|
1204
|
-
|
|
1205
|
-
await waitFor(() => {
|
|
1206
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1207
|
-
});
|
|
1208
|
-
});
|
|
1209
|
-
|
|
1210
|
-
it('re-shows results on refocus when results already exist', async () => {
|
|
1211
|
-
render(
|
|
1212
|
-
<BaseTypeahead
|
|
1213
|
-
searchSource={fruitSource}
|
|
1214
|
-
value={null}
|
|
1215
|
-
onChange={() => {}}
|
|
1216
|
-
hasEntriesOnFocus
|
|
1217
|
-
debounceMs={0}
|
|
1218
|
-
/>,
|
|
1219
|
-
);
|
|
1220
|
-
const input = screen.getByRole('combobox');
|
|
1221
|
-
|
|
1222
|
-
// Initial focus to load bootstrap results
|
|
1223
|
-
fireEvent.focus(input);
|
|
1224
|
-
await waitFor(() => {
|
|
1225
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1226
|
-
});
|
|
1227
|
-
|
|
1228
|
-
// Blur to close, then refocus
|
|
1229
|
-
fireEvent.blur(input);
|
|
1230
|
-
fireEvent.focus(input);
|
|
1231
|
-
|
|
1232
|
-
await waitFor(() => {
|
|
1233
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1234
|
-
});
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
it('reopens on click when the input was already focused and the dropdown closed without a blur (#6845)', async () => {
|
|
1238
|
-
const user = userEvent.setup();
|
|
1239
|
-
render(
|
|
1240
|
-
<BaseTypeahead
|
|
1241
|
-
searchSource={fruitSource}
|
|
1242
|
-
value={null}
|
|
1243
|
-
onChange={() => {}}
|
|
1244
|
-
hasEntriesOnFocus
|
|
1245
|
-
debounceMs={0}
|
|
1246
|
-
/>,
|
|
1247
|
-
);
|
|
1248
|
-
const input = screen.getByRole('combobox');
|
|
1249
|
-
|
|
1250
|
-
// A real click focuses the input (justFocusedRef suppresses the click
|
|
1251
|
-
// handler's own open logic here, since handleFocus already opened it).
|
|
1252
|
-
await user.click(input);
|
|
1253
|
-
await waitFor(() => {
|
|
1254
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1255
|
-
});
|
|
1256
|
-
|
|
1257
|
-
// Escape closes the dropdown via popover.hide() alone — same as
|
|
1258
|
-
// selecting a result (which also re-focuses the input internally) or a
|
|
1259
|
-
// composing field re-focusing this input after committing something
|
|
1260
|
-
// elsewhere, the input is never actually blurred.
|
|
1261
|
-
await user.keyboard('{Escape}');
|
|
1262
|
-
await waitFor(() => {
|
|
1263
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1264
|
-
});
|
|
1265
|
-
expect(input).toHaveFocus();
|
|
1266
|
-
|
|
1267
|
-
// Clicking the still-focused input must reopen it — before the fix,
|
|
1268
|
-
// this click dispatched no focus event (the input never lost focus) and
|
|
1269
|
-
// nothing else reopened the dropdown.
|
|
1270
|
-
await user.click(input);
|
|
1271
|
-
await waitFor(() => {
|
|
1272
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1273
|
-
});
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
it('does not double-fire an async bootstrap source on a single first click (#6848 review)', async () => {
|
|
1277
|
-
// A microtask-based "did a focus event just run" flag, tried first,
|
|
1278
|
-
// cleared before the click event arrived for an async source — the
|
|
1279
|
-
// click path then ran openIfEligible a second time while the first
|
|
1280
|
-
// bootstrap call was still in flight. The fix reads document.activeElement
|
|
1281
|
-
// at pointerdown instead, which has no event-loop timing dependency.
|
|
1282
|
-
const user = userEvent.setup();
|
|
1283
|
-
const bootstrap = vi.fn(async (): Promise<SearchableItem[]> => {
|
|
1284
|
-
await Promise.resolve();
|
|
1285
|
-
return fruits.slice(0, 3);
|
|
1286
|
-
});
|
|
1287
|
-
render(
|
|
1288
|
-
<BaseTypeahead
|
|
1289
|
-
searchSource={{search: () => [], bootstrap}}
|
|
1290
|
-
value={null}
|
|
1291
|
-
onChange={() => {}}
|
|
1292
|
-
hasEntriesOnFocus
|
|
1293
|
-
debounceMs={0}
|
|
1294
|
-
/>,
|
|
1295
|
-
);
|
|
1296
|
-
const input = screen.getByRole('combobox');
|
|
1297
|
-
|
|
1298
|
-
await user.click(input);
|
|
1299
|
-
await waitFor(() => {
|
|
1300
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
expect(bootstrap).toHaveBeenCalledTimes(1);
|
|
1304
|
-
});
|
|
1305
|
-
});
|
|
1306
|
-
|
|
1307
|
-
describe('BaseTypeahead minQueryLength', () => {
|
|
1308
|
-
it('does not search or open the menu below the threshold', async () => {
|
|
1309
|
-
const search = vi.fn((query: string) =>
|
|
1310
|
-
fruits.filter(f => f.label.toLowerCase().includes(query.toLowerCase())),
|
|
1311
|
-
);
|
|
1312
|
-
render(
|
|
1313
|
-
<BaseTypeahead
|
|
1314
|
-
searchSource={{search, bootstrap: () => []}}
|
|
1315
|
-
value={null}
|
|
1316
|
-
onChange={() => {}}
|
|
1317
|
-
minQueryLength={3}
|
|
1318
|
-
debounceMs={0}
|
|
1319
|
-
/>,
|
|
1320
|
-
);
|
|
1321
|
-
const input = screen.getByRole('combobox');
|
|
1322
|
-
|
|
1323
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
1324
|
-
await act(async () => {
|
|
1325
|
-
await Promise.resolve();
|
|
1326
|
-
});
|
|
1327
|
-
|
|
1328
|
-
expect(search).not.toHaveBeenCalled();
|
|
1329
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1330
|
-
|
|
1331
|
-
// Positive control: the third character crosses the threshold, so the
|
|
1332
|
-
// same harness does see the search and the open menu.
|
|
1333
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
1334
|
-
await waitFor(() => {
|
|
1335
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1336
|
-
});
|
|
1337
|
-
expect(search).toHaveBeenCalledExactlyOnceWith('App');
|
|
1338
|
-
});
|
|
1339
|
-
|
|
1340
|
-
it('closes the menu again when the query falls back below the threshold', async () => {
|
|
1341
|
-
render(
|
|
1342
|
-
<BaseTypeahead
|
|
1343
|
-
searchSource={fruitSource}
|
|
1344
|
-
value={null}
|
|
1345
|
-
onChange={() => {}}
|
|
1346
|
-
minQueryLength={3}
|
|
1347
|
-
debounceMs={0}
|
|
1348
|
-
/>,
|
|
1349
|
-
);
|
|
1350
|
-
const input = screen.getByRole('combobox');
|
|
1351
|
-
|
|
1352
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
1353
|
-
await waitFor(() => {
|
|
1354
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1355
|
-
});
|
|
1356
|
-
|
|
1357
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
1358
|
-
await waitFor(() => {
|
|
1359
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1360
|
-
});
|
|
1361
|
-
});
|
|
1362
|
-
|
|
1363
|
-
it('does not fall back to bootstrap entries on ArrowDown below the threshold', async () => {
|
|
1364
|
-
const bootstrap = vi.fn(() => fruits.slice(0, 3));
|
|
1365
|
-
render(
|
|
1366
|
-
<BaseTypeahead
|
|
1367
|
-
searchSource={{search: () => [], bootstrap}}
|
|
1368
|
-
value={null}
|
|
1369
|
-
onChange={() => {}}
|
|
1370
|
-
hasEntriesOnFocus
|
|
1371
|
-
minQueryLength={3}
|
|
1372
|
-
debounceMs={0}
|
|
1373
|
-
/>,
|
|
1374
|
-
);
|
|
1375
|
-
const input = screen.getByRole('combobox');
|
|
1376
|
-
|
|
1377
|
-
fireEvent.focus(input);
|
|
1378
|
-
await waitFor(() => {
|
|
1379
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1380
|
-
});
|
|
1381
|
-
bootstrap.mockClear();
|
|
1382
|
-
|
|
1383
|
-
// Typing below the threshold closes the bootstrap menu...
|
|
1384
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
1385
|
-
await waitFor(() => {
|
|
1386
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1387
|
-
});
|
|
1388
|
-
|
|
1389
|
-
// ...and ArrowDown must not re-open it with entries that ignore the
|
|
1390
|
-
// two characters already typed.
|
|
1391
|
-
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
1392
|
-
await act(async () => {
|
|
1393
|
-
await Promise.resolve();
|
|
1394
|
-
});
|
|
1395
|
-
expect(bootstrap).not.toHaveBeenCalled();
|
|
1396
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1397
|
-
});
|
|
1398
|
-
|
|
1399
|
-
it('searches on the first character when minQueryLength is not set', async () => {
|
|
1400
|
-
const search = vi.fn(() => fruits.slice(0, 1));
|
|
1401
|
-
render(
|
|
1402
|
-
<BaseTypeahead
|
|
1403
|
-
searchSource={{search, bootstrap: () => []}}
|
|
1404
|
-
value={null}
|
|
1405
|
-
onChange={() => {}}
|
|
1406
|
-
debounceMs={0}
|
|
1407
|
-
/>,
|
|
1408
|
-
);
|
|
1409
|
-
const input = screen.getByRole('combobox');
|
|
1410
|
-
|
|
1411
|
-
fireEvent.change(input, {target: {value: 'A'}});
|
|
1412
|
-
await waitFor(() => {
|
|
1413
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1414
|
-
});
|
|
1415
|
-
expect(search).toHaveBeenCalledExactlyOnceWith('A');
|
|
1416
|
-
});
|
|
1417
|
-
|
|
1418
|
-
it('stops reporting "Loading" when the query falls below the threshold mid-search', async () => {
|
|
1419
|
-
// Falling below the threshold abandons the in-flight search by bumping the
|
|
1420
|
-
// search generation, which also makes that search decline to clear the
|
|
1421
|
-
// loading flag on its way out. Backspacing from three characters to two on
|
|
1422
|
-
// a remote source is the everyday way to hit it, and the field would
|
|
1423
|
-
// otherwise report "Loading" to a screen reader until the third character
|
|
1424
|
-
// went back in.
|
|
1425
|
-
let settle: (items: SearchableItem[]) => void = () => {};
|
|
1426
|
-
const search = vi.fn(
|
|
1427
|
-
async () =>
|
|
1428
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
1429
|
-
settle = resolve;
|
|
1430
|
-
}),
|
|
1431
|
-
);
|
|
1432
|
-
render(
|
|
1433
|
-
<BaseTypeahead
|
|
1434
|
-
searchSource={{search, bootstrap: () => []}}
|
|
1435
|
-
value={null}
|
|
1436
|
-
onChange={() => {}}
|
|
1437
|
-
minQueryLength={3}
|
|
1438
|
-
debounceMs={0}
|
|
1439
|
-
/>,
|
|
1440
|
-
);
|
|
1441
|
-
const input = screen.getByRole('combobox');
|
|
1442
|
-
|
|
1443
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
1444
|
-
await waitFor(() => {
|
|
1445
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
1446
|
-
});
|
|
1447
|
-
expect(input).toHaveAttribute('aria-busy', 'true');
|
|
1448
|
-
|
|
1449
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
1450
|
-
await act(async () => {
|
|
1451
|
-
settle(fruits.slice(0, 1));
|
|
1452
|
-
await Promise.resolve();
|
|
1453
|
-
});
|
|
1454
|
-
|
|
1455
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
1456
|
-
expect(input).not.toHaveAttribute('aria-busy');
|
|
1457
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1458
|
-
});
|
|
1459
|
-
});
|
|
1460
|
-
|
|
1461
|
-
describe('BaseTypeahead hasSearched reset', () => {
|
|
1462
|
-
it('does not show "No results found" after selecting an item and re-entering', async () => {
|
|
1463
|
-
const onChange = vi.fn();
|
|
1464
|
-
const {rerender} = render(
|
|
1465
|
-
<BaseTypeahead
|
|
1466
|
-
searchSource={fruitSource}
|
|
1467
|
-
value={null}
|
|
1468
|
-
onChange={onChange}
|
|
1469
|
-
debounceMs={0}
|
|
1470
|
-
/>,
|
|
1471
|
-
);
|
|
1472
|
-
const input = screen.getByRole('combobox');
|
|
1473
|
-
|
|
1474
|
-
// Type a query that returns results
|
|
1475
|
-
fireEvent.change(input, {target: {value: 'Apple'}});
|
|
1476
|
-
await waitFor(() => {
|
|
1477
|
-
expect(screen.getByText('Apple')).toBeInTheDocument();
|
|
1478
|
-
});
|
|
1479
|
-
|
|
1480
|
-
// Select the item
|
|
1481
|
-
fireEvent.click(screen.getByText('Apple'));
|
|
1482
|
-
expect(onChange).toHaveBeenCalledWith(fruits[0]);
|
|
1483
|
-
|
|
1484
|
-
// Re-render with the selected value
|
|
1485
|
-
rerender(
|
|
1486
|
-
<BaseTypeahead
|
|
1487
|
-
searchSource={fruitSource}
|
|
1488
|
-
value={fruits[0]}
|
|
1489
|
-
onChange={onChange}
|
|
1490
|
-
debounceMs={0}
|
|
1491
|
-
/>,
|
|
1492
|
-
);
|
|
1493
|
-
|
|
1494
|
-
// Focus the input again — "No results found" should NOT appear
|
|
1495
|
-
fireEvent.focus(input);
|
|
1496
|
-
|
|
1497
|
-
// The empty state text should not be visible since hasSearched was reset
|
|
1498
|
-
expect(screen.queryByText('No results found')).not.toBeInTheDocument();
|
|
1499
|
-
});
|
|
1500
|
-
|
|
1501
|
-
it('resets hasSearched when query is cleared without hasEntriesOnFocus', async () => {
|
|
1502
|
-
render(
|
|
1503
|
-
<BaseTypeahead
|
|
1504
|
-
searchSource={fruitSource}
|
|
1505
|
-
value={null}
|
|
1506
|
-
onChange={() => {}}
|
|
1507
|
-
debounceMs={0}
|
|
1508
|
-
/>,
|
|
1509
|
-
);
|
|
1510
|
-
const input = screen.getByRole('combobox');
|
|
1511
|
-
|
|
1512
|
-
// Type a query that returns no results
|
|
1513
|
-
fireEvent.change(input, {target: {value: 'xyz'}});
|
|
1514
|
-
await waitFor(() => {
|
|
1515
|
-
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
1516
|
-
});
|
|
1517
|
-
|
|
1518
|
-
// Clear the query
|
|
1519
|
-
fireEvent.change(input, {target: {value: ''}});
|
|
1520
|
-
|
|
1521
|
-
// "No results found" should disappear since hasSearched is reset
|
|
1522
|
-
expect(screen.queryByText('No results found')).not.toBeInTheDocument();
|
|
1523
|
-
});
|
|
1524
|
-
|
|
1525
|
-
it('resets hasSearched and does not announce "No results found" when search source is replaced during a pending search', async () => {
|
|
1526
|
-
let settle: (items: SearchableItem[]) => void = () => {};
|
|
1527
|
-
const initialSource: SearchSource = {
|
|
1528
|
-
search: vi.fn(
|
|
1529
|
-
async () =>
|
|
1530
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
1531
|
-
settle = resolve;
|
|
1532
|
-
}),
|
|
1533
|
-
),
|
|
1534
|
-
bootstrap: () => [],
|
|
1535
|
-
};
|
|
1536
|
-
const replacementSource: SearchSource = {
|
|
1537
|
-
search: vi.fn(async () => []),
|
|
1538
|
-
bootstrap: () => [],
|
|
1539
|
-
};
|
|
1540
|
-
|
|
1541
|
-
const {rerender} = render(
|
|
1542
|
-
<BaseTypeahead
|
|
1543
|
-
searchSource={initialSource}
|
|
1544
|
-
value={null}
|
|
1545
|
-
onChange={() => {}}
|
|
1546
|
-
debounceMs={0}
|
|
1547
|
-
/>,
|
|
1548
|
-
);
|
|
1549
|
-
const input = screen.getByRole('combobox');
|
|
1550
|
-
|
|
1551
|
-
// Type a query that starts an asynchronous search
|
|
1552
|
-
fireEvent.change(input, {target: {value: 'abc'}});
|
|
1553
|
-
expect(initialSource.search).toHaveBeenCalledWith('abc');
|
|
1554
|
-
|
|
1555
|
-
// Replace the search source while the search is still pending
|
|
1556
|
-
rerender(
|
|
1557
|
-
<BaseTypeahead
|
|
1558
|
-
searchSource={replacementSource}
|
|
1559
|
-
value={null}
|
|
1560
|
-
onChange={() => {}}
|
|
1561
|
-
debounceMs={0}
|
|
1562
|
-
/>,
|
|
1563
|
-
);
|
|
1564
|
-
|
|
1565
|
-
// Settle the abandoned search
|
|
1566
|
-
await act(async () => {
|
|
1567
|
-
settle([]);
|
|
1568
|
-
await Promise.resolve();
|
|
1569
|
-
});
|
|
1570
|
-
|
|
1571
|
-
await act(
|
|
1572
|
-
async () =>
|
|
1573
|
-
new Promise<void>(resolve => requestAnimationFrame(() => resolve())),
|
|
1574
|
-
);
|
|
1575
|
-
expect(
|
|
1576
|
-
document.querySelector('[data-astryx-live-region="polite"]')
|
|
1577
|
-
?.textContent ?? '',
|
|
1578
|
-
).not.toContain('No results found');
|
|
1579
|
-
expect(screen.queryByText('No results found')).not.toBeInTheDocument();
|
|
1580
|
-
});
|
|
1581
|
-
});
|
|
1582
|
-
|
|
1583
|
-
describe('BaseTypeahead popover after selection', () => {
|
|
1584
|
-
it('does not show an empty popover after selecting an item with hasEntriesOnFocus', async () => {
|
|
1585
|
-
const onChange = vi.fn();
|
|
1586
|
-
render(
|
|
1587
|
-
<BaseTypeahead
|
|
1588
|
-
searchSource={fruitSource}
|
|
1589
|
-
value={null}
|
|
1590
|
-
onChange={onChange}
|
|
1591
|
-
hasEntriesOnFocus
|
|
1592
|
-
debounceMs={0}
|
|
1593
|
-
/>,
|
|
1594
|
-
);
|
|
1595
|
-
const input = screen.getByRole('combobox');
|
|
1596
|
-
|
|
1597
|
-
// Focus to open bootstrap results
|
|
1598
|
-
fireEvent.focus(input);
|
|
1599
|
-
await waitFor(() => {
|
|
1600
|
-
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
1601
|
-
});
|
|
1602
|
-
|
|
1603
|
-
// Select an item — popover should close
|
|
1604
|
-
fireEvent.click(screen.getByText('Apple'));
|
|
1605
|
-
expect(onChange).toHaveBeenCalledWith(fruits[0]);
|
|
1606
|
-
|
|
1607
|
-
// After selection, input is refocused but popover should NOT reopen
|
|
1608
|
-
// with an empty menu. The handleFocus handler should be suppressed.
|
|
1609
|
-
await waitFor(() => {
|
|
1610
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
1611
|
-
});
|
|
1612
|
-
});
|
|
1613
|
-
});
|
|
1614
|
-
|
|
1615
|
-
describe('Typeahead edit mode', () => {
|
|
1616
|
-
// iOS raises the software keyboard only for a focus made while the browser
|
|
1617
|
-
// still dispatches the person's tap, so the focus must not wait for a frame.
|
|
1618
|
-
it.each([
|
|
1619
|
-
['the token', () => screen.getByText(fruits[0].label)],
|
|
1620
|
-
[
|
|
1621
|
-
'the field around it',
|
|
1622
|
-
() => screen.getByText(fruits[0].label).closest('div')!,
|
|
1623
|
-
],
|
|
1624
|
-
])(
|
|
1625
|
-
'focuses the input with the label selected inside the tap on %s',
|
|
1626
|
-
(_name, target) => {
|
|
1627
|
-
const onChangeQuery = vi.fn();
|
|
1628
|
-
render(
|
|
1629
|
-
<Typeahead
|
|
1630
|
-
label="Fruit"
|
|
1631
|
-
searchSource={fruitSource}
|
|
1632
|
-
value={fruits[0]}
|
|
1633
|
-
onChange={vi.fn()}
|
|
1634
|
-
onChangeQuery={onChangeQuery}
|
|
1635
|
-
/>,
|
|
1636
|
-
);
|
|
1637
|
-
const input = screen.getByRole<HTMLInputElement>('combobox');
|
|
1638
|
-
// The event the browser is dispatching when focus() runs.
|
|
1639
|
-
const focusedDuring: (string | undefined)[] = [];
|
|
1640
|
-
// An own property: some test setups make `focus` an accessor on the
|
|
1641
|
-
// prototype, which a plain assignment cannot shadow.
|
|
1642
|
-
const focus = HTMLElement.prototype.focus;
|
|
1643
|
-
Object.defineProperty(input, 'focus', {
|
|
1644
|
-
configurable: true,
|
|
1645
|
-
value(options?: FocusOptions) {
|
|
1646
|
-
focusedDuring.push(window.event?.type);
|
|
1647
|
-
focus.call(input, options);
|
|
1648
|
-
},
|
|
1649
|
-
});
|
|
1650
|
-
|
|
1651
|
-
fireEvent.click(target());
|
|
1652
|
-
|
|
1653
|
-
// No frame has run: the focus, the text and its selection are all in
|
|
1654
|
-
// place by the time the tap's own dispatch returns.
|
|
1655
|
-
expect(input).toHaveFocus();
|
|
1656
|
-
expect(input.value).toBe(fruits[0].label);
|
|
1657
|
-
expect(input.selectionStart).toBe(0);
|
|
1658
|
-
expect(input.selectionEnd).toBe(fruits[0].label.length);
|
|
1659
|
-
expect(focusedDuring.length).toBeGreaterThan(0);
|
|
1660
|
-
expect(focusedDuring.every(type => type === 'click')).toBe(true);
|
|
1661
|
-
expect(onChangeQuery).toHaveBeenLastCalledWith(fruits[0].label);
|
|
1662
|
-
},
|
|
1663
|
-
);
|
|
1664
|
-
|
|
1665
|
-
// The base sees the label as its query by the time the focus lands, so a
|
|
1666
|
-
// hasEntriesOnFocus field searches the label instead of opening its
|
|
1667
|
-
// bootstrap entries, and Enter cannot commit a bootstrap item.
|
|
1668
|
-
it('with hasEntriesOnFocus, editing the value searches its label instead of opening the bootstrap entries', async () => {
|
|
1669
|
-
const onChange = vi.fn();
|
|
1670
|
-
render(
|
|
1671
|
-
<Typeahead
|
|
1672
|
-
label="Fruit"
|
|
1673
|
-
searchSource={fruitSource}
|
|
1674
|
-
value={fruits[3]}
|
|
1675
|
-
onChange={onChange}
|
|
1676
|
-
hasEntriesOnFocus
|
|
1677
|
-
debounceMs={0}
|
|
1678
|
-
/>,
|
|
1679
|
-
);
|
|
1680
|
-
const input = screen.getByRole<HTMLInputElement>('combobox');
|
|
1681
|
-
|
|
1682
|
-
fireEvent.click(screen.getByText(fruits[3].label));
|
|
1683
|
-
|
|
1684
|
-
expect(input).toHaveFocus();
|
|
1685
|
-
expect(input.value).toBe(fruits[3].label);
|
|
1686
|
-
// Let any bootstrap or search settle (debounceMs is 0).
|
|
1687
|
-
await act(async () => {
|
|
1688
|
-
await new Promise(resolve => setTimeout(resolve, 20));
|
|
1689
|
-
});
|
|
1690
|
-
|
|
1691
|
-
// Apple is the first bootstrap entry, which the label does not match.
|
|
1692
|
-
fireEvent.keyDown(input, {key: 'Enter'});
|
|
1693
|
-
expect(onChange).not.toHaveBeenCalledWith(fruits[0]);
|
|
1694
|
-
});
|
|
1695
|
-
|
|
1696
|
-
it('enters edit mode on token container click', () => {
|
|
1697
|
-
const onChange = vi.fn();
|
|
1698
|
-
render(
|
|
1699
|
-
<Typeahead
|
|
1700
|
-
label="Fruit"
|
|
1701
|
-
searchSource={fruitSource}
|
|
1702
|
-
value={fruits[0]}
|
|
1703
|
-
onChange={onChange}
|
|
1704
|
-
/>,
|
|
1705
|
-
);
|
|
1706
|
-
screen.getByRole('combobox');
|
|
1707
|
-
|
|
1708
|
-
// Click the token text to enter edit mode
|
|
1709
|
-
const tokenText = screen.getByText(fruits[0].label);
|
|
1710
|
-
const tokenContainer = tokenText.closest('div')!;
|
|
1711
|
-
fireEvent.click(tokenContainer);
|
|
1712
|
-
|
|
1713
|
-
// onChange should NOT have been called (value is preserved for restore)
|
|
1714
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
1715
|
-
});
|
|
1716
|
-
|
|
1717
|
-
it('empties the input when cleared while editing the selected value (#7038)', async () => {
|
|
1718
|
-
const onChangeQuery = vi.fn();
|
|
1719
|
-
function Controlled() {
|
|
1720
|
-
const [value, setValue] = useState<SearchableItem | null>(fruits[0]);
|
|
1721
|
-
return (
|
|
1722
|
-
<Typeahead
|
|
1723
|
-
label="Fruit"
|
|
1724
|
-
searchSource={fruitSource}
|
|
1725
|
-
value={value}
|
|
1726
|
-
onChange={setValue}
|
|
1727
|
-
onChangeQuery={onChangeQuery}
|
|
1728
|
-
hasClear
|
|
1729
|
-
/>
|
|
1730
|
-
);
|
|
1731
|
-
}
|
|
1732
|
-
render(<Controlled />);
|
|
1733
|
-
const input = screen.getByRole<HTMLInputElement>('combobox');
|
|
1734
|
-
|
|
1735
|
-
// Enter edit mode: the token gives way to the label as editable text
|
|
1736
|
-
fireEvent.click(screen.getByText(fruits[0].label).closest('div')!);
|
|
1737
|
-
await waitFor(() => expect(input.value).toBe(fruits[0].label));
|
|
1738
|
-
|
|
1739
|
-
fireEvent.click(screen.getByRole('button', {name: 'Clear selection'}));
|
|
1740
|
-
|
|
1741
|
-
// The value and the visible text agree: nothing selected, nothing typed
|
|
1742
|
-
await waitFor(() => expect(input.value).toBe(''));
|
|
1743
|
-
expect(onChangeQuery).toHaveBeenLastCalledWith('');
|
|
1744
|
-
expect(input).toHaveFocus();
|
|
1745
|
-
});
|
|
1746
|
-
|
|
1747
|
-
// Escape and blur restore the token but leave the edit-mode label in the
|
|
1748
|
-
// base's query, so a clear from the restored token faces the same stale text.
|
|
1749
|
-
it.each([
|
|
1750
|
-
[
|
|
1751
|
-
'Escape',
|
|
1752
|
-
(input: HTMLElement) => fireEvent.keyDown(input, {key: 'Escape'}),
|
|
1753
|
-
],
|
|
1754
|
-
['blur', (input: HTMLElement) => fireEvent.blur(input)],
|
|
1755
|
-
])(
|
|
1756
|
-
'empties the input when cleared after %s restored the token (#7038)',
|
|
1757
|
-
async (_name, leaveEditMode) => {
|
|
1758
|
-
const onChangeQuery = vi.fn();
|
|
1759
|
-
function Controlled() {
|
|
1760
|
-
const [value, setValue] = useState<SearchableItem | null>(fruits[0]);
|
|
1761
|
-
return (
|
|
1762
|
-
<Typeahead
|
|
1763
|
-
label="Fruit"
|
|
1764
|
-
searchSource={fruitSource}
|
|
1765
|
-
value={value}
|
|
1766
|
-
onChange={setValue}
|
|
1767
|
-
onChangeQuery={onChangeQuery}
|
|
1768
|
-
hasClear
|
|
1769
|
-
/>
|
|
1770
|
-
);
|
|
1771
|
-
}
|
|
1772
|
-
render(<Controlled />);
|
|
1773
|
-
const input = screen.getByRole<HTMLInputElement>('combobox');
|
|
1774
|
-
|
|
1775
|
-
fireEvent.click(screen.getByText(fruits[0].label).closest('div')!);
|
|
1776
|
-
await waitFor(() => expect(input.value).toBe(fruits[0].label));
|
|
1777
|
-
|
|
1778
|
-
leaveEditMode(input);
|
|
1779
|
-
await screen.findByRole('button', {name: 'Clear selection'});
|
|
1780
|
-
|
|
1781
|
-
fireEvent.click(screen.getByRole('button', {name: 'Clear selection'}));
|
|
1782
|
-
|
|
1783
|
-
await waitFor(() => expect(input.value).toBe(''));
|
|
1784
|
-
expect(onChangeQuery).toHaveBeenLastCalledWith('');
|
|
1785
|
-
},
|
|
1786
|
-
);
|
|
1787
|
-
|
|
1788
|
-
it('does not report a query change when cleared outside edit mode', () => {
|
|
1789
|
-
const onChangeQuery = vi.fn();
|
|
1790
|
-
render(
|
|
1791
|
-
<Typeahead
|
|
1792
|
-
label="Fruit"
|
|
1793
|
-
searchSource={fruitSource}
|
|
1794
|
-
value={fruits[0]}
|
|
1795
|
-
onChange={vi.fn()}
|
|
1796
|
-
onChangeQuery={onChangeQuery}
|
|
1797
|
-
hasClear
|
|
1798
|
-
/>,
|
|
1799
|
-
);
|
|
1800
|
-
|
|
1801
|
-
fireEvent.click(screen.getByRole('button', {name: 'Clear selection'}));
|
|
1802
|
-
|
|
1803
|
-
expect(onChangeQuery).not.toHaveBeenCalled();
|
|
1804
|
-
});
|
|
1805
|
-
|
|
1806
|
-
it('restores token on blur without action', async () => {
|
|
1807
|
-
const onChange = vi.fn();
|
|
1808
|
-
render(
|
|
1809
|
-
<Typeahead
|
|
1810
|
-
label="Fruit"
|
|
1811
|
-
searchSource={fruitSource}
|
|
1812
|
-
value={fruits[0]}
|
|
1813
|
-
onChange={onChange}
|
|
1814
|
-
/>,
|
|
1815
|
-
);
|
|
1816
|
-
const input = screen.getByRole('combobox');
|
|
1817
|
-
|
|
1818
|
-
// Enter edit mode
|
|
1819
|
-
const tokenText = screen.getByText(fruits[0].label);
|
|
1820
|
-
fireEvent.click(tokenText.closest('div')!);
|
|
1821
|
-
|
|
1822
|
-
// Blur without selecting anything
|
|
1823
|
-
fireEvent.blur(input);
|
|
1824
|
-
|
|
1825
|
-
// onChange should not have been called — value restored
|
|
1826
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
1827
|
-
});
|
|
1828
|
-
|
|
1829
|
-
it('does not exit edit mode on a composing Escape (IME)', async () => {
|
|
1830
|
-
const onChange = vi.fn();
|
|
1831
|
-
render(
|
|
1832
|
-
<Typeahead
|
|
1833
|
-
label="Fruit"
|
|
1834
|
-
searchSource={fruitSource}
|
|
1835
|
-
value={fruits[0]}
|
|
1836
|
-
onChange={onChange}
|
|
1837
|
-
/>,
|
|
1838
|
-
);
|
|
1839
|
-
|
|
1840
|
-
// Enter edit mode by clicking the token; the input uncollapses and
|
|
1841
|
-
// rejoins the Tab order (tabindex is cleared).
|
|
1842
|
-
const tokenText = screen.getByText(fruits[0].label);
|
|
1843
|
-
fireEvent.click(tokenText.closest('div')!);
|
|
1844
|
-
await act(async () => {
|
|
1845
|
-
await new Promise(r => requestAnimationFrame(r));
|
|
1846
|
-
});
|
|
1847
|
-
const input = screen.getByRole('combobox');
|
|
1848
|
-
expect(input).not.toHaveAttribute('tabindex', '-1');
|
|
1849
|
-
|
|
1850
|
-
// BaseTypeahead invokes this external handler before its own IME guard, so
|
|
1851
|
-
// a composing Escape (isComposing / legacy keyCode 229) — which an IME uses
|
|
1852
|
-
// to cancel the pending candidate — must not exit edit mode here.
|
|
1853
|
-
fireEvent.keyDown(input, {key: 'Escape', isComposing: true});
|
|
1854
|
-
expect(screen.getByRole('combobox')).not.toHaveAttribute('tabindex', '-1');
|
|
1855
|
-
fireEvent.keyDown(input, {key: 'Escape', keyCode: 229});
|
|
1856
|
-
expect(screen.getByRole('combobox')).not.toHaveAttribute('tabindex', '-1');
|
|
1857
|
-
|
|
1858
|
-
// A real, non-composing Escape still exits edit mode: the token is
|
|
1859
|
-
// restored and the collapsed input drops back out of the Tab order.
|
|
1860
|
-
fireEvent.keyDown(input, {key: 'Escape'});
|
|
1861
|
-
expect(screen.getByRole('combobox')).toHaveAttribute('tabindex', '-1');
|
|
1862
|
-
});
|
|
1863
|
-
});
|
|
1864
|
-
|
|
1865
|
-
describe('Typeahead collapsed input tab order', () => {
|
|
1866
|
-
it('removes the invisible input from the Tab order while a token is shown', () => {
|
|
1867
|
-
render(
|
|
1868
|
-
<Typeahead
|
|
1869
|
-
label="Fruit"
|
|
1870
|
-
searchSource={fruitSource}
|
|
1871
|
-
value={fruits[0]}
|
|
1872
|
-
onChange={() => {}}
|
|
1873
|
-
/>,
|
|
1874
|
-
);
|
|
1875
|
-
// While the token is shown the input is collapsed (width 0 / opacity 0);
|
|
1876
|
-
// it must stay programmatically focusable for token interactions but must
|
|
1877
|
-
// not be an invisible Tab stop (WCAG 2.4.3 / 2.4.7).
|
|
1878
|
-
expect(screen.getByRole('combobox')).toHaveAttribute('tabindex', '-1');
|
|
1879
|
-
});
|
|
1880
|
-
|
|
1881
|
-
it('Tab from the token skips the invisible input', async () => {
|
|
1882
|
-
const user = userEvent.setup();
|
|
1883
|
-
render(
|
|
1884
|
-
<Typeahead
|
|
1885
|
-
label="Fruit"
|
|
1886
|
-
searchSource={fruitSource}
|
|
1887
|
-
value={fruits[0]}
|
|
1888
|
-
onChange={() => {}}
|
|
1889
|
-
/>,
|
|
1890
|
-
);
|
|
1891
|
-
// Focus the token's internal button, then Tab away — focus must not land
|
|
1892
|
-
// on the visually hidden combobox input.
|
|
1893
|
-
const tokenButton = screen.getByRole('button', {name: fruits[0].label});
|
|
1894
|
-
tokenButton.focus();
|
|
1895
|
-
await user.tab();
|
|
1896
|
-
expect(screen.getByRole('combobox')).not.toHaveFocus();
|
|
1897
|
-
});
|
|
1898
|
-
|
|
1899
|
-
it('keeps the input in the Tab order when no token is shown', () => {
|
|
1900
|
-
render(
|
|
1901
|
-
<Typeahead
|
|
1902
|
-
label="Fruit"
|
|
1903
|
-
searchSource={fruitSource}
|
|
1904
|
-
value={null}
|
|
1905
|
-
onChange={() => {}}
|
|
1906
|
-
/>,
|
|
1907
|
-
);
|
|
1908
|
-
expect(screen.getByRole('combobox')).not.toHaveAttribute('tabindex');
|
|
1909
|
-
});
|
|
1910
|
-
|
|
1911
|
-
it('restores the input to the Tab order in edit mode', () => {
|
|
1912
|
-
render(
|
|
1913
|
-
<Typeahead
|
|
1914
|
-
label="Fruit"
|
|
1915
|
-
searchSource={fruitSource}
|
|
1916
|
-
value={fruits[0]}
|
|
1917
|
-
onChange={() => {}}
|
|
1918
|
-
/>,
|
|
1919
|
-
);
|
|
1920
|
-
// Entering edit mode removes the token and uncollapses the input
|
|
1921
|
-
const tokenText = screen.getByText(fruits[0].label);
|
|
1922
|
-
fireEvent.click(tokenText.closest('div')!);
|
|
1923
|
-
expect(screen.getByRole('combobox')).not.toHaveAttribute('tabindex');
|
|
1924
|
-
});
|
|
1925
|
-
});
|
|
1926
|
-
|
|
1927
|
-
describe('BaseTypeahead paste behavior', () => {
|
|
1928
|
-
it('pasting text triggers search results like typing', async () => {
|
|
1929
|
-
const user = userEvent.setup();
|
|
1930
|
-
render(
|
|
1931
|
-
<BaseTypeahead
|
|
1932
|
-
searchSource={fruitSource}
|
|
1933
|
-
value={null}
|
|
1934
|
-
onChange={() => {}}
|
|
1935
|
-
debounceMs={0}
|
|
1936
|
-
/>,
|
|
1937
|
-
);
|
|
1938
|
-
|
|
1939
|
-
const input = screen.getByRole('combobox');
|
|
1940
|
-
await user.click(input);
|
|
1941
|
-
await user.paste('App');
|
|
1942
|
-
|
|
1943
|
-
await waitFor(() => {
|
|
1944
|
-
expect(screen.getByText('Apple')).toBeInTheDocument();
|
|
1945
|
-
});
|
|
1946
|
-
});
|
|
1947
|
-
|
|
1948
|
-
it('pasting non-matching text shows no results', async () => {
|
|
1949
|
-
const user = userEvent.setup();
|
|
1950
|
-
render(
|
|
1951
|
-
<BaseTypeahead
|
|
1952
|
-
searchSource={fruitSource}
|
|
1953
|
-
value={null}
|
|
1954
|
-
onChange={() => {}}
|
|
1955
|
-
debounceMs={0}
|
|
1956
|
-
/>,
|
|
1957
|
-
);
|
|
1958
|
-
|
|
1959
|
-
const input = screen.getByRole('combobox');
|
|
1960
|
-
await user.click(input);
|
|
1961
|
-
await user.paste('xyz');
|
|
1962
|
-
|
|
1963
|
-
await waitFor(() => {
|
|
1964
|
-
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
1965
|
-
});
|
|
1966
|
-
});
|
|
1967
|
-
|
|
1968
|
-
it('scrolls the highlighted option into view during arrow navigation', async () => {
|
|
1969
|
-
const scrollIntoView = vi.fn();
|
|
1970
|
-
Object.defineProperty(HTMLElement.prototype, 'scrollIntoView', {
|
|
1971
|
-
configurable: true,
|
|
1972
|
-
value: scrollIntoView,
|
|
1973
|
-
});
|
|
1974
|
-
try {
|
|
1975
|
-
const user = userEvent.setup();
|
|
1976
|
-
render(
|
|
1977
|
-
<BaseTypeahead
|
|
1978
|
-
searchSource={fruitSource}
|
|
1979
|
-
value={null}
|
|
1980
|
-
onChange={() => {}}
|
|
1981
|
-
debounceMs={0}
|
|
1982
|
-
/>,
|
|
1983
|
-
);
|
|
1984
|
-
|
|
1985
|
-
const input = screen.getByRole('combobox');
|
|
1986
|
-
await user.click(input);
|
|
1987
|
-
await user.paste('e'); // matches multiple fruits, opens listbox
|
|
1988
|
-
await waitFor(() => {
|
|
1989
|
-
expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
|
|
1990
|
-
});
|
|
1991
|
-
|
|
1992
|
-
scrollIntoView.mockClear();
|
|
1993
|
-
await user.keyboard('{ArrowDown}');
|
|
1994
|
-
await user.keyboard('{ArrowDown}');
|
|
1995
|
-
|
|
1996
|
-
expect(scrollIntoView).toHaveBeenCalledWith({block: 'nearest'});
|
|
1997
|
-
} finally {
|
|
1998
|
-
delete (HTMLElement.prototype as unknown as {scrollIntoView?: unknown})
|
|
1999
|
-
.scrollIntoView;
|
|
2000
|
-
}
|
|
2001
|
-
});
|
|
2002
|
-
|
|
2003
|
-
it('highlights on hover without scrolling, keyboard still scrolls (#6077)', async () => {
|
|
2004
|
-
// Hover must highlight only: scrollIntoView under a stationary pointer
|
|
2005
|
-
// moves the next option under it, re-highlighting and scrolling again —
|
|
2006
|
-
// a runaway auto-scroll loop with no user input.
|
|
2007
|
-
const scrollIntoView = vi.fn();
|
|
2008
|
-
Object.defineProperty(HTMLElement.prototype, 'scrollIntoView', {
|
|
2009
|
-
configurable: true,
|
|
2010
|
-
value: scrollIntoView,
|
|
2011
|
-
});
|
|
2012
|
-
try {
|
|
2013
|
-
const user = userEvent.setup();
|
|
2014
|
-
render(
|
|
2015
|
-
<BaseTypeahead
|
|
2016
|
-
searchSource={fruitSource}
|
|
2017
|
-
value={null}
|
|
2018
|
-
onChange={() => {}}
|
|
2019
|
-
debounceMs={0}
|
|
2020
|
-
/>,
|
|
2021
|
-
);
|
|
2022
|
-
|
|
2023
|
-
const input = screen.getByRole('combobox');
|
|
2024
|
-
await user.click(input);
|
|
2025
|
-
await user.paste('e'); // matches Cherry, Date, Elderberry
|
|
2026
|
-
await waitFor(() => {
|
|
2027
|
-
expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
|
|
2028
|
-
});
|
|
2029
|
-
|
|
2030
|
-
scrollIntoView.mockClear();
|
|
2031
|
-
const options = screen.getAllByRole('option', {hidden: true});
|
|
2032
|
-
fireEvent.mouseMove(options[1]);
|
|
2033
|
-
|
|
2034
|
-
expect(input.getAttribute('aria-activedescendant')).toBe(options[1].id);
|
|
2035
|
-
expect(scrollIntoView).not.toHaveBeenCalled();
|
|
2036
|
-
|
|
2037
|
-
await user.keyboard('{ArrowDown}');
|
|
2038
|
-
expect(scrollIntoView).toHaveBeenCalledWith({block: 'nearest'});
|
|
2039
|
-
} finally {
|
|
2040
|
-
delete (HTMLElement.prototype as unknown as {scrollIntoView?: unknown})
|
|
2041
|
-
.scrollIntoView;
|
|
2042
|
-
}
|
|
2043
|
-
});
|
|
2044
|
-
});
|
|
2045
|
-
|
|
2046
|
-
describe('BaseTypeahead highlight under a resting pointer', () => {
|
|
2047
|
-
it('keeps the keyboard highlight when an option appears under the pointer, and follows pointer movement', async () => {
|
|
2048
|
-
// A menu that opens or reflows under a pointer the user is not moving
|
|
2049
|
-
// fires mouseenter on the option that lands beneath it. Taking the
|
|
2050
|
-
// highlight from that would make Enter pick an option the user never
|
|
2051
|
-
// pointed at; only real pointer movement moves the highlight.
|
|
2052
|
-
const user = userEvent.setup();
|
|
2053
|
-
render(
|
|
2054
|
-
<BaseTypeahead
|
|
2055
|
-
searchSource={fruitSource}
|
|
2056
|
-
value={null}
|
|
2057
|
-
onChange={() => {}}
|
|
2058
|
-
debounceMs={0}
|
|
2059
|
-
/>,
|
|
2060
|
-
);
|
|
2061
|
-
|
|
2062
|
-
const input = screen.getByRole('combobox');
|
|
2063
|
-
await user.click(input);
|
|
2064
|
-
await user.paste('e'); // matches Cherry, Date, Elderberry
|
|
2065
|
-
await waitFor(() => {
|
|
2066
|
-
expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
|
|
2067
|
-
});
|
|
2068
|
-
const options = screen.getAllByRole('option', {hidden: true});
|
|
2069
|
-
expect(input.getAttribute('aria-activedescendant')).toBe(options[0].id);
|
|
2070
|
-
|
|
2071
|
-
fireEvent.mouseEnter(options[2]);
|
|
2072
|
-
expect(input.getAttribute('aria-activedescendant')).toBe(options[0].id);
|
|
2073
|
-
|
|
2074
|
-
fireEvent.mouseMove(options[2]);
|
|
2075
|
-
expect(input.getAttribute('aria-activedescendant')).toBe(options[2].id);
|
|
2076
|
-
});
|
|
2077
|
-
});
|
|
2078
|
-
|
|
2079
|
-
describe('Typeahead disabledMessage', () => {
|
|
2080
|
-
// jsdom does not implement the Popover API used by the tooltip, so mock
|
|
2081
|
-
// showPopover/hidePopover to toggle a `popover-open` attribute the tests
|
|
2082
|
-
// can assert on.
|
|
2083
|
-
beforeEach(() => {
|
|
2084
|
-
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
2085
|
-
this.setAttribute('popover-open', '');
|
|
2086
|
-
});
|
|
2087
|
-
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
2088
|
-
this.removeAttribute('popover-open');
|
|
2089
|
-
});
|
|
2090
|
-
});
|
|
2091
|
-
|
|
2092
|
-
// jsdom popover content is in the DOM but not "visible" in the
|
|
2093
|
-
// accessibility tree; use hidden: true to find it.
|
|
2094
|
-
const h = {hidden: true} as const;
|
|
2095
|
-
|
|
2096
|
-
it('shows the reason tooltip on hover when disabled with a reason', async () => {
|
|
2097
|
-
render(
|
|
2098
|
-
<Typeahead
|
|
2099
|
-
label="Assignee"
|
|
2100
|
-
searchSource={fruitSource}
|
|
2101
|
-
value={null}
|
|
2102
|
-
onChange={() => {}}
|
|
2103
|
-
isDisabled
|
|
2104
|
-
disabledMessage="You need the Editor role"
|
|
2105
|
-
/>,
|
|
2106
|
-
);
|
|
2107
|
-
|
|
2108
|
-
// The field itself, by its own class: the input no longer sits directly
|
|
2109
|
-
// inside it — it is in the content lane that bounds the value — and
|
|
2110
|
-
// `mouseEnter` does not bubble, so the hover has to land on the element
|
|
2111
|
-
// the tooltip is actually bound to.
|
|
2112
|
-
const container = document.querySelector(
|
|
2113
|
-
'.astryx-typeahead',
|
|
2114
|
-
) as HTMLElement;
|
|
2115
|
-
const tooltip = screen.getByRole('tooltip', h);
|
|
2116
|
-
expect(tooltip).toHaveTextContent('You need the Editor role');
|
|
2117
|
-
|
|
2118
|
-
fireEvent.mouseEnter(container);
|
|
2119
|
-
await waitFor(() => {
|
|
2120
|
-
expect(tooltip).toHaveAttribute('popover-open');
|
|
2121
|
-
});
|
|
2122
|
-
|
|
2123
|
-
fireEvent.mouseLeave(container);
|
|
2124
|
-
await waitFor(() => {
|
|
2125
|
-
expect(tooltip).not.toHaveAttribute('popover-open');
|
|
2126
|
-
});
|
|
2127
|
-
});
|
|
2128
|
-
|
|
2129
|
-
it('shows the reason tooltip on keyboard focus', async () => {
|
|
2130
|
-
const user = userEvent.setup();
|
|
2131
|
-
render(
|
|
2132
|
-
<Typeahead
|
|
2133
|
-
label="Assignee"
|
|
2134
|
-
searchSource={fruitSource}
|
|
2135
|
-
value={null}
|
|
2136
|
-
onChange={() => {}}
|
|
2137
|
-
isDisabled
|
|
2138
|
-
disabledMessage="You need the Editor role"
|
|
2139
|
-
/>,
|
|
2140
|
-
);
|
|
2141
|
-
|
|
2142
|
-
const tooltip = screen.getByRole('tooltip', h);
|
|
2143
|
-
await user.tab();
|
|
2144
|
-
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
2145
|
-
await waitFor(() => {
|
|
2146
|
-
expect(tooltip).toHaveAttribute('popover-open');
|
|
2147
|
-
});
|
|
2148
|
-
});
|
|
2149
|
-
|
|
2150
|
-
it('does not render a tooltip when not disabled', () => {
|
|
2151
|
-
render(
|
|
2152
|
-
<Typeahead
|
|
2153
|
-
label="Assignee"
|
|
2154
|
-
searchSource={fruitSource}
|
|
2155
|
-
value={null}
|
|
2156
|
-
onChange={() => {}}
|
|
2157
|
-
disabledMessage="You need the Editor role"
|
|
2158
|
-
/>,
|
|
2159
|
-
);
|
|
2160
|
-
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
2161
|
-
});
|
|
2162
|
-
|
|
2163
|
-
it('does not render a tooltip when disabled without a reason', () => {
|
|
2164
|
-
render(
|
|
2165
|
-
<Typeahead
|
|
2166
|
-
label="Assignee"
|
|
2167
|
-
searchSource={fruitSource}
|
|
2168
|
-
value={null}
|
|
2169
|
-
onChange={() => {}}
|
|
2170
|
-
isDisabled
|
|
2171
|
-
/>,
|
|
2172
|
-
);
|
|
2173
|
-
expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
|
|
2174
|
-
});
|
|
2175
|
-
|
|
2176
|
-
it('keeps the input focusable via aria-disabled when a reason is provided', () => {
|
|
2177
|
-
render(
|
|
2178
|
-
<Typeahead
|
|
2179
|
-
label="Assignee"
|
|
2180
|
-
searchSource={fruitSource}
|
|
2181
|
-
value={null}
|
|
2182
|
-
onChange={() => {}}
|
|
2183
|
-
isDisabled
|
|
2184
|
-
disabledMessage="You need the Editor role"
|
|
2185
|
-
/>,
|
|
2186
|
-
);
|
|
2187
|
-
const input = screen.getByRole('combobox');
|
|
2188
|
-
expect(input).not.toBeDisabled();
|
|
2189
|
-
expect(input).toHaveAttribute('aria-disabled', 'true');
|
|
2190
|
-
expect(input).toHaveAttribute('readonly');
|
|
2191
|
-
});
|
|
2192
|
-
|
|
2193
|
-
it('links the reason tooltip from the input via aria-describedby', () => {
|
|
2194
|
-
render(
|
|
2195
|
-
<Typeahead
|
|
2196
|
-
label="Assignee"
|
|
2197
|
-
searchSource={fruitSource}
|
|
2198
|
-
value={null}
|
|
2199
|
-
onChange={() => {}}
|
|
2200
|
-
isDisabled
|
|
2201
|
-
disabledMessage="You need the Editor role"
|
|
2202
|
-
/>,
|
|
2203
|
-
);
|
|
2204
|
-
const input = screen.getByRole('combobox');
|
|
2205
|
-
const tooltip = screen.getByRole('tooltip', h);
|
|
2206
|
-
expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
|
|
2207
|
-
});
|
|
2208
|
-
|
|
2209
|
-
it('blocks typing and selection while focusable-disabled', async () => {
|
|
2210
|
-
const user = userEvent.setup();
|
|
2211
|
-
const onChange = vi.fn();
|
|
2212
|
-
render(
|
|
2213
|
-
<Typeahead
|
|
2214
|
-
label="Assignee"
|
|
2215
|
-
searchSource={fruitSource}
|
|
2216
|
-
value={null}
|
|
2217
|
-
onChange={onChange}
|
|
2218
|
-
isDisabled
|
|
2219
|
-
disabledMessage="You need the Editor role"
|
|
2220
|
-
/>,
|
|
2221
|
-
);
|
|
2222
|
-
|
|
2223
|
-
const input = screen.getByRole('combobox');
|
|
2224
|
-
await user.click(input);
|
|
2225
|
-
await user.type(input, 'App');
|
|
2226
|
-
expect(input).toHaveValue('');
|
|
2227
|
-
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
2228
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
2229
|
-
});
|
|
2230
|
-
|
|
2231
|
-
it('remains natively disabled when disabled without a reason', () => {
|
|
2232
|
-
render(
|
|
2233
|
-
<Typeahead
|
|
2234
|
-
label="Assignee"
|
|
2235
|
-
searchSource={fruitSource}
|
|
2236
|
-
value={null}
|
|
2237
|
-
onChange={() => {}}
|
|
2238
|
-
isDisabled
|
|
2239
|
-
/>,
|
|
2240
|
-
);
|
|
2241
|
-
const input = screen.getByRole('combobox');
|
|
2242
|
-
expect(input).toBeDisabled();
|
|
2243
|
-
expect(input).not.toHaveAttribute('aria-disabled');
|
|
2244
|
-
});
|
|
2245
|
-
});
|
|
2246
|
-
|
|
2247
|
-
describe('Typeahead statusVariant forwarding', () => {
|
|
2248
|
-
it('defaults to attached (status renders with data-variant="attached")', () => {
|
|
2249
|
-
const {container} = render(
|
|
2250
|
-
<Typeahead
|
|
2251
|
-
label="Fruit"
|
|
2252
|
-
searchSource={fruitSource}
|
|
2253
|
-
value={null}
|
|
2254
|
-
onChange={() => {}}
|
|
2255
|
-
status={{type: 'error', message: 'Required'}}
|
|
2256
|
-
/>,
|
|
2257
|
-
);
|
|
2258
|
-
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
2259
|
-
'data-variant',
|
|
2260
|
-
'attached',
|
|
2261
|
-
);
|
|
2262
|
-
});
|
|
2263
|
-
|
|
2264
|
-
it('forwards statusVariant="detached" to the underlying Field status', () => {
|
|
2265
|
-
const {container} = render(
|
|
2266
|
-
<Typeahead
|
|
2267
|
-
label="Fruit"
|
|
2268
|
-
searchSource={fruitSource}
|
|
2269
|
-
value={null}
|
|
2270
|
-
onChange={() => {}}
|
|
2271
|
-
status={{type: 'error', message: 'Required'}}
|
|
2272
|
-
statusVariant="detached"
|
|
2273
|
-
/>,
|
|
2274
|
-
);
|
|
2275
|
-
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
2276
|
-
'data-variant',
|
|
2277
|
-
'detached',
|
|
2278
|
-
);
|
|
2279
|
-
});
|
|
2280
|
-
});
|
|
2281
|
-
|
|
2282
|
-
describe('busy indicator ownership', () => {
|
|
2283
|
-
/** A source that stays in flight until the test settles it. */
|
|
2284
|
-
const pendingSource = () => {
|
|
2285
|
-
let settle: (items: SearchableItem[]) => void = () => {};
|
|
2286
|
-
return {
|
|
2287
|
-
source: {
|
|
2288
|
-
search: async () =>
|
|
2289
|
-
new Promise<SearchableItem[]>(resolve => {
|
|
2290
|
-
settle = resolve;
|
|
2291
|
-
}),
|
|
2292
|
-
bootstrap: () => [],
|
|
2293
|
-
},
|
|
2294
|
-
settle: (items: SearchableItem[] = []) => settle(items),
|
|
2295
|
-
};
|
|
2296
|
-
};
|
|
2297
|
-
|
|
2298
|
-
it('renders its own indicator for a direct caller', async () => {
|
|
2299
|
-
// BaseTypeaheadProps is re-exported from the package entry point, so the
|
|
2300
|
-
// base has direct callers this repo cannot see. They painted no indicator
|
|
2301
|
-
// of their own — the base did it for them — so it keeps doing it. The
|
|
2302
|
-
// Spinner is decorative and the base announces the search's busy start
|
|
2303
|
-
// through the shared channel (spec:AST-086 FR8).
|
|
2304
|
-
const {source, settle} = pendingSource();
|
|
2305
|
-
render(
|
|
2306
|
-
<BaseTypeahead
|
|
2307
|
-
searchSource={source}
|
|
2308
|
-
value={null}
|
|
2309
|
-
onChange={() => {}}
|
|
2310
|
-
debounceMs={0}
|
|
2311
|
-
/>,
|
|
2312
|
-
);
|
|
2313
|
-
const input = screen.getByRole('combobox');
|
|
2314
|
-
|
|
2315
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
2316
|
-
await waitFor(() => {
|
|
2317
|
-
expect(getBusySpinner()).toBeInTheDocument();
|
|
2318
|
-
});
|
|
2319
|
-
// A Spinner, not the static clock glyph this used to render: `clock`
|
|
2320
|
-
// means *time* everywhere else in core, and nothing about it moved while
|
|
2321
|
-
// a search was out.
|
|
2322
|
-
expect(getBusySpinner().querySelector('svg')).toBeInTheDocument();
|
|
2323
|
-
|
|
2324
|
-
await act(async () => {
|
|
2325
|
-
settle();
|
|
2326
|
-
await Promise.resolve();
|
|
2327
|
-
});
|
|
2328
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
2329
|
-
});
|
|
2330
|
-
|
|
2331
|
-
it('hands the indicator over to a field that takes it, and renders none itself', async () => {
|
|
2332
|
-
// Typeahead and Tokenizer paint the spinner in the one inline-end lane
|
|
2333
|
-
// they already own. Two indicators in one field is the defect this PR
|
|
2334
|
-
// exists to fix, so the base must yield rather than add to it.
|
|
2335
|
-
const {source, settle} = pendingSource();
|
|
2336
|
-
// A real lane with its notify spied, rather than a hand-built object: the
|
|
2337
|
-
// lane is a store now, and the base must drive the store the wrappers use.
|
|
2338
|
-
const lane = createBusyIndicatorLane();
|
|
2339
|
-
const onLoadingChange = vi.fn(lane.onBusyChange);
|
|
2340
|
-
render(
|
|
2341
|
-
<BusyIndicatorLaneProvider
|
|
2342
|
-
value={{...lane, onBusyChange: onLoadingChange}}>
|
|
2343
|
-
<BaseTypeahead
|
|
2344
|
-
searchSource={source}
|
|
2345
|
-
value={null}
|
|
2346
|
-
onChange={() => {}}
|
|
2347
|
-
debounceMs={0}
|
|
2348
|
-
/>
|
|
2349
|
-
</BusyIndicatorLaneProvider>,
|
|
2350
|
-
);
|
|
2351
|
-
const input = screen.getByRole('combobox');
|
|
2352
|
-
|
|
2353
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
2354
|
-
await waitFor(() => {
|
|
2355
|
-
expect(input).toHaveAttribute('aria-busy', 'true');
|
|
2356
|
-
});
|
|
2357
|
-
// Scoped by name: the announcer for result counts is a role="status"
|
|
2358
|
-
// live region too, and it is not the indicator.
|
|
2359
|
-
expect(queryBusySpinner()).not.toBeInTheDocument();
|
|
2360
|
-
expect(onLoadingChange).toHaveBeenLastCalledWith(true);
|
|
2361
|
-
|
|
2362
|
-
await act(async () => {
|
|
2363
|
-
settle();
|
|
2364
|
-
await Promise.resolve();
|
|
2365
|
-
});
|
|
2366
|
-
expect(onLoadingChange).toHaveBeenLastCalledWith(false);
|
|
2367
|
-
});
|
|
2368
|
-
|
|
2369
|
-
it('keeps the busy handoff out of the exported prop surface', async () => {
|
|
2370
|
-
// The handoff used to be `__onLoadingChange` on BaseTypeaheadProps, which
|
|
2371
|
-
// the package entry point re-exports — so a builder reading the exported
|
|
2372
|
-
// declaration would find it and could reasonably wire it, pinning a detail
|
|
2373
|
-
// between two wrappers and their base as permanent API. An `@internal` tag
|
|
2374
|
-
// is a note to a reader; a module boundary is the actual seam.
|
|
2375
|
-
const entry = await import('./index');
|
|
2376
|
-
expect(Object.keys(entry)).not.toContain('BusyIndicatorLaneProvider');
|
|
2377
|
-
expect(Object.keys(entry)).not.toContain('useBusyIndicatorLane');
|
|
2378
|
-
|
|
2379
|
-
const source = await readFile(
|
|
2380
|
-
resolve(__dirname, 'BaseTypeahead.tsx'),
|
|
2381
|
-
'utf8',
|
|
2382
|
-
);
|
|
2383
|
-
expect(source).not.toContain('__onLoadingChange');
|
|
2384
|
-
});
|
|
2385
|
-
|
|
2386
|
-
it('reports each transition once, and reports nothing when nothing changed', async () => {
|
|
2387
|
-
// Edge-triggered on purpose. Every keystroke below the query threshold
|
|
2388
|
-
// clears the flag, and an unconditional report would hand the wrapper a
|
|
2389
|
-
// `false` per character — each one a state write, and on a field that is
|
|
2390
|
-
// re-rendering as the user types.
|
|
2391
|
-
const {source, settle} = pendingSource();
|
|
2392
|
-
// A real lane with its notify spied, rather than a hand-built object: the
|
|
2393
|
-
// lane is a store now, and the base must drive the store the wrappers use.
|
|
2394
|
-
const lane = createBusyIndicatorLane();
|
|
2395
|
-
const onLoadingChange = vi.fn(lane.onBusyChange);
|
|
2396
|
-
render(
|
|
2397
|
-
<BusyIndicatorLaneProvider
|
|
2398
|
-
value={{...lane, onBusyChange: onLoadingChange}}>
|
|
2399
|
-
<BaseTypeahead
|
|
2400
|
-
searchSource={source}
|
|
2401
|
-
value={null}
|
|
2402
|
-
onChange={() => {}}
|
|
2403
|
-
minQueryLength={3}
|
|
2404
|
-
debounceMs={0}
|
|
2405
|
-
/>
|
|
2406
|
-
</BusyIndicatorLaneProvider>,
|
|
2407
|
-
);
|
|
2408
|
-
const input = screen.getByRole('combobox');
|
|
2409
|
-
|
|
2410
|
-
// Below the threshold: no search, so nothing to report.
|
|
2411
|
-
fireEvent.change(input, {target: {value: 'A'}});
|
|
2412
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
2413
|
-
await act(async () => {
|
|
2414
|
-
await Promise.resolve();
|
|
2415
|
-
});
|
|
2416
|
-
expect(onLoadingChange).not.toHaveBeenCalled();
|
|
2417
|
-
|
|
2418
|
-
fireEvent.change(input, {target: {value: 'App'}});
|
|
2419
|
-
await waitFor(() => {
|
|
2420
|
-
expect(onLoadingChange).toHaveBeenCalledTimes(1);
|
|
2421
|
-
});
|
|
2422
|
-
expect(onLoadingChange).toHaveBeenCalledWith(true);
|
|
2423
|
-
|
|
2424
|
-
// Back below the threshold: one report out, not one per keystroke.
|
|
2425
|
-
fireEvent.change(input, {target: {value: 'Ap'}});
|
|
2426
|
-
fireEvent.change(input, {target: {value: 'A'}});
|
|
2427
|
-
fireEvent.change(input, {target: {value: ''}});
|
|
2428
|
-
await act(async () => {
|
|
2429
|
-
settle();
|
|
2430
|
-
await Promise.resolve();
|
|
2431
|
-
});
|
|
2432
|
-
expect(onLoadingChange).toHaveBeenCalledTimes(2);
|
|
2433
|
-
expect(onLoadingChange).toHaveBeenLastCalledWith(false);
|
|
2434
|
-
});
|
|
2435
|
-
});
|
|
2436
|
-
|
|
2437
|
-
describe('end controls stay in flow', () => {
|
|
2438
|
-
// The fix for the transform bug, expressed as a rule rather than a
|
|
2439
|
-
// measurement: these controls are ordinary flex siblings of the input, so
|
|
2440
|
-
// they take up room and nothing has to reserve it for them. jsdom performs
|
|
2441
|
-
// no layout, so what is asserted is the absence of the two things that
|
|
2442
|
-
// stopped that being true — an out-of-flow lane, and a padding reserve fed
|
|
2443
|
-
// by a measured width. The geometry itself is browser-verified in the PR.
|
|
2444
|
-
it('renders the clear button without taking it out of flow', () => {
|
|
2445
|
-
const {container} = render(
|
|
2446
|
-
<Typeahead
|
|
2447
|
-
label="Fruit"
|
|
2448
|
-
searchSource={fruitSource}
|
|
2449
|
-
value={fruits[0]}
|
|
2450
|
-
onChange={() => {}}
|
|
2451
|
-
/>,
|
|
2452
|
-
);
|
|
2453
|
-
const field = container.querySelector('.astryx-typeahead');
|
|
2454
|
-
const clear = screen.getByRole('button', {name: /clear/i});
|
|
2455
|
-
expect(field).toContainElement(clear);
|
|
2456
|
-
// The controls sit in a lane that is itself an ordinary in-flow child of
|
|
2457
|
-
// the field — not a box positioned over it, which is what reserved no
|
|
2458
|
-
// room and put the input underneath.
|
|
2459
|
-
const lane = clear.parentElement as HTMLElement;
|
|
2460
|
-
expect(lane.parentElement).toBe(field);
|
|
2461
|
-
expect(getComputedStyle(lane).position).not.toBe('absolute');
|
|
2462
|
-
});
|
|
2463
|
-
|
|
2464
|
-
it('never reserves room with a measured width', () => {
|
|
2465
|
-
// The custom property is Tokenizer's mechanism and must not reappear
|
|
2466
|
-
// here: a width measured in viewport space and spent as local padding is
|
|
2467
|
-
// wrong under any CSS transform (measured on Tokenizer's 123px lane,
|
|
2468
|
-
// scale(.5) covered 14px of the query and scale(2) left a 125.95px gap).
|
|
2469
|
-
const {container} = render(
|
|
2470
|
-
<Typeahead
|
|
2471
|
-
label="Fruit"
|
|
2472
|
-
searchSource={fruitSource}
|
|
2473
|
-
value={fruits[0]}
|
|
2474
|
-
onChange={() => {}}
|
|
2475
|
-
/>,
|
|
2476
|
-
);
|
|
2477
|
-
expect(
|
|
2478
|
-
container.querySelector('[style*="--_tokenizer-end-lane-width"]'),
|
|
2479
|
-
).toBeNull();
|
|
2480
|
-
});
|
|
2481
|
-
|
|
2482
|
-
it('holds the controls at the inline end when a token shows', () => {
|
|
2483
|
-
// `auto` gives free space to the margin rather than to a sibling, so the
|
|
2484
|
-
// controls stay in the corner in the states where the content lane is not
|
|
2485
|
-
// the only flexible item in the row. Without it the clear button sat
|
|
2486
|
-
// against the token in mid-field instead of in the corner (measured:
|
|
2487
|
-
// x=39 in a 300px field, against TextInput's 281).
|
|
2488
|
-
render(
|
|
2489
|
-
<Typeahead
|
|
2490
|
-
label="Fruit"
|
|
2491
|
-
searchSource={fruitSource}
|
|
2492
|
-
value={fruits[0]}
|
|
2493
|
-
onChange={() => {}}
|
|
2494
|
-
/>,
|
|
2495
|
-
);
|
|
2496
|
-
const clear = screen.getByRole('button', {name: /clear/i});
|
|
2497
|
-
const lane = clear.parentElement as HTMLElement;
|
|
2498
|
-
expect(getComputedStyle(lane).marginInlineStart).toBe('auto');
|
|
2499
|
-
});
|
|
2500
|
-
|
|
2501
|
-
it('keeps the field on one row so the controls cannot be pushed off it', () => {
|
|
2502
|
-
// `flex-wrap` moves an item to a new line rather than shrinking it, so a
|
|
2503
|
-
// wrapping field put the controls on a row of their own once the token
|
|
2504
|
-
// got long. The shared field base does not wrap; this must not either.
|
|
2505
|
-
const {container} = render(
|
|
2506
|
-
<Typeahead
|
|
2507
|
-
label="Fruit"
|
|
2508
|
-
searchSource={fruitSource}
|
|
2509
|
-
value={fruits[0]}
|
|
2510
|
-
onChange={() => {}}
|
|
2511
|
-
/>,
|
|
2512
|
-
);
|
|
2513
|
-
const field = container.querySelector('.astryx-typeahead') as HTMLElement;
|
|
2514
|
-
expect(getComputedStyle(field).flexWrap).not.toBe('wrap');
|
|
2515
|
-
});
|
|
2516
|
-
|
|
2517
|
-
it('keeps the input in flow while a token shows, so the field keeps its width', () => {
|
|
2518
|
-
// A field's width must not depend on its value. Every other field in the
|
|
2519
|
-
// family gets that for free: the `<input>` stays in flow and the field is
|
|
2520
|
-
// as wide as the input's own intrinsic width. This one used to take the
|
|
2521
|
-
// input out of flow and zero its width when a token showed, leaving the
|
|
2522
|
-
// field measuring the token — in a shrink-to-fit parent it snapped to the
|
|
2523
|
-
// value's length (199px to 57px in Chromium, #5560). Block parents hid it,
|
|
2524
|
-
// which is why no story caught it. jsdom resolves no layout, so assert the
|
|
2525
|
-
// mechanism: the input still occupies the row.
|
|
2526
|
-
render(
|
|
2527
|
-
<Typeahead
|
|
2528
|
-
label="Fruit"
|
|
2529
|
-
searchSource={fruitSource}
|
|
2530
|
-
value={fruits[0]}
|
|
2531
|
-
onChange={() => {}}
|
|
2532
|
-
/>,
|
|
2533
|
-
);
|
|
2534
|
-
const input = screen.getByRole('combobox');
|
|
2535
|
-
const style = getComputedStyle(input);
|
|
2536
|
-
expect(style.position).not.toBe('absolute');
|
|
2537
|
-
expect(style.width).not.toBe('0px');
|
|
2538
|
-
expect(style.flex).not.toBe('0 0 0');
|
|
2539
|
-
});
|
|
2540
|
-
|
|
2541
|
-
it('paints the token over the input rather than beside it', () => {
|
|
2542
|
-
// In flow the token would add its own width to the row — the same
|
|
2543
|
-
// value-dependent sizing from the other direction, where a long value
|
|
2544
|
-
// grows the field instead of collapsing it.
|
|
2545
|
-
const {container} = render(
|
|
2546
|
-
<Typeahead
|
|
2547
|
-
label="Fruit"
|
|
2548
|
-
searchSource={fruitSource}
|
|
2549
|
-
value={fruits[0]}
|
|
2550
|
-
onChange={() => {}}
|
|
2551
|
-
/>,
|
|
2552
|
-
);
|
|
2553
|
-
const token = container.querySelector('.astryx-token') as HTMLElement;
|
|
2554
|
-
expect(getComputedStyle(token).position).toBe('absolute');
|
|
2555
|
-
});
|
|
2556
|
-
|
|
2557
|
-
it('lets the token take the pointer that the hidden input would swallow', () => {
|
|
2558
|
-
// The input still covers that space, so it has to stop intercepting the
|
|
2559
|
-
// clicks that enter edit mode.
|
|
2560
|
-
render(
|
|
2561
|
-
<Typeahead
|
|
2562
|
-
label="Fruit"
|
|
2563
|
-
searchSource={fruitSource}
|
|
2564
|
-
value={fruits[0]}
|
|
2565
|
-
onChange={() => {}}
|
|
2566
|
-
/>,
|
|
2567
|
-
);
|
|
2568
|
-
expect(getComputedStyle(screen.getByRole('combobox')).pointerEvents).toBe(
|
|
2569
|
-
'none',
|
|
2570
|
-
);
|
|
2571
|
-
});
|
|
2572
|
-
});
|
|
2573
|
-
|
|
2574
|
-
describe('the value is bounded by the content lane', () => {
|
|
2575
|
-
// Keeping the input in flow fixed the field's width, but it left the token
|
|
2576
|
-
// positioned against the whole field, which has no idea where the end
|
|
2577
|
-
// controls start. A value longer than the input then ran under the clear
|
|
2578
|
-
// button and out past the field's own border — measured in Chromium at
|
|
2579
|
-
// 28-33px of overlap and up to 4px outside the border, worse than the 12px
|
|
2580
|
-
// of overlap on main. The lane is the box the value may occupy: an ordinary
|
|
2581
|
-
// flex item that ends exactly where the end lane begins.
|
|
2582
|
-
//
|
|
2583
|
-
// jsdom resolves no layout, so what is asserted here is the mechanism. The
|
|
2584
|
-
// geometry is browser-verified in the PR.
|
|
2585
|
-
const renderWithValue = () =>
|
|
2586
|
-
render(
|
|
2587
|
-
<Typeahead
|
|
2588
|
-
label="Fruit"
|
|
2589
|
-
searchSource={fruitSource}
|
|
2590
|
-
value={fruits[0]}
|
|
2591
|
-
onChange={() => {}}
|
|
2592
|
-
hasClear
|
|
2593
|
-
/>,
|
|
2594
|
-
);
|
|
2595
|
-
|
|
2596
|
-
it('puts the input and the token in one lane, inside the field', () => {
|
|
2597
|
-
const {container} = renderWithValue();
|
|
2598
|
-
const field = container.querySelector('.astryx-typeahead');
|
|
2599
|
-
const input = screen.getByRole('combobox');
|
|
2600
|
-
const token = container.querySelector('.astryx-token') as HTMLElement;
|
|
2601
|
-
|
|
2602
|
-
const lane = input.parentElement as HTMLElement;
|
|
2603
|
-
expect(lane).not.toBe(field);
|
|
2604
|
-
expect(lane.parentElement).toBe(field);
|
|
2605
|
-
// The token's containing block is the lane, which is what bounds it.
|
|
2606
|
-
expect(token.parentElement).toBe(lane);
|
|
2607
|
-
expect(getComputedStyle(lane).position).toBe('relative');
|
|
2608
|
-
});
|
|
2609
|
-
|
|
2610
|
-
it('lets the lane yield its whole width, so a narrow field cannot overflow', () => {
|
|
2611
|
-
// `min-width: 0` is the half of this that the earlier `200px` floor got
|
|
2612
|
-
// wrong: the field states no width of its own, so it still shrinks to
|
|
2613
|
-
// whatever a narrow parent or an InputGroup gives it.
|
|
2614
|
-
const {container} = renderWithValue();
|
|
2615
|
-
const lane = screen.getByRole('combobox').parentElement as HTMLElement;
|
|
2616
|
-
const style = getComputedStyle(lane);
|
|
2617
|
-
expect(style.minWidth).toBe('0');
|
|
2618
|
-
expect(style.flexGrow).toBe('1');
|
|
2619
|
-
// Nothing states a width: a narrow parent gets all of it back.
|
|
2620
|
-
expect(
|
|
2621
|
-
(container.querySelector('.astryx-typeahead') as HTMLElement).style
|
|
2622
|
-
.minWidth,
|
|
2623
|
-
).toBe('');
|
|
2624
|
-
});
|
|
2625
|
-
|
|
2626
|
-
it("anchors the token at the lane's end, not just its start", () => {
|
|
2627
|
-
// The bound that stops the value reaching the end controls. Anchored at
|
|
2628
|
-
// one end only, the token is capped by the field's own padding box, which
|
|
2629
|
-
// is past the clear button.
|
|
2630
|
-
const {container} = renderWithValue();
|
|
2631
|
-
const token = container.querySelector('.astryx-token') as HTMLElement;
|
|
2632
|
-
const style = getComputedStyle(token);
|
|
2633
|
-
expect(style.insetInlineEnd).toBe('0');
|
|
2634
|
-
// `fit-content` against that pair of insets is what shrink-wraps the
|
|
2635
|
-
// label yet still caps it at the lane; the `auto` end margin is what
|
|
2636
|
-
// keeps the pair from being over-constrained and dropping the end inset.
|
|
2637
|
-
expect(style.width).toBe('fit-content');
|
|
2638
|
-
expect(style.marginInlineEnd).toBe('auto');
|
|
2639
|
-
});
|
|
2640
|
-
});
|
|
2641
|
-
|
|
2642
|
-
// `emptySearchResultsText` was renamed to `emptySearchText` and widened from
|
|
2643
|
-
// `string` to `ReactNode` (`spec:AST-056` FR1). The overlap ended in a minor
|
|
2644
|
-
// release (`CLN-0001`–`CLN-0004`, `spec:AST-017` FR31): the old name is no
|
|
2645
|
-
// longer part of the type and is no longer read.
|
|
2646
|
-
describe('emptySearchText rename', () => {
|
|
2647
|
-
function searchForNothing() {
|
|
2648
|
-
fireEvent.change(screen.getByRole('combobox'), {
|
|
2649
|
-
target: {value: 'zzzzz'},
|
|
2650
|
-
});
|
|
2651
|
-
}
|
|
2652
|
-
|
|
2653
|
-
describe('Typeahead', () => {
|
|
2654
|
-
it('renders an element, which the old string type could not express', async () => {
|
|
2655
|
-
render(
|
|
2656
|
-
<Typeahead
|
|
2657
|
-
label="Fruit"
|
|
2658
|
-
searchSource={fruitSource}
|
|
2659
|
-
value={null}
|
|
2660
|
-
onChange={() => {}}
|
|
2661
|
-
debounceMs={0}
|
|
2662
|
-
emptySearchText={
|
|
2663
|
-
<span>
|
|
2664
|
-
Nothing ripe. <a href="/add">Add a fruit</a>
|
|
2665
|
-
</span>
|
|
2666
|
-
}
|
|
2667
|
-
/>,
|
|
2668
|
-
);
|
|
2669
|
-
searchForNothing();
|
|
2670
|
-
|
|
2671
|
-
await waitFor(() => {
|
|
2672
|
-
expect(screen.getByText('Add a fruit')).toBeInTheDocument();
|
|
2673
|
-
});
|
|
2674
|
-
});
|
|
2675
|
-
|
|
2676
|
-
it('no longer reads the removed emptySearchResultsText (CLN-0002)', async () => {
|
|
2677
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
2678
|
-
// The unknown prop reaches the root element, which React reports.
|
|
2679
|
-
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
2680
|
-
render(
|
|
2681
|
-
<Typeahead
|
|
2682
|
-
label="Fruit"
|
|
2683
|
-
searchSource={fruitSource}
|
|
2684
|
-
value={null}
|
|
2685
|
-
onChange={() => {}}
|
|
2686
|
-
debounceMs={0}
|
|
2687
|
-
// @ts-expect-error -- removed in the minor release (CLN-0002)
|
|
2688
|
-
emptySearchResultsText="Nothing ripe"
|
|
2689
|
-
/>,
|
|
2690
|
-
);
|
|
2691
|
-
searchForNothing();
|
|
2692
|
-
|
|
2693
|
-
await waitFor(() => {
|
|
2694
|
-
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
2695
|
-
});
|
|
2696
|
-
expect(screen.queryByText('Nothing ripe')).not.toBeInTheDocument();
|
|
2697
|
-
expect(warn).not.toHaveBeenCalledWith(
|
|
2698
|
-
expect.stringContaining('is deprecated'),
|
|
2699
|
-
);
|
|
2700
|
-
warn.mockRestore();
|
|
2701
|
-
error.mockRestore();
|
|
2702
|
-
});
|
|
2703
|
-
});
|
|
2704
|
-
|
|
2705
|
-
describe('BaseTypeahead', () => {
|
|
2706
|
-
it('announces the text of an element, not the catalog default', async () => {
|
|
2707
|
-
render(
|
|
2708
|
-
<BaseTypeahead
|
|
2709
|
-
searchSource={fruitSource}
|
|
2710
|
-
value={null}
|
|
2711
|
-
onChange={() => {}}
|
|
2712
|
-
debounceMs={0}
|
|
2713
|
-
emptySearchText={
|
|
2714
|
-
<span>
|
|
2715
|
-
Nothing ripe. <a href="/add">Add a fruit</a>
|
|
2716
|
-
</span>
|
|
2717
|
-
}
|
|
2718
|
-
/>,
|
|
2719
|
-
);
|
|
2720
|
-
searchForNothing();
|
|
2721
|
-
|
|
2722
|
-
// Widening to ReactNode without this would hand the live region an
|
|
2723
|
-
// object: the AR1 defect, spread to a fourth component.
|
|
2724
|
-
await waitFor(() => {
|
|
2725
|
-
expect(
|
|
2726
|
-
document.querySelector('[data-astryx-live-region="polite"]'),
|
|
2727
|
-
).toHaveTextContent('Nothing ripe. Add a fruit');
|
|
2728
|
-
});
|
|
2729
|
-
});
|
|
2730
|
-
|
|
2731
|
-
it('no longer announces the removed emptySearchResultsText (CLN-0003)', async () => {
|
|
2732
|
-
// The unknown prop reaches the root element, which React reports.
|
|
2733
|
-
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
2734
|
-
render(
|
|
2735
|
-
<BaseTypeahead
|
|
2736
|
-
searchSource={fruitSource}
|
|
2737
|
-
value={null}
|
|
2738
|
-
onChange={() => {}}
|
|
2739
|
-
debounceMs={0}
|
|
2740
|
-
// @ts-expect-error -- removed in the minor release (CLN-0003)
|
|
2741
|
-
emptySearchResultsText="Nothing ripe"
|
|
2742
|
-
/>,
|
|
2743
|
-
);
|
|
2744
|
-
searchForNothing();
|
|
2745
|
-
|
|
2746
|
-
await waitFor(() => {
|
|
2747
|
-
expect(
|
|
2748
|
-
document.querySelector('[data-astryx-live-region="polite"]'),
|
|
2749
|
-
).toHaveTextContent('No results found');
|
|
2750
|
-
});
|
|
2751
|
-
expect(
|
|
2752
|
-
document.querySelector('[data-astryx-live-region="polite"]')
|
|
2753
|
-
?.textContent,
|
|
2754
|
-
).not.toContain('Nothing ripe');
|
|
2755
|
-
error.mockRestore();
|
|
2756
|
-
});
|
|
2757
|
-
});
|
|
2758
|
-
});
|
|
2759
|
-
|
|
2760
|
-
describe('Typeahead consumer key handler order', () => {
|
|
2761
|
-
it('runs onKeyDown on the input after built-in key handling, so it cannot cancel navigation', async () => {
|
|
2762
|
-
let currentTarget: EventTarget | null = null;
|
|
2763
|
-
const onKeyDown = vi.fn((event: React.KeyboardEvent) => {
|
|
2764
|
-
currentTarget = event.currentTarget;
|
|
2765
|
-
event.preventDefault();
|
|
2766
|
-
});
|
|
2767
|
-
render(
|
|
2768
|
-
<Typeahead
|
|
2769
|
-
label="Fruit"
|
|
2770
|
-
searchSource={fruitSource}
|
|
2771
|
-
value={null}
|
|
2772
|
-
onChange={() => {}}
|
|
2773
|
-
debounceMs={0}
|
|
2774
|
-
hasEntriesOnFocus
|
|
2775
|
-
onKeyDown={onKeyDown}
|
|
2776
|
-
/>,
|
|
2777
|
-
);
|
|
2778
|
-
const input = screen.getByRole('combobox');
|
|
2779
|
-
fireEvent.focus(input);
|
|
2780
|
-
await waitFor(() => {
|
|
2781
|
-
expect(
|
|
2782
|
-
screen.getAllByRole('option', {hidden: true}).length,
|
|
2783
|
-
).toBeGreaterThan(0);
|
|
2784
|
-
});
|
|
2785
|
-
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
2786
|
-
expect(onKeyDown).toHaveBeenCalledOnce();
|
|
2787
|
-
expect(currentTarget).toBe(screen.getByRole('combobox'));
|
|
2788
|
-
expect(input).toHaveAttribute('aria-activedescendant');
|
|
2789
|
-
});
|
|
2790
|
-
});
|
|
2791
|
-
|
|
2792
|
-
describe('Typeahead consumer key handler target', () => {
|
|
2793
|
-
it('still runs onKeyDown for keys on the clear button', () => {
|
|
2794
|
-
const onKeyDown = vi.fn();
|
|
2795
|
-
render(
|
|
2796
|
-
<Typeahead
|
|
2797
|
-
label="Fruit"
|
|
2798
|
-
searchSource={fruitSource}
|
|
2799
|
-
value={fruits[0]}
|
|
2800
|
-
onChange={() => {}}
|
|
2801
|
-
onKeyDown={onKeyDown}
|
|
2802
|
-
/>,
|
|
2803
|
-
);
|
|
2804
|
-
fireEvent.keyDown(screen.getByRole('button', {name: /clear/i}), {
|
|
2805
|
-
key: 'Enter',
|
|
2806
|
-
});
|
|
2807
|
-
expect(onKeyDown).toHaveBeenCalledOnce();
|
|
2808
|
-
});
|
|
2809
|
-
|
|
2810
|
-
it('runs onKeyDown once for a key on the input', () => {
|
|
2811
|
-
const onKeyDown = vi.fn();
|
|
2812
|
-
render(
|
|
2813
|
-
<Typeahead
|
|
2814
|
-
label="Fruit"
|
|
2815
|
-
searchSource={fruitSource}
|
|
2816
|
-
value={null}
|
|
2817
|
-
onChange={() => {}}
|
|
2818
|
-
onKeyDown={onKeyDown}
|
|
2819
|
-
/>,
|
|
2820
|
-
);
|
|
2821
|
-
fireEvent.keyDown(screen.getByRole('combobox'), {key: 'a'});
|
|
2822
|
-
expect(onKeyDown).toHaveBeenCalledOnce();
|
|
2823
|
-
});
|
|
2824
|
-
});
|