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

Implement UIA Invoke Method #11874

Merged
merged 23 commits into from
Aug 9, 2023
Merged

Conversation

chiaramooney
Copy link
Contributor

@chiaramooney chiaramooney commented Jul 11, 2023

Description

Type of Change

  • New feature (non-breaking change which adds functionality)

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

  • Test that method works when called by an automation client.
Microsoft Reviewers: codeflow:open?pullrequest=#11874

@chiaramooney chiaramooney requested a review from a team as a code owner July 11, 2023 18:28
return UIA_E_ELEMENTNOTAVAILABLE;

// Currently calls both onAccessibilityTap and onClick.
// To match Paper behavior, onAccessibilityTap only called if onClick is not defined.
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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

Copy link
Contributor

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)

Copy link
Contributor

@jonthysell jonthysell Aug 2, 2023

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:

https://github.com/facebook/react-native/blob/8d8455c3c34e9554908781485718d8eee252267f/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm#L802-L810

- (BOOL)accessibilityActivate
{
  if (_eventEmitter && _props->onAccessibilityTap) {
    _eventEmitter->onAccessibilityTap();
    return YES;
  } else {
    return NO;
  }
}

Copy link
Contributor Author

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 whether onPress has been set. And the call to dispatchEvent("topClick") runs the onPress 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 the onPress 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 calling onAccessibilityTap 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 if onAccessibilityTap isn't implemented.

Copy link
Contributor

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 if onAccessibilityTap 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.

Copy link
Contributor Author

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.

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) and removed Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) labels Jul 13, 2023
return UIA_E_ELEMENTNOTAVAILABLE;

// Currently calls both onAccessibilityTap and onClick.
// To match Paper behavior, onAccessibilityTap only called if onClick is not defined.
Copy link
Contributor

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

@chiaramooney chiaramooney requested review from a team as code owners July 20, 2023 17:54
@chiaramooney
Copy link
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants