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

Fix how hover card is hidden #1105

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Fix how hover card is hidden #1105

wants to merge 1 commit into from

Conversation

matthew-white
Copy link
Member

@matthew-white matthew-white commented Dec 14, 2024

@lognaturel noticed that there are cases where mousing away from a link that shows a hover card causes an error to be logged in the browser console. She noticed this pattern:

Maybe if I hover over a form title and remove my cursor before the card shows?

Looking into it, I think the specific problem was if the user mouses away while the hover card requests are in progress. Doing so is supposed to cancel the requests, but it wasn't doing so. That led to the following sequence:

  • Requests are sent.
  • mouseleave leads to hoverCard.hide() being called, which sets hoverCard.anchor to null.
  • Requests succeed.
  • component.value is set in the HoverCards component.
  • computePlacement() is called. But computePlacement() needs to know the position of hoverCard.anchor. When it tries to figure that out and sees that hoverCard.anchor is null, it throws.
  • The fact that computePlacement() throws triggers the catch() in the promise chain, which calls resetResources(). That clears the response data, but it doesn't reset component.value or placement.value. It shouldn't have to reset those things, because the only thing that's supposed to trigger the catch() is a request failure. But the fact that component.value is set while the response data is not means that the component attempts to render without any of the data it requires. That leads to an error, and it's that (pretty ugly) error that @lognaturel was seeing in the console.

The more general problem here is that hoverCard.hide() is not triggering the hide() function in the HoverCards component. The hoverCard object is just responsible for managing simple state about the hover card, not sending requests. It's the hide() function that's supposed to cancel requests and reset component.value and placement.value. The fact that it wasn't called was the root cause of the issue.

What has been done to verify that this works as intended?

I wrote a test that succeeds now but failed without the code change. I also tried out the fix locally.

Before submitting this PR, please make sure you have:

  • run npm run test and npm run lint and confirmed all checks still pass OR confirm CircleCI build passes
  • verified that any code or assets from external sources are properly credited in comments or that everything is internally sourced

@@ -159,12 +159,30 @@ describe('HoverCards', () => {
))
.request(hover(clock))
.respondWithData(() => testData.extendedDatasets.last())
.respondWithProblem(() => entity)
Copy link
Member Author

Choose a reason for hiding this comment

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

This is just a separate typo that I noticed.

unusual, but it could happen if preventHide is set to `true` in
useHoverCard().) But most importantly, if hoverCard.state is `true`, then we
need to hide the current hover card before we show a new one. */
if (hoverCard.state) hide();
Copy link
Member Author

Choose a reason for hiding this comment

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

This if meant that the hide() function was not called after hoverCard.state changed to false.

@matthew-white
Copy link
Member Author

I also wanted to make a note of how hover cards were being hidden even though the hide() function wasn't being called. I mentioned that when hoverCard.hide() is called, that sets hoverCard.anchor to null. That alone was apparently enough to hide the popover, since we pass hoverCard.anchor to the Popover component via its target prop. In other words, the last hover card to be "hidden" was still present in the DOM, and its response data was still in memory. But because the target prop of the Popover component was null, the hover card wasn't being shown onscreen.

@matthew-white matthew-white requested a review from ktuite December 14, 2024 00:56
.afterResponses(async (component) => {
should.not.exist(document.querySelector('.popover'));
component.should.not.alert();
// Make sure that this does not result in a Vue warning.
await getAnchor(component).trigger('mouseleave');
});
});

it('does not log an error if mouseleave is triggered during request', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

In case you're wondering how this test works (there's not much in the way of an assertion), our tests automatically fail if Vue logs a warning. Before the fix, Vue was logging a warning, but with the fix in place, it no longer logs a warning.

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.

2 participants