Skip to content
Snippets Groups Projects
Commit 27ba283b authored by Stanislav Lashmanov's avatar Stanislav Lashmanov
Browse files

Lazy init for popovers

Use event delegation to init popovers.
Simulate the delay from the UserPopover in the lazy loading logic.
Remove all the page-specific popover initialization code.
parent 7779cde3
No related branches found
No related tags found
1 merge request!86189Lazy init for popovers
This commit is part of merge request !88633. Comments created here will be created in the context of that merge request.
Showing
with 159 additions and 171 deletions
import $ from 'jquery'; import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight'; import syntaxHighlight from '~/syntax_highlight';
import initUserPopovers from '../../user_popovers';
import highlightCurrentUser from './highlight_current_user'; import highlightCurrentUser from './highlight_current_user';
import { renderKroki } from './render_kroki'; import { renderKroki } from './render_kroki';
import renderMath from './render_math'; import renderMath from './render_math';
...@@ -22,7 +21,6 @@ $.fn.renderGFM = function renderGFM() { ...@@ -22,7 +21,6 @@ $.fn.renderGFM = function renderGFM() {
renderMermaid(this.find('.js-render-mermaid')); renderMermaid(this.find('.js-render-mermaid'));
} }
highlightCurrentUser(this.find('.gfm-project_member').get()); highlightCurrentUser(this.find('.gfm-project_member').get());
initUserPopovers(this.find('.js-user-link').get());
const issuablePopoverElements = this.find('.gfm-issue, .gfm-merge_request').get(); const issuablePopoverElements = this.find('.gfm-issue, .gfm-merge_request').get();
if (issuablePopoverElements.length) { if (issuablePopoverElements.length) {
......
...@@ -7,8 +7,6 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; ...@@ -7,8 +7,6 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import initUserPopovers from '../../user_popovers';
/** /**
* CommitItem * CommitItem
* *
...@@ -82,11 +80,6 @@ export default { ...@@ -82,11 +80,6 @@ export default {
return this.commit.description_html.replace(/^
/, ''); return this.commit.description_html.replace(/^
/, '');
}, },
}, },
created() {
this.$nextTick(() => {
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
});
},
safeHtmlConfig: { safeHtmlConfig: {
ADD_TAGS: ['gl-emoji'], ADD_TAGS: ['gl-emoji'],
}, },
......
...@@ -4,7 +4,6 @@ import { mapState } from 'vuex'; ...@@ -4,7 +4,6 @@ import { mapState } from 'vuex';
import MembersTableCell from 'ee_else_ce/members/components/table/members_table_cell.vue'; import MembersTableCell from 'ee_else_ce/members/components/table/members_table_cell.vue';
import { canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils'; import { canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils';
import { mergeUrlParams } from '~/lib/utils/url_utility'; import { mergeUrlParams } from '~/lib/utils/url_utility';
import initUserPopovers from '~/user_popovers';
import UserDate from '~/vue_shared/components/user_date.vue'; import UserDate from '~/vue_shared/components/user_date.vue';
import { import {
FIELD_KEY_ACTIONS, FIELD_KEY_ACTIONS,
...@@ -85,9 +84,6 @@ export default { ...@@ -85,9 +84,6 @@ export default {
return this.tabQueryParamValue === TAB_QUERY_PARAM_VALUES.invite; return this.tabQueryParamValue === TAB_QUERY_PARAM_VALUES.invite;
}, },
}, },
mounted() {
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
},
methods: { methods: {
hasActionButtons(member) { hasActionButtons(member) {
return ( return (
......
...@@ -3,7 +3,6 @@ import { mapGetters, mapActions } from 'vuex'; ...@@ -3,7 +3,6 @@ import { mapGetters, mapActions } from 'vuex';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import initUserPopovers from '~/user_popovers';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
...@@ -169,7 +168,6 @@ export default { ...@@ -169,7 +168,6 @@ export default {
updated() { updated() {
this.$nextTick(() => { this.$nextTick(() => {
highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member')); highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
}); });
}, },
beforeDestroy() { beforeDestroy() {
......
import Vue from 'vue'; import Vue from 'vue';
import { debounce } from 'lodash';
import UsersCache from './lib/utils/users_cache'; import UsersCache from './lib/utils/users_cache';
import UserPopover from './vue_shared/components/user_popover/user_popover.vue'; import UserPopover from './vue_shared/components/user_popover/user_popover.vue';
...@@ -59,87 +60,81 @@ const populateUserInfo = (user) => { ...@@ -59,87 +60,81 @@ const populateUserInfo = (user) => {
); );
}; };
const initializedPopovers = new Map(); function showPopover(el, user, mountPopover) {
let domObservedForChanges = false; removeTitle(el);
const preloadedUserInfo = getPreloadedUserInfo(el.dataset);
const addPopoversToModifiedTree = new MutationObserver(() => { Object.assign(user, preloadedUserInfo);
const userLinks = document?.querySelectorAll('.js-user-link, .gfm-project_member');
if (userLinks) { if (preloadedUserInfo.userId) {
addPopovers(userLinks); /* eslint-disable-line no-use-before-define */ populateUserInfo(user);
} }
}); const UserPopoverComponent = Vue.extend(UserPopover);
const popoverInstance = new UserPopoverComponent({
propsData: {
target: el,
user,
show: true,
placement: el.dataset.placement || 'top',
},
});
mountPopover(popoverInstance);
return popoverInstance;
}
function observeBody() { function launchPopover(el, mountPopover) {
if (!domObservedForChanges) { if (el.user) return;
addPopoversToModifiedTree.observe(document.body, {
subtree: true,
childList: true,
});
domObservedForChanges = true; const emptyUser = {
} location: null,
bio: null,
workInformation: null,
status: null,
isFollowed: false,
loaded: false,
};
el.user = emptyUser;
el.addEventListener(
'mouseleave',
({ target }) => {
target.removeAttribute('aria-describedby');
},
{ once: true },
);
const renderedPopover = showPopover(el, emptyUser, mountPopover);
const { userId } = el.dataset;
renderedPopover.$on('follow', () => {
UsersCache.updateById(userId, { is_followed: true });
el.user.isFollowed = true;
});
renderedPopover.$on('unfollow', () => {
UsersCache.updateById(userId, { is_followed: false });
el.user.isFollowed = false;
});
} }
export default function addPopovers(elements = document.querySelectorAll('.js-user-link')) { const userLinkSelector = 'a.js-user-link, a.gfm-project_member';
const userLinks = Array.from(elements);
const UserPopoverComponent = Vue.extend(UserPopover); const getUserLinkNode = (node) => {
const startNode = 'matches' in node ? node : node.parentElement;
return startNode.closest(userLinkSelector);
};
observeBody(); const lazyLaunchPopover = debounce((mountPopover, event) => {
const userLink = getUserLinkNode(event.target);
if (userLink) {
launchPopover(userLink, mountPopover);
}
}, 200);
return userLinks let hasAddedLazyPopovers = false;
.filter(({ dataset }) => dataset.user || dataset.userId)
.map((el) => {
if (initializedPopovers.has(el)) {
return initializedPopovers.get(el);
}
const user = { export default function addPopovers(mountPopover = (instance) => instance.$mount()) {
location: null, if (!hasAddedLazyPopovers) {
bio: null, document.addEventListener('mouseover', (event) => lazyLaunchPopover(mountPopover, event));
workInformation: null, hasAddedLazyPopovers = true;
status: null, }
isFollowed: false,
loaded: false,
};
const renderedPopover = new UserPopoverComponent({
propsData: {
target: el,
user,
placement: el.dataset.placement || 'top',
},
});
const { userId } = el.dataset;
renderedPopover.$on('follow', () => {
UsersCache.updateById(userId, { is_followed: true });
user.isFollowed = true;
});
renderedPopover.$on('unfollow', () => {
UsersCache.updateById(userId, { is_followed: false });
user.isFollowed = false;
});
initializedPopovers.set(el, renderedPopover);
renderedPopover.$mount();
el.addEventListener('mouseenter', ({ target }) => {
removeTitle(target);
const preloadedUserInfo = getPreloadedUserInfo(target.dataset);
Object.assign(user, preloadedUserInfo);
if (preloadedUserInfo.userId) {
populateUserInfo(user);
}
});
el.addEventListener('mouseleave', ({ target }) => {
target.removeAttribute('aria-describedby');
});
return renderedPopover;
});
} }
...@@ -18,7 +18,6 @@ import { toggleContainerClasses } from '~/lib/utils/dom_utils'; ...@@ -18,7 +18,6 @@ import { toggleContainerClasses } from '~/lib/utils/dom_utils';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility'; import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import initUserPopovers from '~/user_popovers';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import MetricImagesTab from '~/vue_shared/components/metric_images/metric_images_tab.vue'; import MetricImagesTab from '~/vue_shared/components/metric_images/metric_images_tab.vue';
...@@ -175,7 +174,6 @@ export default { ...@@ -175,7 +174,6 @@ export default {
updated() { updated() {
this.$nextTick(() => { this.$nextTick(() => {
highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member')); highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
}); });
}, },
methods: { methods: {
......
...@@ -48,6 +48,11 @@ export default { ...@@ -48,6 +48,11 @@ export default {
required: false, required: false,
default: 'top', default: 'top',
}, },
show: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -134,7 +139,14 @@ export default { ...@@ -134,7 +139,14 @@ export default {
<template> <template>
<!-- 200ms delay so not every mouseover triggers Popover --> <!-- 200ms delay so not every mouseover triggers Popover -->
<gl-popover :target="target" :delay="200" :placement="placement" boundary="viewport"> <gl-popover
:show="show"
:target="target"
:delay="200"
:placement="placement"
boundary="viewport"
triggers="hover focus manual"
>
<div class="gl-p-3 gl-line-height-normal gl-display-flex" data-testid="user-popover"> <div class="gl-p-3 gl-line-height-normal gl-display-flex" data-testid="user-popover">
<div <div
class="gl-p-2 flex-shrink-1 gl-display-flex gl-flex-direction-column align-items-center gl-w-70p" class="gl-p-2 flex-shrink-1 gl-display-flex gl-flex-direction-column align-items-center gl-w-70p"
......
...@@ -10,7 +10,6 @@ import createFlash from '~/flash'; ...@@ -10,7 +10,6 @@ import createFlash from '~/flash';
import { TYPE_VULNERABILITY } from '~/graphql_shared/constants'; import { TYPE_VULNERABILITY } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import initUserPopovers from '~/user_popovers';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { normalizeGraphQLNote } from '../helpers'; import { normalizeGraphQLNote } from '../helpers';
import GenericReportSection from './generic_report/report_section.vue'; import GenericReportSection from './generic_report/report_section.vue';
...@@ -117,11 +116,6 @@ export default { ...@@ -117,11 +116,6 @@ export default {
this.stopPolling(); this.stopPolling();
this.unbindVisibilityListener(); this.unbindVisibilityListener();
}, },
updated() {
this.$nextTick(() => {
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
});
},
methods: { methods: {
startPolling() { startPolling() {
if (this.pollInterval) { if (this.pollInterval) {
......
...@@ -18,11 +18,9 @@ import createMockApollo from 'helpers/mock_apollo_helper'; ...@@ -18,11 +18,9 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
import initUserPopovers from '~/user_popovers';
import { addTypenamesToDiscussion, generateNote } from './mock_data'; import { addTypenamesToDiscussion, generateNote } from './mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/user_popovers');
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -138,13 +136,6 @@ describe('Vulnerability Footer', () => { ...@@ -138,13 +136,6 @@ describe('Vulnerability Footer', () => {
}); });
}); });
it('calls initUserPopovers when the component is updated', async () => {
createWrapper({ queryHandler: discussionsHandler({ discussions: [] }) });
expect(initUserPopovers).not.toHaveBeenCalled();
await waitForPromises();
expect(initUserPopovers).toHaveBeenCalled();
});
it('shows an error the discussions could not be retrieved', async () => { it('shows an error the discussions could not be retrieved', async () => {
await createWrapperAndFetchDiscussions({ await createWrapperAndFetchDiscussions({
errors: [{ message: 'Something went wrong' }], errors: [{ message: 'Something went wrong' }],
......
...@@ -6,8 +6,6 @@ import Component from '~/diffs/components/commit_item.vue'; ...@@ -6,8 +6,6 @@ import Component from '~/diffs/components/commit_item.vue';
import { getTimeago } from '~/lib/utils/datetime_utility'; import { getTimeago } from '~/lib/utils/datetime_utility';
import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
jest.mock('~/user_popovers');
const TEST_AUTHOR_NAME = 'test'; const TEST_AUTHOR_NAME = 'test';
const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com'; const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com';
const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=40`; const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=40`;
......
...@@ -21,7 +21,6 @@ import { ...@@ -21,7 +21,6 @@ import {
BADGE_LABELS_PENDING_OWNER_APPROVAL, BADGE_LABELS_PENDING_OWNER_APPROVAL,
TAB_QUERY_PARAM_VALUES, TAB_QUERY_PARAM_VALUES,
} from '~/members/constants'; } from '~/members/constants';
import * as initUserPopovers from '~/user_popovers';
import { import {
member as memberMock, member as memberMock,
directMember, directMember,
...@@ -257,14 +256,6 @@ describe('MembersTable', () => { ...@@ -257,14 +256,6 @@ describe('MembersTable', () => {
}); });
}); });
it('initializes user popovers when mounted', () => {
const initUserPopoversMock = jest.spyOn(initUserPopovers, 'default');
createComponent();
expect(initUserPopoversMock).toHaveBeenCalled();
});
it('adds QA selector to table', () => { it('adds QA selector to table', () => {
createComponent(); createComponent();
......
...@@ -19,8 +19,6 @@ import '~/behaviors/markdown/render_gfm'; ...@@ -19,8 +19,6 @@ import '~/behaviors/markdown/render_gfm';
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import * as mockData from '../mock_data'; import * as mockData from '../mock_data';
jest.mock('~/user_popovers', () => jest.fn());
setTestTimeout(1000); setTestTimeout(1000);
const TYPE_COMMENT_FORM = 'comment-form'; const TYPE_COMMENT_FORM = 'comment-form';
......
...@@ -26,12 +26,13 @@ describe('User Popovers', () => { ...@@ -26,12 +26,13 @@ describe('User Popovers', () => {
return link; return link;
}; };
const findPopovers = () => {
return Array.from(document.querySelectorAll('[data-testid="user-popover"]'));
};
const dummyUser = { name: 'root', username: 'root', is_followed: false }; const dummyUser = { name: 'root', username: 'root', is_followed: false };
const dummyUserStatus = { message: 'active' }; const dummyUserStatus = { message: 'active' };
let popovers;
const triggerEvent = (eventName, el) => { const triggerEvent = (eventName, el) => {
const event = new MouseEvent(eventName, { const event = new MouseEvent(eventName, {
bubbles: true, bubbles: true,
...@@ -54,56 +55,70 @@ describe('User Popovers', () => { ...@@ -54,56 +55,70 @@ describe('User Popovers', () => {
.mockImplementation((userId) => userStatusCacheSpy(userId)); .mockImplementation((userId) => userStatusCacheSpy(userId));
jest.spyOn(UsersCache, 'updateById'); jest.spyOn(UsersCache, 'updateById');
popovers = initUserPopovers(document.querySelectorAll(selector)); initUserPopovers((popoverInstance) => {
const mountingRoot = document.createElement('div');
document.body.appendChild(mountingRoot);
popoverInstance.$mount(mountingRoot);
});
}); });
afterEach(() => { afterEach(() => {
resetHTMLFixture(); resetHTMLFixture();
}); });
it('initializes a popover for each user link with a user id', () => { describe('shows a placeholder popover on hover', () => {
const linksWithUsers = findFixtureLinks(); let linksWithUsers;
beforeEach(() => {
linksWithUsers = findFixtureLinks();
linksWithUsers.forEach((el) => {
triggerEvent('mouseover', el);
});
});
expect(linksWithUsers.length).toBe(popovers.length); it('for initial links', () => {
}); expect(findPopovers().length).toBe(linksWithUsers.length);
});
it('adds popovers to user links added to the DOM tree after the initial call', async () => { it('for elements added after initial load', async () => {
document.body.appendChild(createUserLink()); const addedLinks = [createUserLink(), createUserLink()];
document.body.appendChild(createUserLink()); addedLinks.forEach((link) => {
document.body.appendChild(link);
});
const linksWithUsers = findFixtureLinks(); await Promise.resolve();
expect(linksWithUsers.length).toBe(popovers.length + 2); addedLinks.forEach((link) => {
triggerEvent('mouseover', link);
});
expect(findPopovers().length).toBe(linksWithUsers.length + addedLinks.length);
});
}); });
it('does not initialize the user popovers twice for the same element', () => { it('does not initialize the user popovers twice for the same element', () => {
const newPopovers = initUserPopovers(document.querySelectorAll(selector)); const [firstUserLink] = findFixtureLinks();
const samePopovers = popovers.every((popover, index) => newPopovers[index] === popover); triggerEvent('mouseover', firstUserLink);
triggerEvent('mouseleave', firstUserLink);
triggerEvent('mouseover', firstUserLink);
expect(samePopovers).toBe(true); expect(findPopovers().length).toBe(1);
}); });
describe('when user link emits mouseenter event', () => { describe('when user link emits mouseenter event with empty user cache', () => {
let userLink; let userLink;
beforeEach(() => { beforeEach(() => {
UsersCache.retrieveById.mockReset(); UsersCache.retrieveById.mockReset();
userLink = document.querySelector(selector); [userLink] = findFixtureLinks();
triggerEvent('mouseenter', userLink);
});
it('removes title attribute from user links', () => { triggerEvent('mouseover', userLink);
expect(userLink.getAttribute('title')).toBeFalsy();
expect(userLink.dataset.originalTitle).toBeFalsy();
}); });
it('populates popovers with preloaded user data', () => { it('populates popover with preloaded user data', () => {
const { name, userId, username } = userLink.dataset; const { name, userId, username } = userLink.dataset;
const [firstPopover] = popovers;
expect(firstPopover.$props.user).toEqual( expect(userLink.user).toEqual(
expect.objectContaining({ expect.objectContaining({
name, name,
userId, userId,
...@@ -111,6 +126,21 @@ describe('User Popovers', () => { ...@@ -111,6 +126,21 @@ describe('User Popovers', () => {
}), }),
); );
}); });
});
describe('when user link emits mouseenter event', () => {
let userLink;
beforeEach(() => {
[userLink] = findFixtureLinks();
triggerEvent('mouseover', userLink);
});
it('removes title attribute from user links', () => {
expect(userLink.getAttribute('title')).toBeFalsy();
expect(userLink.dataset.originalTitle).toBeFalsy();
});
it('fetches user info and status from the user cache', () => { it('fetches user info and status from the user cache', () => {
const { userId } = userLink.dataset; const { userId } = userLink.dataset;
...@@ -118,42 +148,38 @@ describe('User Popovers', () => { ...@@ -118,42 +148,38 @@ describe('User Popovers', () => {
expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId); expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId);
expect(UsersCache.retrieveStatusById).toHaveBeenCalledWith(userId); expect(UsersCache.retrieveStatusById).toHaveBeenCalledWith(userId);
}); });
});
it('removes aria-describedby attribute from the user link on mouseleave', () => {
const userLink = document.querySelector(selector);
userLink.setAttribute('aria-describedby', 'popover'); it('removes aria-describedby attribute from the user link on mouseleave', () => {
triggerEvent('mouseleave', userLink); userLink.setAttribute('aria-describedby', 'popover');
triggerEvent('mouseleave', userLink);
expect(userLink.getAttribute('aria-describedby')).toBe(null); expect(userLink.getAttribute('aria-describedby')).toBe(null);
}); });
it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => {
const [firstPopover] = popovers;
const withinFirstPopover = within(firstPopover.$el);
const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' });
const findUnfollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Unfollow' });
const userLink = document.querySelector(selector); it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => {
triggerEvent('mouseenter', userLink); const [firstPopover] = findPopovers();
const withinFirstPopover = within(firstPopover);
const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' });
const findUnfollowButton = () =>
withinFirstPopover.queryByRole('button', { name: 'Unfollow' });
await waitForPromises(); await waitForPromises();
const { userId } = document.querySelector(selector).dataset; const { userId } = document.querySelector(selector).dataset;
triggerEvent('click', findFollowButton()); triggerEvent('click', findFollowButton());
await waitForPromises(); await waitForPromises();
expect(findUnfollowButton()).not.toBe(null); expect(findUnfollowButton()).not.toBe(null);
expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true }); expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true });
triggerEvent('click', findUnfollowButton()); triggerEvent('click', findUnfollowButton());
await waitForPromises(); await waitForPromises();
expect(findFollowButton()).not.toBe(null); expect(findFollowButton()).not.toBe(null);
expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false }); expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false });
});
}); });
}); });
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment