@astryxdesign/core 0.7.5 → 0.7.6-canary.bb18c81
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/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/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +4 -1
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +101 -15
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +4 -1
- 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.map +1 -1
- package/dist/Drawer/Drawer.js +5 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +4 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +10 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +16 -2
- 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/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/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +5 -2
- 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/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/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +7 -1
- package/dist/Spinner/Spinner.js +2 -2
- 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/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +2 -2
- package/dist/Timestamp/TimestampHoverCard.js +4 -4
- 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/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 +14 -1
- package/dist/gestures/PinchGestureRecognizer.d.ts +30 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PinchGestureRecognizer.js +125 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +70 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +220 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts +36 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/SwipeGestureRecognizer.js +145 -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/types.d.ts +11 -7
- package/dist/gestures/types.d.ts.map +1 -1
- package/dist/gestures/types.js +22 -2
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
- package/dist/hooks/useAdaptivePresentation.js +4 -1
- 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 +3 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +3 -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.js +1 -1
- 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/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/presentation.d.ts +50 -0
- package/dist/utils/presentation.d.ts.map +1 -0
- package/dist/utils/presentation.js +125 -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/locales/en.json +13 -1
- package/locales/pseudo.json +9 -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/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/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/ContextMenu/ContextMenu.test.tsx +35 -0
- package/src/DateInput/DateInput.test.tsx +53 -2
- package/src/DateInput/DateInput.tsx +15 -11
- package/src/DateInput/MonthScroller.tsx +106 -20
- package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
- 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.spec.md +71 -48
- package/src/DateTimeInput/DateTimeInput.test.tsx +61 -0
- package/src/DateTimeInput/DateTimeInput.tsx +15 -11
- 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 +22 -0
- package/src/Drawer/Drawer.tsx +5 -0
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
- package/src/DropdownMenu/DropdownMenu.tsx +7 -1
- 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 +53 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +23 -6
- 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/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.tsx +3 -3
- 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/MultiSelector.spec.md +7 -4
- package/src/MultiSelector/MultiSelector.test.tsx +118 -1
- package/src/MultiSelector/MultiSelector.tsx +6 -2
- 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/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/Slider/Slider.test.tsx +12 -0
- package/src/Slider/Slider.tsx +4 -1
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
- package/src/Spinner/Spinner.tsx +2 -2
- package/src/Stack/Stack.tsx +1 -1
- package/src/Stack/StackItem.tsx +1 -1
- package/src/TabList/TabList.test.tsx +52 -1
- 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/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 +7 -10
- package/src/Toast/ToastViewport.test.tsx +575 -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/Typeahead/Typeahead.test.tsx +40 -0
- package/src/Typeahead/Typeahead.tsx +12 -1
- package/src/gestures/PinchGestureRecognizer.ts +144 -0
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +252 -0
- package/src/gestures/SwipeGestureRecognizer.ts +193 -0
- package/src/gestures/VelocityTracker.ts +10 -3
- 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__/arenaPinchSwipeEdge.test.ts +188 -0
- package/src/gestures/__tests__/harness.ts +21 -2
- package/src/gestures/types.ts +23 -9
- package/src/hooks/useAdaptivePresentation.ts +4 -1
- package/src/hooks/useMenuHover.ts +15 -1
- package/src/i18n/generated-locales/en.generated.ts +4 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +4 -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/defineTheme.ts +1 -1
- 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/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/presentation.test.ts +380 -0
- package/src/utils/presentation.ts +180 -0
- package/src/utils/pressPaintPriority.test.ts +80 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.test.ts +140 -1
- package/src/utils/touchPresentation.ts +5 -3
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file ScreenEdgePanGestureRecognizer.test.ts
|
|
5
|
+
* @input Synthetic touches with explicit timestamps through a GestureArena
|
|
6
|
+
* @output ScreenEdgePanGestureRecognizer's state machine (the edge band, RTL edges, inward movement) and its edge-zone touch-action
|
|
7
|
+
* @position Unit tests of packages/core/src/gestures/ScreenEdgePanGestureRecognizer.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {describe, expect, it} from 'vitest';
|
|
11
|
+
import {UIKIT_SCREEN_EDGE_PAN} from '../../motion/uikit/gestures';
|
|
12
|
+
import {ALL_NATIVE_TOUCH} from '../GestureRecognizer';
|
|
13
|
+
import {ScreenEdgePanGestureRecognizer} from '../ScreenEdgePanGestureRecognizer';
|
|
14
|
+
import {createHarness} from './harness';
|
|
15
|
+
|
|
16
|
+
describe('ScreenEdgePanGestureRecognizer', () => {
|
|
17
|
+
it('begins from the leading band and fails elsewhere at touch-down', () => {
|
|
18
|
+
const h = createHarness();
|
|
19
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
20
|
+
h.finger(1)
|
|
21
|
+
.down(10, 300, 0, {recognizers: [edge]})
|
|
22
|
+
.drag(40, 4, 50);
|
|
23
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
24
|
+
expect(edge.activeEdge).toBe('left');
|
|
25
|
+
|
|
26
|
+
const middle = createHarness();
|
|
27
|
+
const miss = new ScreenEdgePanGestureRecognizer({name: 'miss'});
|
|
28
|
+
middle.finger(1).down(60, 300, 0, {recognizers: [miss]});
|
|
29
|
+
expect(middle.log).toEqual(['miss:failed@0']);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("is a finger's only: a mouse or a pen dragging from the band never begins it, even when the options list them (spec:AST-094 FR43)", () => {
|
|
33
|
+
for (const pointerType of ['mouse', 'pen'] as const) {
|
|
34
|
+
const h = createHarness();
|
|
35
|
+
const options = {
|
|
36
|
+
name: 'edge',
|
|
37
|
+
allowedPointerTypes: ['touch', 'mouse', 'pen'],
|
|
38
|
+
} as ConstructorParameters<typeof ScreenEdgePanGestureRecognizer>[0];
|
|
39
|
+
const edge = new ScreenEdgePanGestureRecognizer(options);
|
|
40
|
+
h.finger(1)
|
|
41
|
+
.down(10, 300, 0, {recognizers: [edge], pointerType})
|
|
42
|
+
.drag(40, 4, 50);
|
|
43
|
+
expect(edge.state).toBe('possible');
|
|
44
|
+
expect(h.log).toEqual([]);
|
|
45
|
+
}
|
|
46
|
+
const h = createHarness();
|
|
47
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
48
|
+
h.finger(1)
|
|
49
|
+
.down(10, 300, 0, {recognizers: [edge], pointerType: 'touch'})
|
|
50
|
+
.drag(40, 4, 50);
|
|
51
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('forgets the edge a gesture started from once it ends', () => {
|
|
55
|
+
const h = createHarness();
|
|
56
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
57
|
+
h.finger(1)
|
|
58
|
+
.down(10, 300, 0, {recognizers: [edge]})
|
|
59
|
+
.drag(40, 4, 50);
|
|
60
|
+
expect(edge.activeEdge).toBe('left');
|
|
61
|
+
h.finger(1).up(80);
|
|
62
|
+
expect(edge.activeEdge).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('resolves the leading edge under RTL and requires inward movement', () => {
|
|
66
|
+
const h = createHarness();
|
|
67
|
+
h.environment.rtl = true;
|
|
68
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
69
|
+
h.finger(1)
|
|
70
|
+
.down(385, 300, 0, {recognizers: [edge]})
|
|
71
|
+
.drag(-40, 2, 50);
|
|
72
|
+
expect(edge.activeEdge).toBe('right');
|
|
73
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
74
|
+
|
|
75
|
+
const outward = createHarness();
|
|
76
|
+
const back = new ScreenEdgePanGestureRecognizer({name: 'back'});
|
|
77
|
+
outward
|
|
78
|
+
.finger(1)
|
|
79
|
+
.down(10, 300, 0, {recognizers: [back]})
|
|
80
|
+
.drag(-8, 30, 50);
|
|
81
|
+
expect(back.state).toBe('failed');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('a drag 5 pt inward and then down the edge is the page\u2019s scroll: it fails and claims nothing (spec:AST-094 FR19)', () => {
|
|
85
|
+
const h = createHarness();
|
|
86
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
87
|
+
h.finger(1)
|
|
88
|
+
.down(10, 300, 0, {recognizers: [edge]})
|
|
89
|
+
.drag(5, 0, 30)
|
|
90
|
+
.drag(0, 20, 50);
|
|
91
|
+
expect(edge.state).toBe('failed');
|
|
92
|
+
expect(h.claims).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('leaves a view wider than the edge zone every touch behavior, so its scrolling is untouched (spec:AST-094 FR18)', () => {
|
|
96
|
+
expect(new ScreenEdgePanGestureRecognizer().nativeTouchBehavior).toEqual(
|
|
97
|
+
ALL_NATIVE_TOUCH,
|
|
98
|
+
);
|
|
99
|
+
const page = new ScreenEdgePanGestureRecognizer();
|
|
100
|
+
page.view = viewOfSize(390, 844);
|
|
101
|
+
expect(page.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
102
|
+
// A pan from several edges too, off their zones.
|
|
103
|
+
expect(
|
|
104
|
+
new ScreenEdgePanGestureRecognizer({edges: ['leading', 'trailing']})
|
|
105
|
+
.nativeTouchBehavior,
|
|
106
|
+
).toEqual(ALL_NATIVE_TOUCH);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('takes the axis across its edge in the edge zone, a view no wider than the edge region (spec:AST-094 FR18)', () => {
|
|
110
|
+
const leading = new ScreenEdgePanGestureRecognizer();
|
|
111
|
+
leading.view = viewOfSize(leading.edgeRegion, 844);
|
|
112
|
+
expect(leading.nativeTouchBehavior).toEqual({
|
|
113
|
+
panX: false,
|
|
114
|
+
panY: true,
|
|
115
|
+
pinchZoom: true,
|
|
116
|
+
doubleTapZoom: false,
|
|
117
|
+
});
|
|
118
|
+
// One pixel wider than the band is not the edge zone.
|
|
119
|
+
const wide = new ScreenEdgePanGestureRecognizer();
|
|
120
|
+
wide.view = viewOfSize(wide.edgeRegion + 1, 844);
|
|
121
|
+
expect(wide.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('takes a strip authored at the edge region as the edge zone though layout lays it out at 40.203 px', () => {
|
|
125
|
+
const strip = new ScreenEdgePanGestureRecognizer();
|
|
126
|
+
// 40.2 px snapped to layout's 1/64 px.
|
|
127
|
+
strip.view = viewOfSize(Math.ceil(strip.edgeRegion * 64) / 64, 844);
|
|
128
|
+
expect(strip.nativeTouchBehavior).toEqual({
|
|
129
|
+
panX: false,
|
|
130
|
+
panY: true,
|
|
131
|
+
pinchZoom: true,
|
|
132
|
+
doubleTapZoom: false,
|
|
133
|
+
});
|
|
134
|
+
const h = createHarness();
|
|
135
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
136
|
+
h.environment.view = {width: 40.203, height: 844};
|
|
137
|
+
const hysteresis = UIKIT_SCREEN_EDGE_PAN.hysteresis.value;
|
|
138
|
+
h.finger(1)
|
|
139
|
+
.down(10, 300, 0, {recognizers: [edge]})
|
|
140
|
+
.drag(hysteresis - 1, 0, 30);
|
|
141
|
+
expect(edge.state).toBe('possible');
|
|
142
|
+
h.finger(1).drag(2, 0, 50);
|
|
143
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('in the edge zone, still decides at its hysteresis', () => {
|
|
147
|
+
const h = createHarness();
|
|
148
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
149
|
+
h.environment.view = {width: edge.edgeRegion, height: 844};
|
|
150
|
+
const hysteresis = UIKIT_SCREEN_EDGE_PAN.hysteresis.value;
|
|
151
|
+
h.finger(1)
|
|
152
|
+
.down(10, 300, 0, {recognizers: [edge]})
|
|
153
|
+
.drag(hysteresis - 1, 0, 30);
|
|
154
|
+
expect(edge.state).toBe('possible');
|
|
155
|
+
h.finger(1).drag(2, 0, 50);
|
|
156
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('where an in-band touch decides (spec:AST-094 FR18, DEC-22)', () => {
|
|
161
|
+
it('on an element that reaches beyond the band, at 10 px, before the browser commits to a scroll', () => {
|
|
162
|
+
const h = createHarness();
|
|
163
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
164
|
+
// A stack container: as wide as the screen, the touch 4 px from its edge.
|
|
165
|
+
h.environment.view = {width: 390, height: 844};
|
|
166
|
+
h.finger(1)
|
|
167
|
+
.down(4, 300, 0, {recognizers: [edge]})
|
|
168
|
+
.drag(9, 0, 30, 9);
|
|
169
|
+
expect(edge.state).toBe('possible');
|
|
170
|
+
h.finger(1).drag(1, 0, 10, 1);
|
|
171
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('on an element within the band, at 15 px', () => {
|
|
175
|
+
const h = createHarness();
|
|
176
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
177
|
+
h.environment.view = {width: edge.edgeRegion, height: 844};
|
|
178
|
+
const hysteresis = UIKIT_SCREEN_EDGE_PAN.hysteresis.value;
|
|
179
|
+
h.finger(1)
|
|
180
|
+
.down(4, 300, 0, {recognizers: [edge]})
|
|
181
|
+
.drag(hysteresis - 1, 0, 30, 14);
|
|
182
|
+
expect(edge.state).toBe('possible');
|
|
183
|
+
h.finger(1).drag(1, 0, 10, 1);
|
|
184
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('from several edges, at 10 px on an element beyond their bands', () => {
|
|
188
|
+
const h = createHarness();
|
|
189
|
+
const edge = new ScreenEdgePanGestureRecognizer({
|
|
190
|
+
name: 'edge',
|
|
191
|
+
edges: ['leading', 'trailing'],
|
|
192
|
+
});
|
|
193
|
+
h.environment.view = {width: 390, height: 844};
|
|
194
|
+
h.finger(1)
|
|
195
|
+
.down(4, 300, 0, {recognizers: [edge]})
|
|
196
|
+
.drag(9, 0, 30, 9);
|
|
197
|
+
expect(edge.state).toBe('possible');
|
|
198
|
+
h.finger(1).drag(1, 0, 10, 1);
|
|
199
|
+
expect(h.log[0]).toMatch(/^edge:began@/);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('a pan from both edges (spec:AST-094 FR18)', () => {
|
|
204
|
+
it('outside every edge zone leaves the browser every touch behavior', () => {
|
|
205
|
+
const edge = new ScreenEdgePanGestureRecognizer({
|
|
206
|
+
edges: ['leading', 'trailing'],
|
|
207
|
+
});
|
|
208
|
+
edge.view = viewOfSize(390, 844);
|
|
209
|
+
expect(edge.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('inside either edge zone claims pan-x only', () => {
|
|
213
|
+
const leading = new ScreenEdgePanGestureRecognizer({
|
|
214
|
+
edges: ['leading', 'trailing'],
|
|
215
|
+
});
|
|
216
|
+
leading.view = viewOfSize(leading.edgeRegion, 844);
|
|
217
|
+
const trailing = new ScreenEdgePanGestureRecognizer({
|
|
218
|
+
edges: ['leading', 'trailing'],
|
|
219
|
+
});
|
|
220
|
+
trailing.view = viewAt(window.innerWidth - 20, 20, 844);
|
|
221
|
+
for (const edge of [leading, trailing]) {
|
|
222
|
+
expect(edge.nativeTouchBehavior).toEqual({
|
|
223
|
+
panX: false,
|
|
224
|
+
panY: true,
|
|
225
|
+
pinchZoom: true,
|
|
226
|
+
doubleTapZoom: false,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('the edge zone (spec:AST-094 FR18, FR19, DEC-22)', () => {
|
|
233
|
+
it('is a view that lies within the band at its edge, not merely a thin one', () => {
|
|
234
|
+
const claim = {
|
|
235
|
+
panX: false,
|
|
236
|
+
panY: true,
|
|
237
|
+
pinchZoom: true,
|
|
238
|
+
doubleTapZoom: false,
|
|
239
|
+
};
|
|
240
|
+
// Thin, but in the middle of the screen.
|
|
241
|
+
const middle = new ScreenEdgePanGestureRecognizer();
|
|
242
|
+
middle.view = viewAt(200, 20, 844);
|
|
243
|
+
expect(middle.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
244
|
+
// Thin and at the leading edge.
|
|
245
|
+
const atLeading = new ScreenEdgePanGestureRecognizer();
|
|
246
|
+
atLeading.view = viewAt(0, 20, 844);
|
|
247
|
+
expect(atLeading.nativeTouchBehavior).toEqual(claim);
|
|
248
|
+
// At the trailing edge, for a trailing pan only.
|
|
249
|
+
const trailing = new ScreenEdgePanGestureRecognizer({edges: ['trailing']});
|
|
250
|
+
trailing.view = viewAt(window.innerWidth - 20, 20, 844);
|
|
251
|
+
expect(trailing.nativeTouchBehavior).toEqual(claim);
|
|
252
|
+
const wrongEdge = new ScreenEdgePanGestureRecognizer({edges: ['trailing']});
|
|
253
|
+
wrongEdge.view = viewAt(0, 20, 844);
|
|
254
|
+
expect(wrongEdge.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('is measured once for both touch-action and the touch-down decision, and again on a layout change, which says so', () => {
|
|
258
|
+
const h = createHarness();
|
|
259
|
+
const edge = new ScreenEdgePanGestureRecognizer({name: 'edge'});
|
|
260
|
+
let changes = 0;
|
|
261
|
+
edge.onTouchBehaviorChange = () => {
|
|
262
|
+
changes += 1;
|
|
263
|
+
};
|
|
264
|
+
const hysteresis = UIKIT_SCREEN_EDGE_PAN.hysteresis.value;
|
|
265
|
+
h.environment.view = {left: 0, top: 0, width: edge.edgeRegion, height: 844};
|
|
266
|
+
h.finger(1)
|
|
267
|
+
.down(4, 300, 0, {recognizers: [edge]})
|
|
268
|
+
.drag(hysteresis - 1, 0, 30, hysteresis - 1);
|
|
269
|
+
expect(edge.state).toBe('possible');
|
|
270
|
+
h.finger(1).up(40);
|
|
271
|
+
expect(edge.nativeTouchBehavior.panX).toBe(false);
|
|
272
|
+
// The view grows with no layout change reported: touch-action and the
|
|
273
|
+
// next touch-down still share the answer in force.
|
|
274
|
+
h.environment.view = {left: 0, top: 0, width: 390, height: 844};
|
|
275
|
+
expect(edge.nativeTouchBehavior.panX).toBe(false);
|
|
276
|
+
h.finger(2)
|
|
277
|
+
.down(4, 300, 100, {recognizers: [edge]})
|
|
278
|
+
.drag(hysteresis - 1, 0, 30, hysteresis - 1);
|
|
279
|
+
expect(edge.state).toBe('possible');
|
|
280
|
+
h.finger(2).up(140);
|
|
281
|
+
expect(changes).toBe(0);
|
|
282
|
+
// Reported: measured again, the change is announced, and both follow.
|
|
283
|
+
edge.layoutChanged();
|
|
284
|
+
expect(changes).toBe(1);
|
|
285
|
+
expect(edge.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
286
|
+
h.finger(3)
|
|
287
|
+
.down(4, 300, 200, {recognizers: [edge]})
|
|
288
|
+
.drag(9, 0, 30, 9);
|
|
289
|
+
expect(edge.state).toBe('possible');
|
|
290
|
+
h.finger(3).drag(1, 0, 10, 1);
|
|
291
|
+
expect(edge.state).toBe('began');
|
|
292
|
+
// A layout change that leaves the answer as it was says nothing.
|
|
293
|
+
edge.layoutChanged();
|
|
294
|
+
expect(changes).toBe(1);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe('what the host watches for the edge zone (spec:AST-094 FR19, FR24)', () => {
|
|
299
|
+
it('needs the viewport only for a pan that watches the right side, whose band moves with it', () => {
|
|
300
|
+
const cases: [
|
|
301
|
+
ReadonlyArray<'leading' | 'trailing'>,
|
|
302
|
+
'ltr' | 'rtl',
|
|
303
|
+
boolean,
|
|
304
|
+
][] = [
|
|
305
|
+
[['leading'], 'ltr', false],
|
|
306
|
+
[['trailing'], 'ltr', true],
|
|
307
|
+
[['leading'], 'rtl', true],
|
|
308
|
+
[['trailing'], 'rtl', false],
|
|
309
|
+
[['leading', 'trailing'], 'ltr', true],
|
|
310
|
+
];
|
|
311
|
+
for (const [edges, direction, expected] of cases) {
|
|
312
|
+
const pan = new ScreenEdgePanGestureRecognizer({edges});
|
|
313
|
+
pan.view = viewAt(0, 20, 844, direction);
|
|
314
|
+
expect([edges.join('+'), direction, pan.watchesViewport]).toEqual([
|
|
315
|
+
edges.join('+'),
|
|
316
|
+
direction,
|
|
317
|
+
expected,
|
|
318
|
+
]);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe('a writing direction switched in place (design:bidirectionality DR13)', () => {
|
|
324
|
+
it('measures its edges again at touch-down when the view turns RTL with no resize, and announces the change', () => {
|
|
325
|
+
const width = window.innerWidth;
|
|
326
|
+
const h = createHarness();
|
|
327
|
+
h.environment.width = width;
|
|
328
|
+
h.environment.view = {left: 0, top: 0, width, height: 844};
|
|
329
|
+
const pan = new ScreenEdgePanGestureRecognizer({
|
|
330
|
+
name: 'edge',
|
|
331
|
+
edges: ['leading'],
|
|
332
|
+
});
|
|
333
|
+
const view = viewAt(0, width, 844, 'ltr');
|
|
334
|
+
pan.view = view;
|
|
335
|
+
let announced = 0;
|
|
336
|
+
pan.onTouchBehaviorChange = () => {
|
|
337
|
+
announced += 1;
|
|
338
|
+
};
|
|
339
|
+
// Measured under LTR: the leading edge is the left one, so a touch in
|
|
340
|
+
// the right band fails at once.
|
|
341
|
+
expect(pan.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
342
|
+
h.finger(1)
|
|
343
|
+
.down(width - 4, 300, 0, {recognizers: [pan]})
|
|
344
|
+
.drag(-20, 0, 40);
|
|
345
|
+
expect(h.log.some(line => line.startsWith('edge:began'))).toBe(false);
|
|
346
|
+
h.finger(1).up(60);
|
|
347
|
+
expect(announced).toBe(0);
|
|
348
|
+
// The page turns RTL in place: no resize, so no layout report.
|
|
349
|
+
view.style.direction = 'rtl';
|
|
350
|
+
h.environment.rtl = true;
|
|
351
|
+
const before = h.log.length;
|
|
352
|
+
h.finger(2)
|
|
353
|
+
.down(width - 4, 300, 200, {recognizers: [pan]})
|
|
354
|
+
.drag(-20, 0, 40);
|
|
355
|
+
expect(
|
|
356
|
+
h.log.slice(before).some(line => line.startsWith('edge:began')),
|
|
357
|
+
).toBe(true);
|
|
358
|
+
expect(pan.activeEdge).toBe('right');
|
|
359
|
+
expect(announced).toBe(1);
|
|
360
|
+
h.finger(2).up(260);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("decides the touch that measures again at FR19's 10 px when the touch-action in force left the browser the axis, and the next at UIKit's 15 px (spec:AST-094 FR19)", () => {
|
|
364
|
+
const width = window.innerWidth;
|
|
365
|
+
const h = createHarness();
|
|
366
|
+
h.environment.width = width;
|
|
367
|
+
const pan = new ScreenEdgePanGestureRecognizer({
|
|
368
|
+
name: 'edge',
|
|
369
|
+
edges: ['leading'],
|
|
370
|
+
});
|
|
371
|
+
const region = pan.edgeRegion;
|
|
372
|
+
// A strip at the right edge: under LTR the leading edge is the left one,
|
|
373
|
+
// so the strip is out of the zone and its touch-action leaves the
|
|
374
|
+
// browser pan-x.
|
|
375
|
+
const view = viewAt(width - region, region, 844, 'ltr');
|
|
376
|
+
pan.view = view;
|
|
377
|
+
h.environment.view = {
|
|
378
|
+
left: width - region,
|
|
379
|
+
top: 0,
|
|
380
|
+
width: region,
|
|
381
|
+
height: 844,
|
|
382
|
+
};
|
|
383
|
+
expect(pan.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
384
|
+
// The page turns RTL in place: the strip is in the leading band now.
|
|
385
|
+
view.style.direction = 'rtl';
|
|
386
|
+
h.environment.rtl = true;
|
|
387
|
+
// This touch landed under the touch-action from before the switch, with
|
|
388
|
+
// pan-x the browser's: it decides at 10 px, before the browser commits.
|
|
389
|
+
h.finger(1)
|
|
390
|
+
.down(width - 4, 300, 0, {recognizers: [pan]})
|
|
391
|
+
.drag(-9, 0, 30, 9);
|
|
392
|
+
expect(pan.state).toBe('possible');
|
|
393
|
+
h.finger(1).drag(-1, 0, 10, 1);
|
|
394
|
+
expect(pan.state).toBe('began');
|
|
395
|
+
h.finger(1).up(60);
|
|
396
|
+
// The change was announced, so the next touch lands under pan-x
|
|
397
|
+
// claimed: UIKit's 15 px.
|
|
398
|
+
expect(pan.nativeTouchBehavior.panX).toBe(false);
|
|
399
|
+
const hysteresis = UIKIT_SCREEN_EDGE_PAN.hysteresis.value;
|
|
400
|
+
h.finger(2)
|
|
401
|
+
.down(width - 4, 300, 200, {recognizers: [pan]})
|
|
402
|
+
.drag(-(hysteresis - 1), 0, 30, hysteresis - 1);
|
|
403
|
+
expect(pan.state).toBe('possible');
|
|
404
|
+
h.finger(2).drag(-1, 0, 10, 1);
|
|
405
|
+
expect(pan.state).toBe('began');
|
|
406
|
+
h.finger(2).up(260);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe('each writing direction and edge, before a touch, at touch-down and after a resize (spec:AST-094 FR18, FR19, DEC-13, DEC-22)', () => {
|
|
411
|
+
const claim = {
|
|
412
|
+
panX: false,
|
|
413
|
+
panY: true,
|
|
414
|
+
pinchZoom: true,
|
|
415
|
+
doubleTapZoom: false,
|
|
416
|
+
};
|
|
417
|
+
const hysteresis = UIKIT_SCREEN_EDGE_PAN.hysteresis.value;
|
|
418
|
+
for (const rtl of [false, true]) {
|
|
419
|
+
for (const edge of ['leading', 'trailing'] as const) {
|
|
420
|
+
const physical = (edge === 'leading') !== rtl ? 'left' : 'right';
|
|
421
|
+
it(`${rtl ? 'RTL' : 'LTR'} ${edge}, the ${physical} edge: touch-action and the touch-down decision agree`, () => {
|
|
422
|
+
const width = window.innerWidth;
|
|
423
|
+
const h = createHarness();
|
|
424
|
+
h.environment.rtl = rtl;
|
|
425
|
+
h.environment.width = width;
|
|
426
|
+
const left = physical === 'left' ? 0 : width - 20;
|
|
427
|
+
h.environment.view = {left, top: 0, width: 20, height: 844};
|
|
428
|
+
const pan = new ScreenEdgePanGestureRecognizer({
|
|
429
|
+
name: 'edge',
|
|
430
|
+
edges: [edge],
|
|
431
|
+
});
|
|
432
|
+
// The element a host gives it, laid out with its own direction.
|
|
433
|
+
pan.view = viewAt(left, 20, 844, rtl ? 'rtl' : 'ltr');
|
|
434
|
+
// Before any touch, as the host derives touch-action: pan-x is taken.
|
|
435
|
+
expect(pan.nativeTouchBehavior).toEqual(claim);
|
|
436
|
+
// At touch-down, a touch in that band decides at 15 px, as the claim
|
|
437
|
+
// allows.
|
|
438
|
+
const x = physical === 'left' ? 4 : width - 4;
|
|
439
|
+
const inward = physical === 'left' ? 1 : -1;
|
|
440
|
+
h.finger(1)
|
|
441
|
+
.down(x, 300, 0, {recognizers: [pan]})
|
|
442
|
+
.drag(inward * (hysteresis - 1), 0, 30, hysteresis - 1);
|
|
443
|
+
expect(pan.state).toBe('possible');
|
|
444
|
+
h.finger(1).drag(inward * 2, 0, 20, 2);
|
|
445
|
+
expect(['began', 'changed']).toContain(pan.state);
|
|
446
|
+
h.finger(1).up(80);
|
|
447
|
+
// After a resize past the band: no claim, and the next touch decides
|
|
448
|
+
// at 10 px.
|
|
449
|
+
h.environment.view = {left: 0, top: 0, width, height: 844};
|
|
450
|
+
pan.view = viewAt(0, width, 844, rtl ? 'rtl' : 'ltr');
|
|
451
|
+
pan.layoutChanged();
|
|
452
|
+
expect(pan.nativeTouchBehavior).toEqual(ALL_NATIVE_TOUCH);
|
|
453
|
+
h.finger(2)
|
|
454
|
+
.down(x, 300, 200, {recognizers: [pan]})
|
|
455
|
+
.drag(inward * 9, 0, 30, 9);
|
|
456
|
+
expect(pan.state).toBe('possible');
|
|
457
|
+
h.finger(2).drag(inward, 0, 10, 1);
|
|
458
|
+
expect(['began', 'changed']).toContain(pan.state);
|
|
459
|
+
h.finger(2).up(280);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
/** An element whose box has `width` × `height`, as layout would give it. */
|
|
466
|
+
function viewOfSize(width: number, height: number): HTMLElement {
|
|
467
|
+
const view = document.createElement('div');
|
|
468
|
+
view.getBoundingClientRect = () =>
|
|
469
|
+
({width, height, top: 0, left: 0, right: width, bottom: height}) as DOMRect;
|
|
470
|
+
return view;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* An element whose box starts `left` px from the viewport's left edge, in
|
|
475
|
+
* the document with the writing direction given.
|
|
476
|
+
*/
|
|
477
|
+
function viewAt(
|
|
478
|
+
left: number,
|
|
479
|
+
width: number,
|
|
480
|
+
height: number,
|
|
481
|
+
direction: 'ltr' | 'rtl' = 'ltr',
|
|
482
|
+
): HTMLElement {
|
|
483
|
+
const view = document.createElement('div');
|
|
484
|
+
view.style.direction = direction;
|
|
485
|
+
document.body.append(view);
|
|
486
|
+
view.getBoundingClientRect = () =>
|
|
487
|
+
({
|
|
488
|
+
width,
|
|
489
|
+
height,
|
|
490
|
+
top: 0,
|
|
491
|
+
left,
|
|
492
|
+
right: left + width,
|
|
493
|
+
bottom: height,
|
|
494
|
+
x: left,
|
|
495
|
+
y: 0,
|
|
496
|
+
}) as DOMRect;
|
|
497
|
+
return view;
|
|
498
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file SwipeGestureRecognizer.test.ts
|
|
5
|
+
* @input Synthetic touches with explicit timestamps through a GestureArena
|
|
6
|
+
* @output SwipeGestureRecognizer's state machine: recognition during the motion, and failure when too slow, off-axis or the wrong way
|
|
7
|
+
* @position Unit tests of packages/core/src/gestures/SwipeGestureRecognizer.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {describe, expect, it} from 'vitest';
|
|
11
|
+
import {SwipeGestureRecognizer} from '../SwipeGestureRecognizer';
|
|
12
|
+
import {createHarness} from './harness';
|
|
13
|
+
|
|
14
|
+
describe('SwipeGestureRecognizer', () => {
|
|
15
|
+
it('is recognized during the motion, once the distance is covered', () => {
|
|
16
|
+
const h = createHarness();
|
|
17
|
+
const swipe = new SwipeGestureRecognizer({name: 'swipe'});
|
|
18
|
+
const finger = h.finger(1).down(0, 0, 0, {recognizers: [swipe]});
|
|
19
|
+
finger.move(30, 2, 30);
|
|
20
|
+
expect(swipe.state).toBe('possible');
|
|
21
|
+
finger.move(50, 4, 50);
|
|
22
|
+
expect(swipe.recognizedDirection).toBe('right');
|
|
23
|
+
expect(h.log[0]).toBe('swipe:ended@50');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('fails when too slow, off-axis, or the wrong way', () => {
|
|
27
|
+
const slow = createHarness();
|
|
28
|
+
const a = new SwipeGestureRecognizer({name: 'a'});
|
|
29
|
+
slow
|
|
30
|
+
.finger(1)
|
|
31
|
+
.down(0, 0, 0, {recognizers: [a]})
|
|
32
|
+
.move(30, 0, 400);
|
|
33
|
+
slow.advance(500);
|
|
34
|
+
expect(a.state).toBe('failed');
|
|
35
|
+
|
|
36
|
+
const diagonal = createHarness();
|
|
37
|
+
const b = new SwipeGestureRecognizer({name: 'b'});
|
|
38
|
+
diagonal
|
|
39
|
+
.finger(1)
|
|
40
|
+
.down(0, 0, 0, {recognizers: [b]})
|
|
41
|
+
.move(45, 55, 60);
|
|
42
|
+
// 55 px across is past the 50 px secondary allowance.
|
|
43
|
+
expect(b.state).toBe('failed');
|
|
44
|
+
|
|
45
|
+
const wrong = createHarness();
|
|
46
|
+
const c = new SwipeGestureRecognizer({name: 'c', direction: 'left'});
|
|
47
|
+
wrong
|
|
48
|
+
.finger(1)
|
|
49
|
+
.down(100, 0, 0, {recognizers: [c]})
|
|
50
|
+
.move(160, 0, 60);
|
|
51
|
+
expect(c.state).toBe('failed');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('takes several directions and fingers', () => {
|
|
55
|
+
const h = createHarness();
|
|
56
|
+
const swipe = new SwipeGestureRecognizer({
|
|
57
|
+
name: 'swipe',
|
|
58
|
+
direction: ['up', 'down'],
|
|
59
|
+
numberOfTouchesRequired: 2,
|
|
60
|
+
});
|
|
61
|
+
h.finger(1).down(0, 200, 0, {recognizers: [swipe]});
|
|
62
|
+
h.finger(2).down(40, 200, 5, {recognizers: [swipe]});
|
|
63
|
+
h.finger(1).move(0, 140, 50);
|
|
64
|
+
h.finger(2).move(40, 140, 50);
|
|
65
|
+
expect(swipe.recognizedDirection).toBe('up');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('gives the same verdict for the same path however its events are spaced', () => {
|
|
69
|
+
// A straight drag to (55, 40): along 55 past the 50 px distance, 40 px
|
|
70
|
+
// across within the 50 px allowance.
|
|
71
|
+
const sparse = createHarness();
|
|
72
|
+
const a = new SwipeGestureRecognizer({name: 'a'});
|
|
73
|
+
sparse
|
|
74
|
+
.finger(1)
|
|
75
|
+
.down(0, 0, 0, {recognizers: [a]})
|
|
76
|
+
.move(55, 40, 40);
|
|
77
|
+
expect(a.state).toBe('ended');
|
|
78
|
+
|
|
79
|
+
const dense = createHarness();
|
|
80
|
+
const b = new SwipeGestureRecognizer({name: 'b'});
|
|
81
|
+
const finger = dense.finger(1).down(0, 0, 0, {recognizers: [b]});
|
|
82
|
+
for (let step = 1; step <= 10; step += 1) {
|
|
83
|
+
finger.move(5.5 * step, 4 * step, 4 * step);
|
|
84
|
+
}
|
|
85
|
+
expect(b.state).toBe('ended');
|
|
86
|
+
expect(b.recognizedDirection).toBe('right');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('recognizes a 40\u00b0 swipe at 16 px per event as at small steps', () => {
|
|
90
|
+
// At 40\u00b0 the travel along reaches 50 px with 42 px across, inside
|
|
91
|
+
// the 50 px allowance; at 16 px per event the next event is already 51 px
|
|
92
|
+
// across, so the verdict must come from the path between the events.
|
|
93
|
+
const angle = (40 * Math.PI) / 180;
|
|
94
|
+
for (const step of [2, 16]) {
|
|
95
|
+
const h = createHarness();
|
|
96
|
+
const swipe = new SwipeGestureRecognizer({name: 'swipe'});
|
|
97
|
+
const finger = h.finger(1).down(0, 0, 0, {recognizers: [swipe]});
|
|
98
|
+
for (let d = step, t = 4; d <= 96; d += step, t += 4) {
|
|
99
|
+
finger.move(d * Math.cos(angle), d * Math.sin(angle), t);
|
|
100
|
+
if (swipe.state !== 'possible') {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
expect(swipe.state).toBe('ended');
|
|
105
|
+
expect(swipe.recognizedDirection).toBe('right');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|