Skip to content

Commit 03ebb32

Browse files
committed
fix(use-long-press): [react-34] Handle reading position from unrecognised event
1 parent c42f180 commit 03ebb32

File tree

4 files changed

+99
-19
lines changed

4 files changed

+99
-19
lines changed

packages/use-long-press/src/lib/use-long-press.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import {
66
TouchEventHandler,
77
useCallback,
88
useEffect,
9-
useRef
10-
} from "react";
9+
useRef,
10+
} from 'react';
1111
import {
1212
LongPressCallback,
1313
LongPressCallbackReason,
@@ -20,9 +20,9 @@ import {
2020
LongPressPointerHandlers,
2121
LongPressReactEvents,
2222
LongPressResult,
23-
LongPressTouchHandlers
24-
} from "./use-long-press.types";
25-
import { createArtificialReactEvent, getCurrentPosition, isRecognisableEvent } from "./use-long-press.utils";
23+
LongPressTouchHandlers,
24+
} from './use-long-press.types';
25+
import { createArtificialReactEvent, getCurrentPosition, isRecognisableEvent } from './use-long-press.utils';
2626

2727
// Disabled callback
2828
export function useLongPress<Target extends Element = Element, Context = unknown>(
@@ -206,6 +206,11 @@ export function useLongPress<
206206

207207
const move = useCallback(
208208
(context?: Context) => (event: LongPressReactEvents<Target>) => {
209+
// Ignore unrecognised events
210+
if (!isRecognisableEvent(event)) {
211+
return;
212+
}
213+
209214
// First call callback to allow modifying event position
210215
onMove?.(event, { context });
211216

packages/use-long-press/src/lib/use-long-press.utils.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ const recognisedPointerEvents: string[] = [
2929
'pointerout',
3030
] satisfies (keyof WindowEventMap)[];
3131

32+
function hasPageCoordinates(obj: unknown): boolean {
33+
return (
34+
typeof obj === 'object' &&
35+
obj !== null &&
36+
'pageX' in obj &&
37+
typeof obj.pageX === 'number' &&
38+
'pageY' in obj &&
39+
typeof obj.pageY === 'number'
40+
);
41+
}
42+
3243
export function isMouseEvent<Target extends Element>(event: SyntheticEvent<Target>): event is ReactMouseEvent<Target> {
3344
return recognisedMouseEvents.includes(event?.nativeEvent?.type);
3445
}
@@ -58,20 +69,14 @@ export function getCurrentPosition<Target extends Element>(
5869
x: number;
5970
y: number;
6071
} | null {
61-
if (isTouchEvent(event)) {
62-
return {
63-
x: event.touches[0].pageX,
64-
y: event.touches[0].pageY,
65-
};
66-
}
72+
const positionHolder = isTouchEvent(event) ? event?.touches?.[0] : event;
6773

68-
if (isMouseEvent(event) || isPointerEvent(event)) {
74+
if (hasPageCoordinates(positionHolder)) {
6975
return {
70-
x: event.pageX,
71-
y: event.pageY,
76+
x: positionHolder.pageX,
77+
y: positionHolder.pageY,
7278
};
7379
}
74-
7580
return null;
7681
}
7782

packages/use-long-press/src/tests/use-long-press.test.functions.ts

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function createMockedPointerEvent<T extends HTMLElement = HTMLElement>(
3939
): ReactPointerEvent<T> {
4040
return {
4141
nativeEvent: new PointerEvent('pointerdown'),
42+
pointerId: 1,
4243
...options,
4344
} as ReactPointerEvent<T>;
4445
}

packages/use-long-press/src/tests/use-long-press.test.tsx

+73-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from '../lib';
1515
import {
1616
createArtificialReactEvent,
17+
getCurrentPosition,
1718
isMouseEvent,
1819
isPointerEvent,
1920
isRecognisableEvent,
@@ -26,7 +27,13 @@ import {
2627
TestComponent,
2728
TestComponentProps,
2829
} from './TestComponent';
29-
import { TouchEvent as ReactTouchEvent, TouchList as ReactTouchList } from 'react';
30+
import {
31+
TouchEvent as ReactTouchEvent,
32+
PointerEvent as ReactPointerEvent,
33+
MouseEvent as ReactMouseEvent,
34+
TouchList as ReactTouchList,
35+
Touch as ReactTouch,
36+
} from 'react';
3037
import { afterEach, beforeEach, describe, expect, MockedFunction, test } from 'vitest';
3138
import {
3239
emptyContext,
@@ -137,9 +144,63 @@ describe('Hook handlers', () => {
137144
⌞____________________________________________________________________________________________________
138145
*/
139146
describe('Different environment compatibility', () => {
140-
test('Properly detect TouchEvent event even if browser doesnt provide it', () => {
141-
const touchEvent = { touches: {} as ReactTouchList, nativeEvent: { touches: {} as TouchList } } as ReactTouchEvent;
142-
expect(isRecognisableEvent(touchEvent)).toBe(true);
147+
describe('Event absence', () => {
148+
test('Properly detect MouseEvent event even if browser doesnt provide it', () => {
149+
const mouseEvent = {
150+
nativeEvent: {
151+
type: 'mouseup',
152+
},
153+
} as ReactMouseEvent;
154+
expect(isRecognisableEvent(mouseEvent)).toBe(true);
155+
});
156+
test('Properly detect TouchEvent event even if browser doesnt provide it', () => {
157+
const touchEvent = {
158+
nativeEvent: {
159+
type: 'touchstart',
160+
},
161+
} as ReactTouchEvent;
162+
expect(isRecognisableEvent(touchEvent)).toBe(true);
163+
});
164+
test('Properly detect PointerEvent event even if browser doesnt provide it', () => {
165+
const pointerEvent = {
166+
nativeEvent: {
167+
type: 'pointerup',
168+
pointerId: 0,
169+
},
170+
} as ReactPointerEvent;
171+
expect(isRecognisableEvent(pointerEvent)).toBe(true);
172+
});
173+
});
174+
175+
describe('Reading position from event without it', () => {
176+
test('Return null for mouse-like event', () => {
177+
const mouseEvent = {
178+
nativeEvent: {
179+
type: 'mouseup',
180+
},
181+
} as ReactMouseEvent;
182+
expect(isRecognisableEvent(mouseEvent)).toBe(true);
183+
expect(getCurrentPosition(mouseEvent)).toBe(null);
184+
});
185+
test('Return null for touch-like event', () => {
186+
const touchEvent = {
187+
nativeEvent: {
188+
type: 'touchstart',
189+
},
190+
} as ReactTouchEvent;
191+
expect(isRecognisableEvent(touchEvent)).toBe(true);
192+
expect(getCurrentPosition(touchEvent)).toBe(null);
193+
});
194+
test('Return null for pointer-like event', () => {
195+
const pointerEvent = {
196+
nativeEvent: {
197+
type: 'pointerup',
198+
pointerId: 0,
199+
},
200+
} as ReactPointerEvent;
201+
expect(isRecognisableEvent(pointerEvent)).toBe(true);
202+
expect(getCurrentPosition(pointerEvent)).toBe(null);
203+
});
143204
});
144205

145206
describe('Without window', () => {
@@ -1422,11 +1483,19 @@ describe('Utils', () => {
14221483

14231484
test.each([
14241485
['mouseDown' as const],
1486+
['mouseMove' as const],
14251487
['mouseUp' as const],
1488+
['mouseLeave' as const],
1489+
['mouseOut' as const],
14261490
['touchStart' as const],
1491+
['touchMove' as const],
14271492
['touchEnd' as const],
1493+
['touchCancel' as const],
14281494
['pointerDown' as const],
1495+
['pointerMove' as const],
14291496
['pointerUp' as const],
1497+
['pointerLeave' as const],
1498+
['pointerOut' as const],
14301499
])('Create recognisable artificial %s react event', (eventName) => {
14311500
const event = createEvent[eventName](window);
14321501
const reactEvent = createArtificialReactEvent(event as LongPressDomEvents);

0 commit comments

Comments
 (0)