Skip to content

Commit

Permalink
More useKeyProps followup (#2477)
Browse files Browse the repository at this point in the history
* More useKeyProps followup

* Change files

* Revert windows change, add comment
  • Loading branch information
Saadnajmi authored Jan 4, 2023
1 parent 28268c4 commit 60c0e92
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix useKeyProps for Windows + more followup",
"packageName": "@fluentui-react-native/interactive-hooks",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,33 @@ exports[`Pressable with useKeyProps 1`] = `
onStartShouldSetResponder={[Function]}
/>
`;

exports[`useKeyProps called twice 1`] = `
<View
accessible={true}
focusable={true}
keyUpEvents={
Array [
Object {
"key": " ",
},
Object {
"key": "Enter",
},
]
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
37 changes: 27 additions & 10 deletions packages/utils/interactive-hooks/src/__tests__/useKeyProps.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,40 @@ const PressableWithDesktopProps = (props: PressablePropsExtended) => {
};

it('Pressable with useKeyProps', () => {
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
const tree = renderer.create(<PressableWithDesktopProps {...keyboardProps} />).toJSON();
const TestComponent = () => {
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
return <PressableWithDesktopProps {...keyboardProps} />;
};

const tree = renderer.create(<TestComponent />).toJSON();
expect(tree).toMatchSnapshot();
});

it('useKeyProps called twice', () => {
const keyboardProps1 = useKeyProps(dummyFunction, ' ', 'Enter');
const keyboardProps2 = useKeyProps(dummyFunction, ' ', 'Enter');
expect(keyboardProps1).toBe(keyboardProps2);
const TestComponent = () => {
const keyboardProps1 = useKeyProps(dummyFunction, ' ', 'Enter');
const keyboardProps2 = useKeyProps(dummyFunction, ' ', 'Enter');
expect(keyboardProps1).toBe(keyboardProps2);
return <PressableWithDesktopProps {...keyboardProps2} />;
};

const tree = renderer.create(<TestComponent />).toJSON();
expect(tree).toMatchSnapshot();
});

it('Simple Pressable with useKeyProps rendering does not invalidate styling', () => {
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
checkRenderConsistency(() => <PressableWithDesktopProps {...keyboardProps} />, 2);
it('Pressable with useKeyProps simple rendering does not invalidate styling', () => {
const TestComponent = () => {
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
return <PressableWithDesktopProps {...keyboardProps} />;
};

checkRenderConsistency(() => <TestComponent />, 2);
});

it('Pressable with useKeyProps re-renders correctly', () => {
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
checkReRender(() => <PressableWithDesktopProps {...keyboardProps} />, 2);
const TestComponent = () => {
const keyboardProps = useKeyProps(dummyFunction, ' ', 'Enter');
return <PressableWithDesktopProps {...keyboardProps} />;
};
checkReRender(() => <TestComponent />, 2);
});
72 changes: 38 additions & 34 deletions packages/utils/interactive-hooks/src/useKeyProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,7 @@ export const isModifierKey = (nativeEvent: any): boolean => {
);
};

/**
* Re-usable hook for an onKeyDown event.
* @param userCallback The function you want to be called once the key has been activated on key up
* @param keys A string of the key you want to perform some action on. If undefined, always invokes userCallback
* @returns onKeyEvent() - Callback to determine if key was pressed, if so, call userCallback
* @deprecated use the hook `useKeyProps` instead
*/
export function useKeyCallback(userCallback?: KeyCallback, ...keys: string[]) {
const onKeyEvent = React.useCallback(
(e: KeyPressEvent) => {
if (userCallback !== undefined && (keys === undefined || keys.includes(e.nativeEvent.key))) {
userCallback(e);
e.stopPropagation();
}
},
[keys, userCallback],
);

return onKeyEvent;
}

function getKeyCallbackWorker(userCallback?: KeyCallback, ...keys: string[]) {
function keyPressCallback(userCallback?: KeyCallback, ...keys: string[]) {
const onKeyEvent = (e: KeyPressEvent) => {
if (userCallback !== undefined && !isModifierKey(e.nativeEvent) && (keys === undefined || keys.includes(e.nativeEvent.key))) {
userCallback(e);
Expand All @@ -58,18 +37,20 @@ function getKeyUpPropsWorker(userCallback: KeyCallback, ...keys: string[]): KeyP
ios: undefined,
android: undefined,
macos: {
onKeyUp: getKeyCallbackWorker(userCallback, ...keys),
onKeyUp: keyPressCallback(userCallback, ...keys),
validKeysUp: keys,
},
windows: {
onKeyUp: getKeyCallbackWorker(userCallback, ...keys),
keyUpEvents: keys.map((keyCode) => {
return { key: keyCode };
}),
/**
* https://github.com/microsoft/react-native-windows/issues/11049
* Windows doesn't filter on `key` but on `code`, which is quite different ('A' vs 'KeyA' or 'GamepadA').
* While this discrepancy is present, let's not specify `keyUpEvents`.
*/
onKeyUp: keyPressCallback(userCallback, ...keys),
},
// win32
default: {
onKeyUp: getKeyCallbackWorker(userCallback, ...keys),
onKeyUp: keyPressCallback(userCallback, ...keys),
keyUpEvents: keys.map((keyCode) => {
return { key: keyCode };
}),
Expand All @@ -83,18 +64,20 @@ function getKeyDownPropsWorker(userCallback: KeyCallback, ...keys: string[]): Ke
ios: undefined,
android: undefined,
macos: {
onKeyDown: getKeyCallbackWorker(userCallback, ...keys),
onKeyDown: keyPressCallback(userCallback, ...keys),
validKeysDown: keys,
},
windows: {
onKeyDown: getKeyCallbackWorker(userCallback, ...keys),
keyDownEvents: keys.map((keyCode) => {
return { key: keyCode };
}),
/**
* https://github.com/microsoft/react-native-windows/issues/11049
* Windows doesn't filter on `key` but on `code`, which is quite different ('A' vs 'KeyA' or 'GamepadA').
* While this discrepancy is present, let's not specify `keyDownEvents`.
*/
onKeyDown: keyPressCallback(userCallback, ...keys),
},
// win32
default: {
onKeyDown: getKeyCallbackWorker(userCallback, ...keys),
onKeyDown: keyPressCallback(userCallback, ...keys),
keyDownEvents: keys.map((keyCode) => {
return { key: keyCode };
}),
Expand Down Expand Up @@ -132,3 +115,24 @@ export const preferKeyDownForKeyEvents = Platform.select({
* @returns KeyPressProps: An object containing the correct platform specific props to handle key press
*/
export const useKeyProps = preferKeyDownForKeyEvents ? useKeyDownProps : useKeyUpProps;

/**
* Re-usable hook for an onKeyDown event.
* @param userCallback The function you want to be called once the key has been activated on key up
* @param keys A string of the key you want to perform some action on. If undefined, always invokes userCallback
* @returns onKeyEvent() - Callback to determine if key was pressed, if so, call userCallback
* @deprecated use the hook `useKeyProps` instead
*/
export function useKeyCallback(userCallback?: KeyCallback, ...keys: string[]) {
const onKeyEvent = React.useCallback(
(e: KeyPressEvent) => {
if (userCallback !== undefined && (keys === undefined || keys.includes(e.nativeEvent.key))) {
userCallback(e);
e.stopPropagation();
}
},
[keys, userCallback],
);

return onKeyEvent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ export type KeyCallback = (e?: KeyPressEvent) => void;

export type KeyPressProps = {
onKeyDown?: KeyCallback;
validKeysDown?: string[]; // macOS
keyDownEvents?: any[]; // windows
validKeysDown?: string[];
onKeyUp?: KeyCallback;
validKeysUp?: string[]; // macOS
keyUpEvents?: any[]; // windows
validKeysUp?: string[];
};
6 changes: 4 additions & 2 deletions packages/utils/interactive-hooks/src/useKeyProps.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export type KeyCallback = (e?: KeyPressEvent) => void;

export type KeyPressProps = {
onKeyDown?: KeyCallback;
validKeysDown?: string[];
validKeysDown?: string[]; // macOS
keyDownEvents?: any[]; // windows
onKeyUp?: KeyCallback;
validKeysUp?: string[];
validKeysUp?: string[]; // macOS
keyUpEvents?: any[]; // windows
};

0 comments on commit 60c0e92

Please sign in to comment.