From 7f7b2818fd4eb808ce05bb9aa69e36e28c635984 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Tue, 3 May 2022 23:44:44 +0800 Subject: [PATCH] implementing iOS functionalities for https://github.com/facebook/react-native/issues/30848#issue-800746168 iOS has no standard pattern for the presentation of error states on text inputs, so I'd recommend we follow Android's pattern here. To do this, you'll need to implement a key press listener, and on change, detect if the field is now in an error state. If it is, you'll need to make a manual screen reader announcement of the error message, and append this message to the accessibilityValue for the field. --- Libraries/Components/TextInput/RCTTextInputViewConfig.js | 1 + Libraries/Text/TextInput/RCTBaseTextInputViewManager.m | 1 + .../ComponentViews/TextInput/RCTTextInputComponentView.mm | 4 ++++ React/Views/UIView+React.h | 1 + React/Views/UIView+React.m | 5 +++++ .../com/facebook/react/fabric/jni/viewPropConversions.h | 4 ++++ .../components/textinput/iostextinput/TextInputProps.cpp | 6 ++++++ .../components/textinput/iostextinput/TextInputProps.h | 2 ++ 8 files changed, 24 insertions(+) diff --git a/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/Libraries/Components/TextInput/RCTTextInputViewConfig.js index 52134db4419f14..03029bdff9f6c6 100644 --- a/Libraries/Components/TextInput/RCTTextInputViewConfig.js +++ b/Libraries/Components/TextInput/RCTTextInputViewConfig.js @@ -106,6 +106,7 @@ const RCTTextInputViewConfig = { allowFontScaling: true, fontStyle: true, textTransform: true, + screenreaderErrorAndroid: true, textAlign: true, fontFamily: true, lineHeight: true, diff --git a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m index b1ecf854330bbe..a6db0c0ee69c4d 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m @@ -42,6 +42,7 @@ @implementation RCTBaseTextInputViewManager RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString) RCT_REMAP_VIEW_PROPERTY(placeholderTextColor, backedTextInputView.placeholderColor, UIColor) RCT_REMAP_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIReturnKeyType) +RCT_REMAP_VIEW_PROPERTY(screenreaderErrorAndroid, reactAccessibilityElement.screenreaderErrorAndroid, NSString) RCT_REMAP_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor) RCT_REMAP_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType) RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL) diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 3631fb45cbc1ec..31724a22e1800c 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -134,6 +134,10 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & _backedTextInputView.editable = newTextInputProps.traits.editable; } + if (newTextInputProps.screenreaderErrorAndroid != oldTextInputProps.screenreaderErrorAndroid) { + self.accessibilityElement.accessibilityValue = RCTNSStringFromString(newTextInputProps.screenreaderErrorAndroid); + } + if (newTextInputProps.traits.enablesReturnKeyAutomatically != oldTextInputProps.traits.enablesReturnKeyAutomatically) { _backedTextInputView.enablesReturnKeyAutomatically = newTextInputProps.traits.enablesReturnKeyAutomatically; diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index 21a70337d7ac9c..ec15f3ed0c3c70 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -121,6 +121,7 @@ @property (nonatomic, copy) NSArray *accessibilityActions; @property (nonatomic, copy) NSDictionary *accessibilityValueInternal; @property (nonatomic, copy) NSString *accessibilityLanguage; +@property (nonatomic, copy) NSString *screenreaderErrorAndroid; /** * Used in debugging to get a description of the view hierarchy rooted at diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index 94ad951e7179d7..3f270b7127cc5a 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -320,6 +320,11 @@ - (NSString *)accessibilityLanguage return objc_getAssociatedObject(self, _cmd); } +- (NSString *)screenreaderErrorAndroid +{ + return objc_getAssociatedObject(self, _cmd); +} + - (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage { objc_setAssociatedObject( diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/viewPropConversions.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/viewPropConversions.h index 6fd7eb18130632..0ad53da6ef010d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/viewPropConversions.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/viewPropConversions.h @@ -277,6 +277,10 @@ static inline MapBuffer viewPropsDiff( builder.putString(VP_ACCESSIBILITY_LABEL, newProps.accessibilityLabel); } + if (oldProps.screenreaderErrorAndroid != newProps.screenreaderErrorAndroid) { + builder.putString(VP_ACCESSIBILITY_LABEL, newProps.screenreaderErrorAndroid); + } + if (oldProps.accessibilityLabelledBy != newProps.accessibilityLabelledBy) { builder.putMapBuffer( VP_ACCESSIBILITY_LABELLED_BY, diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp index a8716286aeec51..f6919e8a8034c5 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp @@ -88,6 +88,12 @@ TextInputProps::TextInputProps( "selection", sourceProps.selection, std::optional())), + screenreaderErrorAndroid(convertRawProp( + context, + rawProps, + "screenreaderErrorAndroid", + sourceProps.screenreaderErrorAndroid, + "")), inputAccessoryViewID(convertRawProp( context, rawProps, diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h index a72b63063b94ba..57bd31fc5b5106 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h @@ -62,6 +62,8 @@ class TextInputProps final : public ViewProps, public BaseTextProps { std::string const inputAccessoryViewID{}; + std::string screenreaderErrorAndroid{""}; + bool onKeyPressSync{false}; bool onChangeSync{false};