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

byRole queries don't find element that don't have an explicitly set accessibilityRole prop #1130

Closed
AugustinLF opened this issue Sep 20, 2022 · 11 comments · Fixed by #1490
Closed
Labels

Comments

@AugustinLF
Copy link
Collaborator

AugustinLF commented Sep 20, 2022

Describe the bug

react-native built-in elements are not queriable with byRole queries if you don't set the accessibilityRole prop.

Expected behaviour

byRole queries should be able to match some elements without accessibilityRole.

We however need to be conscious of what roles should be used to match what elements. My suggestion is checking each element with a voice-over and see what matches:

  • should <Pressable /> be button by default?
  • <TextInput /> will read Input on ios voiceover, but that is not an AccessibilityRole as exported by react-native.

We might need to think about types. The point raised in #1127 (comment) that suggests using TextMatch for the role will make it harder to discover those cases.

Steps to Reproduce

    Unable to find an element with accessibilityRole: image

      140 |     const { getByRole } = render(<Image />);
      141 |
    > 142 |     getByRole('image');
          |     ^
      143 |   });
      144 | });
      145 |

Versions

Latest.

@AugustinLF AugustinLF added bug Something isn't working good first issue Good for newcomers labels Sep 20, 2022
@mdjastrzebski
Copy link
Member

This is a questions whether we want to emulate that behavior or should React Native actually set these roles by itself.

I wonder why React Native is not supporting more of ARIA roles? It even misses things like textbox or equivalent role, which seems a huge omission, that will somehow limit the usefulness of byRole + name query.

@AugustinLF
Copy link
Collaborator Author

There is a valid point of if RN should support more roles. However, if you look at RTL, they do support by themselves both the raw dom element and custom role prop, with the getImplicitAriaRoles function. They also have similar behaviour to understanding the states that are not user set (checked for instance).

So at least, we should support AccessibilityRole supported by RN, that are handled at the native level and not through accessibilityRole.

@mdjastrzebski
Copy link
Member

Regarding getting implicit roles there is another relevant public API: getRoles() which we should take into account.

Looks like it's responsible for calculating both explicit and implicit roles for given element: https://github.com/testing-library/dom-testing-library/blob/a9a8cf26992ff0f6b4257b7300939f461d04440d/src/role-helpers.js#L155

@mdjastrzebski
Copy link
Member

I think we also need to think whether to support some ARIA roles that are important but not present in RN AccessibilityRole definition, especially: textbox, maybe something more.

@mdjastrzebski
Copy link
Member

mdjastrzebski commented Sep 29, 2022

Do we know which implicit roles we want to support for which components?

Initially we could start with:

  • text for Text
  • textbox for TextInput (not present in AccessibilityRole!)
  • image for Image
  • button for Pressable and all Touchable* - not 100% sure about this, but seems reasonable

By default View would have none implicit role (or have none role - which probably be equivalent).

Implicit roles would be used by *ByRole queries only in case when there is no explicit role. Otherwise explicit role always overrrides implicit role.

@AugustinLF
Copy link
Collaborator Author

I think we could take some inspiration from aria role, which is more exhaustive than AccessibilityRole (that I feel designed as something to set, rather than to query).

If we follow their logic:

  • Text by itself doesn't have a role, <p> doesn't have one on web
  • TextInput is a hard one, web makes a difference between the different type of <input />, and it could be a combobox, but textbox would be a reasonable one to start with. How does a textarea reads on web? Because they suggest using the different type of input, if a screen reader reads them differently, we could try using the same ones, and taking multiline into account
  • Yes I agree for Image
  • Pressable, it depends how it reads. Does the screen readers reads something if you don't specify a role? If thats' the case, yes, if not, I think it'd be safe to ignore it from an accessibility perspective, since it'd create a false sense of security for our users: they'd getByRole('button') while the element wouldn't appear as a button to user.

I strongly believe that if an element is not read with a specific role by screen-readers, then we shouldn't determine an implicit role.

@mdjastrzebski
Copy link
Member

I think we need to build some playground repos for native, so that we have easier job when examining these roles. For native we could build a sample Expo app and the test it using VoiceOver on iOS and TalkBack on Android. If I understand correctly these screen readers should announce roles for the elements.

It also seems that there are difference between RN Accessiblity Roles, ARIA roles, UIKit Accessibility Traits and Android where I could not find anything in the API resembling roles.

E.g. ARIA does not have a role for static text, iOS has staticText trait, and RN has text.

We should follow recent RN activity facebook/react-native#34538, and base most of our decisions on ARIA roles, but also taking into account how screen readers act on iOS/Android.

Add role. facebook/react-native#34538 (comment)
Alias for accessibilityRole but with full list of ARIA roles allowed.
Map role='slider' to accessibilityRole='adjustable'
Map role='img' to accessibilityRole='image'
Map role='presentation' to accessibilityRole='none'
Map role='summary' to accessibilityRole='region'

This RN PR has mapping between ARIA and RN roles.

@mdjastrzebski mdjastrzebski added discussion and removed good first issue Good for newcomers labels Sep 29, 2022
@mdjastrzebski
Copy link
Member

Not sure how reliable is ESLint A11y plugin, but there are some of interesting rules:

  • has-accessibility-props: Enforce that <Touchable*> components only have either the accessibilityRole prop or both accessibilityTraits and accessibilityComponentType props set
  • no-nested-touchables: Enforce if a view has accessible={true}, that there are no clickable elements inside

If we treat this as valid, we should assume that touchables (& pressable) do not have default role, neither button nor anything else. Also we should be only taking role into account if accessible is not false, as otherwise the element is no an accessiblity element (i.e. is presentation element). Not sure about the default value of accessible prop, probably is false for View as Pressable/TouchableOpacity render themselves as Views with accessible={true}.

Snapshot of:

<Pressable testID="test">

is

<View
      accessible={true}
      collapsable={false}
      focusable={true}
      onBlur={[Function]}
      onClick={[Function]}
      onFocus={[Function]}
      onResponderGrant={[Function]}
      onResponderMove={[Function]}
      onResponderRelease={[Function]}
      onResponderTerminate={[Function]}
      onResponderTerminationRequest={[Function]}
      onStartShouldSetResponder={[Function]}
      testID="test"
    />

Default value of accessible for Text: seems to depend on plantform and props:

Default value of accessible for TextInput seems to be true:

  • const accessible = props.accessible !== false;

@mdjastrzebski
Copy link
Member

@mdjastrzebski
Copy link
Member

In order to have more concrete information about iOS/Android behaviour, I've stared doing experiments how different a11y props combinations affects behaviour of assistive tech on iOS and Android. I share initial findings in newly created wiki page. If you have time, please contribute your research for other component types (TextInput, Image, Pressable/TouchableXx, View, etc) there.

@MattAgn
Copy link
Collaborator

MattAgn commented Jan 20, 2023

I've checked for Pressable and Image on ios using VoiceOver:

  • Pressable: it's not a button by default unfortunately, Voice just reads the text inside if not role is given
  • Image: not accessible by default and not read as an image by default by VoiceOver. So the props accesisble and accessibilityRole are compulsory to make it accessible

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

Successfully merging a pull request may close this issue.

3 participants