diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index d8c543299..1d9a30d61 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -2496,6 +2496,8 @@ export class TextBase extends View implements AddChildFromBuilder { export class TextField extends EditableTextBase { android: any /* android.widget.EditText */; + closeOnReturn: boolean; + ios: any /* UITextField */; // (undocumented) diff --git a/nativescript-core/ui/text-field/text-field-common.ts b/nativescript-core/ui/text-field/text-field-common.ts index 2dff648f3..1e75ea9c2 100644 --- a/nativescript-core/ui/text-field/text-field-common.ts +++ b/nativescript-core/ui/text-field/text-field-common.ts @@ -7,9 +7,13 @@ export * from "../editable-text-base"; export class TextFieldBase extends EditableTextBase implements TextFieldDefinition { public static returnPressEvent = "returnPress"; public secure: boolean; + public closeOnReturn: boolean; } TextFieldBase.prototype.recycleNativeView = "auto"; export const secureProperty = new Property({ name: "secure", defaultValue: false, valueConverter: booleanConverter }); secureProperty.register(TextFieldBase); + +export const closeOnReturnProperty = new Property({ name: "closeOnReturn", defaultValue: true, valueConverter: booleanConverter }); +closeOnReturnProperty.register(TextFieldBase); diff --git a/nativescript-core/ui/text-field/text-field.d.ts b/nativescript-core/ui/text-field/text-field.d.ts index 0fa71bb1e..5746e1bdf 100644 --- a/nativescript-core/ui/text-field/text-field.d.ts +++ b/nativescript-core/ui/text-field/text-field.d.ts @@ -27,4 +27,9 @@ export class TextField extends EditableTextBase { * Gets or sets if a text field is for password entry. */ secure: boolean; + + /** + * Gets or sets if a text field should dismiss on return. + */ + closeOnReturn: boolean; } diff --git a/nativescript-core/ui/text-field/text-field.ios.ts b/nativescript-core/ui/text-field/text-field.ios.ts index 34be82920..00e969174 100644 --- a/nativescript-core/ui/text-field/text-field.ios.ts +++ b/nativescript-core/ui/text-field/text-field.ios.ts @@ -66,7 +66,9 @@ class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate { // Called when the user presses the return button. const owner = this._owner.get(); if (owner) { - owner.dismissSoftInput(); + if (owner.closeOnReturn) { + owner.dismissSoftInput(); + } owner.notify({ eventName: TextField.returnPressEvent, object: owner }); } diff --git a/tests/app/ui/text-field/text-field-tests-native.android.ts b/tests/app/ui/text-field/text-field-tests-native.android.ts index 8dfa28ab6..b23d62dc3 100644 --- a/tests/app/ui/text-field/text-field-tests-native.android.ts +++ b/tests/app/ui/text-field/text-field-tests-native.android.ts @@ -59,8 +59,17 @@ export function getNativeTextAlignment(textField: textFieldModule.TextField): st return "unexpected value"; } +export function getNativeFocus(textField: textFieldModule.TextField): boolean { + // + return true; +} + export function typeTextNatively(textField: textFieldModule.TextField, text: string): void { textField.android.requestFocus(); textField.android.setText(text); textField.android.clearFocus(); } + +export function typeTextNativelyWithReturn(textField: textFieldModule.TextField, text: string): void { + // +} diff --git a/tests/app/ui/text-field/text-field-tests-native.d.ts b/tests/app/ui/text-field/text-field-tests-native.d.ts index 42e294ffd..2aed31bde 100644 --- a/tests/app/ui/text-field/text-field-tests-native.d.ts +++ b/tests/app/ui/text-field/text-field-tests-native.d.ts @@ -10,4 +10,7 @@ export declare function getNativeColor(textField: textFieldModule.TextField): co export declare function getNativePlaceholderColor(textField: textFieldModule.TextField): colorModule.Color; export declare function getNativeBackgroundColor(textField: textFieldModule.TextField): colorModule.Color; export declare function getNativeTextAlignment(textField: textFieldModule.TextField): string; +export declare function getNativeFocus(textField: textFieldModule.TextField): boolean; export declare function typeTextNatively(textField: textFieldModule.TextField, text: string): void; +export declare function typeTextNativelyWithReturn(textField: textFieldModule.TextField, text: string): void; + diff --git a/tests/app/ui/text-field/text-field-tests-native.ios.ts b/tests/app/ui/text-field/text-field-tests-native.ios.ts index 03f166593..1d697024b 100644 --- a/tests/app/ui/text-field/text-field-tests-native.ios.ts +++ b/tests/app/ui/text-field/text-field-tests-native.ios.ts @@ -44,9 +44,21 @@ export function getNativeTextAlignment(textField: textFieldModule.TextField): st } } +export function getNativeFocus(textField: textFieldModule.TextField): boolean { + return textField.nativeView.isFirstResponder; +} + export function typeTextNatively(textField: textFieldModule.TextField, text: string): void { textField.ios.text = text; // Setting the text will not trigger the delegate method, so we have to do it by hand. textField.ios.delegate.textFieldDidEndEditing(textField.ios); } + +export function typeTextNativelyWithReturn(textField: textFieldModule.TextField, text: string): void { + textField.nativeView.becomeFirstResponder(); + + textField.ios.text = text; + + textField.ios.delegate.textFieldShouldReturn(textField.ios); +} diff --git a/tests/app/ui/text-field/text-field-tests.ts b/tests/app/ui/text-field/text-field-tests.ts index b17cff785..7f46aa022 100644 --- a/tests/app/ui/text-field/text-field-tests.ts +++ b/tests/app/ui/text-field/text-field-tests.ts @@ -5,9 +5,9 @@ import { Page } from "@nativescript/core/ui/page"; import { StackLayout } from "@nativescript/core/ui/layouts/stack-layout"; import { Color } from "@nativescript/core/color"; import { - getNativeText, getNativeHint, typeTextNatively, getNativeSecure, + getNativeText, getNativeHint, typeTextNatively, typeTextNativelyWithReturn, getNativeSecure, getNativeFontSize, getNativeColor, getNativeBackgroundColor, - getNativeTextAlignment, getNativePlaceholderColor + getNativeTextAlignment, getNativePlaceholderColor, getNativeFocus } from "./text-field-tests-native"; import { FormattedString } from "@nativescript/core/text/formatted-string"; import { Span } from "@nativescript/core/text/span"; @@ -400,6 +400,100 @@ export var testBindSecureToBindingConext = function () { }); }; +// iOS only +export var testBindCloseOnReturnToBindingConext = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + var page = views[1]; + + var model = new Observable(); + model.set("closeOnReturn", false); + page.bindingContext = model; + + var options: BindingOptions = { + sourceProperty: "closeOnReturn", + targetProperty: "closeOnReturn" + }; + + textField.bind(options); + TKUnit.assert(textField.closeOnReturn === false, "Actual: " + textField.closeOnReturn + "; Expected: " + false); + typeTextNativelyWithReturn(textField, "Should not close textfield"); + TKUnit.assert(getNativeFocus(textField) === true, "Actual: " + getNativeFocus(textField) + "; Expected: " + true); + + model.set("closeOnReturn", true); + TKUnit.assert(textField.closeOnReturn === true, "Actual: " + textField.closeOnReturn + "; Expected: " + true); + typeTextNativelyWithReturn(textField, "Should close textfield"); + TKUnit.assert(getNativeFocus(textField) === false, "Actual: " + getNativeFocus(textField) + "; Expected: " + false); + }); +}; + +// iOS only +export var testDontCloseOnReturn = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + + // >> setting-closeOnReturn-property + textField.closeOnReturn = false; + // << setting-closeOnReturn-property + + typeTextNativelyWithReturn(textField, "Should not close textfield"); + + var expectedValue = true; + var actualValue = getNativeFocus(textField); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +}; + +// iOS only +export var testCloseOnReturn = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + + // >> setting-closeOnReturn-property + textField.closeOnReturn = true; + // << setting-closeOnReturn-property + + typeTextNativelyWithReturn(textField, "Should close textfield"); + + var expectedValue = false; + var actualValue = getNativeFocus(textField); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +}; + +// iOS only +export var testCloseOnReturnByDefault = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + + typeTextNativelyWithReturn(textField, "Should close textfield by default"); + + var expectedValue = false; + var actualValue = getNativeFocus(textField); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +}; + var expectedFontSize = 42; export var testLocalFontSizeFromCss = function () { helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) {