Skip to content
6 changes: 6 additions & 0 deletions experiments-app/src/experiments.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AccessibilityScreen } from './screens/Accessibility';
import { PressEvents } from './screens/PressEvents';
import { TextInputEventPropagation } from './screens/TextInputEventPropagation';
import { TextInputEvents } from './screens/TextInputEvents';
import { ScrollViewEvents } from './screens/ScrollViewEvents';
Expand All @@ -13,6 +14,11 @@ export const experiments = [
title: 'Accessibility',
component: AccessibilityScreen,
},
{
key: 'PressEvents',
title: 'Press Events',
component: PressEvents,
},
{
key: 'TextInputEvents',
title: 'TextInput Events',
Expand Down
82 changes: 82 additions & 0 deletions experiments-app/src/screens/PressEvents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as React from 'react';
import {
StyleSheet,
SafeAreaView,
Text,
TextInput,
View,
Pressable,
TouchableOpacity,
} from 'react-native';
import { nativeEventLogger, logEvent } from '../utils/helpers';

export function PressEvents() {
const [value, setValue] = React.useState('');

const handleChangeText = (value: string) => {
setValue(value);
logEvent('changeText', value);
};

return (
<SafeAreaView style={styles.container}>
<View style={styles.wrapper}>
<TextInput
style={styles.textInput}
value={value}
onPress={nativeEventLogger('press')}
onPressIn={nativeEventLogger('pressIn')}
onPressOut={nativeEventLogger('pressOut')}
/>
</View>
<View style={styles.wrapper}>
<Text
onPress={nativeEventLogger('press')}
onLongPress={nativeEventLogger('longPress')}
onPressIn={nativeEventLogger('pressIn')}
onPressOut={nativeEventLogger('pressOut')}
>
Text
</Text>
</View>
<View style={styles.wrapper}>
<Pressable
onPress={nativeEventLogger('press')}
onLongPress={nativeEventLogger('longPress')}
onPressIn={nativeEventLogger('pressIn')}
onPressOut={nativeEventLogger('pressOut')}
>
<Text>Pressable</Text>
</Pressable>
</View>
<View style={styles.wrapper}>
<TouchableOpacity
onPress={nativeEventLogger('press')}
onLongPress={nativeEventLogger('longPress')}
onPressIn={nativeEventLogger('pressIn')}
onPressOut={nativeEventLogger('pressOut')}
>
<Text>Pressable</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
wrapper: {
padding: 20,
backgroundColor: 'yellow',
},
textInput: {
backgroundColor: 'white',
margin: 20,
padding: 8,
fontSize: 18,
borderWidth: 1,
borderColor: 'grey',
},
});
5 changes: 4 additions & 1 deletion experiments-app/src/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NativeSyntheticEvent } from 'react-native/types';

let lastEventTimeStamp: number | null = null;

export function nativeEventLogger(name: string) {
return (event: NativeSyntheticEvent<unknown>) => {
logEvent(name, event?.nativeEvent);
Expand All @@ -14,5 +16,6 @@ export function customEventLogger(name: string) {

export function logEvent(name: string, ...args: unknown[]) {
// eslint-disable-next-line no-console
console.log(`Event: ${name}`, ...args);
console.log(`[${Date.now() - (lastEventTimeStamp ?? Date.now())}ms] Event: ${name}`, ...args);
lastEventTimeStamp = Date.now();
}
15 changes: 12 additions & 3 deletions src/__tests__/render.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import * as React from 'react';
import { Pressable, Text, TextInput, View } from 'react-native';
import { getConfig, resetToDefaults } from '../config';
import { configure, getConfig, resetToDefaults } from '../config';
import { fireEvent, render, RenderAPI, screen } from '..';

const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
Expand Down Expand Up @@ -247,7 +247,16 @@ test('supports legacy rendering', () => {
expect(screen.root).toBeDefined();
});

test('supports concurrent rendering', () => {
// Enable concurrent rendering globally
configure({ concurrentRoot: true });

test('globally enable concurrent rendering', () => {
render(<View testID="test" />);
expect(screen.root).toBeOnTheScreen();
});

// Enable concurrent rendering locally
test('locally enable concurrent rendering', () => {
render(<View testID="test" />, { concurrentRoot: true });
expect(screen.root).toBeDefined();
expect(screen.root).toBeOnTheScreen();
});
8 changes: 4 additions & 4 deletions src/user-event/press/__tests__/press.real-timers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ describe('userEvent.press with real timers', () => {
);
await userEvent.press(screen.getByText('press me'));

expect(getEventsNames(events)).toEqual(['pressIn', 'press', 'pressOut']);
expect(getEventsNames(events)).toEqual(['pressIn', 'pressOut', 'press']);
});

test('does not trigger on disabled Text', async () => {
Expand Down Expand Up @@ -240,7 +240,7 @@ describe('userEvent.press with real timers', () => {
expect(events).toEqual([]);
});

test('works on TetInput', async () => {
test('works on TextInput', async () => {
const { events, logEvent } = createEventLogger();

render(
Expand All @@ -255,7 +255,7 @@ describe('userEvent.press with real timers', () => {
expect(getEventsNames(events)).toEqual(['pressIn', 'pressOut']);
});

test('does not call onPressIn and onPressOut on non editable TetInput', async () => {
test('does not call onPressIn and onPressOut on non editable TextInput', async () => {
const { events, logEvent } = createEventLogger();

render(
Expand All @@ -270,7 +270,7 @@ describe('userEvent.press with real timers', () => {
expect(events).toEqual([]);
});

test('does not call onPressIn and onPressOut on TetInput with pointer events disabled', async () => {
test('does not call onPressIn and onPressOut on TextInput with pointer events disabled', async () => {
const { events, logEvent } = createEventLogger();

render(
Expand Down
2 changes: 1 addition & 1 deletion src/user-event/press/__tests__/press.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ describe('userEvent.press with fake timers', () => {
);

await userEvent.press(screen.getByText('press me'));
expect(getEventsNames(events)).toEqual(['pressIn', 'press', 'pressOut']);
expect(getEventsNames(events)).toEqual(['pressIn', 'pressOut', 'press']);
});

test('press works on Button', async () => {
Expand Down
14 changes: 11 additions & 3 deletions src/user-event/press/press.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,19 @@ async function emitTextPressEvents(
await wait(config);
dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());

// Emit either `press` or `longPress`.
dispatchEvent(element, options.type, EventBuilder.Common.touch());

await wait(config, options.duration);

// Long press events are emitted before `pressOut`.
if (options.type === 'longPress') {
dispatchEvent(element, 'longPress', EventBuilder.Common.touch());
}

dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());

// Regular press events are emitted after `pressOut`.
if (options.type === 'press') {
dispatchEvent(element, 'press', EventBuilder.Common.touch());
}
}

/**
Expand Down