Skip to content

Querying by role with name on nested touchables return multiple elements #1152

Closed as not planned

Description

@AugustinLF

Describe the bug

We have a problem when querying nested touchables, see the example for more details:

describe('nested touchables', () => {
  it('conflicting elements', () => {
    const { getByRole } = render(
      <Pressable accessibilityRole="button">
        <Text>Some other text</Text>
        <Pressable accessibilityRole="button">
          <Text>Save</Text>
        </Pressable>
      </Pressable>
    );

    // Found multiple elements with accessibilityRole: button
    // Here I'm not sure what we should do. This is not an accessible pattern.
    // At least we should suggest a better error (might be just a warning/error on nested touchables)
    expect(getByRole('button', { name: 'Save' })).toBeTruthy();
  });

  it('with accessible={false}', () => {
    const { getByRole } = render(
      <Pressable accessibilityRole="button" accessible={false}>
        <Text>Some other text</Text>
        <Pressable accessibilityRole="button">
          <Text>Save</Text>
        </Pressable>
      </Pressable>
    );

    // Found multiple elements with accessibilityRole: button
    // Here it should work and only getting the lower Pressable.
    // `accessible={false}` means it won't be read to the screen-reader.
    // This is a common pattern I've seen where you make a whole area implicitly touchable for better UX
    // but add a nested explicit button for better discoverability (and accessibility)
    expect(getByRole('button', { name: 'Save' })).toBeTruthy();
  });
});

Versions

RNTL 11.2

Activity

  1. mdjastrzebski commented on Sep 29, 2022

    @mdjastrzebski
    Member

    I think that this might actually be a more general aspect of out tree walking code that uses findAll from RTR.

    I imagine that it also should manifest itself for any scenario with matching nested elements, like: *ByLabel with following setup:

    <View accessiblityLabel="hello">
      <View accessiblityLabel="hello">
        <Text>Hello</Text>
      </View>
    </View>

    or *ByA11yStates with following setup:

    <View accessiblityStates={{ disabled: true }}>
      <View accessiblityStates={{ disabled: true }}>
        <Text>Hello</Text>
      </View>
    </View>

    Essentially for any case when component structure allows for nesting elements matching query predicate, it currently will result in matching both the inner and outer one. This might be more visible in certain scenarios like *ByRole queries, but probably affects all nesting scenarios.

    It seems that RTL/DTL should behave the same as our code because their queries rely on using querySelectorAll which seems to have the same behavior of returning both outer and inner for nested elements.

    I think we should first investigate how RTL would behave in a similar case, and then decide if this is a bug or a feature.
    @AugustinLF wdyt?

  2. AugustinLF commented on Sep 29, 2022

    @AugustinLF
    CollaboratorAuthor

    Overall I agree, that sounds fair and we should probably use the same API/behaviour. I do think the accessible={true} might be a special case, even though ideally the developer would use an onPress with a role on the parent element.

  3. mdjastrzebski commented on Sep 30, 2022

    @mdjastrzebski
    Member

    Regarding the original tests mentioned in this issue, we should consider excluding elements with explicit accessible={false} prop from *ByRole query, since these are explicitly marked as not being accessibility elements, and afaiu should not have a role.

    @AugustinLF wdyt?

  4. AugustinLF commented on Oct 1, 2022

    @AugustinLF
    CollaboratorAuthor

    Yes, I think that'd be a great start!

  5. mdjastrzebski commented on Oct 17, 2022

    @mdjastrzebski
    Member

    Closing in favour of #1180 with more clear scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions