Skip to content

Commit d13bc56

Browse files
Abbondanzometa-codesync[bot]
authored andcommitted
Add Key Events example to RNTester (facebook#54327)
Summary: Pull Request resolved: facebook#54327 Introduces a test bench into RNTester for easily logging onKeyDown/onKeyUp events in the context of a focusable view and text input field Changelog: [Internal] Reviewed By: alanleedev Differential Revision: D85022653 fbshipit-source-id: d5b4cb5c0f523d720ea45d0df4b8f9bc902b1436
1 parent 5f14001 commit d13bc56

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
14+
import type {KeyEvent, NativeSyntheticEvent} from 'react-native';
15+
16+
import RNTesterButton from '../../components/RNTesterButton';
17+
import RNTesterText from '../../components/RNTesterText';
18+
import React, {useCallback, useState} from 'react';
19+
import {ScrollView, StyleSheet, TextInput, View} from 'react-native';
20+
21+
type KeyEventLog = {
22+
timeStamp: number,
23+
type: string,
24+
modifiers: string,
25+
...KeyEvent,
26+
};
27+
28+
function KeyEventExample(): React.Node {
29+
const [log, setLog] = useState<Array<KeyEventLog>>([]);
30+
const [viewFocused, setViewFocused] = useState(false);
31+
32+
const clearLog = useCallback(() => {
33+
setLog([]);
34+
}, []);
35+
36+
const appendLog = useCallback(
37+
(eventType: string, e: NativeSyntheticEvent<KeyEvent>) => {
38+
const nativeEvent = e.nativeEvent;
39+
const modifiers = [];
40+
if (nativeEvent.altKey) {
41+
modifiers.push('Alt');
42+
}
43+
if (nativeEvent.ctrlKey) {
44+
modifiers.push('Ctrl');
45+
}
46+
if (nativeEvent.metaKey) {
47+
modifiers.push('Meta');
48+
}
49+
if (nativeEvent.shiftKey) {
50+
modifiers.push('Shift');
51+
}
52+
53+
const newEntry: KeyEventLog = {
54+
...nativeEvent,
55+
timeStamp: e.timeStamp,
56+
type: eventType,
57+
modifiers: modifiers.length > 0 ? modifiers.join('+') : 'none',
58+
};
59+
60+
const limit = 20;
61+
62+
setLog(oldLog => {
63+
return [newEntry, ...oldLog.slice(0, limit - 1)];
64+
});
65+
},
66+
[],
67+
);
68+
69+
return (
70+
<ScrollView>
71+
<View style={styles.container}>
72+
<RNTesterText style={styles.description}>
73+
Android keyboard events capture key presses from physical keyboards,
74+
remote controls, and game controllers. This example demonstrates
75+
onKeyDown and onKeyUp events.
76+
</RNTesterText>
77+
78+
<RNTesterText style={styles.sectionTitle}>Focusable View</RNTesterText>
79+
<View
80+
focusable={true}
81+
style={[styles.focusableBox, viewFocused && styles.focusedBox]}
82+
onFocus={() => setViewFocused(true)}
83+
onBlur={() => setViewFocused(false)}
84+
onKeyDown={e => {
85+
appendLog('KeyDown - View', e);
86+
}}
87+
onKeyUp={e => {
88+
appendLog('KeyUp - View', e);
89+
}}>
90+
<RNTesterText style={styles.boxText}>Focus Me!</RNTesterText>
91+
</View>
92+
93+
<RNTesterText style={styles.sectionTitle}>
94+
RNTesterTextInput
95+
</RNTesterText>
96+
<TextInput
97+
placeholder="Type here to test keyboard events"
98+
style={styles.textInput}
99+
onKeyDown={e => {
100+
appendLog('KeyDown - Input', e);
101+
}}
102+
onKeyUp={e => {
103+
appendLog('KeyUp - Input', e);
104+
}}
105+
/>
106+
107+
<RNTesterButton onPress={clearLog}>Clear Event Log</RNTesterButton>
108+
109+
<RNTesterText style={styles.logTitle}>
110+
Event Log (Most Recent First):
111+
</RNTesterText>
112+
<View style={styles.logContainer}>
113+
{log.length === 0 ? (
114+
<RNTesterText style={styles.emptyLog}>
115+
No events yet. Focus a view and press keys.
116+
</RNTesterText>
117+
) : (
118+
log.map((entry, index) => (
119+
<View key={`${entry.timeStamp}-${index}`} style={styles.logEntry}>
120+
<RNTesterText style={styles.logType}>{entry.type}</RNTesterText>
121+
<RNTesterText style={styles.logKey}>
122+
Key: "{entry.key}" (code: {entry.code})
123+
</RNTesterText>
124+
{entry.modifiers !== 'none' && (
125+
<RNTesterText style={styles.logModifiers}>
126+
Modifiers: {entry.modifiers}
127+
</RNTesterText>
128+
)}
129+
</View>
130+
))
131+
)}
132+
</View>
133+
</View>
134+
</ScrollView>
135+
);
136+
}
137+
138+
const styles = StyleSheet.create({
139+
container: {
140+
padding: 10,
141+
},
142+
description: {
143+
fontSize: 14,
144+
marginBottom: 16,
145+
},
146+
sectionTitle: {
147+
fontSize: 16,
148+
fontWeight: 'bold',
149+
marginTop: 8,
150+
marginBottom: 4,
151+
},
152+
info: {
153+
fontSize: 12,
154+
marginBottom: 8,
155+
fontStyle: 'italic',
156+
},
157+
focusedBox: {
158+
borderColor: '#f00',
159+
},
160+
focusableBox: {
161+
height: 60,
162+
marginBottom: 12,
163+
borderWidth: 2,
164+
borderColor: '#999',
165+
borderRadius: 4,
166+
padding: 10,
167+
justifyContent: 'center',
168+
alignItems: 'center',
169+
},
170+
boxText: {
171+
fontSize: 14,
172+
fontWeight: '600',
173+
},
174+
textInput: {
175+
height: 44,
176+
marginBottom: 12,
177+
backgroundColor: '#fff',
178+
borderWidth: 1,
179+
borderColor: '#ccc',
180+
borderRadius: 4,
181+
padding: 10,
182+
fontSize: 14,
183+
},
184+
multilineInput: {
185+
height: 80,
186+
textAlignVertical: 'top',
187+
},
188+
logTitle: {
189+
fontSize: 16,
190+
fontWeight: 'bold',
191+
marginTop: 20,
192+
marginBottom: 8,
193+
},
194+
logContainer: {
195+
borderRadius: 4,
196+
padding: 8,
197+
minHeight: 100,
198+
maxHeight: 300,
199+
},
200+
emptyLog: {
201+
fontSize: 12,
202+
fontStyle: 'italic',
203+
textAlign: 'center',
204+
paddingVertical: 20,
205+
},
206+
logEntry: {
207+
borderRadius: 3,
208+
padding: 8,
209+
marginBottom: 6,
210+
borderLeftWidth: 3,
211+
borderLeftColor: '#2196F3',
212+
},
213+
logType: {
214+
fontSize: 13,
215+
fontWeight: 'bold',
216+
marginBottom: 2,
217+
},
218+
logKey: {
219+
fontSize: 12,
220+
221+
marginBottom: 2,
222+
},
223+
logModifiers: {
224+
fontSize: 10,
225+
fontStyle: 'italic',
226+
},
227+
});
228+
229+
export default {
230+
title: 'Key Events',
231+
description:
232+
'Examples demonstrating Android keyboard events including D-Pad navigation, media controls, TV remote keys, and physical keyboard input with modifier keys.',
233+
examples: [
234+
{
235+
title: 'KeyEvent Example',
236+
render: function (): React.Node {
237+
return <KeyEventExample />;
238+
},
239+
},
240+
] as Array<RNTesterModuleExample>,
241+
};

packages/rn-tester/js/utils/RNTesterList.android.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ const Components: Array<RNTesterModuleInfo> = [
5858
key: 'KeyboardAvoidingViewExample',
5959
module: require('../examples/KeyboardAvoidingView/KeyboardAvoidingViewExample'),
6060
},
61+
{
62+
key: 'KeyEvents',
63+
module: require('../examples/KeyboardEventsExample/KeyboardEventsExample')
64+
.default,
65+
},
6166
{
6267
key: 'ModalExample',
6368
category: 'UI',

0 commit comments

Comments
 (0)