Skip to content

Commit 368c2ab

Browse files
committed
Merge branch 'next' into @mbert/fix-scrolls
2 parents 3428546 + 6afb378 commit 368c2ab

File tree

20 files changed

+351
-68
lines changed

20 files changed

+351
-68
lines changed

.github/workflows/check-relations-traversal-algorithm.yml renamed to .github/workflows/rngh-api-v3.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
name: Test relations traversal algorithm
1+
name: Test Gesture Handler 3 API
22

33
on:
44
pull_request:
55
paths:
66
- packages/react-native-gesture-handler/src/v3/**
77
- packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx
8+
- packages/react-native-gesture-handler/src/__tests__/API_V3.test.tsx
89
push:
910
branches:
1011
- main
@@ -34,4 +35,4 @@ jobs:
3435

3536
- name: Run tests
3637
working-directory: packages/react-native-gesture-handler
37-
run: yarn test RelationsTraversal
38+
run: yarn test RelationsTraversal API_V3
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { usePanGesture } from '../v3/hooks/gestures';
2+
import { render, renderHook } from '@testing-library/react-native';
3+
import { fireGestureHandler, getByGestureTestId } from '../jestUtils';
4+
import { State } from '../State';
5+
import GestureHandlerRootView from '../components/GestureHandlerRootView';
6+
import { RectButton } from '../v3/components';
7+
import { act } from 'react';
8+
9+
describe('[API v3] Hooks', () => {
10+
test('Pan gesture', () => {
11+
const onBegin = jest.fn();
12+
const onStart = jest.fn();
13+
14+
const panGesture = renderHook(() =>
15+
usePanGesture({
16+
disableReanimated: true,
17+
onBegin: (e) => onBegin(e),
18+
onActivate: (e) => onStart(e),
19+
})
20+
).result.current;
21+
22+
fireGestureHandler(panGesture, [
23+
{ oldState: State.UNDETERMINED, state: State.BEGAN },
24+
{ oldState: State.BEGAN, state: State.ACTIVE },
25+
{ oldState: State.ACTIVE, state: State.ACTIVE },
26+
{ oldState: State.ACTIVE, state: State.END },
27+
]);
28+
29+
expect(onBegin).toHaveBeenCalledTimes(1);
30+
expect(onStart).toHaveBeenCalledTimes(1);
31+
});
32+
});
33+
34+
describe('[API v3] Components', () => {
35+
test('Rect Button', () => {
36+
const pressFn = jest.fn();
37+
38+
const RectButtonExample = () => {
39+
return (
40+
<GestureHandlerRootView>
41+
<RectButton testID="btn" onPress={pressFn} />
42+
</GestureHandlerRootView>
43+
);
44+
};
45+
46+
render(<RectButtonExample />);
47+
48+
const nativeGesture = getByGestureTestId('btn');
49+
50+
act(() => {
51+
fireGestureHandler(nativeGesture, [
52+
{ oldState: State.UNDETERMINED, state: State.BEGAN },
53+
{ oldState: State.BEGAN, state: State.ACTIVE },
54+
{ oldState: State.ACTIVE, state: State.END },
55+
]);
56+
});
57+
58+
expect(pressFn).toHaveBeenCalledTimes(1);
59+
});
60+
});

packages/react-native-gesture-handler/src/handlers/handlersRegistry.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,37 @@
11
import { isTestEnv } from '../utils';
22
import { GestureType } from './gestures/gesture';
33
import { GestureEvent, HandlerStateChangeEvent } from './gestureHandlerCommon';
4+
import { SingleGesture } from '../v3/types';
45

56
export const handlerIDToTag: Record<string, number> = {};
7+
8+
// There were attempts to create types that merge possible HandlerData and Config,
9+
// but ts was not able to infer them properly in many cases, so we use any here.
10+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
11+
const hookGestures = new Map<number, SingleGesture<any, any>>();
612
const gestures = new Map<number, GestureType>();
713
const oldHandlers = new Map<number, GestureHandlerCallbacks>();
814
const testIDs = new Map<string, number>();
915

16+
export function registerGesture<THandlerData, TConfig>(
17+
handlerTag: number,
18+
gesture: SingleGesture<THandlerData, TConfig>
19+
) {
20+
if (isTestEnv() && gesture.config.testID) {
21+
hookGestures.set(handlerTag, gesture);
22+
testIDs.set(gesture.config.testID, handlerTag);
23+
}
24+
}
25+
26+
export function unregisterGesture(handlerTag: number) {
27+
const gesture = hookGestures.get(handlerTag);
28+
29+
if (gesture && isTestEnv() && gesture.config.testID) {
30+
testIDs.delete(gesture.config.testID);
31+
hookGestures.delete(handlerTag);
32+
}
33+
}
34+
1035
export function registerHandler(
1136
handlerTag: number,
1237
handler: GestureType,
@@ -40,14 +65,18 @@ export function findHandler(handlerTag: number) {
4065
return gestures.get(handlerTag);
4166
}
4267

68+
export function findGesture(handlerTag: number) {
69+
return hookGestures.get(handlerTag);
70+
}
71+
4372
export function findOldGestureHandler(handlerTag: number) {
4473
return oldHandlers.get(handlerTag);
4574
}
4675

4776
export function findHandlerByTestID(testID: string) {
4877
const handlerTag = testIDs.get(testID);
4978
if (handlerTag !== undefined) {
50-
return findHandler(handlerTag) ?? null;
79+
return findHandler(handlerTag) ?? findGesture(handlerTag) ?? null;
5180
}
5281
return null;
5382
}

packages/react-native-gesture-handler/src/index.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -152,20 +152,8 @@ export type {
152152
} from './components/Pressable';
153153
export { default as Pressable } from './components/Pressable';
154154

155-
export {
156-
GestureDetector,
157-
InterceptingGestureDetector,
158-
GestureDetectorProps,
159-
VirtualGestureDetector,
160-
} from './v3/detectors';
161-
162-
export * from './v3/hooks/composition';
163-
164-
export type { ComposedGesture } from './v3/types';
165155
export type { GestureTouchEvent as SingleGestureTouchEvent } from './handlers/gestureHandlerCommon';
166156

167-
export * from './v3/hooks/gestures';
168-
169-
export * from './v3/components';
157+
export * from './v3';
170158

171159
initialize();

packages/react-native-gesture-handler/src/jestUtils/jestUtils.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ import {
6060
} from '../handlers/TapGestureHandler';
6161
import { State } from '../State';
6262
import { hasProperty, withPrevAndCurrent } from '../utils';
63+
import type { SingleGesture } from '../v3/types';
64+
import { maybeUnpackValue } from '../v3/hooks/utils';
6365

6466
// Load fireEvent conditionally, so RNGH may be used in setups without testing-library
6567
let fireEvent = (
@@ -164,11 +166,17 @@ const handlersDefaultEvents: DefaultEventsMapping = {
164166
};
165167

166168
function isGesture(
167-
componentOrGesture: ReactTestInstance | GestureType
169+
componentOrGesture: ReactTestInstance | GestureType | SingleGesture<any, any>
168170
): componentOrGesture is GestureType {
169171
return componentOrGesture instanceof BaseGesture;
170172
}
171173

174+
function isHookGesture(
175+
componentOrGesture: ReactTestInstance | SingleGesture<any, any>
176+
): componentOrGesture is SingleGesture<any, any> {
177+
return 'detectorCallbacks' in componentOrGesture;
178+
}
179+
172180
interface WrappedGestureHandlerTestEvent {
173181
nativeEvent: GestureHandlerTestEvent;
174182
}
@@ -408,7 +416,7 @@ interface HandlerData {
408416
enabled: boolean | undefined;
409417
}
410418
function getHandlerData(
411-
componentOrGesture: ReactTestInstance | GestureType
419+
componentOrGesture: ReactTestInstance | GestureType | SingleGesture<any, any>
412420
): HandlerData {
413421
if (isGesture(componentOrGesture)) {
414422
const gesture = componentOrGesture;
@@ -421,6 +429,33 @@ function getHandlerData(
421429
enabled: gesture.config.enabled,
422430
};
423431
}
432+
433+
if (isHookGesture(componentOrGesture)) {
434+
return {
435+
handlerType: componentOrGesture.type as HandlerNames,
436+
handlerTag: componentOrGesture.tag,
437+
enabled: maybeUnpackValue(componentOrGesture.config.enabled),
438+
emitEvent: (eventName, args) => {
439+
const { state, oldState, handlerTag, ...rest } = args.nativeEvent;
440+
441+
const event = {
442+
state,
443+
handlerTag,
444+
handlerData: { ...rest },
445+
};
446+
447+
if (eventName === 'onGestureHandlerStateChange') {
448+
componentOrGesture.detectorCallbacks.onGestureHandlerStateChange({
449+
oldState: oldState as State,
450+
...event,
451+
});
452+
} else if (eventName === 'onGestureHandlerEvent') {
453+
componentOrGesture.detectorCallbacks.onGestureHandlerEvent?.(event);
454+
}
455+
},
456+
};
457+
}
458+
424459
const gestureHandlerComponent = componentOrGesture;
425460
return {
426461
emitEvent: (eventName, args) => {
@@ -465,7 +500,7 @@ type ExtractConfig<T> =
465500
: Record<string, unknown>;
466501

467502
export function fireGestureHandler<THandler extends AllGestures | AllHandlers>(
468-
componentOrGesture: ReactTestInstance | GestureType,
503+
componentOrGesture: ReactTestInstance | GestureType | SingleGesture<any, any>,
469504
eventList: Partial<GestureHandlerTestEvent<ExtractConfig<THandler>>>[] = []
470505
): void {
471506
const { emitEvent, handlerType, handlerTag, enabled } =

packages/react-native-gesture-handler/src/v3/components/GestureComponents.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import createNativeWrapper from '../createNativeWrapper';
2020

2121
import { NativeWrapperProperties } from '../types/NativeWrapperType';
2222
import { NativeWrapperProps } from '../hooks/utils';
23-
import { DetectorType } from '../detectors';
23+
import { GestureDetectorType } from '../detectors';
2424
import { NativeGesture } from '../hooks/gestures/native/useNativeGesture';
2525
import { ghQueueMicrotask } from '../../ghQueueMicrotask';
2626

@@ -30,7 +30,7 @@ export const RefreshControl = createNativeWrapper(
3030
disallowInterruption: true,
3131
shouldCancelWhenOutside: false,
3232
},
33-
DetectorType.Virtual
33+
GestureDetectorType.Virtual
3434
);
3535

3636
// eslint-disable-next-line @typescript-eslint/no-redeclare
@@ -42,7 +42,7 @@ const GHScrollView = createNativeWrapper<PropsWithChildren<RNScrollViewProps>>(
4242
disallowInterruption: true,
4343
shouldCancelWhenOutside: false,
4444
},
45-
DetectorType.Intercepting
45+
GestureDetectorType.Intercepting
4646
);
4747

4848
export const ScrollView = (

packages/react-native-gesture-handler/src/v3/createNativeWrapper.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import { useNativeGesture } from './hooks/gestures';
55
import { NativeDetector } from './detectors/NativeDetector';
66
import type { NativeWrapperProperties } from './types/NativeWrapperType';
77
import { NativeGesture } from './hooks/gestures/native/useNativeGesture';
8-
import { DetectorType, InterceptingGestureDetector } from './detectors';
8+
import { GestureDetectorType, InterceptingGestureDetector } from './detectors';
99
import { VirtualDetector } from './detectors/VirtualDetector/VirtualDetector';
1010

1111
export default function createNativeWrapper<P>(
1212
Component: React.ComponentType<P>,
1313
config: Readonly<NativeWrapperProperties> = {},
14-
detectorType: DetectorType = DetectorType.Native
14+
detectorType: GestureDetectorType = GestureDetectorType.Native
1515
) {
1616
const ComponentWrapper = (
1717
props: P &
@@ -57,9 +57,9 @@ export default function createNativeWrapper<P>(
5757
}, [native, onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER]);
5858

5959
const DetectorComponent =
60-
detectorType === DetectorType.Intercepting
60+
detectorType === GestureDetectorType.Intercepting
6161
? InterceptingGestureDetector
62-
: detectorType === DetectorType.Virtual
62+
: detectorType === GestureDetectorType.Virtual
6363
? VirtualDetector
6464
: NativeDetector;
6565

packages/react-native-gesture-handler/src/v3/detectors/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import { Animated, StyleSheet } from 'react-native';
44
import HostGestureDetector from './HostGestureDetector';
55
import { GestureDetectorProps as LegacyDetectorProps } from '../../handlers/gestures/GestureDetector';
66

7+
export enum GestureDetectorType {
8+
Native,
9+
Virtual,
10+
Intercepting,
11+
}
12+
713
export interface NativeDetectorProps<THandlerData, TConfig> {
814
children?: React.ReactNode;
915
gesture: Gesture<THandlerData, TConfig>;
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
export type { GestureDetectorProps } from './common';
2+
export { GestureDetectorType } from './common';
23
export { GestureDetector } from './GestureDetector';
34
export { VirtualDetector as VirtualGestureDetector } from './VirtualDetector/VirtualDetector';
45
export { InterceptingGestureDetector } from './VirtualDetector/InterceptingGestureDetector';
5-
6-
export enum DetectorType {
7-
Native,
8-
Virtual,
9-
Intercepting,
10-
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './composition';
2+
export * from './gestures';

0 commit comments

Comments
 (0)