diff --git a/static/js/components/DuplicatesManager.js b/static/js/components/DuplicatesManager.js index c235b889..5c95c7aa 100644 --- a/static/js/components/DuplicatesManager.js +++ b/static/js/components/DuplicatesManager.js @@ -72,7 +72,7 @@ export class DuplicatesManager { this.updateSelectedCount(); } - exitDuplicateMode() { + async exitDuplicateMode() { this.inDuplicateMode = false; this.selectedForDeletion.clear(); @@ -95,13 +95,15 @@ export class DuplicatesManager { recipeGrid.innerHTML = ''; } - // Re-enable virtual scrolling - state.virtualScroller.enable(); - - // Apply a layout switch that was deferred while duplicates mode was active + // Re-enable virtual scrolling, or apply a layout switch deferred + // while duplicates mode was active (enabling the old scroller first + // would let its pending rAF render repopulate the grid after the + // new scroller is created, leaving orphaned/overlapping cards). if (state.pendingLayoutRecreate) { state.pendingLayoutRecreate = false; - recreateVirtualScroll('recipes'); + await recreateVirtualScroll('recipes'); + } else if (state.virtualScroller) { + state.virtualScroller.enable(); } } diff --git a/tests/frontend/components/duplicatesManager.test.js b/tests/frontend/components/duplicatesManager.test.js new file mode 100644 index 00000000..b7527691 --- /dev/null +++ b/tests/frontend/components/duplicatesManager.test.js @@ -0,0 +1,87 @@ +import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; + +const showToastMock = vi.fn(); +const recreateVirtualScrollMock = vi.fn(); + +vi.mock('../../../static/js/utils/uiHelpers.js', () => ({ + showToast: showToastMock, +})); + +vi.mock('../../../static/js/components/RecipeCard.js', () => ({ + RecipeCard: class {}, +})); + +vi.mock('../../../static/js/utils/infiniteScroll.js', () => ({ + recreateVirtualScroll: recreateVirtualScrollMock, +})); + +const { DuplicatesManager } = await import('../../../static/js/components/DuplicatesManager.js'); +const { state, getCurrentPageState, setCurrentPageType } = await import('../../../static/js/state/index.js'); + +function setupDom() { + document.body.innerHTML = ` +
+