Skip to content

fix: tooltips not working as expected#3765

Merged
lukewalczak merged 9 commits intomainfrom
fix/tooltips-not-working-as-expected
Jun 26, 2023
Merged

fix: tooltips not working as expected#3765
lukewalczak merged 9 commits intomainfrom
fix/tooltips-not-working-as-expected

Conversation

@pac-guerreiro
Copy link
Contributor

@pac-guerreiro pac-guerreiro commented Mar 20, 2023

Summary

This PR fixes issues related to the tooltip component not working correctly on web and native, as reported on #3729.

The changes are as follows:

  • Fixed console warnings when using tooltip component with components that didn't forward refs. I introduced changes to how the tooltip component handles hovering/pressing events, so now there's no need to pass refs to wrapped components.

  • Fixed tooltip not working with non-pressable components, like avatar text, on both web and native.

  • Fixed tooltip not working with FAB component. First part of the problem was that FAB didn't use handlers for hover/press events, so the tooltip would not be triggered to show. Second part of the problem was that when using tooltip with a component that has a position: 'absolute' the tooltip isn't placed correctly on the screen. The solution was to take the component styling into account when calculating the right placement for the tooltip.

  • Added reproducibility sample from Not all componetns can receive ref #3729 to Tooltips screen/page on the example app.

  • Changed how tooltips are triggered:

    • on web, the tooltip is triggered if the user mouse pointer hovers the wrapped component;
    • on native, the tooltip is triggered by long pressing the wrapped component.

Test plan

These changes can be tested on the Tooltips screen/page of the example app.

Screen recordings

Web:

Screen.Recording.2023-03-20.at.10.29.44.mov

Native:

Screen.Recording.2023-03-20.at.10.34.55.mov

@lukewalczak lukewalczak changed the title Fix/tooltips not working as expected fix: tooltips not working as expected Mar 20, 2023
@github-actions
Copy link

The mobile version of example app from this branch is ready! You can see it here.

@tjaniczek
Copy link
Collaborator

Any chance we can implement immediate tooltip hiding on something like onMouseLeave on web, to avoid this situation?

image

@pac-guerreiro
Copy link
Contributor Author

Any chance we can implement immediate tooltip hiding on something like onMouseLeave on web, to avoid this situation?

image

There's already a 'leaveTouchDelay' prop but it has a default value of 1500. I can change the value to 0 when Platform.OS === 'web', what do you think?

@DimitarNestorov
Copy link
Collaborator

There's already a 'leaveTouchDelay' prop but it has a default value of 1500. I can change the value to 0 when Platform.OS === 'web', what do you think?

Platform.OS === 'web' doesn't necessarily mean that the user has a mouse pointer, you could be browsing on your smartphone. Platform.OS === 'ios' || Platform.OS === 'android' also doesn't necessarily mean touch, you may have a mouse connected. Both hover and touch tooltips should be available on all platforms. I say we don't modify the current behaviour but definitely change this with Paper 6.0.
Pressable has onHoverIn and onHoverOut since RN 0.68.
Pointer events are available in the new architecture since RN 0.71.
leaveTouchDelay is a bad name for the prop. Maybe we can rename it to delayPressOut (inspired by TouchableWithoutHighlight which has had a prop with the same name since RN 0.60 at least).
And also a separate delayHoverOut/delayPointerLeave would be nice, that one can be 0 by default.

Comment on lines 217 to 221
{React.cloneElement(children, {
...rest,
ref: childrenWrapperRef,
...(isWeb ? webPressProps : mobilePressProps),
})}
Copy link
Collaborator

@DimitarNestorov DimitarNestorov Apr 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-          {React.cloneElement(children, {
-            ...rest,
-            ...(isWeb ? webPressProps : mobilePressProps),
-          })}
+          {children}

If the container has the press handlers the child doesn't need them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case the child is a button, it is needed, otherwise the tooltip wont be triggered

@pac-guerreiro
Copy link
Contributor Author

There's already a 'leaveTouchDelay' prop but it has a default value of 1500. I can change the value to 0 when Platform.OS === 'web', what do you think?

Platform.OS === 'web' doesn't necessarily mean that the user has a mouse pointer, you could be browsing on your smartphone. Platform.OS === 'ios' || Platform.OS === 'android' also doesn't necessarily mean touch, you may have a mouse connected. Both hover and touch tooltips should be available on all platforms. I say we don't modify the current behaviour but definitely change this with Paper 6.0. Pressable has onHoverIn and onHoverOut since RN 0.68. Pointer events are available in the new architecture since RN 0.71. leaveTouchDelay is a bad name for the prop. Maybe we can rename it to delayPressOut (inspired by TouchableWithoutHighlight which has had a prop with the same name since RN 0.60 at least). And also a separate delayHoverOut/delayPointerLeave would be nice, that one can be 0 by default.

Wow these are very good valid points! Thank you for mentioning these @DimitarNestorov!

I can apply these changes you suggested 😄

@callstack-bot
Copy link

callstack-bot commented May 10, 2023

Hey @pac-guerreiro, thank you for your pull request 🤗. The documentation from this branch can be viewed here.

@pac-guerreiro pac-guerreiro force-pushed the fix/tooltips-not-working-as-expected branch from 4e7faa0 to ce32750 Compare May 12, 2023 09:30
@pac-guerreiro pac-guerreiro force-pushed the fix/tooltips-not-working-as-expected branch from ce32750 to 92b5b3a Compare May 12, 2023 09:37
Copy link
Collaborator

@tjaniczek tjaniczek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the Avatar component in the example page, since this was the core issue reported: #3729

@tjaniczek
Copy link
Collaborator

Closes #3729

>
>;

export type NavigationParams = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✂️ as @lukewalczak pointed out previously there's no need to touch react navigation.

testSetup.js Outdated
Comment on lines 3 to 7
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useRoute: () => ({}),
useNavigation: () => ({ setParams: jest.fn() }),
}));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✂️ same

Suggested change
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useRoute: () => ({}),
useNavigation: () => ({ setParams: jest.fn() }),
}));

describe('Unmount', () => {
beforeAll(() => jest.spyOn(global, 'clearTimeout'));
afterEach(() => jest.clearAllMocks());
describe('Mobile', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about splitting this file in two since there's a lot here already? One test file for mobile and another for web?
Also, can we have a test where the children component has absolute position?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah absolute positioning is still a bit wonky at the moment. I think in the interest of time we'll merge this piece now and create an issue to rework the positioning. We can address the tests files as well there.

accessibilityState={newAccessibilityState}
testID={testID}
style={{ borderRadius }}
{...rest}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this change?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pac-guerreiro remember why it was added there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like without added {...rest}, Tooltip is not displayed over the FAB on the web.


showTooltipTimer.current = setTimeout(() => {
if (isWeb) {
showTooltipTimer.current = setTimeout(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if I want to have an enterTouchDelay on mobile? Since the base component is a Pressable now, would it be possible to replace this prop enterTouchDelay with delayLongPress?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing prop would be a breaking change and require releasing new version. enterTouchDelay is translated to delayLongPress on mobile:

delayLongPress: enterTouchDelay,

childrenHeight: number,
tooltipHeight: number
tooltipHeight: number,
headerHeight: number = 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one isn't being used

Suggested change
headerHeight: number = 0

@lukewalczak lukewalczak added this to the 5.9.0 milestone Jun 16, 2023
@lukewalczak lukewalczak merged commit c724ad9 into main Jun 26, 2023
@lukewalczak lukewalczak deleted the fix/tooltips-not-working-as-expected branch June 26, 2023 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants