@@ -18,6 +18,30 @@ import * as React from 'react';
1818import { createRef , useEffect , useLayoutEffect , useRef } from 'react' ;
1919import { TextInput } from 'react-native' ;
2020
21+ function touchStart ( element : HostInstance ) : void {
22+ Fantom . dispatchNativeEvent (
23+ element ,
24+ 'onTouchStart' ,
25+ {
26+ touches : [ { identifier : 0 , pageX : 5 , pageY : 5 , timestamp : 0 } ] ,
27+ changedTouches : [ { identifier : 0 , pageX : 5 , pageY : 5 , timestamp : 0 } ] ,
28+ } ,
29+ { category : Fantom . NativeEventCategory . Discrete } ,
30+ ) ;
31+ }
32+
33+ function touchEnd ( element : HostInstance ) : void {
34+ Fantom . dispatchNativeEvent (
35+ element ,
36+ 'onTouchEnd' ,
37+ {
38+ touches : [ ] ,
39+ changedTouches : [ { identifier : 0 , pageX : 5 , pageY : 5 , timestamp : 200 } ] ,
40+ } ,
41+ { category : Fantom . NativeEventCategory . Discrete } ,
42+ ) ;
43+ }
44+
2145describe ( '<TextInput>' , ( ) => {
2246 describe ( 'props' , ( ) => {
2347 describe ( 'selection' , ( ) => {
@@ -255,6 +279,134 @@ describe('<TextInput>', () => {
255279 } ) ;
256280 } ) ;
257281
282+ describe ( 'onLongPress' , ( ) => {
283+ let timers : Fantom . TimerMock ;
284+
285+ beforeEach ( ( ) => {
286+ timers = Fantom . installTimerMock ( ) ;
287+ } ) ;
288+
289+ afterEach ( ( ) => {
290+ timers . uninstall ( ) ;
291+ } ) ;
292+
293+ it ( 'is called once a touch is held past the long press delay' , ( ) => {
294+ const root = Fantom . createRoot ( ) ;
295+ const nodeRef = createRef < HostInstance > ( ) ;
296+ const onLongPress = jest . fn ( ) ;
297+
298+ Fantom . runTask ( ( ) => {
299+ root . render (
300+ < TextInput
301+ onLongPress = { onLongPress }
302+ ref = { nodeRef }
303+ style = { { height : 100 , width : 100 } }
304+ /> ,
305+ ) ;
306+ } ) ;
307+
308+ const element = nullthrows ( nodeRef . current ) ;
309+ touchStart ( element ) ;
310+ timers . advanceTimersByTime ( 499 ) ;
311+ expect ( onLongPress ) . toHaveBeenCalledTimes ( 0 ) ;
312+
313+ timers . advanceTimersByTime ( 1 ) ;
314+ expect ( onLongPress ) . toHaveBeenCalledTimes ( 1 ) ;
315+ } ) ;
316+
317+ it ( 'takes the place of the press, so the release calls no onPress and sends no focus command' , ( ) => {
318+ const root = Fantom . createRoot ( ) ;
319+ const nodeRef = createRef < HostInstance > ( ) ;
320+ const onPress = jest . fn ( ) ;
321+
322+ Fantom . runTask ( ( ) => {
323+ root . render (
324+ < TextInput
325+ nativeID = "text-input"
326+ onLongPress = { ( ) => { } }
327+ onPress = { onPress }
328+ ref = { nodeRef }
329+ style = { { height : 100 , width : 100 } }
330+ /> ,
331+ ) ;
332+ } ) ;
333+
334+ root . takeMountingManagerLogs ( ) ;
335+
336+ const element = nullthrows ( nodeRef . current ) ;
337+ touchStart ( element ) ;
338+ timers . advanceTimersByTime ( 500 ) ;
339+ touchEnd ( element ) ;
340+ timers . runAllTimers ( ) ;
341+
342+ expect ( onPress ) . toHaveBeenCalledTimes ( 0 ) ;
343+ expect ( root . takeMountingManagerLogs ( ) ) . toEqual ( [ ] ) ;
344+ } ) ;
345+
346+ it ( 'leaves a press released before the delay a press that focuses' , ( ) => {
347+ const root = Fantom . createRoot ( ) ;
348+ const nodeRef = createRef < HostInstance > ( ) ;
349+ const onLongPress = jest . fn ( ) ;
350+ const onPress = jest . fn ( ) ;
351+
352+ Fantom . runTask ( ( ) => {
353+ root . render (
354+ < TextInput
355+ nativeID = "text-input"
356+ onLongPress = { onLongPress }
357+ onPress = { onPress }
358+ ref = { nodeRef }
359+ style = { { height : 100 , width : 100 } }
360+ /> ,
361+ ) ;
362+ } ) ;
363+
364+ root . takeMountingManagerLogs ( ) ;
365+
366+ const element = nullthrows ( nodeRef . current ) ;
367+ touchStart ( element ) ;
368+ timers . advanceTimersByTime ( 100 ) ;
369+ touchEnd ( element ) ;
370+ timers . runAllTimers ( ) ;
371+
372+ expect ( onLongPress ) . toHaveBeenCalledTimes ( 0 ) ;
373+ expect ( onPress ) . toHaveBeenCalledTimes ( 1 ) ;
374+ expect ( root . takeMountingManagerLogs ( ) ) . toEqual ( [
375+ 'Command {type: "AndroidTextInput", nativeID: "text-input", name: "focus"}' ,
376+ ] ) ;
377+ } ) ;
378+
379+ it ( 'leaves a held press a press that focuses when it is not set' , ( ) => {
380+ const root = Fantom . createRoot ( ) ;
381+ const nodeRef = createRef < HostInstance > ( ) ;
382+ const onPress = jest . fn ( ) ;
383+
384+ Fantom . runTask ( ( ) => {
385+ root . render (
386+ < TextInput
387+ nativeID = "text-input"
388+ onPress = { onPress }
389+ ref = { nodeRef }
390+ style = { { height : 100 , width : 100 } }
391+ /> ,
392+ ) ;
393+ } ) ;
394+
395+ root . takeMountingManagerLogs ( ) ;
396+
397+ const element = nullthrows ( nodeRef . current ) ;
398+ touchStart ( element ) ;
399+ timers . advanceTimersByTime ( 500 ) ;
400+ touchEnd ( element ) ;
401+ timers . runAllTimers ( ) ;
402+
403+ expect ( onPress ) . toHaveBeenCalledTimes ( 1 ) ;
404+ expect ( root . takeMountingManagerLogs ( ) ) . toEqual ( [
405+ 'Command {type: "AndroidTextInput", nativeID: "text-input", name: "focus"}' ,
406+ ] ) ;
407+ } ) ;
408+ } ) ;
409+
258410 describe ( 'id and nativeID' , ( ) => {
259411 it ( `has 'id' propagated as 'nativeID' to the mounting layer` , ( ) => {
260412 const root = Fantom . createRoot ( ) ;
0 commit comments