fix(webapp): fix popover flickering (#8555)

Add boundary to v-popover to avoid random flickering when hovering a user teaser
Preload user data to avoid resizing (and sometimes repositioning) of popover after data has been loaded
This commit is contained in:
Max 2025-05-20 09:21:22 +02:00 committed by GitHub
parent 35729dfb6a
commit 7ea8107ed0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 80 additions and 9 deletions

View File

@ -4,6 +4,7 @@ exports[`GroupContentMenu renders as groupProfile when I am the owner 1`] = `
<div> <div>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu" class="group-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -121,6 +122,7 @@ exports[`GroupContentMenu renders as groupProfile, muted 1`] = `
<div> <div>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu" class="group-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -196,6 +198,7 @@ exports[`GroupContentMenu renders as groupProfile, not muted 1`] = `
<div> <div>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu" class="group-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -271,6 +274,7 @@ exports[`GroupContentMenu renders as groupTeaser 1`] = `
<div> <div>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu" class="group-content-menu"
container="body" container="body"
delay="0" delay="0"

View File

@ -6,6 +6,7 @@
:disabled="disabled" :disabled="disabled"
trigger="manual" trigger="manual"
:offset="offset" :offset="offset"
boundaries-element="body"
> >
<slot :toggleMenu="toggleMenu" :openMenu="openMenu" :closeMenu="closeMenu" :isOpen="isOpen" /> <slot :toggleMenu="toggleMenu" :openMenu="openMenu" :closeMenu="closeMenu" :isOpen="isOpen" />
<div slot="popover" @mouseover="popoverMouseEnter" @mouseleave="popoverMouseLeave"> <div slot="popover" @mouseover="popoverMouseEnter" @mouseleave="popoverMouseLeave">
@ -72,7 +73,7 @@ export default {
} }
}, },
closeMenu(useTimeout) { closeMenu(useTimeout) {
if (this.disabled) { if (this.noMouseLeaveClosing || this.disabled) {
return return
} }
this.clearTimeouts() this.clearTimeouts()

View File

@ -86,6 +86,12 @@ describe('UserTeaser', () => {
}, },
mocks: { mocks: {
$t: jest.fn((t) => t), $t: jest.fn((t) => t),
$i18n: {
locale: jest.fn(() => 'en'),
},
$apollo: {
query: jest.fn(() => Promise.resolve({ data: { user } })),
},
}, },
}) })
} }

View File

@ -6,7 +6,7 @@
:link-to-profile="linkToProfile" :link-to-profile="linkToProfile"
:show-popover="showPopover" :show-popover="showPopover"
:user-link="userLink" :user-link="userLink"
@open-menu="openMenu(false)" @open-menu="loadPopover(openMenu)"
@close-menu="closeMenu(false)" @close-menu="closeMenu(false)"
data-test="avatarUserLink" data-test="avatarUserLink"
> >
@ -18,7 +18,7 @@
:link-to-profile="linkToProfile" :link-to-profile="linkToProfile"
:show-popover="showPopover" :show-popover="showPopover"
:user-link="userLink" :user-link="userLink"
@open-menu="openMenu(false)" @open-menu="loadPopover(openMenu)"
@close-menu="closeMenu(false)" @close-menu="closeMenu(false)"
> >
<span class="slug">{{ userSlug }}</span> <span class="slug">{{ userSlug }}</span>
@ -57,6 +57,7 @@
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { userTeaserQuery } from '~/graphql/User.js'
import DateTime from '~/components/DateTime' import DateTime from '~/components/DateTime'
import Dropdown from '~/components/Dropdown' import Dropdown from '~/components/Dropdown'
import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar' import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar'
@ -119,5 +120,15 @@ export default {
return name || this.$t('profile.userAnonym') return name || this.$t('profile.userAnonym')
}, },
}, },
methods: {
async loadPopover(openMenu) {
// Load user data if not already loaded, to avoid flickering
await this.$apollo.query({
query: userTeaserQuery(this.$i18n),
variables: { id: this.user.id },
})
openMenu(false)
},
},
} }
</script> </script>

View File

@ -1,6 +1,5 @@
<template> <template>
<div class="placeholder" v-if="!user" /> <div class="user-teaser-popover">
<div class="user-teaser-popover" v-else>
<badges <badges
v-if="$env.BADGES_ENABLED && user.badgeVerification" v-if="$env.BADGES_ENABLED && user.badgeVerification"
:badges="[user.badgeVerification, ...user.badgeTrophiesSelected]" :badges="[user.badgeVerification, ...user.badgeTrophiesSelected]"
@ -67,10 +66,6 @@ export default {
</script> </script>
<style scoped> <style scoped>
.placeholder {
height: 200px;
width: 200px;
}
.user-teaser-popover { .user-teaser-popover {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -9,6 +9,7 @@ exports[`UserTeaser given an user avatar is disabled does not render the avatar
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -135,6 +136,7 @@ exports[`UserTeaser given an user user is disabled current user is a moderator r
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -250,6 +252,7 @@ exports[`UserTeaser given an user with linkToProfile, on desktop renders 1`] = `
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -333,6 +336,7 @@ exports[`UserTeaser given an user with linkToProfile, on desktop when hovering t
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -422,6 +426,7 @@ exports[`UserTeaser given an user with linkToProfile, on touch screen renders 1`
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -505,6 +510,7 @@ exports[`UserTeaser given an user with linkToProfile, on touch screen when click
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -594,6 +600,7 @@ exports[`UserTeaser given an user without linkToProfile, on desktop renders 1`]
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -677,6 +684,7 @@ exports[`UserTeaser given an user without linkToProfile, on desktop when hoverin
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -765,6 +773,7 @@ exports[`UserTeaser given an user without linkToProfile, on desktop when hoverin
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -853,6 +862,7 @@ exports[`UserTeaser given an user without linkToProfile, on touch screen renders
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -936,6 +946,7 @@ exports[`UserTeaser given an user without linkToProfile, on touch screen when cl
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -1024,6 +1035,7 @@ exports[`UserTeaser given an user without linkToProfile, on touch screen when cl
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"

View File

@ -67,6 +67,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu group-profile-content-menu" class="group-content-menu group-profile-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -517,6 +518,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -597,6 +599,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -677,6 +680,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -757,6 +761,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -1981,6 +1986,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu group-profile-content-menu" class="group-content-menu group-profile-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -2388,6 +2394,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -2468,6 +2475,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -2548,6 +2556,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -2628,6 +2637,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a close
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -2946,6 +2956,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu group-profile-content-menu" class="group-content-menu group-profile-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -3362,6 +3373,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -3442,6 +3454,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -3522,6 +3535,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -3602,6 +3616,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -4181,6 +4196,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -4261,6 +4277,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -4341,6 +4358,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -4421,6 +4439,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -4998,6 +5017,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -5078,6 +5098,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -5158,6 +5179,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -5238,6 +5260,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -5511,6 +5534,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu group-profile-content-menu" class="group-content-menu group-profile-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -5884,6 +5908,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -5964,6 +5989,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -6044,6 +6070,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -6124,6 +6151,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a curre
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -6447,6 +6475,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu group-profile-content-menu" class="group-content-menu group-profile-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -6905,6 +6934,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -6985,6 +7015,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -7065,6 +7096,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -7145,6 +7177,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -7464,6 +7497,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="group-content-menu group-profile-content-menu" class="group-content-menu group-profile-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -7879,6 +7913,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -7959,6 +7994,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -8039,6 +8075,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"
@ -8119,6 +8156,7 @@ exports[`GroupProfileSlug given a puplic group "yoga-practice" given a hidde
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="user-teaser" class="user-teaser"
container="body" container="body"
delay="0" delay="0"

View File

@ -37,6 +37,7 @@ exports[`ProfileSlug given an authenticated user given another profile user and
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="content-menu user-content-menu" class="content-menu user-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -655,6 +656,7 @@ exports[`ProfileSlug given an authenticated user given another profile user and
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="content-menu user-content-menu" class="content-menu user-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -1338,6 +1340,7 @@ exports[`ProfileSlug given an authenticated user given the logged in user as pro
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="content-menu user-content-menu" class="content-menu user-content-menu"
container="body" container="body"
delay="0" delay="0"
@ -1901,6 +1904,7 @@ exports[`ProfileSlug given an authenticated user given the logged in user as pro
<client-only-stub> <client-only-stub>
<v-popover-stub <v-popover-stub
autohide="true" autohide="true"
boundarieselement="body"
class="content-menu user-content-menu" class="content-menu user-content-menu"
container="body" container="body"
delay="0" delay="0"