react-native
react-native copied to clipboard
Adding TextInput prop accessibilityErrorMessage to announce with TalkBack/VoiceOver screenreaders
Summary
Android: The functionality consists of calling the AccessibilityNodeInfo#setError and #setContentInvalid method to display the error message in the TextInput.
iOS: The accessibilityErrorMessage is stored in the accessibilityValue of the TextInput and it is announced with UIAccessibilityPostNotification.
The acceptable parameters of accessibilityErrorMessage is a string. Setting accessibilityInvalid to true activates the error message. Setting accessibilityInvalid to false removes the error message.
Fixes #30848 - Adding an accessibilityErrorMessage prop to the TextInput Component: Android: The prop accessibilityErrorMessage triggers the AccessibilityNodeInfo method setError which automatically sets the correct properties on the AccessibilityNodeInfo that will inform screen readers of this state. The method calls setContentInvalid(true) and setError(youErrorString) on the AccessibilityNodeInfo. iOS: iOS has no standard pattern for the presentation of error states on text inputs. The message is saved in the accessibilityValue of the field.
Fixes #30859 - Detecting changes in the Error state (text inputs) Fabric - Android - Adding accessibilityErrorMessage to field AndroidTextInputState. ReactTextInputManager and ReactEditText receive state updates both from Javascript and cpp (fabric).
- accessibilityErrorMessage is added to the fabric AndroidTextInputState field
- The updates are received in the ReactAndroid API with method updateState from ReactTextInputManager
- After updating the TextInput text with onChangeText, the update of the accessibilityErrorMessage is triggered with method maybeSetAccessibilityError which triggers setError.
More info:
- An explanation of state updates between fabric and ReactAndroid for the TextInput component
- ReactNative renderer state updates
Fabric - iOS - State Updates are triggered with RCTTextInputComponentView#updateProps. When the accessibilityErrorMessage or the text value of the TextInput changes, the accessibilityValue is set to that value and a VoiceOver announcement is triggered with UIAccessibilityPostNotification. Error announcements are triggered onChangeText with setAttributeString.
Paper - Android - Adding accessibilityErrorMessage to ReactTextInputShadowNode to trigger updates in Paper renderer when accessibilityErrorMessage is changed within the onChange callback.
Paper - iOS - To avoid duplicate screenreader announcement of the accessibilityError when typing, a screenreader announcement of the next typed character is triggered with RCTBaseTextInputView#setAttributedText if the error message is removed when accessibilityInvalid is true.
Related Links (Android):
- In this diff I'm shipping and deleting mapBufferSerialization for Text measurement
- This diff implement and integrates Mapbuffer into Fabric text measure system
- Refactor ViewPropsMapBuffer -> general MapBuffer props mechanism
- TextInput: support modifying TextInputs with multiple Fragments (Cxx side)
- TextInput: keep C++ state in-sync with updated AttributedStrings in Java
- AccessibilityNodeInfo#setError
- Explanation on how TextInput calls SET_TEXT_AND_SELECTION in Java API
- Fabric: convertRawProp was extended to accept an optional default value
- understanding onChangeText callback
- Editable method replace()
- Change of error state from onChangeText show/hides a TextInput error
- AndroidTextInput: support using commands instead of setNativeProps (native change)
- TextInput: support editing completely empty TextInputs
- [Android] Fix letters duplication when using autoCapitalize #29070
- Support optional types for C++ TurboModules
- discussion on using announceForAccessibility in ReactEditText
- fix annoucement delayed to next character
- Announce accessibility state changes happening in the background
- Refactor MountingManager into MountingManager + SurfaceMountingManager
Related Links (iOS):
- Diff C++ props for Android consumption
- RCTTextField was spliited into two classes
- Introducing RCTBackedTextInputDelegate
- Add option to hide context menu for TextInput
- RCTParagraphComponentAccessibilityProvider accessibilityElements
Changelog
[General] [Added] - Adding TextInput prop accessibilityErrorMessage to announce with TalkBack/VoiceOver screenreaders
Test Plan
PR Branch - Android and iOS 24th June 87: iOS - Paper - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Paper) (link) 88: Android - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Fabric) (link) 89: iOS - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Fabric) (link)
PR Branch - Android 1. Test Cases of the functionality (Fabric) (link) 2. Test Cases of the functionality (Paper) (link)
PR Branch - iOS 73. Announcing error onChangeText and screenreader focus (Fabric) (link) 74. iOS - The screenreader announces the TextInput value after the errorMessage is cleared (link) 76. iOS - announce lastChar (not entire text) onChangeText and avoid multiple announcements (Fabric) (link) 77. iOS - announces or does not announce the accessibilityError through Button onPress (not onChangeText) (Fabric) (link) 78. iOS - the error is announced with accessibilityInvalid true and does not clear after typing text (onChangeText) (Fabric) (link)
Main Branch 6. Android - Runtime Error in main branch when passing value of 1 to TextInput placeholder prop (link) 70. iOS - Paper renderer does not update the accessibilityValue (link) 75. iOS - Exception thrown while executing UI block: - [RCTTextView setOnAccessibiltyAction:]: unrecognized selector sent to instance (Paper) (main branch) (link) 79. iOS - Exception thrown while executing UI block: - RCTUITextView setAccessibilityErrorMessage:]: unrecognized selector sent to instance (iOS - Paper on main branch) (link)
Issues Solved 7. TalkBack error does not clear error on the next typed character when using onChangeText (link) Other Tests 8. Setting the TextInput errorMessage state with setTextAndSelection Java API from JavaScript (link) 9. Setting the TextInput errorMessage state from fabric TextInput internal state to Java ReactTextUpdate API (link)
| Platform | Engine | Arch | Size (bytes) | Diff |
|---|---|---|---|---|
| ios | - | universal | n/a | -- |
Base commit: 25a25ea234fc60c7a0b99e9c70253f77a69edc60 Branch: main
| Platform | Engine | Arch | Size (bytes) | Diff |
|---|---|---|---|---|
| android | hermes | arm64-v8a | 8,460,303 | +1,669 |
| android | hermes | armeabi-v7a | 7,783,042 | +1,147 |
| android | hermes | x86 | 8,936,192 | +1,657 |
| android | hermes | x86_64 | 8,793,493 | +1,869 |
| android | jsc | arm64-v8a | 9,093,774 | +1,002 |
| android | jsc | armeabi-v7a | 8,291,352 | +483 |
| android | jsc | x86 | 9,144,575 | +985 |
| android | jsc | x86_64 | 9,403,650 | +1,195 |
Base commit: 26b2bb5343f92672ed4e8f42c6f839e903124b06 Branch: main
The documentation is included in PR https://github.com/facebook/react-native-website/pull/3010
Documentation PR https://github.com/facebook/react-native-website/pull/3010
Thanks, @kacieb. I'm currently working on iOS side of this functionality and I plan to complete it by Friday 6th of May.
Thanks @blavalla. Once I complete the changes for code review Android: Role description is announced before the components text, rather than after #31042, I will further test the iOS functionality of the Issue Text input error for screenreaders #30848 and fix any issue in the iOS functionalities or think about potential improvements to the PR on the iOS side.
The PR summary includes a test case for iOS iOS - announcing error onChangeText and screenreader focus.
@kacieb equivalent functionality in terms of web aria api https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-errormessage
@kacieb equivalent functionality in terms of web aria api https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-errormessage
This is awesome @fabriziobertoglio1987! Would it be possible to follow the aria names and pattern for the JavaScript API? No native changes would be needed, you can do all of this in JavaScript I think. So instead of:
The acceptable parameters are null or a string. The parameter null removes the error message.
It would be something like
The acceptable parameters of
accessibilityErrorMessageis a string. SettingaccessibilityInvalidtotrueactivates the error message. SettingaccessibilityInvalidtofalseremoves the error message.
Instead of checking for null on the error message to clear it, you'd check the accessibilityInvalid boolean for when to set or clear the message.
Does that make sense? Please let me know if I can help explain better!
cc @necolas
something to note is that the value of aria-errormessage is meant to be the id of an element that contains the error message.
something to note is that the value of aria-errormessage is meant to be the id of an element that contains the error message.
Good note. I think this is out of scope for this PR. Definitely something we can improve in the future.
- [x] fix regression in Fabric iOS. Reintroduce solution from commits https://github.com/facebook/react-native/pull/33468/commits/318a96420f0593dc59c030193009e9cba31eb666 https://github.com/facebook/react-native/pull/33468/commits/99b39d5e344463ee9ca2f4528e203411da02b9ee
- [x] fix regression - the error message is cleared when screenreader focus is moved out of the textinput
- [x] extensive iOS and Android tests
- [x] review diff and PR summary
- [x] update test cases video recording
This looks good to me on the JS side! I'll work with blavalla to make sure we can land safely. cc @lunaleaps for awareness.
@blavalla has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.
@fabriziobertoglio1987 Do we have accompanying API documentation on the website for this?
Thanks for this great feature!
Related… when this gets landed, it would be great to also submit a PR to @types/react-native to add the new prop(s).
Thanks. I agree @blavalla https://github.com/facebook/react-native/pull/33468#pullrequestreview-1061123093. I added some comments and links with this commit https://github.com/facebook/react-native/pull/33468/commits/d85c90ad3fd80a408343cd3924e0af1ef0963713.
@fabriziobertoglio1987 Do we have accompanying API documentation on the website for this?
@lunaleaps This is the PR https://github.com/facebook/react-native-website/pull/3010 for the documentation. Thanks
Related… when this gets landed, it would be great to also submit a PR to
@types/react-nativeto add the new prop(s).
@yungsters I published PR https://github.com/DefinitelyTyped/DefinitelyTyped/pull/61748. Thanks a lot.
App crashing after trying to parse accessibilityErrorMessage prop raw value (rebase 14th Oct.)
The issue was solved after rebasing again on 17th Oct, including this for documentation purposes.
The app crashed at RawProps.cpp:92 with error:
function at: assertion failed (parser_ && "The object is not parsed. `parse` must be called before `at`.")'
https://www.icloud.com/iclouddrive/0e4zx_DEQrjC6s2uDVLTLXYPQ#null_pointer_dereference
The issue would not reproduce after removing the changes to the attributedstring ParagraphAttributes field accessibilityErrorMessage, but the error message is not displayed onChangeText.
The error initially seems triggered by conversions fromRawValue, RawProps, convertRawProp. The error could be caused by a conversion issue in ParagraphAttributes[accessibilityErrorMessage] and an error in parsing the value.
The fix consisted in:
- rebase again (diff from 14th Oct at 09:00 and 17th Oct 13:22 Rome)
- clean build (which is not the cause of the issue as I tried it several times)
crash with log
https://user-images.githubusercontent.com/24992535/196162666-62c12b88-2ba1-4f15-bcc8-31918ecc3e5c.mp4
after rebasing and clean build
https://user-images.githubusercontent.com/24992535/196162657-8dda9513-aa96-4de4-b661-97d9c126f39a.mp4
- [x] add fabric .cpp configurations https://github.com/fabriziobertoglio1987/react-native/commit/110b191b14e3cb692bb6a33f0f129b4f0215f9a6
@fabriziobertoglio1987 Is this ready to be reviewed again?
Fabric Android - trigger the error onChangeText
https://user-images.githubusercontent.com/24992535/200121108-de546f2f-2238-44d9-a028-a69fe05183bc.mp4
Fabric Android - trigger the error with a button (outside of onChangeText callback)
https://user-images.githubusercontent.com/24992535/200121219-1189d00f-bb44-46e2-941b-43740b3792f5.mp4
Fabric Android - announce error after changing text value
https://user-images.githubusercontent.com/24992535/200121367-e4ce5324-ef2a-48d2-b391-c56d28d9ee94.mp4
Paper Android - setting accessibilityError onChangeText
https://user-images.githubusercontent.com/24992535/200121527-26a193cb-a85c-466d-8df1-7c23abea3a82.mp4
PR build artifact for b1b8bf0d1d9c5ab51fa5f0d65670a5b22c83f799 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for b1b8bf0d1d9c5ab51fa5f0d65670a5b22c83f799 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for f0d0186ebad7312cdcd54b74d8d47e47bd4d741b is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for f0d0186ebad7312cdcd54b74d8d47e47bd4d741b is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
Fabric iOS - triggering accessibilityErrorMessage onChangeText
https://user-images.githubusercontent.com/24992535/200287750-44973c43-59e2-4268-8ee4-9916b53330f5.mp4