diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 34dfa8f1c5fa..54436e190f11 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -319,7 +319,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & if (newTextInputProps.inputAccessoryViewButtonLabel != oldTextInputProps.inputAccessoryViewButtonLabel) { _backedTextInputView.inputAccessoryViewButtonLabel = - RCTNSStringFromString(newTextInputProps.inputAccessoryViewButtonLabel); + RCTNSStringFromStringNilIfEmpty(newTextInputProps.inputAccessoryViewButtonLabel); } if (newTextInputProps.disableKeyboardShortcuts != oldTextInputProps.disableKeyboardShortcuts) { diff --git a/packages/react-native/React/Tests/Text/RCTTextInputComponentViewTests.mm b/packages/react-native/React/Tests/Text/RCTTextInputComponentViewTests.mm new file mode 100644 index 000000000000..cb942d7b2c14 --- /dev/null +++ b/packages/react-native/React/Tests/Text/RCTTextInputComponentViewTests.mm @@ -0,0 +1,105 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import +#import +#import +#import +#import +#import + +#if TARGET_OS_IOS + +using namespace facebook::react; + +static Props::Shared makeNumberPadProps(folly::dynamic rawPropsValue = folly::dynamic::object()) +{ + rawPropsValue["keyboardType"] = "number-pad"; + + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + auto rawProps = RawProps(rawPropsValue); + auto parser = RawPropsParser(); + parser.prepare(); + rawProps.parse(parser); + + return std::make_shared(parserContext, TextInputProps{}, rawProps); +} + +static Props::Shared makeLabelledNumberPadProps() +{ + return makeNumberPadProps(folly::dynamic::object("inputAccessoryViewButtonLabel", "Next")); +} + +static UIView *backedTextInputView(RCTTextInputComponentView *view) +{ + return [view valueForKey:@"_backedTextInputView"]; +} + +static NSString *accessoryButtonTitle(RCTTextInputComponentView *view) +{ + UIToolbar *toolbar = (UIToolbar *)backedTextInputView(view).inputAccessoryView; + return toolbar.items.lastObject.title; +} + +@interface RCTTextInputComponentViewTests : XCTestCase +@end + +@implementation RCTTextInputComponentViewTests + +- (void)testUnsetButtonLabelAddsNoAccessoryViewOnRecycledView +{ + RCTTextInputComponentView *view = [RCTTextInputComponentView new]; + [view updateProps:makeLabelledNumberPadProps() oldProps:TextInputShadowNode::defaultSharedProps()]; + XCTAssertEqualObjects(accessoryButtonTitle(view), @"Next"); + + [view prepareForRecycle]; + [view updateProps:makeNumberPadProps() oldProps:nullptr]; + + XCTAssertNil(backedTextInputView(view).inputAccessoryViewButtonLabel); + XCTAssertNil(backedTextInputView(view).inputAccessoryView); +} + +- (void)testUnsetButtonLabelFallsBackToReturnKeyOnRecycledView +{ + RCTTextInputComponentView *view = [RCTTextInputComponentView new]; + [view updateProps:makeLabelledNumberPadProps() oldProps:TextInputShadowNode::defaultSharedProps()]; + [view prepareForRecycle]; + + [view updateProps:makeNumberPadProps(folly::dynamic::object("returnKeyType", "done")) oldProps:nullptr]; + + XCTAssertEqualObjects(accessoryButtonTitle(view), @"Done"); +} + +- (void)testButtonLabelIsKeptOnRecycledView +{ + RCTTextInputComponentView *view = [RCTTextInputComponentView new]; + [view updateProps:makeLabelledNumberPadProps() oldProps:TextInputShadowNode::defaultSharedProps()]; + [view prepareForRecycle]; + + [view updateProps:makeLabelledNumberPadProps() oldProps:nullptr]; + + XCTAssertEqualObjects(accessoryButtonTitle(view), @"Next"); +} + +- (void)testRemovingButtonLabelRemovesAccessoryView +{ + RCTTextInputComponentView *view = [RCTTextInputComponentView new]; + auto labelledProps = makeLabelledNumberPadProps(); + [view updateProps:labelledProps oldProps:TextInputShadowNode::defaultSharedProps()]; + XCTAssertNotNil(backedTextInputView(view).inputAccessoryView); + + [view updateProps:makeNumberPadProps() oldProps:labelledProps]; + + XCTAssertNil(backedTextInputView(view).inputAccessoryView); +} + +@end + +#endif