@astryxdesign/core 0.7.5 → 0.7.6-canary.0ef60dd
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/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 +7 -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/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/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 +101 -15
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +8 -3
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +1 -1
- 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 +7 -3
- 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 +4 -4
- package/dist/Field/InputClearButton.js +3 -3
- package/dist/Field/PanelSearchInput.js +2 -2
- package/dist/FieldStatus/FieldStatus.js +2 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +7 -1
- 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 +2 -1
- 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 +93 -59
- 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/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -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/CreateRow.a11y.chromium.spec.js +7 -6
- package/dist/MultiSelector/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +26 -10
- package/dist/NumberInput/NumberInput.d.ts +3 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +3 -3
- 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/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +16 -12
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +28 -2
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
- 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/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 +10 -2
- 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/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +16 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
- 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 +82 -51
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +13 -2
- package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
- 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/Typeahead/Typeahead.d.ts +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +9 -1
- package/dist/astryx.css +20 -4
- package/dist/gestures/GestureArena.d.ts +6 -0
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +39 -0
- package/dist/gestures/GestureHost.d.ts +352 -0
- package/dist/gestures/GestureHost.d.ts.map +1 -0
- package/dist/gestures/GestureHost.js +1773 -0
- package/dist/gestures/GestureRecognizer.d.ts +10 -0
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +16 -0
- 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/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/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/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/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/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +5 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +5 -0
- package/dist/motion/animator.d.ts +7 -1
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +119 -27
- 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/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/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.map +1 -1
- package/dist/utils/touchPresentation.js +5 -3
- 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/unicodeDigits.d.ts +20 -0
- package/dist/utils/unicodeDigits.d.ts.map +1 -0
- package/dist/utils/unicodeDigits.js +70 -0
- package/locales/en.json +23 -3
- package/locales/pseudo.json +15 -0
- package/package.json +5 -4
- package/src/AlertDialog/AlertDialog.tsx +6 -5
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
- package/src/Avatar/Avatar.spec.md +22 -22
- package/src/Avatar/Avatar.tsx +18 -1
- package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
- package/src/Badge/Badge.doc.mjs +1 -1
- package/src/Badge/Badge.tsx +19 -2
- package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
- package/src/BottomSheet/BottomSheet.test.tsx +46 -0
- package/src/BottomSheet/BottomSheet.tsx +1 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
- package/src/BottomSheet/useSheetGestures.test.ts +52 -0
- package/src/BottomSheet/useSheetGestures.ts +37 -6
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
- package/src/Button/Button.tsx +4 -4
- package/src/Calendar/Calendar.spec.md +87 -48
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
- package/src/CheckboxInput/CheckboxInput.tsx +6 -5
- package/src/ClickableCard/ClickableCard.test.tsx +96 -0
- 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/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
- package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
- package/src/ContextMenu/ContextMenu.test.tsx +35 -0
- package/src/DateInput/DateInput.test.tsx +115 -3
- package/src/DateInput/DateInput.tsx +29 -14
- package/src/DateInput/DateInputTouch.test.tsx +76 -0
- package/src/DateInput/MonthScroller.tsx +106 -20
- package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
- package/src/DateInput/TouchDateField.tsx +14 -3
- package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
- package/src/DateRangeInput/DateRangeInput.tsx +16 -12
- package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
- package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
- package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
- 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.test.tsx +216 -2
- package/src/Dialog/Dialog.tsx +42 -9
- package/src/Dialog/DialogHeader.tsx +5 -4
- package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
- package/src/Divider/Divider.tsx +2 -1
- package/src/Drawer/Drawer.spec.md +111 -80
- package/src/Drawer/Drawer.test.tsx +35 -0
- package/src/Drawer/Drawer.tsx +13 -2
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
- package/src/DropdownMenu/DropdownMenu.tsx +10 -3
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
- package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
- package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
- package/src/EmptyState/EmptyState.tsx +2 -1
- package/src/Field/FieldLabel.tsx +5 -4
- package/src/Field/InputClearButton.tsx +3 -3
- package/src/Field/PanelSearchInput.tsx +2 -2
- package/src/FieldStatus/FieldStatus.tsx +2 -2
- package/src/FileInput/FileInput.test.tsx +21 -0
- package/src/FileInput/FileInput.tsx +4 -1
- package/src/FormLayout/FormLayout.spec.md +7 -8
- 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 +2 -1
- package/src/Item/ItemSwipeLayer.tsx +1 -1
- package/src/Kbd/Kbd.spec.md +81 -38
- package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
- package/src/Layer/popoverOperations.test.ts +205 -0
- package/src/Layer/popoverOperations.ts +172 -0
- package/src/Layer/useLayer.test.tsx +348 -1
- package/src/Layer/useLayer.tsx +118 -71
- 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/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
- package/src/Lightbox/Lightbox.test.tsx +18 -0
- package/src/Lightbox/Lightbox.tsx +5 -0
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
- 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.test.tsx +15 -0
- package/src/MobileNav/MobileNav.tsx +9 -1
- package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
- package/src/MoreMenu/MoreMenu.test.tsx +11 -0
- package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
- package/src/MultiSelector/MultiSelector.spec.md +7 -4
- package/src/MultiSelector/MultiSelector.test.tsx +270 -1
- package/src/MultiSelector/MultiSelector.tsx +25 -10
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
- package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
- package/src/NumberInput/NumberInput.doc.mjs +13 -7
- package/src/NumberInput/NumberInput.spec.md +59 -37
- package/src/NumberInput/NumberInput.tsx +13 -3
- package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
- package/src/NumberInput/numberParser.ts +7 -45
- package/src/ProgressBar/ProgressBar.test.tsx +108 -32
- package/src/ProgressBar/ProgressBar.tsx +58 -10
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
- package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
- package/src/Resizable/useResizable.test.ts +43 -0
- package/src/Resizable/useResizable.ts +32 -9
- package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
- package/src/Selector/Selector.test.tsx +90 -1
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
- package/src/Selector/useSelectorPresentation.ts +31 -3
- package/src/SideNav/SideNav.test.tsx +27 -0
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
- package/src/Skeleton/Skeleton.tsx +3 -1
- package/src/Slider/Slider.test.tsx +31 -0
- package/src/Slider/Slider.tsx +8 -3
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
- package/src/Spinner/Spinner.tsx +5 -3
- package/src/Stack/Stack.tsx +1 -1
- package/src/Stack/StackItem.tsx +1 -1
- package/src/TabList/TabList.test.tsx +58 -4
- package/src/TabList/TabList.tsx +16 -1
- package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
- package/src/Text/Text.tsx +1 -1
- package/src/Text/useTruncation.test.tsx +128 -1
- package/src/Text/useTruncation.ts +23 -5
- package/src/TextArea/TextArea.spec.md +10 -13
- package/src/TextArea/TextArea.test.tsx +60 -0
- package/src/TextArea/TextArea.tsx +15 -1
- package/src/TextInput/TextInput.test.tsx +21 -0
- package/src/TextInput/TextInput.tsx +3 -1
- package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
- package/src/TimeInput/Presentation.test.tsx +21 -0
- package/src/TimeInput/TimeInput.doc.mjs +8 -8
- package/src/TimeInput/TimeInput.spec.md +45 -48
- package/src/TimeInput/TimeInput.test.tsx +76 -0
- 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.test.tsx +30 -5
- package/src/Timestamp/Timestamp.tsx +6 -2
- package/src/Timestamp/TimestampHoverCard.tsx +4 -4
- package/src/Toast/Toast.spec.md +14 -20
- package/src/Toast/Toast.tsx +104 -36
- package/src/Toast/ToastViewport.test.tsx +719 -1
- package/src/Toast/ToastViewport.tsx +100 -50
- package/src/Tokenizer/Tokenizer.spec.md +5 -3
- package/src/Tokenizer/Tokenizer.test.tsx +68 -0
- package/src/Tokenizer/Tokenizer.tsx +14 -2
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
- package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
- package/src/Toolbar/Toolbar.spec.md +5 -6
- package/src/Toolbar/Toolbar.test.tsx +30 -0
- package/src/Toolbar/Toolbar.tsx +2 -1
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
- package/src/TopNav/TopNav.test.tsx +27 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
- package/src/TopNav/TopNavMegaMenu.tsx +4 -1
- package/src/TopNav/TopNavMenu.test.tsx +17 -0
- package/src/TopNav/TopNavMenu.tsx +1 -0
- package/src/Typeahead/Typeahead.test.tsx +40 -0
- package/src/Typeahead/Typeahead.tsx +12 -1
- package/src/__tests__/pressState.ts +8 -4
- package/src/gestures/GestureArena.ts +35 -0
- package/src/gestures/GestureHost.ts +2065 -0
- package/src/gestures/GestureRecognizer.ts +18 -0
- package/src/gestures/LongPressGestureRecognizer.ts +4 -0
- package/src/gestures/PanGestureRecognizer.ts +4 -0
- package/src/gestures/PinchGestureRecognizer.ts +148 -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/__tests__/GestureHost.test.ts +2087 -0
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
- package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
- package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
- package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
- package/src/gestures/__tests__/arena.test.ts +5 -0
- package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
- package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
- package/src/gestures/__tests__/browser/fixture.ts +307 -0
- package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
- package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
- package/src/gestures/__tests__/haptics.test.ts +187 -0
- package/src/gestures/__tests__/harness.ts +21 -2
- package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
- package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
- package/src/gestures/haptics.ts +203 -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/hooks/index.ts +5 -1
- package/src/hooks/modalAnnounce.ts +129 -72
- package/src/hooks/useAdaptivePresentation.ts +4 -1
- package/src/hooks/useAnnounce.doc.mjs +35 -3
- package/src/hooks/useAnnounce.replace.test.tsx +516 -0
- package/src/hooks/useAnnounce.ts +209 -102
- package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
- package/src/hooks/useAnnounceRenderedText.ts +17 -3
- package/src/hooks/useBusyAnnouncement.ts +7 -10
- package/src/hooks/useMenuHover.ts +15 -1
- package/src/i18n/generated-locales/en.generated.ts +6 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +6 -0
- package/src/motion/README.md +11 -1
- package/src/motion/animator.test.ts +151 -3
- package/src/motion/animator.ts +149 -33
- package/src/theme/Theme.doc.mjs +1 -1
- package/src/theme/defineTheme.ts +117 -8
- package/src/theme/derivedVarRegistry.test.ts +4 -0
- package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
- package/src/theme/domainTokens/index.ts +6 -10
- package/src/theme/expandMotionScale.test.ts +1 -1
- 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.test.ts +620 -0
- 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/focusReturn.ts +17 -6
- package/src/utils/getNodeText.test.ts +24 -0
- package/src/utils/getNodeText.ts +37 -0
- package/src/utils/interactionModality.test.ts +45 -1
- package/src/utils/interactionModality.ts +64 -5
- package/src/utils/interactionOverlay.stylex.ts +12 -3
- package/src/utils/plainDate.test.ts +90 -0
- package/src/utils/plainDate.ts +58 -3
- package/src/utils/presentation.test.ts +380 -0
- package/src/utils/presentation.ts +180 -0
- package/src/utils/pressFeedback.test.ts +138 -0
- package/src/utils/pressFeedback.ts +34 -11
- package/src/utils/pressPaintPriority.test.ts +80 -0
- package/src/utils/primaryClick.test.ts +38 -0
- package/src/utils/primaryClick.ts +28 -0
- package/src/utils/timeFormat.test.ts +204 -0
- package/src/utils/timeFormat.ts +314 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.test.ts +140 -1
- package/src/utils/touchPresentation.ts +5 -3
- package/src/utils/unicodeDigits.test.ts +44 -0
- package/src/utils/unicodeDigits.ts +71 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file popoverOperations.ts
|
|
5
|
+
* @input Popover API events (`beforetoggle`, `toggle`) and the layers' own show/hide calls
|
|
6
|
+
* @output The per-document ledger of popover operations in flight, and the deferral of a show that arrives inside one
|
|
7
|
+
* @position Internal to the layer runtime; used by useLayer
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When modified, update:
|
|
10
|
+
* - /packages/core/src/Layer/useLayer.tsx
|
|
11
|
+
* - /packages/core/src/Layer/popoverOperations.test.ts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Popover operations in flight, per document.
|
|
16
|
+
*
|
|
17
|
+
* The Popover API refuses to show a popover while the document is in the
|
|
18
|
+
* middle of showing or hiding ANY popover: `showPopover()` throws an
|
|
19
|
+
* `InvalidStateError` ("Invalid to show a popover during another show
|
|
20
|
+
* operation"; Chromium 150+, where 149 still accepts such a show).
|
|
21
|
+
* Hiding a popover restores focus to the element that had it —
|
|
22
|
+
* synchronously, inside that window — so a focus-driven layer on the element
|
|
23
|
+
* receiving focus (a tooltip on the trigger that just closed a popover) asks
|
|
24
|
+
* to show while the hide is still running.
|
|
25
|
+
*
|
|
26
|
+
* Two kinds of operation are counted:
|
|
27
|
+
*
|
|
28
|
+
* - The layers' own shows and hides pass through `runPopoverOperation`, so
|
|
29
|
+
* their windows are known exactly: a `show()` that arrives inside one is
|
|
30
|
+
* deferred ahead of asking the browser.
|
|
31
|
+
* - A hide the browser runs on its own — an Escape its close watcher answers,
|
|
32
|
+
* a light dismiss, an `auto` popover evicted by another one opening — never
|
|
33
|
+
* passes through here. It is observable from its element's events:
|
|
34
|
+
* `beforetoggle` fires synchronously as the hide starts, and `toggle` in a
|
|
35
|
+
* task once it has ended; `observeBrowserHide` counts it between the two.
|
|
36
|
+
* That window is wider than the engine's, which ends when the hide returns,
|
|
37
|
+
* so a show inside it is asked of the browser first and deferred only when
|
|
38
|
+
* the browser refuses it: a light dismiss at `pointerup` followed by a show
|
|
39
|
+
* at `click` in the same gesture still opens at once.
|
|
40
|
+
*
|
|
41
|
+
* A deferred show waits in `afterPopoverOperations` and replays, one
|
|
42
|
+
* microtask each, once the document's last operation of either kind has
|
|
43
|
+
* ended and the script that started it has unwound.
|
|
44
|
+
*/
|
|
45
|
+
const ownOperationsByDocument = new WeakMap<Document, number>();
|
|
46
|
+
const browserHidesByDocument = new WeakMap<Document, number>();
|
|
47
|
+
const settledCallbacksByDocument = new WeakMap<Document, (() => void)[]>();
|
|
48
|
+
|
|
49
|
+
function count(map: WeakMap<Document, number>, doc: Document, delta: number) {
|
|
50
|
+
map.set(doc, Math.max(0, (map.get(doc) ?? 0) + delta));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function anyInFlight(doc: Document): boolean {
|
|
54
|
+
return (
|
|
55
|
+
(ownOperationsByDocument.get(doc) ?? 0) > 0 ||
|
|
56
|
+
(browserHidesByDocument.get(doc) ?? 0) > 0
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function ended(doc: Document): void {
|
|
61
|
+
if (!anyInFlight(doc) && settledCallbacksByDocument.has(doc)) {
|
|
62
|
+
// The operation's own script is still on the stack (the `finally` of a
|
|
63
|
+
// `runPopoverOperation`, a `toggle` listener): replay once it has unwound.
|
|
64
|
+
queueMicrotask(() => replaySettled(doc));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function replaySettled(doc: Document): void {
|
|
69
|
+
if (anyInFlight(doc)) {
|
|
70
|
+
// Another operation started before the replay ran; its end replays.
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const callbacks = settledCallbacksByDocument.get(doc);
|
|
74
|
+
if (callbacks == null) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
settledCallbacksByDocument.delete(doc);
|
|
78
|
+
// One microtask each: a replay that throws reaches the page on its own and
|
|
79
|
+
// leaves the ones after it to run.
|
|
80
|
+
for (const callback of callbacks) {
|
|
81
|
+
queueMicrotask(callback);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Runs `operation`, counting it as one of the layers' own popover operations in `doc`. */
|
|
86
|
+
export function runPopoverOperation(
|
|
87
|
+
doc: Document,
|
|
88
|
+
operation: () => void,
|
|
89
|
+
): void {
|
|
90
|
+
count(ownOperationsByDocument, doc, 1);
|
|
91
|
+
try {
|
|
92
|
+
operation();
|
|
93
|
+
} finally {
|
|
94
|
+
count(ownOperationsByDocument, doc, -1);
|
|
95
|
+
ended(doc);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Whether one of the layers' own shows or hides is running in `doc`: a show asked now would be refused. */
|
|
100
|
+
export function isPopoverOperationInFlight(doc: Document): boolean {
|
|
101
|
+
return (ownOperationsByDocument.get(doc) ?? 0) > 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Whether a hide the browser runs itself is counted in `doc`. The count
|
|
106
|
+
* outlasts the engine's own window (it ends at the element's `toggle`, a task
|
|
107
|
+
* later), so a show asked inside it may well succeed; one the browser refuses
|
|
108
|
+
* is deferred to the count's end.
|
|
109
|
+
*/
|
|
110
|
+
export function isBrowserHideInFlight(doc: Document): boolean {
|
|
111
|
+
return (browserHidesByDocument.get(doc) ?? 0) > 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Popovers whose browser-run hide is being counted, so one hide is counted once. */
|
|
115
|
+
const browserHidesInFlight = new WeakSet<Element>();
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Counts a hide the browser runs itself. Called as the element's `beforetoggle`
|
|
119
|
+
* fires, which the hide dispatches synchronously as it starts; the count ends
|
|
120
|
+
* at the element's `toggle`, which the browser queues for the task after the
|
|
121
|
+
* hide, and in any case at the next task: the hide is synchronous, so the task
|
|
122
|
+
* after it is past the window even when the element has left the document and
|
|
123
|
+
* its `toggle` never arrives. Calling it again for the same hide is a no-op.
|
|
124
|
+
*/
|
|
125
|
+
export function observeBrowserHide(popover: HTMLElement): void {
|
|
126
|
+
if (browserHidesInFlight.has(popover)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
browserHidesInFlight.add(popover);
|
|
130
|
+
const doc = popover.ownerDocument;
|
|
131
|
+
count(browserHidesByDocument, doc, 1);
|
|
132
|
+
const view = doc.defaultView ?? globalThis;
|
|
133
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
134
|
+
let done = false;
|
|
135
|
+
const finish = () => {
|
|
136
|
+
if (done) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
done = true;
|
|
140
|
+
popover.removeEventListener('toggle', finish);
|
|
141
|
+
if (timer != null) {
|
|
142
|
+
view.clearTimeout(timer);
|
|
143
|
+
timer = null;
|
|
144
|
+
}
|
|
145
|
+
browserHidesInFlight.delete(popover);
|
|
146
|
+
count(browserHidesByDocument, doc, -1);
|
|
147
|
+
ended(doc);
|
|
148
|
+
};
|
|
149
|
+
popover.addEventListener('toggle', finish);
|
|
150
|
+
timer = view.setTimeout(finish, 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Runs `callback` once no popover operation of either kind is in flight in
|
|
155
|
+
* `doc`: at once when none is, otherwise in its own microtask after the last
|
|
156
|
+
* one has ended and the script that started it has unwound.
|
|
157
|
+
*/
|
|
158
|
+
export function afterPopoverOperations(
|
|
159
|
+
doc: Document,
|
|
160
|
+
callback: () => void,
|
|
161
|
+
): void {
|
|
162
|
+
if (!anyInFlight(doc)) {
|
|
163
|
+
callback();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const callbacks = settledCallbacksByDocument.get(doc);
|
|
167
|
+
if (callbacks == null) {
|
|
168
|
+
settledCallbacksByDocument.set(doc, [callback]);
|
|
169
|
+
} else {
|
|
170
|
+
callbacks.push(callback);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
waitFor,
|
|
19
19
|
} from '@testing-library/react';
|
|
20
20
|
import userEvent from '@testing-library/user-event';
|
|
21
|
-
import {Profiler, useEffect} from 'react';
|
|
21
|
+
import {Profiler, useEffect, useState} from 'react';
|
|
22
22
|
import {hydrateRoot} from 'react-dom/client';
|
|
23
23
|
import {renderToString} from 'react-dom/server';
|
|
24
24
|
import * as stylex from '@stylexjs/stylex';
|
|
@@ -1143,6 +1143,353 @@ describe('useLayer', () => {
|
|
|
1143
1143
|
expect(onTooltipShow).toHaveBeenCalledTimes(2);
|
|
1144
1144
|
});
|
|
1145
1145
|
|
|
1146
|
+
/**
|
|
1147
|
+
* The browser's own hide: an Escape its close watcher answers never calls
|
|
1148
|
+
* the hook's hide(). The engine fires `beforetoggle` synchronously as the
|
|
1149
|
+
* hide starts, restores focus inside the hide when focus was inside the
|
|
1150
|
+
* popover, and fires `toggle` in a task once it has ended; `showPopover()`
|
|
1151
|
+
* throws throughout.
|
|
1152
|
+
*/
|
|
1153
|
+
function installBrowserHidePopoverApi() {
|
|
1154
|
+
// The ledger's backstop for a hide whose `toggle` never arrives is the
|
|
1155
|
+
// next task; under fake timers, the test decides when that task runs,
|
|
1156
|
+
// so the `toggle` tasks below end a hide first, as in a browser.
|
|
1157
|
+
vi.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
|
|
1158
|
+
let operationsInFlight = 0;
|
|
1159
|
+
const previouslyFocused = new WeakMap<HTMLElement, Element | null>();
|
|
1160
|
+
const toggleTasks: (() => void)[] = [];
|
|
1161
|
+
const fireToggle = (
|
|
1162
|
+
popover: HTMLElement,
|
|
1163
|
+
type: 'beforetoggle' | 'toggle',
|
|
1164
|
+
newState: 'open' | 'closed',
|
|
1165
|
+
) => {
|
|
1166
|
+
const event = new Event(type, {bubbles: false});
|
|
1167
|
+
Object.defineProperty(event, 'newState', {
|
|
1168
|
+
value: newState,
|
|
1169
|
+
configurable: true,
|
|
1170
|
+
});
|
|
1171
|
+
popover.dispatchEvent(event);
|
|
1172
|
+
};
|
|
1173
|
+
const hide = (popover: HTMLElement) => {
|
|
1174
|
+
operationsInFlight += 1;
|
|
1175
|
+
try {
|
|
1176
|
+
fireToggle(popover, 'beforetoggle', 'closed');
|
|
1177
|
+
delete popover.dataset.open;
|
|
1178
|
+
toggleTasks.push(() => fireToggle(popover, 'toggle', 'closed'));
|
|
1179
|
+
const target = previouslyFocused.get(popover);
|
|
1180
|
+
previouslyFocused.delete(popover);
|
|
1181
|
+
if (
|
|
1182
|
+
target instanceof HTMLElement &&
|
|
1183
|
+
popover.contains(document.activeElement)
|
|
1184
|
+
) {
|
|
1185
|
+
target.focus();
|
|
1186
|
+
}
|
|
1187
|
+
} finally {
|
|
1188
|
+
operationsInFlight -= 1;
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
let refusals = 0;
|
|
1192
|
+
const showSpy = vi.fn(function (this: HTMLElement) {
|
|
1193
|
+
if (operationsInFlight > 0 || refusals > 0) {
|
|
1194
|
+
refusals = Math.max(0, refusals - 1);
|
|
1195
|
+
throw new DOMException(
|
|
1196
|
+
"Failed to execute 'showPopover' on 'HTMLElement': Invalid to show a popover during another show operation",
|
|
1197
|
+
'InvalidStateError',
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
operationsInFlight += 1;
|
|
1201
|
+
try {
|
|
1202
|
+
fireToggle(this, 'beforetoggle', 'open');
|
|
1203
|
+
previouslyFocused.set(this, this.ownerDocument.activeElement);
|
|
1204
|
+
this.dataset.open = 'true';
|
|
1205
|
+
toggleTasks.push(() => fireToggle(this, 'toggle', 'open'));
|
|
1206
|
+
} finally {
|
|
1207
|
+
operationsInFlight -= 1;
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
const hideSpy = vi.fn(function (this: HTMLElement) {
|
|
1211
|
+
hide(this);
|
|
1212
|
+
});
|
|
1213
|
+
HTMLElement.prototype.showPopover = showSpy;
|
|
1214
|
+
HTMLElement.prototype.hidePopover = hideSpy;
|
|
1215
|
+
return {
|
|
1216
|
+
showSpy,
|
|
1217
|
+
hideSpy,
|
|
1218
|
+
/** The browser hides the popover itself: a close watcher's Escape. */
|
|
1219
|
+
browserHide: (popover: HTMLElement) => hide(popover),
|
|
1220
|
+
/** The task after the hide: the queued `toggle` events fire. */
|
|
1221
|
+
runToggleTasks: () => {
|
|
1222
|
+
for (const task of toggleTasks.splice(0)) {
|
|
1223
|
+
task();
|
|
1224
|
+
}
|
|
1225
|
+
},
|
|
1226
|
+
/** The engine refuses the next show too, whatever is in flight. */
|
|
1227
|
+
refuseNextShow: () => {
|
|
1228
|
+
refusals += 1;
|
|
1229
|
+
},
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
afterEach(() => {
|
|
1234
|
+
vi.useRealTimers();
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
it('defers the show a browser-run hide wakes until that hide has ended, and replays it at its toggle', async () => {
|
|
1238
|
+
const {showSpy, browserHide, runToggleTasks} =
|
|
1239
|
+
installBrowserHidePopoverApi();
|
|
1240
|
+
const errors: unknown[] = [];
|
|
1241
|
+
const onError = (event: ErrorEvent) => {
|
|
1242
|
+
errors.push(event.error);
|
|
1243
|
+
event.preventDefault();
|
|
1244
|
+
};
|
|
1245
|
+
window.addEventListener('error', onError);
|
|
1246
|
+
const onTooltipShow = vi.fn();
|
|
1247
|
+
const {getByRole} = render(
|
|
1248
|
+
<FocusReturnHarness onTooltipShow={onTooltipShow} />,
|
|
1249
|
+
);
|
|
1250
|
+
const trigger = getByRole('button', {name: 'Trigger'});
|
|
1251
|
+
const inside = getByRole('button', {name: 'Inside', hidden: true});
|
|
1252
|
+
const popover = getByRole('dialog', {hidden: true});
|
|
1253
|
+
|
|
1254
|
+
act(() => trigger.focus());
|
|
1255
|
+
fireEvent.click(trigger);
|
|
1256
|
+
act(() => inside.focus());
|
|
1257
|
+
runToggleTasks();
|
|
1258
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(1);
|
|
1259
|
+
expect(popover).toHaveAttribute('data-open', 'true');
|
|
1260
|
+
|
|
1261
|
+
// Escape, answered by the browser's close watcher: it hides the popover
|
|
1262
|
+
// on its own and returns focus to the trigger inside that hide; the
|
|
1263
|
+
// tooltip asks to show.
|
|
1264
|
+
act(() => browserHide(popover));
|
|
1265
|
+
window.removeEventListener('error', onError);
|
|
1266
|
+
|
|
1267
|
+
expect(errors).toEqual([]);
|
|
1268
|
+
expect(document.activeElement).toBe(trigger);
|
|
1269
|
+
expect(popover).not.toHaveAttribute('data-open');
|
|
1270
|
+
// The show was asked of the browser inside the hide and refused; the
|
|
1271
|
+
// hook kept the refusal to itself and parked the show. Nothing else is
|
|
1272
|
+
// asked before the hide ends at its `toggle`, not even after the
|
|
1273
|
+
// script unwound.
|
|
1274
|
+
await act(async () => {});
|
|
1275
|
+
const refusals = () =>
|
|
1276
|
+
showSpy.mock.results.filter(r => r.type === 'throw').length;
|
|
1277
|
+
expect(refusals()).toBe(1);
|
|
1278
|
+
expect(getByRole('tooltip', {hidden: true})).not.toHaveAttribute(
|
|
1279
|
+
'data-open',
|
|
1280
|
+
);
|
|
1281
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(1);
|
|
1282
|
+
|
|
1283
|
+
// The task after the hide: the popover's `toggle` fires, the hook
|
|
1284
|
+
// reconciles its state, and the parked show runs exactly once.
|
|
1285
|
+
act(() => runToggleTasks());
|
|
1286
|
+
await act(async () => {});
|
|
1287
|
+
expect(refusals()).toBe(1);
|
|
1288
|
+
expect(getByRole('tooltip', {hidden: true})).toHaveAttribute(
|
|
1289
|
+
'data-open',
|
|
1290
|
+
'true',
|
|
1291
|
+
);
|
|
1292
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(2);
|
|
1293
|
+
expect(popover).not.toHaveAttribute('data-open');
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
it('lets a replay the browser refuses again reach the page, and parks nothing more', async () => {
|
|
1297
|
+
const {showSpy, browserHide, runToggleTasks, refuseNextShow} =
|
|
1298
|
+
installBrowserHidePopoverApi();
|
|
1299
|
+
const onTooltipShow = vi.fn();
|
|
1300
|
+
const {getByRole} = render(
|
|
1301
|
+
<FocusReturnHarness onTooltipShow={onTooltipShow} />,
|
|
1302
|
+
);
|
|
1303
|
+
const trigger = getByRole('button', {name: 'Trigger'});
|
|
1304
|
+
const inside = getByRole('button', {name: 'Inside', hidden: true});
|
|
1305
|
+
const popover = getByRole('dialog', {hidden: true});
|
|
1306
|
+
act(() => trigger.focus());
|
|
1307
|
+
fireEvent.click(trigger);
|
|
1308
|
+
act(() => inside.focus());
|
|
1309
|
+
runToggleTasks();
|
|
1310
|
+
const refusals = () =>
|
|
1311
|
+
showSpy.mock.results.filter(r => r.type === 'throw').length;
|
|
1312
|
+
|
|
1313
|
+
// The hide wakes the tooltip; the browser refuses; the show is parked.
|
|
1314
|
+
act(() => browserHide(popover));
|
|
1315
|
+
expect(refusals()).toBe(1);
|
|
1316
|
+
|
|
1317
|
+
// Hold the microtasks the ledger queues, to run the replay by hand.
|
|
1318
|
+
const held: (() => void)[] = [];
|
|
1319
|
+
const queue = vi
|
|
1320
|
+
.spyOn(globalThis, 'queueMicrotask')
|
|
1321
|
+
.mockImplementation(callback => {
|
|
1322
|
+
held.push(callback);
|
|
1323
|
+
});
|
|
1324
|
+
try {
|
|
1325
|
+
act(() => runToggleTasks());
|
|
1326
|
+
// The hide's end queued the batch; the batch queues the one replay.
|
|
1327
|
+
expect(held).toHaveLength(1);
|
|
1328
|
+
held.shift()?.();
|
|
1329
|
+
expect(held).toHaveLength(1);
|
|
1330
|
+
const replay = held.shift();
|
|
1331
|
+
// The replay runs outside the hide, so a browser that refuses again
|
|
1332
|
+
// throws to the page from the replay's own microtask: nothing catches
|
|
1333
|
+
// it, and nothing is parked again.
|
|
1334
|
+
refuseNextShow();
|
|
1335
|
+
expect(replay).toThrow(
|
|
1336
|
+
expect.objectContaining({name: 'InvalidStateError'}),
|
|
1337
|
+
);
|
|
1338
|
+
expect(held).toHaveLength(0);
|
|
1339
|
+
} finally {
|
|
1340
|
+
queue.mockRestore();
|
|
1341
|
+
}
|
|
1342
|
+
await act(async () => {});
|
|
1343
|
+
expect(refusals()).toBe(2);
|
|
1344
|
+
expect(getByRole('tooltip', {hidden: true})).not.toHaveAttribute(
|
|
1345
|
+
'data-open',
|
|
1346
|
+
);
|
|
1347
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(1);
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1350
|
+
it('opens a layer at once when the press that closed another one opens it after the hide has returned', () => {
|
|
1351
|
+
// A light dismiss runs at pointerup and the click that follows opens the
|
|
1352
|
+
// next layer in the same gesture: the browser accepts that show (its
|
|
1353
|
+
// hide has returned) although the hidden layer's `toggle` has not fired.
|
|
1354
|
+
const {showSpy, browserHide, runToggleTasks} =
|
|
1355
|
+
installBrowserHidePopoverApi();
|
|
1356
|
+
const api: {first?: FixedLayerReturn; second?: FixedLayerReturn} = {};
|
|
1357
|
+
const onSecondShow = vi.fn();
|
|
1358
|
+
function Harness() {
|
|
1359
|
+
api.first = useLayer({mode: 'fixed'});
|
|
1360
|
+
api.second = useLayer({mode: 'fixed', onShow: onSecondShow});
|
|
1361
|
+
return (
|
|
1362
|
+
<>
|
|
1363
|
+
{api.first.render(<span>First</span>, {x: 0, y: 0})}
|
|
1364
|
+
{api.second.render(<span>Second</span>, {x: 0, y: 0})}
|
|
1365
|
+
</>
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
const {getByText} = render(<Harness />);
|
|
1369
|
+
act(() => api.first?.show());
|
|
1370
|
+
const first = getByText('First').closest('[popover]') as HTMLElement;
|
|
1371
|
+
const second = getByText('Second').closest('[popover]') as HTMLElement;
|
|
1372
|
+
|
|
1373
|
+
act(() => {
|
|
1374
|
+
browserHide(first);
|
|
1375
|
+
api.second?.show();
|
|
1376
|
+
});
|
|
1377
|
+
expect(showSpy.mock.results.every(r => r.type === 'return')).toBe(true);
|
|
1378
|
+
expect(second).toHaveAttribute('data-open', 'true');
|
|
1379
|
+
expect(onSecondShow).toHaveBeenCalledTimes(1);
|
|
1380
|
+
expect(api.second?.isOpen).toBe(true);
|
|
1381
|
+
// The first layer's hide ends at its toggle, with nothing left to replay.
|
|
1382
|
+
act(() => runToggleTasks());
|
|
1383
|
+
expect(onSecondShow).toHaveBeenCalledTimes(1);
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
it('keeps counting a browser-run hide across a render it causes (an inline onHide, a state update in the focus handler)', async () => {
|
|
1387
|
+
const {showSpy, browserHide, runToggleTasks} =
|
|
1388
|
+
installBrowserHidePopoverApi();
|
|
1389
|
+
const errors: unknown[] = [];
|
|
1390
|
+
const onError = (event: ErrorEvent) => {
|
|
1391
|
+
errors.push(event.error);
|
|
1392
|
+
event.preventDefault();
|
|
1393
|
+
};
|
|
1394
|
+
window.addEventListener('error', onError);
|
|
1395
|
+
const onTooltipShow = vi.fn();
|
|
1396
|
+
// The host re-renders inside the hide: the focus handler sets state,
|
|
1397
|
+
// and `onHide` is a new function each render, so React rebinds the
|
|
1398
|
+
// layer's `toggle` listener and ref while the hide is still running.
|
|
1399
|
+
function RerenderingHarness() {
|
|
1400
|
+
const [focusCount, setFocusCount] = useState(0);
|
|
1401
|
+
const popover = useLayer({mode: 'context', onHide: () => {}});
|
|
1402
|
+
const tooltip = useLayer({mode: 'context', onShow: onTooltipShow});
|
|
1403
|
+
return (
|
|
1404
|
+
<>
|
|
1405
|
+
<button
|
|
1406
|
+
type="button"
|
|
1407
|
+
ref={el => {
|
|
1408
|
+
popover.ref(el);
|
|
1409
|
+
tooltip.ref(el);
|
|
1410
|
+
}}
|
|
1411
|
+
onFocus={() => {
|
|
1412
|
+
setFocusCount(count => count + 1);
|
|
1413
|
+
tooltip.show();
|
|
1414
|
+
}}
|
|
1415
|
+
onBlur={tooltip.hide}
|
|
1416
|
+
onClick={popover.show}>
|
|
1417
|
+
Trigger {focusCount}
|
|
1418
|
+
</button>
|
|
1419
|
+
{popover.render(<button type="button">Inside</button>, {
|
|
1420
|
+
role: 'dialog',
|
|
1421
|
+
})}
|
|
1422
|
+
{tooltip.render(<span>Tip</span>, {role: 'tooltip'})}
|
|
1423
|
+
</>
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
const {getByRole} = render(<RerenderingHarness />);
|
|
1427
|
+
const trigger = getByRole('button', {name: /Trigger/});
|
|
1428
|
+
const inside = getByRole('button', {name: 'Inside', hidden: true});
|
|
1429
|
+
const popover = getByRole('dialog', {hidden: true});
|
|
1430
|
+
|
|
1431
|
+
act(() => trigger.focus());
|
|
1432
|
+
fireEvent.click(trigger);
|
|
1433
|
+
act(() => inside.focus());
|
|
1434
|
+
runToggleTasks();
|
|
1435
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(1);
|
|
1436
|
+
|
|
1437
|
+
act(() => browserHide(popover));
|
|
1438
|
+
window.removeEventListener('error', onError);
|
|
1439
|
+
expect(errors).toEqual([]);
|
|
1440
|
+
await act(async () => {});
|
|
1441
|
+
const refusals = () =>
|
|
1442
|
+
showSpy.mock.results.filter(r => r.type === 'throw').length;
|
|
1443
|
+
// One refusal, kept by the hook; still inside the hide's window, so
|
|
1444
|
+
// nothing replayed yet, however the host re-rendered meanwhile.
|
|
1445
|
+
expect(refusals()).toBe(1);
|
|
1446
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(1);
|
|
1447
|
+
|
|
1448
|
+
act(() => runToggleTasks());
|
|
1449
|
+
await act(async () => {});
|
|
1450
|
+
expect(refusals()).toBe(1);
|
|
1451
|
+
expect(getByRole('tooltip', {hidden: true})).toHaveAttribute(
|
|
1452
|
+
'data-open',
|
|
1453
|
+
'true',
|
|
1454
|
+
);
|
|
1455
|
+
expect(onTooltipShow).toHaveBeenCalledTimes(2);
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
it('releases a browser-run hide whose element detaches before its toggle, so later shows run', async () => {
|
|
1459
|
+
const {showSpy, browserHide} = installBrowserHidePopoverApi();
|
|
1460
|
+
const api: {popover?: FixedLayerReturn; tooltip?: FixedLayerReturn} = {};
|
|
1461
|
+
function Harness({withPopover}: {withPopover: boolean}) {
|
|
1462
|
+
api.popover = useLayer({mode: 'fixed'});
|
|
1463
|
+
api.tooltip = useLayer({mode: 'fixed'});
|
|
1464
|
+
return (
|
|
1465
|
+
<>
|
|
1466
|
+
{withPopover &&
|
|
1467
|
+
api.popover.render(<span>Popover</span>, {x: 0, y: 0})}
|
|
1468
|
+
{api.tooltip.render(<span>Tip</span>, {x: 0, y: 0})}
|
|
1469
|
+
</>
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
const {getByText, rerender} = render(<Harness withPopover />);
|
|
1473
|
+
act(() => api.popover?.show());
|
|
1474
|
+
const popover = getByText('Popover').closest('[popover]') as HTMLElement;
|
|
1475
|
+
// The browser hides it, and React unmounts it before the `toggle` task.
|
|
1476
|
+
act(() => browserHide(popover));
|
|
1477
|
+
rerender(<Harness withPopover={false} />);
|
|
1478
|
+
expect(popover.isConnected).toBe(false);
|
|
1479
|
+
|
|
1480
|
+
// The hide was synchronous: the task after it is past the window.
|
|
1481
|
+
act(() => {
|
|
1482
|
+
vi.runOnlyPendingTimers();
|
|
1483
|
+
});
|
|
1484
|
+
act(() => api.tooltip?.show());
|
|
1485
|
+
await act(async () => {});
|
|
1486
|
+
expect(showSpy).toHaveBeenCalledTimes(2);
|
|
1487
|
+
expect(getByText('Tip').closest('[popover]')).toHaveAttribute(
|
|
1488
|
+
'data-open',
|
|
1489
|
+
'true',
|
|
1490
|
+
);
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1146
1493
|
it('drops a deferred show that hide() cancels before it replays', async () => {
|
|
1147
1494
|
const api: {popover?: FixedLayerReturn; tooltip?: FixedLayerReturn} = {};
|
|
1148
1495
|
// While the popover hides, the tooltip asks to show and then changes
|