Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TYPE_VIEW_HOVER_ENTER to AccessibilityNodeInfo sendAccessibilityEvent #34969

Closed
wants to merge 31 commits into from
Rate limit · GitHub

Access has been restricted

You have triggered a rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

Closed
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fd0923e
adding accessibilityTitle prop to ReactAndroid
fabOnReact Oct 13, 2022
accbbfc
improve types in java and js
fabOnReact Oct 17, 2022
17d7827
implementing alternative solution for iOS/Android
fabOnReact Oct 24, 2022
5e1e72f
Merge branch 'main' into modal-title
fabOnReact Oct 24, 2022
42ab164
moving Title logic to modal rn-tester example
fabOnReact Oct 25, 2022
5c7ed5e
removing changes from Modal
fabOnReact Oct 25, 2022
98b4a94
fix issues with sendAccessibilityEvent
fabOnReact Oct 25, 2022
0fe1e3e
refactor useTimer with useEffect
fabOnReact Oct 25, 2022
4213491
minor fix to example
fabOnReact Oct 25, 2022
f89a01f
fix ref flow type in example
fabOnReact Oct 26, 2022
9e7fe9f
eslint fix check
fabOnReact Oct 26, 2022
129d152
minor change
fabOnReact Oct 26, 2022
036e4b5
Merge branch 'main' into modal-title
fabOnReact Oct 26, 2022
8a25a6f
minor change
fabOnReact Oct 26, 2022
dbd01c6
minor change
fabOnReact Oct 26, 2022
3c0f531
avoid passing vars to setTimeout
fabOnReact Oct 26, 2022
23bf335
Merge branch 'main' into modal-title
fabOnReact Nov 18, 2022
43e2ffa
remove sendAccessibilityEvent setTimeout as not required on iOS
fabOnReact Nov 18, 2022
8f2e0f0
adding flowfixme
fabOnReact Nov 18, 2022
d351891
Merge branch 'main' into modal-title
fabOnReact Nov 28, 2022
3126e49
Add viewHoverEnter to sendAccessibilityEventFromJS
fabOnReact Nov 28, 2022
ef5e490
remove state modalOpened
fabOnReact Nov 28, 2022
4cf1605
remove state modalOpened
fabOnReact Nov 28, 2022
e7f0023
adding viewHoverInfo to AccessiblityInfo
fabOnReact Nov 28, 2022
fd152d2
minor change
fabOnReact Nov 28, 2022
bf37a34
using correct flow type
fabOnReact Nov 28, 2022
dc4c54e
remove example
fabOnReact Nov 29, 2022
69f6a18
Merge branch 'main' into modal-title
fabOnReact Nov 29, 2022
a5c0cd8
Merge branch 'main' into modal-title
fabOnReact Feb 13, 2023
8debb60
remove AccInfo.flow.js
fabOnReact Feb 13, 2023
b5f3b21
Merge branch 'main' into modal-title
fabOnReact Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implementing alternative solution for iOS/Android
changes discussed in
#34969 (comment)
which consist in:

- Modal accepts prop TitleComponent which is a React.Element or
  Component and represents the title of the modal.
  The solution is taken from ListHeaderComponent in VirtList
- AccessibilityInfo.sendAccessibilityEvent is triggered to focus on the
  Title when the modal opens
- sendAccessibilityEvent needs to set a timeout of 1s (see #30097 and https://stackoverflow.com/questions/28472985/android-set-talkback-accessibility-focus-to-a-specific-view)
- TitleComponent Styling and positioning is defined outside of reactnative
  This has to be reviewed as ListHeaderComponent uses
  ListHeaderComponentStyle

Notes:

"Improvements:
Trigger focus on components using Fabric API (findNodeHandle is deprecated, setAccessibilityFocus). setAccessibilityFocus has several issues (stackoverflow), and I can not set a ref on a modal.

componentDidMount seems to trigger before rendering the Modal children, the lifecycle does not work correctly with a modal on Fabric.

You need to use forwardRef to call sendAccessibilityEvent
Need to change the way you create the title component so that you create a forwardRef and then use this to send the accessibility event"	"https://reactnative.dev/docs/new-architecture-library-intro#preparing-your-javascript-codebase-for-the-new-react-native-renderer-fabric
https://reactnative.dev/docs/accessibilityinfo#setaccessibilityfocus
https://reactnative.dev/docs/new-architecture-library-intro#preparing-your-javascript-codebase-for-the-new-react-native-renderer-fabric"
"Test Text component solution without using getNativeRef, as the problem could be caused by timeout and not ref. Check the keys and try to use the _nativeRef
Implement getNativeRef for the Text component and use it in Modal to call setAccessibilityFocus"	https://github.com/fabriziobertoglio1987/react-native/blob/accbbfc01af57eda34374ce3b13e36b6e7c12a93/Libraries/Components/TextInput/TextInput.js#L1213
"Adding setTimeout with a wait of 1-second fixes issues with setAccessibilityFocus does not onLoad

The reason is triggered focus on other elements, so we need to use an API to wait for the focus to display on that element

Try to use runAfterInteraction"	"#30097
https://reactnative.dev/docs/next/interactionmanager#runafterinteractions"
"Review meeting notes and Brett suggestion
Consider removing the sendAccessibilityEvent as violates WCAG 2.0 regulations, including 3.2.1 and 3.2.3"
Test Android
"-  The title component displays under the Modal on Android.
The title receives focus with the ModalPresentation example.
The title does not receive focus in the ModalOnShow example.
=> In both examples, the titles displays under the modal
=> In ModalOnShow, the title does not receive focus
Apply the same prop as in the ModalPresentation and see if the title receives focus
Remove style
Put the title directly in the ModalOnShow and trigger the focus
Trigger the focus manually with a button instead of using useEffect( _ref )
Trigger focus manually with a button in the Modal.js"
"setAccessibilityFocus correctly moves the focus when triggered with Button. The issue could be caused:
ref is undefined
read conversation #30097
alternative callback to _showModal (onLoad)
TalkBack moves focus after calling sendAccessibilityEvent => increase timeout"	#30097
"Test functionality on iOS:
test functionality on iOS with Xcode build"
"Improve solution and finalize diff before final commit:
Fix issue with sendAccessibilityEvent based on stackoverflow solution or runAfterIteraction (setAccessibilityFocus does not onLoad)
TitleComponent should move to rn-tester ModalPresentation and ModalOnShow
forwardRef may not work"	"https://github.com/fabriziobertoglio1987/react-native/blob/accbbfc01af57eda34374ce3b13e36b6e7c12a93/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java#L968
https://stackoverflow.com/questions/28472985/android-set-talkback-accessibility-focus-to-a-specific-view
#30097"
Rate limit · GitHub

Whoa there!

You have triggered an abuse detection mechanism.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

fabOnReact committed Oct 24, 2022
commit 17d78273dbfffd68c66cd5bab82f4a68e236c3dc
25 changes: 21 additions & 4 deletions Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import {type EventSubscription} from '../vendor/emitter/EventEmitter';
import ModalInjection from './ModalInjection';
import NativeModalManager from './NativeModalManager';
import RCTModalHostView from './RCTModalHostViewNativeComponent';
import AccessibilityInfo from '../Components/AccessibilityInfo/AccessibilityInfo';

const ScrollView = require('../Components/ScrollView/ScrollView');
const View = require('../Components/View/View');
@@ -61,14 +62,15 @@ export type Props = $ReadOnly<{|
...ViewProps,

/**
* The `accessibilityTitle` prop controls the title announced with the TalkBack screen reader.
* The `TitleComponent` represents the title of the Modal.
* The title is announced with TalkBack/VoiceOver screenreaders.
*
* See https://reactnative.dev/docs/modal#
*/
accessibilityTitle?: ?Stringish,
TitleComponent?: ?(React.ComponentType<any> | React.Element<any>),

/**
* The `animationType` prop controls how the modal animates.
The `animationType` prop controls how the modal animates.
*
* See https://reactnative.dev/docs/modal#animationtype
*/
@@ -213,6 +215,13 @@ class Modal extends React.Component<Props> {
}
}

_captureRef = ref => {
if (ref) {
this._ref = ref;
AccessibilityInfo.sendAccessibilityEvent(ref, 'focus');
}
};

componentWillUnmount() {
if (this._eventSubscription) {
this._eventSubscription.remove();
@@ -226,6 +235,14 @@ class Modal extends React.Component<Props> {
}

render(): React.Node {
const {TitleComponent} = this.props;
const element = React.isValidElement(TitleComponent) ? (
TitleComponent
) : (
// $FlowFixMe[not-a-component]
// $FlowFixMe[incompatible-type-arg]
<TitleComponent />
);
if (this.props.visible !== true) {
return null;
}
@@ -271,7 +288,6 @@ class Modal extends React.Component<Props> {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onStartShouldSetResponder={this._shouldSetResponder}
supportedOrientations={this.props.supportedOrientations}
accessibilityTitle={this.props.accessibilityTitle}
onOrientationChange={this.props.onOrientationChange}
testID={this.props.testID}>
<VirtualizedListContextResetter>
@@ -280,6 +296,7 @@ class Modal extends React.Component<Props> {
style={[styles.container, containerStyles]}
collapsable={false}>
{innerChildren}
<TitleComponent ref={this._captureRef} />
</View>
</ScrollView.Context.Provider>
</VirtualizedListContextResetter>
7 changes: 0 additions & 7 deletions Libraries/Modal/RCTModalHostViewNativeComponent.js
Original file line number Diff line number Diff line change
@@ -25,13 +25,6 @@ type OrientationChangeEvent = $ReadOnly<{|
type NativeProps = $ReadOnly<{|
...ViewProps,

/**
* The `accessibilityTitle` prop controls the title announced with the TalkBack screen reader.
*
* See https://reactnative.dev/docs/modal#
*/
accessibilityTitle?: WithDefault<string, null>,

/**
* The `animationType` prop controls how the modal animates.
*
Original file line number Diff line number Diff line change
@@ -57,6 +57,9 @@
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;

public class SurfaceMountingManager {
@@ -963,7 +966,18 @@ public void sendAccessibilityEvent(int reactTag, int eventType) {
"Unable to find viewState view for tag " + reactTag);
}

viewState.mView.sendAccessibilityEvent(eventType);
Runnable task =
new Runnable() {

@Override
public void run() {
viewState.mView.sendAccessibilityEvent(eventType);
}
};

final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

worker.schedule(task, 1000, TimeUnit.MILLISECONDS);
}

@UiThread
Original file line number Diff line number Diff line change
@@ -86,12 +86,6 @@ public void setStatusBarTranslucent(ReactModalHostView view, boolean statusBarTr
view.setStatusBarTranslucent(statusBarTranslucent);
}

@Override
@ReactProp(name = "accessibilityTitle")
public void setAccessibilityTitle(ReactModalHostView view, @Nullable String accessibilityTitle) {
view.setAccessibilityTitle(accessibilityTitle);
}

@ReactProp(name = "hardwareAccelerated")
public void setHardwareAccelerated(ReactModalHostView view, boolean hardwareAccelerated) {
view.setHardwareAccelerated(hardwareAccelerated);
Original file line number Diff line number Diff line change
@@ -79,7 +79,6 @@ public interface OnRequestCloseListener {
private boolean mTransparent;
private boolean mStatusBarTranslucent;
private String mAnimationType;
private @Nullable String mAccessibilityTitle;
private boolean mHardwareAccelerated;
// Set this flag to true if changing a particular property on the view requires a new Dialog to
// be created. For instance, animation does since it affects Dialog creation through the theme
@@ -204,11 +203,6 @@ protected void setAnimationType(String animationType) {
mPropertyRequiresNewDialog = true;
}

protected void setAccessibilityTitle(String accessibilityTitle) {
mAccessibilityTitle = accessibilityTitle;
mPropertyRequiresNewDialog = true;
}

protected void setHardwareAccelerated(boolean hardwareAccelerated) {
mHardwareAccelerated = hardwareAccelerated;
mPropertyRequiresNewDialog = true;
@@ -330,9 +324,6 @@ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (mHardwareAccelerated) {
mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
if (mAccessibilityTitle != null) {
mDialog.setTitle(mAccessibilityTitle);
}

if (currentActivity != null && !currentActivity.isFinishing()) {
mDialog.show();
17 changes: 17 additions & 0 deletions packages/rn-tester/js/examples/Modal/ModalPresentation.js
Original file line number Diff line number Diff line change
@@ -55,6 +55,22 @@ function ModalPresentation() {
}
};

const TitleComponent = React.forwardRef((props, forwardedRef) => {
return (
<Text
ref={forwardedRef}
style={{
width: '100%',
position: 'absolute',
top: 100,
textAlign: 'center',
backgroundColor: 'red',
zIndex: 20,
}}>
My custom title
</Text>
);
});
const onShow = () => {
if (action === 'onShow') {
alert('onShow');
@@ -76,6 +92,7 @@ function ModalPresentation() {
Show Modal
</RNTesterButton>
<Modal
TitleComponent={TitleComponent}
animationType={animationType}
presentationStyle={presentationStyle}
transparent={transparent}