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

Show full link when hovering over hyperlink #8815

Merged
merged 16 commits into from
May 4, 2022

Conversation

eVoloshchak
Copy link
Contributor

Details

If you have a message in chat containing hyperlink, there should be a tooltip displaying the full link address

Fixed Issues

#7844

Tests

  1. Navigate to chat
  2. Send any text hyperlinked
  3. Web/Desktop App: hower over hyperlink and verify that the tooltip containing full link address is shown
  4. mWeb/Native: long press on link hyperlink verify that full link address is displayed below the Copy URL to clipboard title

PR Review Checklist

Contributor (PR Author) Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

PR Reviewer Checklist

  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

QA Steps

  1. Navigate to chat
  2. Send any text hyperlinked
  3. Web/Desktop App: hower over hyperlink and verify that the tooltip containing full link address is shown
  4. mWeb/Native: long press on hyperlink and verify that full link address is displayed below the Copy URL to clipboard title

Screenshots

Web

cinnamon-20220428-1.mp4

Mobile Web

22-04-28-17-09-39.mp4

Note: I had to apply a temporary workaround to record this video, it's impossible to test this in prod due to #8311

Desktop

I'm unable to launch the desktop app due to this bug. It is not present if you downgrade to certain node and npm versions, but since I use MacinCloud, I'm unable to do so. So if someone with a mac could test it and upload a video, I would highly appreciate it.

iOS

Screen.Recording.2022-04-28.at.4.24.59.PM.mp4

Android

22-04-28-17-11-10.mp4

@eVoloshchak eVoloshchak requested a review from a team as a code owner April 28, 2022 15:05
@melvin-bot melvin-bot bot requested review from parasharrajat and tgolen and removed request for a team April 28, 2022 15:05
Copy link
Contributor

@tgolen tgolen left a comment

Choose a reason for hiding this comment

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

Works well. I uploaded a video of desktop to the PR description for you.

* @returns {String}
*/
getPopoverDescription() {
const isNative = Platform.OS === 'ios' || Platform.OS === 'android';
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an incorrect way to do platform detection. You can read more about that here: https://github.com/Expensify/App#platform-specific-file-extensions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an incorrect way to do platform detection. You can read more about that here: https://github.com/Expensify/App#platform-specific-file-extensions

I'm unsure on how to divide this logic into platform-specific files.
How about implementing isNative() method in getOperatingSystem, which in index.native.js would just return true and index.js would return false

I uploaded a video of desktop to the PR description for you.

Thanks!

Copy link
Member

@parasharrajat parasharrajat Apr 28, 2022

Choose a reason for hiding this comment

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

+1.

Copy link
Contributor

Choose a reason for hiding this comment

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

You could take a look at some of the existing platform-specific files to get some ideas, but without digging too deep into this code, my initial ideas are:

  • Create a module like src/pages/home/report/ContextMenu/ContextMenuUtils/index.js which would have a method that accepts this.state.type and this.state.selection as parameters.
  • Create a new platform-specific component that wraps <BaseReportActionContextMenu> and provides the necessary logic

this.props.href,
lodashGet(linkRef, 'current'),
);
<Tooltip text={!Str.isValidEmail(this.props.displayName) && this.props.href}>
Copy link
Member

Choose a reason for hiding this comment

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

Let's move this tooltip on to the Text Node.

@@ -101,7 +101,7 @@ const MenuItem = props => (
>
{props.title}
</Text>
{props.description && (
{!!props.description && (
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
{!!props.description && (
{Boolean(props.description) && (

Comment on lines 100 to 107
getPopoverDescription() {
const isNative = Platform.OS === 'ios' || Platform.OS === 'android';
if ((isNative || this.props.isSmallScreenWidth) && this.state.type === ContextMenuActions.CONTEXT_MENU_TYPES.LINK) {
return this.state.selection;
}
return '';
}

Copy link
Member

Choose a reason for hiding this comment

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

Not a good way of doing this. I don't agree with this change. How the whole menu description goes to each item's description? Can you think of a way to do this with ContextMenuActions?

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

* @returns {String}
*/
getPopoverDescription() {
const isNative = Platform.OS === 'ios' || Platform.OS === 'android';
Copy link
Member

@parasharrajat parasharrajat Apr 28, 2022

Choose a reason for hiding this comment

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

+1.

@eVoloshchak
Copy link
Contributor Author

Updated

@@ -293,6 +293,7 @@ class PopoverReportActionContextMenu extends React.Component {
reportID={this.state.reportID}
reportAction={this.state.reportAction}
draftMessage={this.state.reportActionDraftMessage}
selection={this.state.selection}
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here which I used for the description key.

How the whole menu description goes to each item's description? Can you think of a way to do this with ContextMenuActions?

We don't need this selection at all.

@@ -49,6 +53,7 @@ class BaseReportActionContextMenu extends React.Component {
draftMessage: this.props.draftMessage,
selection: this.props.selection,
})}
description={ContextMenuUtils.getPopoverDescription(this.props.type, this.props.selection, this.props.isSmallScreenWidth)}
Copy link
Member

Choose a reason for hiding this comment

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

Did you notice how most of the props are coming from contextAction? This change is breaking the existing pattern. This is a side-effect.

Let's discuss the alternative first before making the change. Consider that the only way to present data here is to get it from ContextMenuActions. Now, what can be done to solve this problem?

Look at this

shouldShow: type => type === CONTEXT_MENU_TYPES.LINK,
. How are we picking between actions?

Can we show a new item here dynamically?

What if we have to show different descriptions for different menu items. Will your solution work in an ad-hoc way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Consider that the only way to present data here is to get it from ContextMenuActions. Now, what can be done to solve this problem?
Can we show a new item here dynamically?
What if we have to show different descriptions for different menu items. Will your solution work in an ad-hoc way?

We could

  1. Add getDescription method here
  {
      textTranslateKey: 'reportActionContextMenu.copyURLToClipboard',
      icon: Expensicons.Clipboard,
      successTextTranslateKey: 'reportActionContextMenu.copied',
      successIcon: Expensicons.Checkmark,
      shouldShow: type => type === CONTEXT_MENU_TYPES.LINK,
      onPress: (closePopover, {selection}) => {
          Clipboard.setString(selection);
          hideContextMenu(true, ReportActionComposeFocusManager.focus);
      },
      getDescription: ({selection}) => selection,
  },
  1. Call it from BaseReportActionContextMenu here
description={contextAction.getDescription && contextAction.getDescription({selection: this.props.selection})}

And if we will need to show different description for, say, CONTEXT_MENU_TYPES.EMAIL, we could add a getDescription method to it too

getDescription: ({otherParam}) => otherParam,

Then step 2 would change to

description={contextAction.getDescription && contextAction.getDescription({selection: this.props.selection, otherParam: 'lorem ipsum'})}

Would this work as an alternative?

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me. How will you control to hide the description on Desktop | Web?

Copy link
Contributor Author

@eVoloshchak eVoloshchak Apr 28, 2022

Choose a reason for hiding this comment

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

Sounds good to me. How will you control to hide the description on Desktop | Web?

I would implement shouldShowDescription method in src/pages/home/report/ContextMenu/ContextMenuUtils, for native it would just return true, and for web it would look like this:

function shouldShowDescription(isSmallScreenWidth) {
    return isSmallScreenWidth;
}

But I'm not sure where to call it
We could call it in getDescription method for each action type individually, like this:

getDescription: ({selection}) => shouldShowDescription() ? selection : '',

But i think it makes more sense to call it for all actions in BaseReportActionContextMenu?

description={(contextAction.getDescription && ContextMenuUtils.shouldShowDescription(props.isSmallScreenWidth)) && contextAction.getDescription({selection: this.props.selection})}

Copy link
Member

Choose a reason for hiding this comment

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

What if we pass the isSmallScreenWidth directly to the description callback and decide to show the value here.
getDescription: ({selection}) => shouldShowDescription() ? selection : '',

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What if we pass the isSmallScreenWidth directly to the description callback and decide to show the value here. getDescription: ({selection}) => shouldShowDescription() ? selection : '',

We need to check for native platform, so it would look like this:

getDescription: ({selection, isSmallScreenWidth}) => ContextMenuUtils.shouldShowDescription(isSmallScreenWidth) ? selection : '',

Copy link
Member

Choose a reason for hiding this comment

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

I guess so.

@eVoloshchak
Copy link
Contributor Author

Updated

Copy link
Contributor

@tgolen tgolen left a comment

Choose a reason for hiding this comment

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

I think I mostly followed the changes, but here is a proposal that can clean it up even further. I didn't test this, but can you see if it works?

Note: some of the comments also would need to be updated with this change.

Comment on lines 9 to 15
function shouldShowDescription(isSmallScreenWidth) {
return isSmallScreenWidth;
}

export {
shouldShowDescription,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
function shouldShowDescription(isSmallScreenWidth) {
return isSmallScreenWidth;
}
export {
shouldShowDescription,
};
function getPopoverDescription(selection, isSmallScreenWidth) {
return isSmallScreenWidth ? selection : '';
}
export {
getPopoverDescription,
};

Copy link
Contributor

Choose a reason for hiding this comment

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

Change the method name to be more descriptive and perform slightly different logic.

Comment on lines 8 to 14
function shouldShowDescription() {
return true;
}

export {
shouldShowDescription,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
function shouldShowDescription() {
return true;
}
export {
shouldShowDescription,
};
function getPopoverDescription() {
return '';
}
export {
getPopoverDescription,
};

@@ -49,6 +52,7 @@ class BaseReportActionContextMenu extends React.Component {
draftMessage: this.props.draftMessage,
selection: this.props.selection,
})}
description={contextAction.getDescription ? contextAction.getDescription({selection: this.props.selection, isSmallScreenWidth: this.props.isSmallScreenWidth}) : ''}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
description={contextAction.getDescription ? contextAction.getDescription({selection: this.props.selection, isSmallScreenWidth: this.props.isSmallScreenWidth}) : ''}
description={contextAction.getDescription ? contextAction.getDescription(this.props.selection, this.props.isSmallScreenWidth) : ''}

Copy link
Contributor

Choose a reason for hiding this comment

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

No need to pass those arguments as an object.

@@ -62,6 +63,7 @@ export default [
Clipboard.setString(selection);
hideContextMenu(true, ReportActionComposeFocusManager.focus);
},
getDescription: ({selection, isSmallScreenWidth}) => (ContextMenuUtils.shouldShowDescription(isSmallScreenWidth) ? selection : ''),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
getDescription: ({selection, isSmallScreenWidth}) => (ContextMenuUtils.shouldShowDescription(isSmallScreenWidth) ? selection : ''),
getDescription: ContextMenuUtils.getPopoverDescription,

Copy link
Contributor

Choose a reason for hiding this comment

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

This cleans up really well now and it's just passing a reference

@eVoloshchak
Copy link
Contributor Author

I think I mostly followed the changes, but here is a proposal that can clean it up even further. I didn't test this, but can you see if it works?

Note: some of the comments also would need to be updated with this change.

Verified it working on all platforms and updated the PR

@@ -49,6 +52,7 @@ class BaseReportActionContextMenu extends React.Component {
draftMessage: this.props.draftMessage,
selection: this.props.selection,
})}
description={contextAction.getDescription ? contextAction.getDescription(this.props.selection, this.props.isSmallScreenWidth) : ''}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
description={contextAction.getDescription ? contextAction.getDescription(this.props.selection, this.props.isSmallScreenWidth) : ''}
description={contextAction.getDescription(this.props.selection, this.props.isSmallScreenWidth)}

Now noop (() => {}) the getDescription for all other actions.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I was trying to see if we could use something like _.result() or even https://github.com/Expensify/expensify-common/blob/main/lib/Func.jsx#L13 and neither of those are really what would be nice. Ideally it would be something where:

  • If it's a function
    • invoke the function with the given params
  • if it's not a function
    • return a default value

I had thought about a no-op, but didn't have too be of an opinion on adding it to all the other actions. The noop does make it a bit cleaner.

Comment on lines 4 to 8
* We should show popover description only on mWeb
*
* @param {String} selection
* @param {Boolean} isSmallScreenWidth
* @returns {Boolean}
Copy link
Contributor

Choose a reason for hiding this comment

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

JS docs need updated now in both modules (eg. @returns is wrong) and the method description should probably say something like:

The popover description will be an empty string on anything but mobile web because... (I'm not actually 100% why)

@eVoloshchak
Copy link
Contributor Author

Updated

Copy link
Member

@parasharrajat parasharrajat left a comment

Choose a reason for hiding this comment

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

Please proactively test your changes. These findings tell me that you are not carefully testing.

On the web, when Content_menu is open. Tooltip covers it.
image


on MWeb, the link overflows the Popover menu.

image


On Native, the link is spread across multiple lines and overflowing.

image

@parasharrajat
Copy link
Member

parasharrajat commented Apr 29, 2022

@shawnborton Could you please help us finalize the UI for native?

IMO, link should be limited to 2 lines and then clipped with ellipsis.

@eVoloshchak
Copy link
Contributor Author

Please proactively test your changes. These findings tell me that you are not carefully testing.

Will do

On the web, when Content_menu is open. Tooltip covers it.

I thought this is a separate issue, since it was present before this commit. For instance, when you right-click on username

cinnamon-20220429-2.mp4

But is we need to fix it, what is the intended behavior here? To show context menu on top of the tooltip?
If so, we would need to change this line in react-native-modal to zIndex: 10051, since Tooltip has a zIndex of 10050
(It would probably be better to implement a way to pass zIndex as a variable).
It would look like this:

cinnamon-20220429-3.mp4

on MWeb, the link overflows the Popover menu.
On Native, the link is spread across multiple lines and overflowing.

True, waiting for the design confirmation

Copy link
Member

@parasharrajat parasharrajat left a comment

Choose a reason for hiding this comment

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

Please test these changes if they work fine without breaking other parts of the app.

@@ -90,7 +90,7 @@ const MenuItem = props => (
/>
</View>
)}
<View style={[styles.justifyContentCenter, styles.menuItemTextContainer]}>
<View style={[styles.justifyContentCenter, styles.menuItemTextContainer, styles.w100]}>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<View style={[styles.justifyContentCenter, styles.menuItemTextContainer, styles.w100]}>
<View style={[styles.justifyContentCenter, styles.menuItemTextContainer, styles.flex1]}>

@@ -59,7 +59,7 @@ const MenuItem = props => (
>
{({hovered, pressed}) => (
<>
<View style={[styles.flexRow, styles.pointerEventsNone]}>
<View style={[styles.flexRow, styles.pointerEventsNone, styles.w100, styles.pr5]}>
Copy link
Member

Choose a reason for hiding this comment

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

This padding will break the UI.

Suggested change
<View style={[styles.flexRow, styles.pointerEventsNone, styles.w100, styles.pr5]}>
<View style={[styles.flexRow, styles.pointerEventsNone, styles.flex1]}>

@eVoloshchak
Copy link
Contributor Author

Please test these changes if they work fine without breaking other parts of the app.

Works like a charm!
Updated

@parasharrajat
Copy link
Member

On the web, when Content_menu is open. Tooltip covers it.

Now only this is the problem. I think we can hide the tooltip as soon as the menu is visible.

If so, we would need to change this line in react-native-modal to zIndex: 10051, since Tooltip has a zIndex of 10050
(It would probably be better to implement a way to pass zIndex as a variable).

It is not that simple. It will break many other things. I guess we can just close the tooltip when the dropdown opens. What alternatives do you suggest?

@eVoloshchak
Copy link
Contributor Author

I guess we can just close the tooltip when the dropdown opens. What alternatives do you suggest?

We could do it like this:

  1. In BaseAnchorForCommentsOnly's render method add a variable to store a reference to the Tooltip
const tooltipRef = React.createRef();
  1. And then set it here
<Tooltip 
    text={Str.isValidEmail(this.props.displayName) ? '' : this.props.href} 
    ref={tooltipRef}
>
  1. And pass ref when showContextMenu is called
  onSecondaryInteraction={
  (event) => {
      ReportActionContextMenu.showContextMenu(
          Str.isValidEmail(this.props.displayName) ? ContextMenuActions.CONTEXT_MENU_TYPES.EMAIL : ContextMenuActions.CONTEXT_MENU_TYPES.LINK,
          event,
          this.props.href,
          lodashGet(linkRef, 'current'),
          undefined,
          undefined,
          undefined,
          undefined,
          undefined,
          tooltipRef
      );
  }
}
  1. Then in ReportActionContextMenu.showContextMenu, we could just call
if (tooltipRef.current) {
    tooltipRef.current.hideTooltip();
}
  1. But just hiding it when menu opens is not enough, because user can hover over the link again while popup is open, and the tooltip will show up again. So we need to implement isPopoverVisible method in ReportActionContextMenu.js
function isPopoverVisible() {
    if (!contextMenuRef.current) {
        return;
    }
    return contextMenuRef.current.state.isPopoverVisible;
}
  1. And check if it is visible in Tooltip's showTooltip method
showTooltip() {
    if (ReportActionContextMenu.isPopoverVisible()) {
        return;
    }

It looks like this:

cinnamon-20220501-1.mp4

If this is the acceptable approach, there is one thing to consider. Do we hide only the tooltips that were added with this commit (when you hover over links) or for all tooltips in the app? If it's the first, we should probably add a prop (for example called disableWhenPopoverIsVisible) and check for it in step 6. If we need to do this for all tooltips of the app, I would ask for your help to think if and how it's possible to get the reference for the Tooltip in one place (inside Tooltip.js or maybe with some sort of a wrapper component)

@parasharrajat
Copy link
Member

parasharrajat commented May 2, 2022

It is much more complex than I thought @eVoloshchak.

What alternatives do you suggest?

Do you have any other suggestions other than hiding the tooltip?


Another thing that we can do is to set fullscreen={true} On

. Only drawback is that user has to close the modal before doing any other actions. But I feel that this will be fine as other apps also have this behavior I added this prop to allow direct interaction with the app even if the context menu is open. It closes the menu as soon as use trigger any other interaction.

for example, the user can directly open the context menu on any other location if the context menu is already open.

@eVoloshchak
Copy link
Contributor Author

Do you have any other suggestions other than hiding the tooltip?

Apart from hiding it, the only alternative I can think of is moving the tooltip up when the ContextMenu opens (or opening the ContextMenu below the tooltip). But i think that would look kinda strange. And I think It would require a lot of changes, judging by the code used to calculate Tooltip and ContextMenu positions.

Another thing that we can do is to set fullscreen={true}

It works good imho. It's kind of intuitive, if user has opened the ContextMenu, it's their main focus

@parasharrajat
Copy link
Member

Another thing that we can do is to set fullscreen={true}

Ok. Great. @eVoloshchak Could you please start a slack discussion for it? Please tag all reviewers in it. Let's see what others have to say about it. It can be helpful to show videos on the slack with or without fullscreen.

@eVoloshchak
Copy link
Contributor Author

Ok. Great. @eVoloshchak Could you please start a slack discussion for it? Please tag all reviewers in it. Let's see what others have to say about it. It can be helpful to show videos on the slack with or without fullscreen.

Raised here. I'm not sure who you meant by 'all reviewers', so I tagged you and tgolen, but please correct me if you meant all the reviewers that maintain this repository and where can I find full list and edit the message in slack

@parasharrajat
Copy link
Member

reviewers = PR reviewers. You did it correctly.

@eVoloshchak
Copy link
Contributor Author

@parasharrajat
What should I do here?
The last message on slack stated

I think it would be cool for there to only be a single thing that appears on hover (so they don't compete). Like, if the context menu could show the full URL (on-hover), that would be pretty cool.

This means we set fullscreen to true? Or is there a full redesign needed for the context menu?

Also, I think we could make the URL tooltip look a little nicer. I like how slack has a max-width on their tooltip

I'm guessing this is already being done in some other issue?

@tgolen
Copy link
Contributor

tgolen commented May 4, 2022

Sounds like the max-width is being done already, so that's OK. I think I'm OK with merging this the way it is, but I would like to have you open up a new GH to explore making this work nicer with the context-menu.

tgolen
tgolen previously approved these changes May 4, 2022
@parasharrajat
Copy link
Member

parasharrajat commented May 4, 2022

@eVoloshchak IMO, we should go with setting the fullscreen to true. It seems Shawn agrees to it. We can tackle this if anyone has an issue with this which they should not as the browser context menu also works the same way.

One thing that we would want after changing to fullscreen is that right-clicking should not open the browser context menu when our menu is opened. But this is not part of this issue so you can leave this part.

@eVoloshchak
Copy link
Contributor Author

@eVoloshchak IMO, we should go with setting the fullscreen to true. It seems Shawn agrees to it. We can tackle this if anyone has an issue with this which they should not as the browser context menu also works the same way.

Should I open a new PR which sets it to true?

@parasharrajat
Copy link
Member

I think it can be done in this one. It is a 1-liner change. Right?

@eVoloshchak
Copy link
Contributor Author

I think it can be done in this one. It is a 1-liner change. Right?

Sorry, my brain somehow confused "approved" with "merged" :)
Updated

Copy link
Member

@parasharrajat parasharrajat left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for doing the changes.

cc: @tgolen

🎀 👀 🎀 C+ reviewed

@tgolen tgolen merged commit 388cfe3 into Expensify:main May 4, 2022
@OSBotify
Copy link
Contributor

OSBotify commented May 4, 2022

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify
Copy link
Contributor

OSBotify commented May 9, 2022

🚀 Cherry-picked to staging by @sketchydroide in version: 1.1.57-8 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@Expensify/applauseleads please QA this PR and check it off on the deploy checklist if it passes.

@OSBotify
Copy link
Contributor

🚀 Deployed to production by @chiragsalian in version: 1.1.57-17 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

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

Successfully merging this pull request may close these issues.

5 participants