Closed KrastanD closed 4 months ago
To make a View
an accessibility element just pass accessible={true}
props to it.
The elements types you mention: Text
, TextInput
and Switch
are considered to be accessible by default (even without accessible={true}
prop).
Not sure what happens in Tamagui, as the code is pretty abstract. If you think that RNTL is missing something, the create a minimal Expo app using Tamagui that showcases the missing case. Having something like this at hand would allow me verify whether to tweak our simulation code.
Adding accessible
did make the element selectable, thank you!!! Idk how I missed that line.
I created an example expo app, but my test using userEvent wasn't checking the component. I tried all kinds of selectors but that didn't make a difference. Out of desperation I tried using fireEvent and that worked. Could you take a look?
My hunch (from my understanding) is that fireEvent checks the parents handlers until it finds one, and userEvent doesn't do that so here, the onValueChange is on the RadioGroup level, not the RadioGroupItem so userEvent can't trigger it. I'm not sure how to trigger it with userEvent since I need to click on the specific item.
Just ran into this myself! I believe Tamagui should handle adding accessible
to components, right? React Native Elements takes care of this for you.
@KrastanD User Event has code code that performs lookup for pressable element from given element up: https://github.com/callstack/react-native-testing-library/blob/4e148bdb0573fcb86686482c3af1d1e02e1cc937/src/user-event/press/press.ts#L58
From that point it locks on that element, and tries to send all of touch events there without further lookups.
@KrastanD I've reviewed this issue to check if it can be closed as it looked stale. I've run your repro repository and found it interesting. When I run screen.debug()
on our failing test, it does not seems to contain onPress
or similar event handler:
<View>
<View
__scopeRovingFocusGroup="RadioGroup"
loop={true}
>
<View
accessibilityRole="radiogroup"
>
<View>
<View
__scopeRovingFocusGroup="RadioGroup"
accessibilityRole="radio"
accessibilityState={
{
"checked": false,
}
}
accessible={true}
active={false}
focusable={true}
id="radiogroup-1"
/>
<View
pointerEvents="none"
/>
<Text
htmlFor="radiogroup-1"
id=":r3:"
suppressHighlighting={true}
userSelect="none"
>
First value
</Text>
</View>
<View>
<View
__scopeRovingFocusGroup="RadioGroup"
accessibilityRole="radio"
accessibilityState={
{
"checked": false,
}
}
accessible={true}
active={false}
focusable={true}
id="radiogroup-2"
/>
<View
pointerEvents="none"
/>
<Text
htmlFor="radiogroup-2"
id=":r4:"
suppressHighlighting={true}
userSelect="none"
>
Second value
</Text>
</View>
<View>
<View
__scopeRovingFocusGroup="RadioGroup"
accessibilityRole="radio"
accessibilityState={
{
"checked": false,
}
}
accessible={true}
active={false}
focusable={true}
id="radiogroup-3"
/>
<View
pointerEvents="none"
/>
<Text
htmlFor="radiogroup-3"
id=":r5:"
suppressHighlighting={true}
userSelect="none"
>
Third value
</Text>
</View>
</View>
</View>
</View>
screen.debug
displays only host views, and it seems that none of these views actually has any event handler 🧐 This will make userEvent.press
fail as it expects onPress
or onPressIn
/onPressOut
to be present. The reason why Fire Event works is that it invokes event handlers on both host and composite components, and Tamagui's RadioGroup seems to contain such onPress
handler on RadioGroupItemFrame
.
Tamagui is quite complex as it uses some additional abstractions, so it's hard for me to say what exactly is happening there. The thing that I can see, is that the host element tree does not seem to contain any event handlers (onXxx
) at all. Perhaps this is supposed to be selected by the following effect code in React Native 🤷♂️
Let me know if you are willing to explore this more or should we close this issue.
I am on the exact same situation, after hours of research I found that using the accesible attribute works fine and I'm thinking on adding a wrapper for all these elements from Tamagui that need it.
I wasn't very happy but I think that's the only solution, then I found that only fireEvent works instead of user event.
My concern is, can we rely on all these and be in peace with having the wrapper plus using fire event? It's messing with my head a little bit since I have the feeling that fireEvent will be away eventually, perhaps I'm wrong on that.
Thanks
@mfrfinbox you should raise this issue with Tamagui maintainers. Their code is pretty complex due to abstractions and RN/web shared code, so they should be the best people to consult on this.
From my perspective the issue is on Tamagui's side (lack of accessible
prop) but I would be happy to be proven wrong 😊
For the accessible={true}, that should be contributed to tamagui directly imo so everyone gets it out the box and don't run into it like @mfrfinbox and I have.
As for the host elements not showing onPress, I'm not quite sure how that happens. Aren't all composite elements built from host elements? The RadioGroupItemFrame
has the onPress and extends a ThemableStack, which extends a YStack, which extends a View.
I've open the issues on the Tamagui side, thanks
https://github.com/tamagui/tamagui/issues/2613 https://github.com/tamagui/tamagui/issues/2614
@mfrfinbox thank's for logging the issues, please update them with MyComponent
source, as otherwise they are incomplete.
@KrastanD as for host elements, in React the components are not being extended (subclassed) in classic OOP way, they are being rendered (composed) by other (composite) components.
The YStack
is a composite component that renders View
, and should pass all it's props there as it uses styled
, however for some reasons onPress
from RadioGroupItemFrame
does not get to the final View
host element.
The reason why User Events insist on invoking only events on the host components, is that these are the only components that the user can see as they correspond to UI controls in iOS/Android. Composite components are just an abstraction, and cannot be directly observed or interacted by the users.
@mfrfinbox thank's for logging the issues, please update them with
MyComponent
source, as otherwise they are incomplete.
Done!
Closing as it requires fixing upstream (Tamagui)
Ask your Question
I am using Tamagui and I want to check that a radio button has been checked but it's not able to and I think it's because Tamagui adds the radio role to a view but React Native Testing Library only checks text, textInput and switch elements for a role.
Could you explain the reasoning for having only those elements as accessibility elements? Do you have a recommended way that I can check that the radio button has been checked, or does Tamagui have to change how they set role?