facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.8k stars 24.29k forks source link

Accessibility: return focus to component that triggered an action #29454

Closed wilsolutions closed 2 years ago

wilsolutions commented 4 years ago

Description

In order to improve the UX for users using the Screen Reader, it would be good if certain actions like Linking.openURL(tel:${phoneNumber}) could return the Screen Reader focus to the Button (or TouchableOpacity) that triggered it when the user taps on Cancel.

React Native version:

System: OS: macOS 10.15.5 CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz Memory: 19.41 MB / 16.00 GB Shell: 5.7.1 - /bin/zsh Binaries: Node: 12.14.1 - /var/folders/ry/2df_jn7x5tj6pcfkwqmxpsyw61v7mm/T/yarn--1595350883824-0.12006287496447543/node Yarn: 1.22.4 - /var/folders/ry/2df_jn7x5tj6pcfkwqmxpsyw61v7mm/T/yarn--1595350883824-0.12006287496447543/yarn npm: 6.13.4 - ~/.nvm/versions/node/v12.14.1/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.9.3 - /Users/w/.rvm/gems/ruby-2.7.0/bin/pod SDKs: iOS SDK: Platforms: iOS 13.5, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2 Android SDK: API Levels: 23, 26, 27, 28, 29 Build Tools: 23.0.1, 24.0.0, 26.0.1, 26.0.2, 27.0.1, 27.0.3, 28.0.0, 28.0.1, 28.0.3, 29.0.3, 30.0.1 System Images: android-26 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom Android NDK: Not Found IDEs: Android Studio: 4.0 AI-193.6911.18.40.6514223 Xcode: 11.5/11E608c - /usr/bin/xcodebuild Languages: Java: 1.8.0_181 - /usr/bin/javac Python: 2.7.16 - /usr/bin/python npmPackages: @react-native-community/cli: Not Found react: 16.13.1 => 16.13.1 react-native: 0.62.2 => 0.62.2 npmGlobalPackages: react-native: Not Found

Steps To Reproduce

  1. Add a Button or TouchableOpacity to a View.
  2. Add an event that would open a Native view like: Linking.openURL(tel:${phoneNumber}`);``
  3. Activate the screen reader, double tab the new Button then simply cancel it.

Current result: the screen reader focus is set to the first top left element, in my case a back button.

Expected Results

The screen reader focus returns to the element that triggered the event.

chrisglein commented 4 years ago

Having some memory to focus so it restores to the place it was before may just not be implemented across platforms or on each native platform. I know that for Windows we've had this sort of issue crop up on focus lost scenarios. I don't know if there's been any work in RN to make that experience better than it is on native. Would like clarity on whether this is expected to work and isn't or is a proposal for an improvement in behavior, which generally goes to the discussions repo.

wilsolutions commented 4 years ago

That's true, thanks for the heads up, I think it should go to the discussion repo as you mentioned since this is an improvement.

I see that on iOS, with Voice over on, when you double tap the phone number it brings up the "Call number action sheet" and then if you swipe right and select the "Cancel button", the Voice Over will send the focus to the button next to the "Phone No button".

In a RN App, it will send the user to very first top left element, what is fine but now a user that is using Voice Over will need to swipe right the whole way down in order to get to the place he/she was before...

stale[bot] commented 3 years ago

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

wilsolutions commented 3 years ago

I still can reproduce the issue and I'm hoping that now React Native Accessibility is part of the GAAD Pledge to make React Native more accessible this would receive some attention...

blavalla commented 2 years ago

@wilsolutions , I agree with you that this would be a cool feature to have, but it would be very complicated to support in a general sense. Triggering an element can do anything, including re-rendering the screen so that the button that was clicked no longer even exists, so keeping a reference to it is not guaranteed to even be possible.

In order to handle something like this globally there would basically need to be an entire focus management system built into React Native, which would be very complex to build as neither iOS nor Android have very good systems of informing apps of what is currently in focus, and when that focus changes.

I think this could be resolved in a number of common cases, such as when opening a modal dialog or bottom sheet, as those components could be designed to require some sort of trigger element to be passed in, but solving this in a general sense is likely not feasible.

For your specific example, of a telephone link, the dialog that appears is actually not a RN rendered dialog, but an OS level dialog that Apple renders to make sure you wanted to call the number. We unfortunately don't know when it's either confirmed or cancelled, so have no way to respond to this action. In this case, VoiceOver itself should keep a reference to the previously focused element when their dialog appears, and re-focus it when it's dismissed, which is how this works from the web, or other native apps. From some cursory testing this appears to work in RN as well (see this snack link for an example - https://snack.expo.dev/@blavalla/telephone-link-example).

If this isn't working in your app, I suspect that there is an issue on Apple's side with finding this element again to move focus back to it, and with a simplified example I may be able to help you debug further.