Skip to content

Commit 60c0e92

Browse files
authored
More useKeyProps followup (#2477)
* More useKeyProps followup * Change files * Revert windows change, add comment
1 parent 28268c4 commit 60c0e92

6 files changed

+108
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "Fix useKeyProps for Windows + more followup",
4+
"packageName": "@fluentui-react-native/interactive-hooks",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/utils/interactive-hooks/src/__tests__/__snapshots__/useKeyProps.test.tsx.snap

+30
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,33 @@ exports[`Pressable with useKeyProps 1`] = `
2929
onStartShouldSetResponder={[Function]}
3030
/>
3131
`;
32+
33+
exports[`useKeyProps called twice 1`] = `
34+
<View
35+
accessible={true}
36+
focusable={true}
37+
keyUpEvents={
38+
Array [
39+
Object {
40+
"key": " ",
41+
},
42+
Object {
43+
"key": "Enter",
44+
},
45+
]
46+
}
47+
onBlur={[Function]}
48+
onClick={[Function]}
49+
onFocus={[Function]}
50+
onKeyDown={[Function]}
51+
onKeyUp={[Function]}
52+
onMouseEnter={[Function]}
53+
onMouseLeave={[Function]}
54+
onResponderGrant={[Function]}
55+
onResponderMove={[Function]}
56+
onResponderRelease={[Function]}
57+
onResponderTerminate={[Function]}
58+
onResponderTerminationRequest={[Function]}
59+
onStartShouldSetResponder={[Function]}
60+
/>
61+
`;

packages/utils/interactive-hooks/src/__tests__/useKeyProps.test.tsx

+27-10
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,40 @@ const PressableWithDesktopProps = (props: PressablePropsExtended) => {
1515
};
1616

1717
it('Pressable with useKeyProps', () => {
18-
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
19-
const tree = renderer.create(<PressableWithDesktopProps {...keyboardProps} />).toJSON();
18+
const TestComponent = () => {
19+
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
20+
return <PressableWithDesktopProps {...keyboardProps} />;
21+
};
22+
23+
const tree = renderer.create(<TestComponent />).toJSON();
2024
expect(tree).toMatchSnapshot();
2125
});
2226

2327
it('useKeyProps called twice', () => {
24-
const keyboardProps1 = useKeyProps(dummyFunction, ' ', 'Enter');
25-
const keyboardProps2 = useKeyProps(dummyFunction, ' ', 'Enter');
26-
expect(keyboardProps1).toBe(keyboardProps2);
28+
const TestComponent = () => {
29+
const keyboardProps1 = useKeyProps(dummyFunction, ' ', 'Enter');
30+
const keyboardProps2 = useKeyProps(dummyFunction, ' ', 'Enter');
31+
expect(keyboardProps1).toBe(keyboardProps2);
32+
return <PressableWithDesktopProps {...keyboardProps2} />;
33+
};
34+
35+
const tree = renderer.create(<TestComponent />).toJSON();
36+
expect(tree).toMatchSnapshot();
2737
});
2838

29-
it('Simple Pressable with useKeyProps rendering does not invalidate styling', () => {
30-
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
31-
checkRenderConsistency(() => <PressableWithDesktopProps {...keyboardProps} />, 2);
39+
it('Pressable with useKeyProps simple rendering does not invalidate styling', () => {
40+
const TestComponent = () => {
41+
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
42+
return <PressableWithDesktopProps {...keyboardProps} />;
43+
};
44+
45+
checkRenderConsistency(() => <TestComponent />, 2);
3246
});
3347

3448
it('Pressable with useKeyProps re-renders correctly', () => {
35-
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
36-
checkReRender(() => <PressableWithDesktopProps {...keyboardProps} />, 2);
49+
const TestComponent = () => {
50+
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
51+
return <PressableWithDesktopProps {...keyboardProps} />;
52+
};
53+
checkReRender(() => <TestComponent />, 2);
3754
});

packages/utils/interactive-hooks/src/useKeyProps.ts

+38-34
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,7 @@ export const isModifierKey = (nativeEvent: any): boolean => {
2222
);
2323
};
2424

25-
/**
26-
* Re-usable hook for an onKeyDown event.
27-
* @param userCallback The function you want to be called once the key has been activated on key up
28-
* @param keys A string of the key you want to perform some action on. If undefined, always invokes userCallback
29-
* @returns onKeyEvent() - Callback to determine if key was pressed, if so, call userCallback
30-
* @deprecated use the hook `useKeyProps` instead
31-
*/
32-
export function useKeyCallback(userCallback?: KeyCallback, ...keys: string[]) {
33-
const onKeyEvent = React.useCallback(
34-
(e: KeyPressEvent) => {
35-
if (userCallback !== undefined && (keys === undefined || keys.includes(e.nativeEvent.key))) {
36-
userCallback(e);
37-
e.stopPropagation();
38-
}
39-
},
40-
[keys, userCallback],
41-
);
42-
43-
return onKeyEvent;
44-
}
45-
46-
function getKeyCallbackWorker(userCallback?: KeyCallback, ...keys: string[]) {
25+
function keyPressCallback(userCallback?: KeyCallback, ...keys: string[]) {
4726
const onKeyEvent = (e: KeyPressEvent) => {
4827
if (userCallback !== undefined && !isModifierKey(e.nativeEvent) && (keys === undefined || keys.includes(e.nativeEvent.key))) {
4928
userCallback(e);
@@ -58,18 +37,20 @@ function getKeyUpPropsWorker(userCallback: KeyCallback, ...keys: string[]): KeyP
5837
ios: undefined,
5938
android: undefined,
6039
macos: {
61-
onKeyUp: getKeyCallbackWorker(userCallback, ...keys),
40+
onKeyUp: keyPressCallback(userCallback, ...keys),
6241
validKeysUp: keys,
6342
},
6443
windows: {
65-
onKeyUp: getKeyCallbackWorker(userCallback, ...keys),
66-
keyUpEvents: keys.map((keyCode) => {
67-
return { key: keyCode };
68-
}),
44+
/**
45+
* https://github.com/microsoft/react-native-windows/issues/11049
46+
* Windows doesn't filter on `key` but on `code`, which is quite different ('A' vs 'KeyA' or 'GamepadA').
47+
* While this discrepancy is present, let's not specify `keyUpEvents`.
48+
*/
49+
onKeyUp: keyPressCallback(userCallback, ...keys),
6950
},
7051
// win32
7152
default: {
72-
onKeyUp: getKeyCallbackWorker(userCallback, ...keys),
53+
onKeyUp: keyPressCallback(userCallback, ...keys),
7354
keyUpEvents: keys.map((keyCode) => {
7455
return { key: keyCode };
7556
}),
@@ -83,18 +64,20 @@ function getKeyDownPropsWorker(userCallback: KeyCallback, ...keys: string[]): Ke
8364
ios: undefined,
8465
android: undefined,
8566
macos: {
86-
onKeyDown: getKeyCallbackWorker(userCallback, ...keys),
67+
onKeyDown: keyPressCallback(userCallback, ...keys),
8768
validKeysDown: keys,
8869
},
8970
windows: {
90-
onKeyDown: getKeyCallbackWorker(userCallback, ...keys),
91-
keyDownEvents: keys.map((keyCode) => {
92-
return { key: keyCode };
93-
}),
71+
/**
72+
* https://github.com/microsoft/react-native-windows/issues/11049
73+
* Windows doesn't filter on `key` but on `code`, which is quite different ('A' vs 'KeyA' or 'GamepadA').
74+
* While this discrepancy is present, let's not specify `keyDownEvents`.
75+
*/
76+
onKeyDown: keyPressCallback(userCallback, ...keys),
9477
},
9578
// win32
9679
default: {
97-
onKeyDown: getKeyCallbackWorker(userCallback, ...keys),
80+
onKeyDown: keyPressCallback(userCallback, ...keys),
9881
keyDownEvents: keys.map((keyCode) => {
9982
return { key: keyCode };
10083
}),
@@ -132,3 +115,24 @@ export const preferKeyDownForKeyEvents = Platform.select({
132115
* @returns KeyPressProps: An object containing the correct platform specific props to handle key press
133116
*/
134117
export const useKeyProps = preferKeyDownForKeyEvents ? useKeyDownProps : useKeyUpProps;
118+
119+
/**
120+
* Re-usable hook for an onKeyDown event.
121+
* @param userCallback The function you want to be called once the key has been activated on key up
122+
* @param keys A string of the key you want to perform some action on. If undefined, always invokes userCallback
123+
* @returns onKeyEvent() - Callback to determine if key was pressed, if so, call userCallback
124+
* @deprecated use the hook `useKeyProps` instead
125+
*/
126+
export function useKeyCallback(userCallback?: KeyCallback, ...keys: string[]) {
127+
const onKeyEvent = React.useCallback(
128+
(e: KeyPressEvent) => {
129+
if (userCallback !== undefined && (keys === undefined || keys.includes(e.nativeEvent.key))) {
130+
userCallback(e);
131+
e.stopPropagation();
132+
}
133+
},
134+
[keys, userCallback],
135+
);
136+
137+
return onKeyEvent;
138+
}

packages/utils/interactive-hooks/src/useKeyProps.types.macos.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ export type KeyCallback = (e?: KeyPressEvent) => void;
2525

2626
export type KeyPressProps = {
2727
onKeyDown?: KeyCallback;
28-
validKeysDown?: string[]; // macOS
29-
keyDownEvents?: any[]; // windows
28+
validKeysDown?: string[];
3029
onKeyUp?: KeyCallback;
31-
validKeysUp?: string[]; // macOS
32-
keyUpEvents?: any[]; // windows
30+
validKeysUp?: string[];
3331
};

packages/utils/interactive-hooks/src/useKeyProps.types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ export type KeyCallback = (e?: KeyPressEvent) => void;
66

77
export type KeyPressProps = {
88
onKeyDown?: KeyCallback;
9-
validKeysDown?: string[];
9+
validKeysDown?: string[]; // macOS
10+
keyDownEvents?: any[]; // windows
1011
onKeyUp?: KeyCallback;
11-
validKeysUp?: string[];
12+
validKeysUp?: string[]; // macOS
13+
keyUpEvents?: any[]; // windows
1214
};

0 commit comments

Comments
 (0)