diff --git a/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
index e896a77f810..e12b1ad054c 100644
--- a/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
+++ b/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
@@ -66,6 +66,8 @@ export type ReturnKeyType =
| 'route'
| 'yahoo';
+export type SubmitBehavior = 'submit' | 'blurAndSubmit' | 'newline';
+
export type NativeProps = $ReadOnly<{|
// This allows us to inherit everything from ViewProps except for style (see below)
// This must be commented for Fabric codegen to work.
@@ -520,9 +522,34 @@ export type NativeProps = $ReadOnly<{|
* multiline fields. Note that for multiline fields, setting `blurOnSubmit`
* to `true` means that pressing return will blur the field and trigger the
* `onSubmitEditing` event instead of inserting a newline into the field.
+ *
+ * @deprecated
+ * Note that `submitBehavior` now takes the place of `blurOnSubmit` and will
+ * override any behavior defined by `blurOnSubmit`.
+ * @see submitBehavior
*/
blurOnSubmit?: ?boolean,
+ /**
+ * When the return key is pressed,
+ *
+ * For single line inputs:
+ *
+ * - `'newline`' defaults to `'blurAndSubmit'`
+ * - `undefined` defaults to `'blurAndSubmit'`
+ *
+ * For multiline inputs:
+ *
+ * - `'newline'` adds a newline
+ * - `undefined` defaults to `'newline'`
+ *
+ * For both single line and multiline inputs:
+ *
+ * - `'submit'` will only send a submit event and not blur the input
+ * - `'blurAndSubmit`' will both blur the input and send a submit event
+ */
+ submitBehavior?: ?SubmitBehavior,
+
/**
* Note that not all Text styles are supported, an incomplete list of what is not supported includes:
*
@@ -657,7 +684,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
process: require('../../StyleSheet/processColor'),
},
textDecorationLine: true,
- blurOnSubmit: true,
+ submitBehavior: true,
textAlignVertical: true,
fontStyle: true,
textShadowOffset: true,
diff --git a/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/Libraries/Components/TextInput/RCTTextInputViewConfig.js
index 52134db4419..68ee77143d1 100644
--- a/Libraries/Components/TextInput/RCTTextInputViewConfig.js
+++ b/Libraries/Components/TextInput/RCTTextInputViewConfig.js
@@ -128,7 +128,7 @@ const RCTTextInputViewConfig = {
keyboardType: true,
selection: true,
returnKeyType: true,
- blurOnSubmit: true,
+ submitBehavior: true,
mostRecentEventCount: true,
scrollEnabled: true,
selectionColor: {process: require('../../StyleSheet/processColor')},
diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js
index 8fa117192fa..8e3296f5d2c 100644
--- a/Libraries/Components/TextInput/TextInput.js
+++ b/Libraries/Components/TextInput/TextInput.js
@@ -173,6 +173,8 @@ export type ReturnKeyType =
| 'route'
| 'yahoo';
+export type SubmitBehavior = 'submit' | 'blurAndSubmit' | 'newline';
+
export type AutoCapitalize = 'none' | 'sentences' | 'words' | 'characters';
export type TextContentType =
@@ -502,15 +504,6 @@ export type Props = $ReadOnly<{|
*/
allowFontScaling?: ?boolean,
- /**
- * If `true`, the text field will blur when submitted.
- * The default value is true for single-line fields and false for
- * multiline fields. Note that for multiline fields, setting `blurOnSubmit`
- * to `true` means that pressing return will blur the field and trigger the
- * `onSubmitEditing` event instead of inserting a newline into the field.
- */
- blurOnSubmit?: ?boolean,
-
/**
* If `true`, caret is hidden. The default value is `false`.
*
@@ -775,6 +768,40 @@ export type Props = $ReadOnly<{|
*/
selectTextOnFocus?: ?boolean,
+ /**
+ * If `true`, the text field will blur when submitted.
+ * The default value is true for single-line fields and false for
+ * multiline fields. Note that for multiline fields, setting `blurOnSubmit`
+ * to `true` means that pressing return will blur the field and trigger the
+ * `onSubmitEditing` event instead of inserting a newline into the field.
+ *
+ * @deprecated
+ * Note that `submitBehavior` now takes the place of `blurOnSubmit` and will
+ * override any behavior defined by `blurOnSubmit`.
+ * @see submitBehavior
+ */
+ blurOnSubmit?: ?boolean,
+
+ /**
+ * When the return key is pressed,
+ *
+ * For single line inputs:
+ *
+ * - `'newline`' defaults to `'blurAndSubmit'`
+ * - `undefined` defaults to `'blurAndSubmit'`
+ *
+ * For multiline inputs:
+ *
+ * - `'newline'` adds a newline
+ * - `undefined` defaults to `'newline'`
+ *
+ * For both single line and multiline inputs:
+ *
+ * - `'submit'` will only send a submit event and not blur the input
+ * - `'blurAndSubmit`' will both blur the input and send a submit event
+ */
+ submitBehavior?: ?SubmitBehavior,
+
/**
* Note that not all Text styles are supported, an incomplete list of what is not supported includes:
*
@@ -1185,9 +1212,31 @@ function InternalTextInput(props: Props): React.Node {
let textInput = null;
- // The default value for `blurOnSubmit` is true for single-line fields and
- // false for multi-line fields.
- const blurOnSubmit = props.blurOnSubmit ?? !props.multiline;
+ const multiline = props.multiline ?? false;
+
+ let submitBehavior: SubmitBehavior;
+ if (props.submitBehavior != null) {
+ // `submitBehavior` is set explicitly
+ if (!multiline && props.submitBehavior === 'newline') {
+ // For single line text inputs, `'newline'` is not a valid option
+ submitBehavior = 'blurAndSubmit';
+ } else {
+ submitBehavior = props.submitBehavior;
+ }
+ } else if (multiline) {
+ if (props.blurOnSubmit === true) {
+ submitBehavior = 'blurAndSubmit';
+ } else {
+ submitBehavior = 'newline';
+ }
+ } else {
+ // Single line
+ if (props.blurOnSubmit !== false) {
+ submitBehavior = 'blurAndSubmit';
+ } else {
+ submitBehavior = 'submit';
+ }
+ }
const accessible = props.accessible !== false;
const focusable = props.focusable !== false;
@@ -1246,7 +1295,7 @@ function InternalTextInput(props: Props): React.Node {
{...props}
{...eventHandlers}
accessible={accessible}
- blurOnSubmit={blurOnSubmit}
+ submitBehavior={submitBehavior}
caretHidden={caretHidden}
dataDetectorTypes={props.dataDetectorTypes}
focusable={focusable}
@@ -1294,7 +1343,7 @@ function InternalTextInput(props: Props): React.Node {
{...eventHandlers}
accessible={accessible}
autoCapitalize={autoCapitalize}
- blurOnSubmit={blurOnSubmit}
+ submitBehavior={submitBehavior}
caretHidden={caretHidden}
children={children}
disableFullscreenUI={props.disableFullscreenUI}
diff --git a/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap b/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap
index 874fae2b158..d83ac32fe9b 100644
--- a/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap
+++ b/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap
@@ -4,7 +4,6 @@ exports[`TextInput tests should render as expected: should deep render when mock
@@ -33,7 +33,6 @@ exports[`TextInput tests should render as expected: should deep render when not
diff --git a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m
index 0cbaea87b42..51d70e0c684 100644
--- a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m
+++ b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m
@@ -19,9 +19,6 @@
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super initWithBridge:bridge]) {
- // `blurOnSubmit` defaults to `false` for by design.
- self.blurOnSubmit = NO;
-
_backedTextInputView = [[RCTUITextView alloc] initWithFrame:self.bounds];
_backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_backedTextInputView.textInputDelegate = self;
diff --git a/Libraries/Text/TextInput/RCTBackedTextInputDelegate.h b/Libraries/Text/TextInput/RCTBackedTextInputDelegate.h
index c2a4362f028..0f073dce992 100644
--- a/Libraries/Text/TextInput/RCTBackedTextInputDelegate.h
+++ b/Libraries/Text/TextInput/RCTBackedTextInputDelegate.h
@@ -19,9 +19,11 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)textInputShouldEndEditing; // Return `YES` to allow editing to stop and to resign first responder status. `NO` to disallow the editing session to end.
- (void)textInputDidEndEditing; // May be called if forced even if `textInputShouldEndEditing` returns `NO` (e.g. view removed from window) or `[textInput endEditing:YES]` called.
-- (BOOL)textInputShouldReturn; // May be called right before `textInputShouldEndEditing` if "Return" button was pressed.
+- (BOOL)textInputShouldReturn; // May be called right before `textInputShouldEndEditing` if "Return" button was pressed. Dismisses keyboard if true
- (void)textInputDidReturn;
+- (BOOL)textInputShouldSubmitOnReturn; // Checks whether to submit when return is pressed and emits an event if true.
+
/*
* Called before any change in the TextInput. The delegate has the opportunity to change the replacement string or reject the change completely.
* To change the replacement, return the changed version of the `text`.
diff --git a/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m b/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
index c6c254ce5db..f46fa123241 100644
--- a/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
+++ b/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
@@ -100,6 +100,8 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
- (BOOL)textFieldShouldReturn:(__unused UITextField *)textField
{
+ // Ignore the value of whether we submitted; just make sure the submit event is called if necessary.
+ [_backedTextInputView.textInputDelegate textInputShouldSubmitOnReturn];
return [_backedTextInputView.textInputDelegate textInputShouldReturn];
}
@@ -209,10 +211,14 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
{
// Custom implementation of `textInputShouldReturn` and `textInputDidReturn` pair for `UITextView`.
if (!_backedTextInputView.textWasPasted && [text isEqualToString:@"\n"]) {
- if ([_backedTextInputView.textInputDelegate textInputShouldReturn]) {
+ const BOOL shouldSubmit = [_backedTextInputView.textInputDelegate textInputShouldSubmitOnReturn];
+ const BOOL shouldReturn = [_backedTextInputView.textInputDelegate textInputShouldReturn];
+ if (shouldReturn) {
[_backedTextInputView.textInputDelegate textInputDidReturn];
[_backedTextInputView endEditing:NO];
return NO;
+ } else if (shouldSubmit) {
+ return NO;
}
}
diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.h b/Libraries/Text/TextInput/RCTBaseTextInputView.h
index 5c6c5cfcfa4..967101da624 100644
--- a/Libraries/Text/TextInput/RCTBaseTextInputView.h
+++ b/Libraries/Text/TextInput/RCTBaseTextInputView.h
@@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) NSInteger mostRecentEventCount;
@property (nonatomic, assign, readonly) NSInteger nativeEventCount;
@property (nonatomic, assign) BOOL autoFocus;
-@property (nonatomic, assign) BOOL blurOnSubmit;
+@property (nonatomic, copy) NSString *submitBehavior;
@property (nonatomic, assign) BOOL selectTextOnFocus;
@property (nonatomic, assign) BOOL clearTextOnFocus;
@property (nonatomic, assign) BOOL secureTextEntry;
diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m
index a4924923f19..c719b562891 100644
--- a/Libraries/Text/TextInput/RCTBaseTextInputView.m
+++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m
@@ -350,20 +350,27 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
eventCount:_nativeEventCount];
}
+- (BOOL)textInputShouldSubmitOnReturn
+{
+ const BOOL shouldSubmit = [_submitBehavior isEqualToString:@"blurAndSubmit"] || [_submitBehavior isEqualToString:@"submit"];
+ if (shouldSubmit) {
+ // We send `submit` event here, in `textInputShouldSubmit`
+ // (not in `textInputDidReturn)`, because of semantic of the event:
+ // `onSubmitEditing` is called when "Submit" button
+ // (the blue key on onscreen keyboard) did pressed
+ // (no connection to any specific "submitting" process).
+ [_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
+ reactTag:self.reactTag
+ text:[self.backedTextInputView.attributedText.string copy]
+ key:nil
+ eventCount:_nativeEventCount];
+ }
+ return shouldSubmit;
+}
+
- (BOOL)textInputShouldReturn
{
- // We send `submit` event here, in `textInputShouldReturn`
- // (not in `textInputDidReturn)`, because of semantic of the event:
- // `onSubmitEditing` is called when "Submit" button
- // (the blue key on onscreen keyboard) did pressed
- // (no connection to any specific "submitting" process).
- [_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
- reactTag:self.reactTag
- text:[self.backedTextInputView.attributedText.string copy]
- key:nil
- eventCount:_nativeEventCount];
-
- return _blurOnSubmit;
+ return [_submitBehavior isEqualToString:@"blurAndSubmit"];
}
- (void)textInputDidReturn
diff --git a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
index b1ecf854330..fc59b088fe6 100644
--- a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
+++ b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
@@ -49,7 +49,7 @@ RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UI
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, backedTextInputView.scrollEnabled, BOOL)
RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL)
RCT_EXPORT_VIEW_PROPERTY(autoFocus, BOOL)
-RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(submitBehavior, NSString)
RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType)
RCT_EXPORT_VIEW_PROPERTY(showSoftInputOnFocus, BOOL)
diff --git a/Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputView.m b/Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputView.m
index 51feea12f86..f2d6ee4c849 100644
--- a/Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputView.m
+++ b/Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputView.m
@@ -19,8 +19,8 @@
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super initWithBridge:bridge]) {
- // `blurOnSubmit` defaults to `true` for by design.
- self.blurOnSubmit = YES;
+ // `submitBehavior` defaults to `"blurAndSubmit"` for by design.
+ self.submitBehavior = @"blurAndSubmit";
_backedTextInputView = [[RCTUITextField alloc] initWithFrame:self.bounds];
_backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
index 3631fb45cbc..c7c0f1d1a79 100644
--- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
+++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
@@ -300,20 +300,25 @@ using namespace facebook::react;
}
}
-- (BOOL)textInputShouldReturn
+- (BOOL)textInputShouldSubmitOnReturn
{
- // We send `submit` event here, in `textInputShouldReturn`
+ const SubmitBehavior submitBehavior = [self getSubmitBehavior];
+ const BOOL shouldSubmit = submitBehavior == SubmitBehavior::Submit || submitBehavior == SubmitBehavior::BlurAndSubmit;
+ // We send `submit` event here, in `textInputShouldSubmitOnReturn`
// (not in `textInputDidReturn)`, because of semantic of the event:
// `onSubmitEditing` is called when "Submit" button
// (the blue key on onscreen keyboard) did pressed
// (no connection to any specific "submitting" process).
- if (_eventEmitter) {
+ if (_eventEmitter && shouldSubmit) {
std::static_pointer_cast(_eventEmitter)->onSubmitEditing([self _textInputMetrics]);
}
+ return shouldSubmit;
+}
- auto const &props = *std::static_pointer_cast(_props);
- return props.traits.blurOnSubmit;
+- (BOOL)textInputShouldReturn
+{
+ return [self getSubmitBehavior] == SubmitBehavior::BlurAndSubmit;
}
- (void)textInputDidReturn
@@ -644,6 +649,19 @@ using namespace facebook::react;
}
}
+- (SubmitBehavior)getSubmitBehavior
+{
+ auto const &props = *std::static_pointer_cast(_props);
+ const SubmitBehavior submitBehaviorDefaultable = props.traits.submitBehavior;
+
+ // We should always have a non-default `submitBehavior`, but in case we don't, set it based on multiline.
+ if (submitBehaviorDefaultable == SubmitBehavior::Default) {
+ return props.traits.multiline ? SubmitBehavior::Newline : SubmitBehavior::BlurAndSubmit;
+ }
+
+ return submitBehaviorDefaultable;
+}
+
@end
Class RCTTextInputCls(void)
diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java
index b4b3aaa7b4b..f58bd22b301 100644
--- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java
+++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java
@@ -232,12 +232,12 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase {
private void fireEditorActionAndCheckRecording(
final ReactEditText reactEditText, final int actionId) throws Throwable {
- fireEditorActionAndCheckRecording(reactEditText, actionId, true);
- fireEditorActionAndCheckRecording(reactEditText, actionId, false);
+ fireEditorActionAndCheckRecording(reactEditText, actionId, "blurAndSubmit");
+ fireEditorActionAndCheckRecording(reactEditText, actionId, "newline");
}
private void fireEditorActionAndCheckRecording(
- final ReactEditText reactEditText, final int actionId, final boolean blurOnSubmit)
+ final ReactEditText reactEditText, final int actionId, final String submitBehavior)
throws Throwable {
mRecordingModule.reset();
@@ -246,14 +246,14 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase {
@Override
public void run() {
reactEditText.requestFocusFromJS();
- reactEditText.setBlurOnSubmit(blurOnSubmit);
+ reactEditText.setSubmitBehavior(submitBehavior);
reactEditText.onEditorAction(actionId);
}
});
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
- assertEquals(!blurOnSubmit, reactEditText.isFocused());
+ assertEquals(!submitBehavior.equals("blurAndSubmit"), reactEditText.isFocused());
}
/**
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
index 4847b9b5d86..14863e49801 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
@@ -95,7 +95,7 @@ public class ReactEditText extends AppCompatEditText
private @Nullable TextWatcherDelegator mTextWatcherDelegator;
private int mStagedInputType;
protected boolean mContainsImages;
- private @Nullable Boolean mBlurOnSubmit;
+ private @Nullable String mSubmitBehavior = null;
private boolean mDisableFullscreen;
private @Nullable String mReturnKeyType;
private @Nullable SelectionWatcher mSelectionWatcher;
@@ -135,7 +135,6 @@ public class ReactEditText extends AppCompatEditText
mDefaultGravityVertical = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
mNativeEventCount = 0;
mIsSettingTextFromJS = false;
- mBlurOnSubmit = null;
mDisableFullscreen = false;
mListeners = null;
mTextWatcherDelegator = null;
@@ -256,7 +255,7 @@ public class ReactEditText extends AppCompatEditText
inputConnection, reactContext, this, mEventDispatcher);
}
- if (isMultiline() && getBlurOnSubmit()) {
+ if (isMultiline() && (shouldBlurOnReturn() || shouldSubmitOnReturn())) {
// Remove IME_FLAG_NO_ENTER_ACTION to keep the original IME_OPTION
outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
@@ -380,21 +379,52 @@ public class ReactEditText extends AppCompatEditText
mSelectionWatcher = selectionWatcher;
}
- public void setBlurOnSubmit(@Nullable Boolean blurOnSubmit) {
- mBlurOnSubmit = blurOnSubmit;
- }
-
public void setOnKeyPress(boolean onKeyPress) {
mOnKeyPress = onKeyPress;
}
- public boolean getBlurOnSubmit() {
- if (mBlurOnSubmit == null) {
- // Default blurOnSubmit
- return isMultiline() ? false : true;
+ public boolean shouldBlurOnReturn() {
+ String submitBehavior = getSubmitBehavior();
+ boolean shouldBlur;
+
+ // Default shouldBlur
+ if (submitBehavior == null) {
+ if (!isMultiline()) {
+ shouldBlur = true;
+ } else {
+ shouldBlur = false;
+ }
+ } else {
+ shouldBlur = submitBehavior.equals("blurAndSubmit");
}
- return mBlurOnSubmit;
+ return shouldBlur;
+ }
+
+ public boolean shouldSubmitOnReturn() {
+ String submitBehavior = getSubmitBehavior();
+ boolean shouldSubmit;
+
+ // Default shouldSubmit
+ if (submitBehavior == null) {
+ if (!isMultiline()) {
+ shouldSubmit = true;
+ } else {
+ shouldSubmit = false;
+ }
+ } else {
+ shouldSubmit = submitBehavior.equals("submit") || submitBehavior.equals("blurAndSubmit");
+ }
+
+ return shouldSubmit;
+ }
+
+ public String getSubmitBehavior() {
+ return mSubmitBehavior;
+ }
+
+ public void setSubmitBehavior(String submitBehavior) {
+ mSubmitBehavior = submitBehavior;
}
public void setDisableFullscreenUI(boolean disableFullscreenUI) {
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
index f3134f9cb1d..aac043da34f 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
@@ -441,9 +441,9 @@ public class ReactTextInputManager extends BaseViewManager Clear focus; prevent default behaviour (return
+ // * shouldSubmit => Clear focus; prevent default behavior (return true);
+ // * shouldBlur => Submit; prevent default behavior (return true);
+ // * !shouldBlur && !shouldSubmit && isMultiline => Perform default behavior (return
+ // false);
+ // * !shouldBlur && !shouldSubmit && !isMultiline => Prevent default behavior (return
// true);
- // * blurOnSubmit && !isMultiline => Clear focus; prevent default behaviour (return
- // true);
- // * !blurOnSubmit && isMultiline => Perform default behaviour (return false);
- // * !blurOnSubmit && !isMultiline => Prevent default behaviour (return true).
- // Additionally we always generate a `submit` event.
+ if (shouldSubmit) {
+ EventDispatcher eventDispatcher = getEventDispatcher(reactContext, editText);
+ eventDispatcher.dispatchEvent(
+ new ReactTextInputSubmitEditingEvent(
+ reactContext.getSurfaceId(),
+ editText.getId(),
+ editText.getText().toString()));
+ }
- EventDispatcher eventDispatcher = getEventDispatcher(reactContext, editText);
- eventDispatcher.dispatchEvent(
- new ReactTextInputSubmitEditingEvent(
- reactContext.getSurfaceId(),
- editText.getId(),
- editText.getText().toString()));
-
- if (blurOnSubmit) {
+ if (shouldBlur) {
editText.clearFocus();
}
// Prevent default behavior except when we want it to insert a newline.
- if (blurOnSubmit || !isMultiline) {
+ if (shouldBlur || shouldSubmit || !isMultiline) {
return true;
}
- // If we've reached this point, it means that the TextInput has 'blurOnSubmit' set to
- // false and 'multiline' set to true. But it's still possible to get IME_ACTION_NEXT
+ // If we've reached this point, it means that the TextInput has 'submitBehavior' set
+ // nullish and 'multiline' set to true. But it's still possible to get IME_ACTION_NEXT
// and IME_ACTION_PREVIOUS here in case if 'disableFullscreenUI' is false and Android
// decides to render this EditText in the full screen mode (when a phone has the
// landscape orientation for example). The full screen EditText also renders an action
diff --git a/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java b/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java
index a817b810d41..edf798ab86e 100644
--- a/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java
+++ b/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java
@@ -194,7 +194,7 @@ public class ReactTextInputPropertyTest {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles("multiline", true));
- mManager.updateProperties(view, buildStyles("blurOnSubmit", true));
+ mManager.updateProperties(view, buildStyles("submitBehavior", "blurAndSubmit"));
EditorInfo editorInfo = new EditorInfo();
editorInfo.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_ENTER_ACTION;
diff --git a/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp b/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp
index becfec590be..b3950b9eacb 100644
--- a/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp
+++ b/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp
@@ -145,10 +145,10 @@ AndroidTextInputProps::AndroidTextInputProps(
"selectTextOnFocus",
sourceProps.selectTextOnFocus,
{false})),
- blurOnSubmit(convertRawProp(context, rawProps,
- "blurOnSubmit",
- sourceProps.blurOnSubmit,
- {false})),
+ submitBehavior(convertRawProp(context, rawProps,
+ "submitBehavior",
+ sourceProps.submitBehavior,
+ {})),
caretHidden(convertRawProp(context, rawProps,
"caretHidden",
sourceProps.caretHidden,
@@ -298,7 +298,7 @@ folly::dynamic AndroidTextInputProps::getDynamic() const {
props["value"] = value;
props["defaultValue"] = defaultValue;
props["selectTextOnFocus"] = selectTextOnFocus;
- props["blurOnSubmit"] = blurOnSubmit;
+ props["submitBehavior"] = submitBehavior;
props["caretHidden"] = caretHidden;
props["contextMenuHidden"] = contextMenuHidden;
props["textShadowColor"] = toAndroidRepr(textShadowColor);
diff --git a/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h b/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h
index 6843425b190..e487bddd34a 100644
--- a/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h
+++ b/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h
@@ -143,7 +143,7 @@ class AndroidTextInputProps final : public ViewProps, public BaseTextProps {
const std::string value{};
const std::string defaultValue{};
const bool selectTextOnFocus{false};
- const bool blurOnSubmit{false};
+ const std::string submitBehavior{};
const bool caretHidden{false};
const bool contextMenuHidden{false};
const SharedColor textShadowColor{};
diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h b/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h
index 98e44fd7c07..f8b16d24add 100644
--- a/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h
+++ b/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h
@@ -126,6 +126,26 @@ inline void fromRawValue(
abort();
}
+inline void fromRawValue(
+ const PropsParserContext &context,
+ const RawValue &value,
+ SubmitBehavior &result) {
+ auto string = (std::string)value;
+ if (string == "newline") {
+ result = SubmitBehavior::Newline;
+ return;
+ }
+ if (string == "submit") {
+ result = SubmitBehavior::Submit;
+ return;
+ }
+ if (string == "blurAndSubmit") {
+ result = SubmitBehavior::BlurAndSubmit;
+ return;
+ }
+ abort();
+}
+
inline void fromRawValue(
const PropsParserContext &context,
const RawValue &value,
diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h b/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h
index 1747a98bf4c..bbb56d26d9a 100644
--- a/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h
+++ b/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h
@@ -48,6 +48,14 @@ enum class ReturnKeyType {
Continue,
};
+// iOS & Android.
+enum class SubmitBehavior {
+ Default,
+ Submit,
+ BlurAndSubmit,
+ Newline,
+};
+
// iOS-only
enum class TextInputAccessoryVisibilityMode {
Never,
@@ -170,9 +178,9 @@ class TextInputTraits final {
/*
* iOS & Android
- * Default value: `false`.
+ * Default value: `Default`.
*/
- bool blurOnSubmit{false};
+ SubmitBehavior submitBehavior{SubmitBehavior::Default};
/*
* iOS-only (implemented only on iOS for now)
diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h b/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h
index 84c52eeace6..15dc0004596 100644
--- a/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h
+++ b/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h
@@ -93,12 +93,12 @@ static TextInputTraits convertRawProp(
"secureTextEntry",
sourceTraits.secureTextEntry,
defaultTraits.secureTextEntry);
- traits.blurOnSubmit = convertRawProp(
+ traits.submitBehavior = convertRawProp(
context,
rawProps,
- "blurOnSubmit",
- sourceTraits.blurOnSubmit,
- defaultTraits.blurOnSubmit);
+ "submitBehavior",
+ sourceTraits.submitBehavior,
+ defaultTraits.submitBehavior);
traits.clearTextOnFocus = convertRawProp(
context,
rawProps,
diff --git a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js
index 2e9a4d754dc..9b8e62126e5 100644
--- a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js
+++ b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js
@@ -248,6 +248,90 @@ class BlurOnSubmitExample extends React.Component<{...}> {
}
}
+class SubmitBehaviorExample extends React.Component<{...}> {
+ ref1 = React.createRef();
+ ref2 = React.createRef();
+ ref3 = React.createRef();
+ ref4 = React.createRef();
+ ref5 = React.createRef();
+ ref6 = React.createRef();
+ ref7 = React.createRef();
+ ref8 = React.createRef();
+ ref9 = React.createRef();
+ ref10 = React.createRef();
+ ref11 = React.createRef();
+
+ render() {
+ return (
+
+ this.ref2.current?.focus()}
+ />
+ this.ref3.current?.focus()}
+ />
+ this.ref4.current?.focus()}
+ />
+ this.ref5.current?.focus()}
+ />
+ this.ref6.current?.focus()}
+ />
+ this.ref7.current?.focus()}
+ />
+ this.ref8.current?.focus()}
+ />
+ this.ref9.current?.focus()}
+ />
+
+
+
+
+ );
+ }
+}
+
class TextEventsExample extends React.Component<{...}, $FlowFixMeState> {
state = {
curText: '',
@@ -620,6 +704,12 @@ module.exports = ([
return ;
},
},
+ {
+ title: 'Submit behavior',
+ render: function (): React.Element {
+ return ;
+ },
+ },
{
title: 'Event handling',
render: function (): React.Element {