Skip to content

Commit c0ec10d

Browse files
HeyImChrisalloy
andauthored
macOS Keyboard Support (#657)
* Update RCTCxxBridge.mm * macOS keyboard support * update comment * add key value checks so we don't send all events * macOS keyboard support * update comment * add key value checks so we don't send all events * [ado] Workaround homebrew openssl issue (#669) actions/runner-images#1811 (comment) * Fix detox yarn error with Xcode 12 (#670) * Update RCTCxxBridge.mm * fix detox errors with xcode 12 * only use valid keys, bubble events to super * macOS keyboard support * update comment * add key value checks so we don't send all events * only use valid keys, bubble events to super * macOS keyboard support * add key value checks so we don't send all events * resolve bad merge * update valid key bug, api typo * spacing fix * fix flow errors * fix snapshot tests for new APIs * yarn lint --fix * fix flipper Co-authored-by: Eloy Durán <eloy.de.enige@gmail.com>
1 parent a2f4df1 commit c0ec10d

File tree

23 files changed

+585
-3
lines changed

23 files changed

+585
-3
lines changed

Libraries/Components/Button.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const View = require('./View/View');
2020

2121
const invariant = require('invariant');
2222

23-
import type {PressEvent} from '../Types/CoreEventTypes';
23+
import type {PressEvent, KeyEvent} from '../Types/CoreEventTypes';
2424
import type {FocusEvent, BlurEvent} from './TextInput/TextInput'; // TODO(OSS Candidate ISS#2710739)
2525
import type {ColorValue} from '../StyleSheet/StyleSheetTypes';
2626

@@ -113,6 +113,28 @@ type ButtonProps = $ReadOnly<{|
113113
* Handler to be called when the button loses key focus
114114
*/
115115
onFocus?: ?(e: FocusEvent) => void,
116+
117+
/**
118+
* Handler to be called when a key down press is detected
119+
*/
120+
onKeyDown?: ?(e: KeyEvent) => void,
121+
122+
/**
123+
* Handler to be called when a key up press is detected
124+
*/
125+
onKeyUp?: ?(e: KeyEvent) => void,
126+
127+
/*
128+
* Array of keys to receive key down events for
129+
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
130+
*/
131+
validKeysDown?: ?Array<string>,
132+
133+
/*
134+
* Array of keys to receive key up events for
135+
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
136+
*/
137+
validKeysUp?: ?Array<string>,
116138
// ]TODO(OSS Candidate ISS#2710739)
117139
|}>;
118140

@@ -163,6 +185,10 @@ class Button extends React.Component<ButtonProps> {
163185
testID,
164186
onFocus, // TODO(OSS Candidate ISS#2710739)
165187
onBlur, // TODO(OSS Candidate ISS#2710739)
188+
onKeyDown,
189+
validKeysDown,
190+
validKeysUp,
191+
onKeyUp,
166192
} = this.props;
167193
const buttonStyles = [styles.button];
168194
const textStyles = [styles.text];
@@ -207,6 +233,10 @@ class Button extends React.Component<ButtonProps> {
207233
onPress={onPress}
208234
onFocus={onFocus} // TODO(OSS Candidate ISS#2710739)
209235
onBlur={onBlur} // TODO(OSS Candidate ISS#2710739)
236+
onKeyDown={onKeyDown}
237+
onKeyUp={onKeyUp}
238+
validKeysDown={validKeysDown}
239+
validKeysUp={validKeysUp}
210240
touchSoundDisabled={touchSoundDisabled}>
211241
<View style={buttonStyles}>
212242
<Text style={textStyles} disabled={disabled}>

Libraries/Components/Pressable/__tests__/__snapshots__/Pressable-test.js.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ exports[`<Pressable /> should render as expected: should deep render when mocked
99
onBlur={[Function]}
1010
onClick={[Function]}
1111
onFocus={[Function]}
12+
onKeyDown={[Function]}
13+
onKeyUp={[Function]}
1214
onResponderGrant={[Function]}
1315
onResponderMove={[Function]}
1416
onResponderRelease={[Function]}
@@ -29,6 +31,8 @@ exports[`<Pressable /> should render as expected: should deep render when not mo
2931
onBlur={[Function]}
3032
onClick={[Function]}
3133
onFocus={[Function]}
34+
onKeyDown={[Function]}
35+
onKeyUp={[Function]}
3236
onResponderGrant={[Function]}
3337
onResponderMove={[Function]}
3438
onResponderRelease={[Function]}

Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ exports[`TextInput tests should render as expected: should deep render when mock
1313
onChange={[Function]}
1414
onClick={[Function]}
1515
onFocus={[Function]}
16+
onKeyDown={[Function]}
17+
onKeyUp={[Function]}
1618
onResponderGrant={[Function]}
1719
onResponderMove={[Function]}
1820
onResponderRelease={[Function]}
@@ -42,6 +44,8 @@ exports[`TextInput tests should render as expected: should deep render when not
4244
onChange={[Function]}
4345
onClick={[Function]}
4446
onFocus={[Function]}
47+
onKeyDown={[Function]}
48+
onKeyUp={[Function]}
4549
onResponderGrant={[Function]}
4650
onResponderMove={[Function]}
4751
onResponderRelease={[Function]}

Libraries/Components/Touchable/TouchableNativeFeedback.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ type Props = $ReadOnly<{|
8282
*/
8383
nextFocusUp?: ?number,
8484

85+
/*
86+
* Array of keys to receive key down events for
87+
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
88+
*/
89+
validKeysDown?: ?Array<string>,
90+
91+
/*
92+
* Array of keys to receive key up events for
93+
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
94+
*/
95+
validKeysUp?: ?Array<string>,
96+
8597
/**
8698
* Set to true to add the ripple effect to the foreground of the view, instead
8799
* of the background. This is useful if one of your child views has a
@@ -297,6 +309,8 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
297309
nextFocusUp: this.props.nextFocusUp,
298310
onLayout: this.props.onLayout,
299311
testID: this.props.testID,
312+
validKeysDown: this.props.validKeysDown,
313+
validKeysUp: this.props.validKeysUp,
300314
},
301315
...children,
302316
);

Libraries/Components/Touchable/TouchableOpacity.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ type Props = $ReadOnly<{|
4040
style?: ?ViewStyleProp,
4141

4242
hostRef: React.Ref<typeof Animated.View>,
43+
/*
44+
* Array of keys to receive key down events for
45+
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
46+
*/
47+
validKeysDown?: ?Array<string>,
48+
49+
/*
50+
* Array of keys to receive key up events for
51+
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
52+
*/
53+
validKeysUp?: ?Array<string>,
4354
|}>;
4455

4556
type State = $ReadOnly<{|
@@ -165,6 +176,18 @@ class TouchableOpacity extends React.Component<Props, State> {
165176
this.props.onFocus(event);
166177
}
167178
},
179+
onKeyDown: event => {
180+
if (this.props.onKeyDown != null) {
181+
this.props.onKeyDown(event);
182+
}
183+
},
184+
onKeyUp: event => {
185+
if (this.props.onKeyUp != null) {
186+
this.props.onKeyUp(event);
187+
}
188+
},
189+
validKeysDown: this.props.validKeysDown,
190+
validKeysUp: this.props.validKeysUp,
168191
onLongPress: this.props.onLongPress,
169192
onPress: this.props.onPress,
170193
onPressIn: event => {
@@ -279,6 +302,10 @@ class TouchableOpacity extends React.Component<Props, State> {
279302
onDrop={this.props.onDrop}
280303
onFocus={this.props.onFocus}
281304
onBlur={this.props.onBlur}
305+
onKeyDown={this.props.onKeyDown}
306+
onKeyUp={this.props.onKeyUp}
307+
validKeysDown={this.props.validKeysDown}
308+
validKeysUp={this.props.validKeysUp}
282309
draggedTypes={this.props.draggedTypes} // ]TODO(macOS ISS#2323203)
283310
ref={this.props.hostRef}
284311
{...eventHandlersWithoutBlurAndFocus}>

Libraries/Components/Touchable/TouchableWithoutFeedback.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
2626
import type {
2727
BlurEvent,
2828
FocusEvent,
29+
KeyEvent,
2930
LayoutEvent,
3031
PressEvent,
3132
MouseEvent, // TODO(macOS ISS#2323203)
@@ -64,6 +65,10 @@ type Props = $ReadOnly<{|
6465
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
6566
onBlur?: ?(event: BlurEvent) => mixed,
6667
onFocus?: ?(event: FocusEvent) => mixed,
68+
onKeyDown?: ?(event: KeyEvent) => mixed,
69+
onKeyUp?: ?(event: KeyEvent) => mixed,
70+
validKeysDown?: ?Array<string>,
71+
validKeysUp?: ?Array<string>,
6772
onLayout?: ?(event: LayoutEvent) => mixed,
6873
onLongPress?: ?(event: PressEvent) => mixed,
6974
onPress?: ?(event: PressEvent) => mixed,
@@ -106,6 +111,10 @@ const PASSTHROUGH_PROPS = [
106111
'onAccessibilityAction',
107112
'onBlur',
108113
'onFocus',
114+
'onKeyDown',
115+
'onKeyUp',
116+
'validKeysDown',
117+
'validKeysUp',
109118
'onLayout',
110119
'onMouseEnter', // [TODO(macOS ISS#2323203)
111120
'onMouseLeave',
@@ -227,6 +236,10 @@ function createPressabilityConfig(props: Props): PressabilityConfig {
227236
android_disableSound: props.touchSoundDisabled,
228237
onBlur: props.onBlur,
229238
onFocus: props.onFocus,
239+
onKeyDown: props.onKeyDown,
240+
onKeyUp: props.onKeyUp,
241+
validKeysDown: props.validKeysDown,
242+
validKeysUp: props.validKeysUp,
230243
onLongPress: props.onLongPress,
231244
onPress: props.onPress,
232245
onPressIn: props.onPressIn,

Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ exports[`TouchableHighlight renders correctly 1`] = `
77
enableFocusRing={true}
88
focusable={false}
99
onClick={[Function]}
10+
onKeyDown={[Function]}
11+
onKeyUp={[Function]}
1012
onResponderGrant={[Function]}
1113
onResponderMove={[Function]}
1214
onResponderRelease={[Function]}

Libraries/Components/View/ReactNativeViewAttributes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const UIView = {
4242
onDragEnter: true,
4343
onDragLeave: true,
4444
onDrop: true,
45+
onKeyDown: true,
46+
onKeyUp: true,
47+
validKeysDown: true,
48+
validKeysUp: true,
4549
draggedTypes: true, // ]TODO(macOS ISS#2323203)
4650
style: ReactNativeStyleAttributes,
4751
};

Libraries/Components/View/ReactNativeViewViewConfig.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ const ReactNativeViewConfig = {
4545
captured: 'onFocusCapture',
4646
},
4747
},
48+
topKeyUp: {
49+
phasedRegistrationNames: {
50+
bubbled: 'onKeyUp',
51+
captured: 'onKeyUpCapture',
52+
},
53+
},
54+
topKeyDown: {
55+
phasedRegistrationNames: {
56+
bubbled: 'onKeyDown',
57+
captured: 'onKeyDownCapture',
58+
},
59+
},
4860
topKeyPress: {
4961
phasedRegistrationNames: {
5062
bubbled: 'onKeyPress',
@@ -343,6 +355,8 @@ const ReactNativeViewConfig = {
343355
: {process: require('../../StyleSheet/processTransform')}): any),
344356
translateX: true,
345357
translateY: true,
358+
validKeysDown: true,
359+
validKeysUp: true,
346360
width: true,
347361
zIndex: true,
348362
},

Libraries/Components/View/ReactNativeViewViewConfigMacOS.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ const ReactNativeViewViewConfigMacOS = {
4747
onDragLeave: true,
4848
onDrop: true,
4949
onFocus: true,
50+
onKeyDown: true,
51+
onKeyUp: true,
52+
validKeysDown: true,
53+
validKeysUp: true,
5054
onMouseEnter: true,
5155
onMouseLeave: true,
5256
tooltip: true,

0 commit comments

Comments
 (0)