mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-19 13:11:26 +00:00
[fix] TextInput onKeyPress and onSubmitEditing events
onKeyPress forwards the synthetic keydown event. onSubmitEditing is only called if IME composition is not in progress. Fix #1332
This commit is contained in:
@@ -42,6 +42,7 @@ function createKeyboardEvent(
|
||||
ctrlKey = false,
|
||||
isComposing = false,
|
||||
key = '',
|
||||
keyCode = 0,
|
||||
metaKey = false,
|
||||
preventDefault = () => {},
|
||||
shiftKey = false
|
||||
@@ -52,6 +53,7 @@ function createKeyboardEvent(
|
||||
ctrlKey,
|
||||
isComposing,
|
||||
key,
|
||||
keyCode,
|
||||
metaKey,
|
||||
preventDefault,
|
||||
shiftKey
|
||||
@@ -271,14 +273,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: 'ArrowLeft',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -293,14 +295,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: 'Backspace',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -315,14 +317,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: 'Enter',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -337,14 +339,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: 'Escape',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -359,14 +361,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: ' ',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -381,14 +383,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: 'Tab',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -403,14 +405,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
key: 'a',
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -433,14 +435,14 @@ describe('components/TextInput', () => {
|
||||
expect(onKeyPress).toHaveBeenCalledTimes(1);
|
||||
expect(onKeyPress).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
nativeEvent: {
|
||||
nativeEvent: expect.objectContaining({
|
||||
altKey: true,
|
||||
ctrlKey: true,
|
||||
key: ' ',
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
target: expect.anything()
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -500,6 +502,17 @@ describe('components/TextInput', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('single-line input while composing', () => {
|
||||
const onSubmitEditing = jest.fn();
|
||||
const { container } = render(
|
||||
<TextInput defaultValue="12345" onSubmitEditing={onSubmitEditing} />
|
||||
);
|
||||
const input = findInput(container);
|
||||
input.dispatchEvent(keydown({ key: 'Enter', isComposing: true, keyCode: 13 }));
|
||||
input.dispatchEvent(keydown({ key: 'Enter', isComposing: false, keyCode: 229 }));
|
||||
expect(onSubmitEditing).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('multi-line input', () => {
|
||||
const onSubmitEditing = jest.fn();
|
||||
const { container } = render(
|
||||
@@ -567,17 +580,15 @@ describe('components/TextInput', () => {
|
||||
test('set cursor location', () => {
|
||||
const cursorLocation = { start: 3, end: 3 };
|
||||
const { container: defaultContainer } = render(<TextInput defaultValue="12345" />);
|
||||
const { container: customContainer } = render(
|
||||
<TextInput defaultValue="12345" selection={cursorLocation} />
|
||||
);
|
||||
|
||||
const inputDefaultSelection = findInput(defaultContainer);
|
||||
const inputCustomSelection = findInput(customContainer);
|
||||
|
||||
// default selection is 0
|
||||
expect(inputDefaultSelection.selectionStart).toEqual(0);
|
||||
expect(inputDefaultSelection.selectionEnd).toEqual(0);
|
||||
|
||||
const { container: customContainer } = render(
|
||||
<TextInput defaultValue="12345" selection={cursorLocation} />
|
||||
);
|
||||
const inputCustomSelection = findInput(customContainer);
|
||||
// custom selection sets cursor at custom position
|
||||
expect(inputCustomSelection.selectionStart).toEqual(cursorLocation.start);
|
||||
expect(inputCustomSelection.selectionEnd).toEqual(cursorLocation.end);
|
||||
|
||||
+19
-17
@@ -104,6 +104,12 @@ const forwardPropsList = {
|
||||
|
||||
const pickProps = props => pick(props, forwardPropsList);
|
||||
|
||||
// If an Input Method Editor is processing key input, the 'keyCode' is 229.
|
||||
// https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
|
||||
function isEventComposing(nativeEvent) {
|
||||
return nativeEvent.isComposing || nativeEvent.keyCode === 229;
|
||||
}
|
||||
|
||||
const TextInput = forwardRef<TextInputProps, *>((props, forwardedRef) => {
|
||||
const {
|
||||
autoCapitalize = 'sentences',
|
||||
@@ -267,31 +273,27 @@ const TextInput = forwardRef<TextInputProps, *>((props, forwardedRef) => {
|
||||
const blurOnSubmitDefault = !multiline;
|
||||
const shouldBlurOnSubmit = blurOnSubmit == null ? blurOnSubmitDefault : blurOnSubmit;
|
||||
|
||||
if (onKeyPress) {
|
||||
const keyValue = e.key;
|
||||
const nativeEvent = e.nativeEvent;
|
||||
const isComposing = isEventComposing(nativeEvent);
|
||||
|
||||
if (keyValue) {
|
||||
e.nativeEvent = {
|
||||
altKey: e.altKey,
|
||||
ctrlKey: e.ctrlKey,
|
||||
key: keyValue,
|
||||
metaKey: e.metaKey,
|
||||
shiftKey: e.shiftKey,
|
||||
target: e.target
|
||||
};
|
||||
onKeyPress(e);
|
||||
}
|
||||
if (onKeyPress) {
|
||||
onKeyPress(e);
|
||||
}
|
||||
|
||||
if (!e.isDefaultPrevented() && e.key === 'Enter' && !e.shiftKey) {
|
||||
if (
|
||||
e.key === 'Enter' &&
|
||||
!e.shiftKey &&
|
||||
// Do not call submit if composition is occuring.
|
||||
!isComposing &&
|
||||
!e.isDefaultPrevented()
|
||||
) {
|
||||
if ((blurOnSubmit || !multiline) && onSubmitEditing) {
|
||||
// prevent "Enter" from inserting a newline
|
||||
// prevent "Enter" from inserting a newline or submitting a form
|
||||
e.preventDefault();
|
||||
e.nativeEvent = { target: e.target, text: e.target.value };
|
||||
nativeEvent.text = e.target.value;
|
||||
onSubmitEditing(e);
|
||||
}
|
||||
if (shouldBlurOnSubmit && hostRef.current != null) {
|
||||
// $FlowFixMe
|
||||
hostRef.current.blur();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user