From 137ea89a7bf0aa4f849c8565d1bdbdbf8b04763e Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Thu, 12 Dec 2024 17:33:47 +0000 Subject: [PATCH] Fixed incorrect pagination after deleting top level comment closes https://linear.app/ghost/issue/PLG-304 - added a refresh of the comments list when a top-level comment with no replies is deleted so the pagination resets and replies aren't missed when loading more due to a shift in the underlying paginated data --- apps/comments-ui/src/actions.ts | 10 +++++++++- apps/comments-ui/test/e2e/actions.test.ts | 16 ++++++++++++++++ apps/comments-ui/test/utils/MockedApi.ts | 11 +++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/apps/comments-ui/src/actions.ts b/apps/comments-ui/src/actions.ts index 09c6b0f736..8cfb828815 100644 --- a/apps/comments-ui/src/actions.ts +++ b/apps/comments-ui/src/actions.ts @@ -271,7 +271,7 @@ async function unlikeComment({state, api, data: comment}: {state: EditableAppCon }; } -async function deleteComment({state, api, data: comment}: {state: EditableAppContext, api: GhostApi, data: {id: string}}) { +async function deleteComment({state, api, data: comment, dispatchAction}: {state: EditableAppContext, api: GhostApi, data: {id: string}, dispatchAction: DispatchActionType}) { await api.comments.edit({ comment: { id: comment.id, @@ -279,6 +279,14 @@ async function deleteComment({state, api, data: comment}: {state: EditableAppCon } }); + // If we're deleting a top-level comment with no replies we refresh the + // whole comments section to maintain correct pagination + const commentToDelete = state.comments.find(c => c.id === comment.id); + if (commentToDelete && (!commentToDelete.replies || commentToDelete.replies.length === 0)) { + dispatchAction('setOrder', {order: state.order}); + return null; + } + return { comments: state.comments.map((topLevelComment) => { // If the comment has replies we want to keep it so the replies are diff --git a/apps/comments-ui/test/e2e/actions.test.ts b/apps/comments-ui/test/e2e/actions.test.ts index 8741389e98..6e50cde7b0 100644 --- a/apps/comments-ui/test/e2e/actions.test.ts +++ b/apps/comments-ui/test/e2e/actions.test.ts @@ -433,6 +433,22 @@ test.describe('Actions', async () => { await expect(frame.getByTestId('replies-line')).toBeVisible(); }); + test('Resets comments list after deleting a top-level comment', async ({page}) => { + const loggedInMember = buildMember(); + mockedApi.setMember(loggedInMember); + // We have a page limit of 20, this will show the load more button + mockedApi.addComments(21, {member: loggedInMember}); + + const {frame} = await initializeTest(page); + await expect(frame.getByTestId('pagination-component')).toBeVisible(); + + const commentToDelete = frame.getByTestId('comment-component').nth(0); + await deleteComment(page, frame, commentToDelete); + + // more button should have disappeared because the list was reloaded + await expect(frame.getByTestId('pagination-component')).not.toBeVisible(); + }); + test.describe('Sorting', () => { test('Renders Sorting Form dropdown', async ({page}) => { mockedApi.addComment({ diff --git a/apps/comments-ui/test/utils/MockedApi.ts b/apps/comments-ui/test/utils/MockedApi.ts index 9aeca70f3f..2b60800f3e 100644 --- a/apps/comments-ui/test/utils/MockedApi.ts +++ b/apps/comments-ui/test/utils/MockedApi.ts @@ -330,7 +330,7 @@ export class MockedApi { }); }, - async getComment(route) { + async getOrDeleteComment(route) { await this.#delayResponse(); const url = new URL(route.request().url()); const commentId = url.pathname.split('/').reverse()[1]; @@ -344,6 +344,13 @@ export class MockedApi { order: '' })) }); + + if (route.request().method() === 'PUT' && JSON.parse(route.request().postData()).comments?.[0]?.status === 'deleted') { + const comment = findCommentById(this.comments, commentId); + if (comment) { + comment.status = 'deleted'; + } + } }, async likeComment(route) { @@ -523,7 +530,7 @@ export class MockedApi { await page.route(`${path}/members/api/member/`, this.requestHandlers.getMember.bind(this)); await page.route(`${path}/members/api/comments/*`, this.requestHandlers.addComment.bind(this)); await page.route(`${path}/members/api/comments/post/*/*`, this.requestHandlers.browseComments.bind(this)); - await page.route(`${path}/members/api/comments/*/`, this.requestHandlers.getComment.bind(this)); + await page.route(`${path}/members/api/comments/*/`, this.requestHandlers.getOrDeleteComment.bind(this)); await page.route(`${path}/members/api/comments/*/like/`, this.requestHandlers.likeComment.bind(this)); await page.route(`${path}/members/api/comments/*/replies/*`, this.requestHandlers.getReplies.bind(this)); await page.route(`${path}/members/api/comments/counts/*`, this.requestHandlers.getCommentCounts.bind(this));