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

Android: Role description is announced before the components text, rather than after #31042 #14

Closed
fabOnReact opened this issue Apr 4, 2022 · 26 comments

Comments

@fabOnReact
Copy link
Owner

fabOnReact commented Apr 4, 2022

Issue facebook/react-native#31042
PR facebook/react-native#33690

@fabOnReact

This comment was marked as off-topic.

@fabOnReact

This comment was marked as off-topic.

@fabOnReact

This comment was marked as resolved.

@fabOnReact
Copy link
Owner Author

The View does not announce the child component Text when accessibilityLabel is missing (automatic content grouping)

The View has accessibilityRole="button" and child component with:

  • importantForAccessibility="no". It is not focusable or clickable. The ScreenReader focus will not move on this element.
  • has text This is a child Text

Expected Result:
The View announces This is a child Text, Button.

Actual Result:
The View announces Button.

click to open code used in the test

<View
  style={{height: 100, width: 400, backgroundColor: 'red'}}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <Text
    importantForAccessibility="no"
    style={{
      height: 50,
      marginLeft: 50,
      marginTop: 50,
      backgroundColor: 'white',
    }}>
    {childText}
  </Text>
</View>

click to open recording of test case

2022-04-05.21-01-39.mp4

@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 5, 2022

The View announces the child component Text when accessibilityLabel is missing (automatic content grouping)

The View has accessibilityRole="button" and child component with:

  • importantForAccessibility="no". It is not focusable or clickable. The ScreenReader focus will not move on this element.
  • has text This is a child Text

Expected/Actual Result:
The View announces This is a child Text, Button.

Notes

  • The solution here is checking if an accessibilityLabel already exist otherwise over-ride it with the childText
  • The child text should be non focusable, otherwise their label/text is announced when TalkBack ScreenReader focuses on the element
  • The fix is applied to the affected components (Button, TouchableOpacity etc..)
click to open code used in the test

<View
  style={{height: 100, width: 400, backgroundColor: 'red'}}
  importantForAccessibility="yes"
  accessibilityRole="button"
  accessibilityLabel={props.accessibilityLabel ? props.accessibilityLabel : childText}>
  <Text
    importantForAccessibility="no"
    style={{
      height: 50,
      marginLeft: 50,
      marginTop: 50,
      backgroundColor: 'white',
    }}>
    {childText}
  </Text>
</View>

click to open recording of test case

2022-04-05.21-08-43.mp4

@fabOnReact fabOnReact pinned this issue Apr 5, 2022
@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 11, 2022

Scenarios - TouchableNativeFeedback

Touchable with one child

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <Text
    accessible={false}
    focusable={false}>
    Text number 1
  </Text>
</TouchableNativeFeedback>

Touchable with multiple children

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <View>
      <Text
        accessible={false}
        focusable={false}>
        Text number 1
      </Text>
      <Text
        accessible={false}
        focusable={false}>
        Text number 2
      </Text>
   </View>
</TouchableNativeFeedback>

Touchable with children that have children

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <View>
      <Text
        accessible={false}
        focusable={false}>
        Text number 1
      </Text>
      <Text
        accessible={false}
        focusable={false}>
        Text number 2
          <Text
            accessible={false}
            focusable={false}>
            Text number 3
          </Text>
      </Text>
   </View>
</TouchableNativeFeedback>

Touchable with children's that are not Text components

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <View>
      <Text
        accessible={false}
        focusable={false}>
        Text number 1
      </Text>
      <Text
        accessible={false}
        focusable={false}>
        Text number 2
      </Text>
   </View>
</TouchableNativeFeedback>

Touchable with one Text link

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">          
    <Text accessible={false} focusable={false}>
      Text number 1<Text accessibilityRole="link">Link</Text>
    </Text>
</TouchableNativeFeedback>

@fabOnReact

This comment was marked as resolved.

@fabOnReact

This comment was marked as resolved.

@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 22, 2022

Screenreader focuses on ScrollView Items

Expected/Actual Result:
This has to be verified again before moving the pr to Ready for Review.

click to open code used in the test

function TouchableExample(props) {
  return (
    <ScrollView
      accessible={true}
      focusable={true}
      style={{backgroundColor: 'red'}}>
      <Text>Text number 1</Text>
      <Text>
        Text number 2<Text accessible={false}>Text number 3</Text>
      </Text>
    </ScrollView>
  );
}

click to open recording of test case - without the implementation

2022-04-27.14-09-04.mp4

click to open recording of test case - with the implementation

2022-04-22.13-00-53.mp4

@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 22, 2022

Screenreader announces Pressability items

Expected/Actual Result:
Screenreader announces Text number 1Text number 2 Text number 3, Button.

click to open code used in the test

function TouchableExample(props) {
  return (
    <Pressable
      accessible={true}
      focusable={true}
      onPress={() => console.warn('pressed')}
      accessibilityRole="button"
      style={{backgroundColor: 'red'}}>
      <View>
        <Text accessible={false}>Text number 1</Text>
        <Text accessible={false}>
          Text number 2<Text accessible={false}>Text number 3</Text>
        </Text>
      </View>
    </Pressable>
  );
}

click to open recording of test case

2022-04-22.16-56-13.mp4

@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 22, 2022

Screenreader correctly announcing accessible/non-accessible items

Expected/Actual Result:
Talkback announces Text number 1Text number 2Text number3, Button.
Talkback announces Text clickable.

click to open code used in the test

function TouchableExample(props) {
  return (
    <Pressable
      accessible={true}
      focusable={true}
      onPress={() => console.warn('pressed')}
      accessibilityRole="button"
      style={{backgroundColor: 'red'}}>
      <View>
        <Text onPress={() => console.warn('pressed')}>Text clickable</Text>
        <Text accessible={false}>Text number 1</Text>
        <Text accessible={false}>
          Text number 2<Text accessible={false}>Text number 3</Text>
        </Text>
      </View>
    </Pressable>
  );
}

click to open recording of test case

2022-04-22.18-48-40.mp4

@fabOnReact

This comment was marked as resolved.

@fabOnReact

This comment was marked as off-topic.

@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 26, 2022

Button role is announced after child contentDescription with TouchableNativeFeedback

Expected/Actual Result:
Talkback announces Text number 1, Text number 1, Button

click to open code used in the test

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <View accessible={false}>
    <Text accessible={false} focusable={false}>
      Text number 1
    </Text>
    <Text accessible={false} focusable={false}>
      Text number 1
    </Text>
  </View>
</TouchableNativeFeedback>

without fix - Button role is announced before child text

withoutImplementation.mp4

without fix - Button role is announced before child text (second example)

withoutImplementationAccEx.mp4

with fix - Button role is announced after child text

withImplementation.mp4

with fix - Button role is announced after child text (second example)

withImplementationAccEx.mp4

@fabOnReact
Copy link
Owner Author

Testing for regressions in Accessibility Actions

Expected/Actual Result: Same behavior with/out implementation

click to open code used in the test

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button"
  accessibilityActions={[
    {name: 'activate', label: 'activate label'},
    {name: 'copy', label: 'copy label'},
  ]}
  onAccessibilityAction={event => {
    switch (event.nativeEvent.actionName) {
      case 'activate':
        Alert.alert('Alert', 'Activate accessiblity action');
        break;
      case 'copy':
        Alert.alert('Alert', 'copy action success');
        break;
    }
  }}>
  <View>
    <Text accessible={false}>Text number 1</Text>
    <Text accessible={false}>
      Text number 2<Text accessible={false}>Text number 3</Text>
    </Text>
  </View>
</TouchableNativeFeedback>

click to open recording of test case - without implementation

withActionNoImple.mp4

click to open recording of test case - with implementation

withActionwithImpl.mp4

@fabOnReact

This comment was marked as off-topic.

@fabOnReact
Copy link
Owner Author

fabOnReact commented Apr 28, 2022

Recordings of complete test cases in rn-tester app main and pr branch

click to open recording of test case - main branch

full-record-main.mp4

click to open recording of test case - pr branch

full-recording-branch.mp4

@fabOnReact

This comment was marked as resolved.

@fabOnReact
Copy link
Owner Author

fabOnReact commented May 27, 2022

TouchableOpacity with child EditText annouces Button role before the child contentDescription

test case added with commit fabOnReact/react-native@9d72a70

The issue is caused by this line. info.getText() retrieves the text of the child TextInput (the placeholder), but the current element (in js the TouchableNativeFeedback), does not have contentDescription or Text and the role (button) of the TouchableNativeFeedback is announced before the contentDescription of the child component.

TouchableNativeFeedback does not exist in ReactAndroid, and it is just a wrapper to return the child component with an onPress event handler, for this reason info.getText() returns the child text.

The functionalities are developed with fabOnReact/react-native@9d1bf23

test done on the 27.05

in this case the first text is not announced, because it's a secure entry.

low.quality.mp4
test done on the 30.05

on commit fabOnReact/react-native@abed89e

pr_text_input_placeholder_2.mp4

@fabOnReact
Copy link
Owner Author

fabOnReact commented May 30, 2022

TouchableOpacity with child EditText annouces placeholder text before and after the role

The issue reproduces on the main/pr branch.
The placeholder text is announced before and after the role.

Expected Result this is the placeholder, Button
Actual Result this is the placeholder, Button, this is the placeholder

Explanation of the result:

  • The first announcement corresponds to the value returned from nodeInfo.getText()
  • The second announcement corresponds to the role
  • The last announcement corresponds to the hint text

Flipper includes implementation of getTalkbackHint and (getTalkbackData).

Source Code of Test Case

<TouchableNativeFeedback
  accessible={true}
  importantForAccessibility="yes"
  accessibilityRole="button">
  <TextInput
    accessible={false}
    style={styles.default}
    placeholder="this is the placeholder"
  />
</TouchableNativeFeedback>

Test Case of Main Branch

placeholder_issue_main.mp4

Test Case of PR Branch

placeholder_issue_pr.mp4

@fabOnReact
Copy link
Owner Author

TouchableOpacity with TextInput child announces contentDescription before the Role

pr_branch_textinput_test.mp4

@fabOnReact
Copy link
Owner Author

fabOnReact commented May 31, 2022

EditText is allways accessible to TalkBack screenreader

I could not find a test scenarios for this logic implemented with commit fabOnReact/react-native@388911e

The EditText is always accessible, so it makes no sense to over-ride the TalkBack logic.
The logic could be deleted.

Sourcecode

The code of the example

        <RNTesterBlock title="TouchableNativeFeedback with non-accessible child Texts, one of them is an EditText">
          <View
            accessible={true}
            importantForAccessibility="yes"
            accessibilityRole="button">
            <View>
              <Text accessible={false}>Text number 1</Text>
              <TouchableNativeFeedback accessible={false} focusable={false}>
                <TextInput
                  accessible={false}
                  focusable={false}
                  style={styles.default}
                />
              </TouchableNativeFeedback>
            </View>
          </View>
        </RNTesterBlock>

textinput.allways.focusable.mp4

@fabOnReact

This comment was marked as resolved.

@fabOnReact
Copy link
Owner Author

fabOnReact commented Jun 8, 2022

  • code reuse - use talkback isSpeakingNode instead of making new implementation.
    This method can call itself recursively through #hasNonActionableSpeakingChildren

@fabOnReact
Copy link
Owner Author

fabOnReact commented Jun 8, 2022

One of the child has accessibilityState (hasStateDescription triggers the announcement)

fabOnReact/react-native@c17f00e
facebook/react-native#33690 (comment)

sourcecode from test case

<View accessible={true} accessibilityRole="button">
  <Text accessible={false}>Text number 1</Text>
  <Text
    style={styles.smallRedSquare}
    accessible={false}
    accessibilityState={{checked: true}}
    accessibilityLabel="this child Text does not have text, but has state and should be announced by TalkBack"
    accessibilityRole="image"
  />
</View>

XRecorder_08062022_144525.mp4

@fabOnReact
Copy link
Owner Author

One of the child has accessibilityHint (hasText triggers the announcement)

fabOnReact/react-native@c17f00e
facebook/react-native#33690 (comment)

sourcecode from test case

<View accessible={true} accessibilityRole="button">
  <Text
    style={styles.smallRedSquare}
    accessible={false}
    accessibilityHint="this child Text does not have text, but has hint and should be announced by TalkBack"
  />
</View>

XRecorder_08062022_144750.mp4

facebook-github-bot pushed a commit to facebook/react-native that referenced this issue Dec 2, 2022
…ustom contentDescription (#33690)

Summary:
The Implementation of the functionality consists of:

1) Checking that an element has no contentDescription and no text and has other content to announce (role, state, etc.) which causes this issue (for ex. the accessibilityRole is announced before the contentDescription for ex. "Button, My text children component")
2) If Talkback finds no content to announce on the current node, a custom contentDescription is generated from the child elements that respect the following conditions:

>If an AccessibilityNodeInfo is considered "actionable" (which Talkback defines as having clickable=true, longClickable=true, or focusable=true, or having AccessibilityActions for any of those), AND it has some content to read like a contentDescription or text, it will be considered focusable.
>If an AccessibilityNodeInfo is considered "actionable" AND it does not have content to read like a contentDescription or text Talkback will parse descendant elements looking for non-focusable descendants to use as content.

3) implementation of a method getTalkbackDescription to generate the above contentDescription from child elements
4) over-ride parent contentDescription (accessibilityLabel) with the value returned from getTalkbackDescription

Related [notes on Android: Role description is announced before the components text, rather than after https://github.com/facebook/react-native/issues/31042][51]. This issue fixes [https://github.com/facebook/react-native/issues/31042][50].

## Changelog

[Android] [Added] - Override default Talkback automatic content grouping and generate a custom contentDescription

Pull Request resolved: #33690

Test Plan:
**PR Branch**
[1]. Screenreader correctly announcing accessible/non-accessible items ([link][1])
[2]. Screenreader announces Pressability items ([link][2])
[3]. Button role is announced after child contentDescription with TouchableNativeFeedback ([link][3])
[4]. Testing for regressions in Accessibility Actions ([link][4])
[5]. Screenreader focuses on ScrollView Items ([link][5])
[6]. Recordings of complete test cases in rn-tester app main and pr branch ([link][6])
[9]. TouchableOpacity with TextInput child announces contentDescription before the Role ([link][9])
[10]. One of the child has accessibilityState (hasStateDescription triggers the announcement) ([link][10])
[11]. One of the child has accessibilityHint (hasText triggers the announcement) ([link][11])

**Main**
[15]. The View does not announce the child component Text when accessibilityLabel is missing (automatic content grouping) ([link][15])
[8]. TouchableOpacity with child EditText annouces placeholder text before and after the role ([link][8])

[1]: fabOnReact/react-native-notes#14 (comment) "Screenreader correctly announcing accessible/non-accessible items"
[2]: fabOnReact/react-native-notes#14 (comment) "Screenreader announces Pressability items"
[3]: fabOnReact/react-native-notes#14 (comment) "Button role is announced after child contentDescription"
[4]: fabOnReact/react-native-notes#14 (comment) "Testing for regressions in Accessibility Actions"
[5]: fabOnReact/react-native-notes#14 (comment) "Screenreader focuses on ScrollView Items"
[6]: fabOnReact/react-native-notes#14 (comment) "Recordings of complete test cases in rn-tester app main and pr branch"
[7]: fabOnReact/react-native-notes#14 (comment) "TouchableOpacity with child EditText annouces Button role before the child contentDescription"
[8]: fabOnReact/react-native-notes#14 (comment) "TouchableOpacity with child EditText annouces placholder text before and after the role"
[9]: fabOnReact/react-native-notes#14 (comment) "TouchableOpacity with TextInput child announces contentDescription before the Role"
[10]: fabOnReact/react-native-notes#14 (comment) "One of the child has accessibilityState (hasStateDescription triggers the announcement)"
[11]: fabOnReact/react-native-notes#14 (comment) "One of the child has accessibilityHint (hasText triggers the announcement)"

[15]: fabOnReact/react-native-notes#14 (comment) "The View does not announce the child component Text when accessibilityLabel is missing (automatic content grouping)"

[50]: #31042 "Android: Role description is announced before the components text, rather than after #31042"
[51]: fabOnReact/react-native-notes#14 "notes on Android: Role description is announced before the components text, rather than after #31042"

Reviewed By: cipolleschi

Differential Revision: D39177512

Pulled By: blavalla

fbshipit-source-id: 6bd0fba9c347bc14b3839e903184c86d2bcab3d2
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this issue May 22, 2023
…ustom contentDescription (facebook#33690)

Summary:
The Implementation of the functionality consists of:

1) Checking that an element has no contentDescription and no text and has other content to announce (role, state, etc.) which causes this issue (for ex. the accessibilityRole is announced before the contentDescription for ex. "Button, My text children component")
2) If Talkback finds no content to announce on the current node, a custom contentDescription is generated from the child elements that respect the following conditions:

>If an AccessibilityNodeInfo is considered "actionable" (which Talkback defines as having clickable=true, longClickable=true, or focusable=true, or having AccessibilityActions for any of those), AND it has some content to read like a contentDescription or text, it will be considered focusable.
>If an AccessibilityNodeInfo is considered "actionable" AND it does not have content to read like a contentDescription or text Talkback will parse descendant elements looking for non-focusable descendants to use as content.

3) implementation of a method getTalkbackDescription to generate the above contentDescription from child elements
4) over-ride parent contentDescription (accessibilityLabel) with the value returned from getTalkbackDescription

Related [notes on Android: Role description is announced before the components text, rather than after https://github.com/facebook/react-native/issues/31042][51]. This issue fixes [https://github.com/facebook/react-native/issues/31042][50].

## Changelog

[Android] [Added] - Override default Talkback automatic content grouping and generate a custom contentDescription

Pull Request resolved: facebook#33690

Test Plan:
**PR Branch**
[1]. Screenreader correctly announcing accessible/non-accessible items ([link][1])
[2]. Screenreader announces Pressability items ([link][2])
[3]. Button role is announced after child contentDescription with TouchableNativeFeedback ([link][3])
[4]. Testing for regressions in Accessibility Actions ([link][4])
[5]. Screenreader focuses on ScrollView Items ([link][5])
[6]. Recordings of complete test cases in rn-tester app main and pr branch ([link][6])
[9]. TouchableOpacity with TextInput child announces contentDescription before the Role ([link][9])
[10]. One of the child has accessibilityState (hasStateDescription triggers the announcement) ([link][10])
[11]. One of the child has accessibilityHint (hasText triggers the announcement) ([link][11])

**Main**
[15]. The View does not announce the child component Text when accessibilityLabel is missing (automatic content grouping) ([link][15])
[8]. TouchableOpacity with child EditText annouces placeholder text before and after the role ([link][8])

[1]: fabOnReact/react-native-notes#14 (comment) "Screenreader correctly announcing accessible/non-accessible items"
[2]: fabOnReact/react-native-notes#14 (comment) "Screenreader announces Pressability items"
[3]: fabOnReact/react-native-notes#14 (comment) "Button role is announced after child contentDescription"
[4]: fabOnReact/react-native-notes#14 (comment) "Testing for regressions in Accessibility Actions"
[5]: fabOnReact/react-native-notes#14 (comment) "Screenreader focuses on ScrollView Items"
[6]: fabOnReact/react-native-notes#14 (comment) "Recordings of complete test cases in rn-tester app main and pr branch"
[7]: fabOnReact/react-native-notes#14 (comment) "TouchableOpacity with child EditText annouces Button role before the child contentDescription"
[8]: fabOnReact/react-native-notes#14 (comment) "TouchableOpacity with child EditText annouces placholder text before and after the role"
[9]: fabOnReact/react-native-notes#14 (comment) "TouchableOpacity with TextInput child announces contentDescription before the Role"
[10]: fabOnReact/react-native-notes#14 (comment) "One of the child has accessibilityState (hasStateDescription triggers the announcement)"
[11]: fabOnReact/react-native-notes#14 (comment) "One of the child has accessibilityHint (hasText triggers the announcement)"

[15]: fabOnReact/react-native-notes#14 (comment) "The View does not announce the child component Text when accessibilityLabel is missing (automatic content grouping)"

[50]: facebook#31042 "Android: Role description is announced before the components text, rather than after facebook#31042"
[51]: fabOnReact/react-native-notes#14 "notes on Android: Role description is announced before the components text, rather than after facebook#31042"

Reviewed By: cipolleschi

Differential Revision: D39177512

Pulled By: blavalla

fbshipit-source-id: 6bd0fba9c347bc14b3839e903184c86d2bcab3d2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant