-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Implement UIA Invoke Method #11874
Implement UIA Invoke Method #11874
Conversation
05f44d3
to
50a5e90
Compare
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
// Currently calls both onAccessibilityTap and onClick. | ||
// To match Paper behavior, onAccessibilityTap only called if onClick is not defined. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do wonder if this is correct? What if we have some special code that needs to behave differently for pointer and AT users? (e.g. Telemetry recording invocation method).
I feel like if both are implemented, maybe we should only call onAccessibilityTap and rely on users to handle the normal onClick behavior in the JS callback. Or maybe both? Maybe this was already discussed with the Paper impl, but I don't know that I like the idea that we deprive people of an AT-specific code path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure! Just to confirm I understand what you're saying, you're wondering that maybe the behavior we had on Paper was wrong. And that instead we should either...
(1) always call onAccessibilityTap
(2) always call both onAccessibilityTap and onClick
That makes sense to me! It did seem odd that the onAccessibilityTap functionality would be ignored when onClick functionality is set. Option 2 seems like the right option to me, do you have an instinct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked and this:
maybe we should only call onAccessibilityTap and rely on users to handle the normal onClick behavior in the JS callback
is what we do in Office, though I do want to get this right. So while (2) sounds reasonable, I wouldn't mind a second opinion here from @jonthysell or @acoates-ms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go with (1). I dont see any indication in code that core calls onClick when invoking onAccessibilityTap. @Saadnajmi might be able to verify the behavior on iOS. (I think onClick might be fabric only in iOS)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that onAccessibilityTap
is its own, independent event in RN code, and technically an iOS-only event (though not documented that way, see facebook/react-native#34136).
See also https://reactnative.dev/docs/view#onaccessibilitytap
I think our Paper impl is too tied up with how we interacted with XAML and XAML's behaviors. onClick
is a windows-only event, and I don't really like that we assume to send and onClick
when hit via UIA. Why not an onPress
? Nowhere do I see code that defaults to calling RN's own built-in onPress
during an onAccessibilityTap
.
I'd rather leave that up to app devs in JS rather than assume a default behavior "by default". Every example online where I can find people who care about accessibility, they define both onPress and onAccessibilityTap (even if it's usually to the same function). If any particular dev wants it to pass through they can easily define their own JS components to do so.
That said, it appears in RN for iOS, there is a kind of event chaining where the 'activate' accessibilityAction presumes to call onAccessibilityTap
if it's available:
- (BOOL)accessibilityActivate
{
if (_eventEmitter && _props->onAccessibilityTap) {
_eventEmitter->onAccessibilityTap();
return YES;
} else {
return NO;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of additional pieces of info:
- Upstream has recently added a topClick/onClick event, so it won't be Windows only anymore. See Use Upstream TopClick Type Instead of Windows-Specific Type #11969. More of a fyi than something actionable on this PR.
- The value of
props->onClick
matches whetheronPress
has been set. And the call todispatchEvent("topClick")
runs theonPress
function. I haven't dug into the code to figure out why this is the case, but that's the behavior we get. - From testing, it does NOT appear that if
onAccessibilityTap
is not set, then theonPress
function is run instead. The program simply does a noop. So if the Invoke method expects the behavior to match when you click without AT, then just callingonAccessibilityTap
might not be sufficient. - Based on the RN docs, I believe we'll want to implement the accessibilityAction "activate" to match whatever behavior we land on for Invoke. They are both described in the same way. What's interesting is that with this description "Typically this should perform the same action as when the user touches or clicks the component when not using an assistive technology. This is generated when a screen reader user double taps the component." I would expect
_props->onClick
to be called ifonAccessibilityTap
isn't implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Typically this should perform the same action as when the user touches or clicks the component when not using an assistive technology. This is generated when a screen reader user double taps the component."
I would expect
_props->onClick
to be called ifonAccessibilityTap
isn't implemented.
I think that it is a note to the developer that they should typically set onAccessibilityTap
to the same value of onClick
. Not that the platform would do it for you. If the platform does it, then it is no longer typically, but always, which could be problematic for some use cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that makes sense. Seems consensus is to only calling onAccessibilityTap. I'll make those edits.
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
Outdated
Show resolved
Hide resolved
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
// Currently calls both onAccessibilityTap and onClick. | ||
// To match Paper behavior, onAccessibilityTap only called if onClick is not defined. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked and this:
maybe we should only call onAccessibilityTap and rely on users to handle the normal onClick behavior in the JS callback
is what we do in Office, though I do want to get this right. So while (2) sounds reasonable, I wouldn't mind a second opinion here from @jonthysell or @acoates-ms
760ddca
to
10b6a31
Compare
Description
Type of Change
Why
Implement UIA Invoke method. Method is used by automation clients to invoke the action of pressable, stateless controls.
What
Implement UIA Invoke method.
Note current implementation is not control specific. i.e. the implementation applies to all controls that CompositionDynamicAutomationPeer controls. To match Paper behavior, I believe we will need to only implement the invoke method for controls that are pressable and stateless. Also, the implementation invokes both the onClick and onAccessibilityTap functionality when the method is called. To match Paper behavior, this will need to be adjusted so that the onAccessibilityTap method is only called when onClick is not implemented.
Testing
Programmatically called method to confirm method performs intended function of carrying out the action of the control upon press.
TODO
Microsoft Reviewers: codeflow:open?pullrequest=#11874