From d14b8921c927e74dc97f0736f58ed07cf80be32c Mon Sep 17 00:00:00 2001 From: Pedro Almeida Date: Fri, 26 Jun 2026 17:11:42 -0300 Subject: [PATCH 1/2] Fix comments UI comment type errors Guard nullable comment HTML before rendering bodies or generating reply snippets, and only pass concrete open reply forms to ReplyFormBox. --- apps/comments-ui/src/components/content/comment.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/comments-ui/src/components/content/comment.tsx b/apps/comments-ui/src/components/content/comment.tsx index 19e45eca9a8..82fc99980c1 100644 --- a/apps/comments-ui/src/components/content/comment.tsx +++ b/apps/comments-ui/src/components/content/comment.tsx @@ -133,8 +133,11 @@ const PublishedComment: React.FC = ({children, comment, p const inReplyToDetails: Partial = {}; if (parent) { + if (comment.html === null) { + return; + } inReplyToDetails.in_reply_to_id = comment.id; - inReplyToDetails.in_reply_to_snippet = getCommentInReplyToSnippet(comment); + inReplyToDetails.in_reply_to_snippet = getCommentInReplyToSnippet({html: comment.html}); } const newForm: OpenCommentForm = { @@ -166,7 +169,7 @@ const PublishedComment: React.FC = ({children, comment, p layoutVariant={layoutVariant} memberUuid={comment.member?.uuid} replies={{children}} - replyForm={displayReplyForm ? : null} + replyForm={displayReplyForm && openForm ? : null} useThreading={useThreading} >
@@ -178,7 +181,7 @@ const PublishedComment: React.FC = ({children, comment, p ) : ( <> - + {comment.html && } {children}} - replyForm={displayReplyForm ? : null} + replyForm={displayReplyForm && openForm ? : null} useThreading={useThreading} >
From a76d5e02f3e1ce410f07609cd44eabc89eae1743 Mon Sep 17 00:00:00 2001 From: Pedro Almeida Date: Fri, 26 Jun 2026 17:52:47 -0300 Subject: [PATCH 2/2] Add comments UI null html reply tombstone test --- apps/comments-ui/test/e2e/content.test.ts | 41 ++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/comments-ui/test/e2e/content.test.ts b/apps/comments-ui/test/e2e/content.test.ts index 95b9798db87..380eb0a797d 100644 --- a/apps/comments-ui/test/e2e/content.test.ts +++ b/apps/comments-ui/test/e2e/content.test.ts @@ -132,5 +132,44 @@ test.describe('Deleted and Hidden Content', async () => { // parent comment is hidden but shows text await expect (frame.getByText('This comment has been hidden')).toBeVisible(); }); -}); + test('deleted reply with null html renders as a tombstone without actions', async ({page}) => { + const mockedApi = new MockedApi({}); + const childReply = mockedApi.buildReply({ + id: 'child-reply', + html: '

Visible child reply

', + in_reply_to_id: 'deleted-reply', + in_reply_to_snippet: '[removed]' + }); + + mockedApi.addComment({ + id: 'parent-comment', + html: '

Parent comment

', + replies: [ + mockedApi.buildReply({ + id: 'deleted-reply', + html: null, + status: 'deleted', + replies: [childReply] + }), + childReply + ] + }); + + const {frame} = await initialize({ + mockedApi, + page, + publication: 'Publisher Weekly', + labs: { + commentsThreads: true + } + }); + + const deletedReply = frame.locator('[id="deleted-reply"]'); + await expect(deletedReply).toContainText('This comment has been removed'); + await expect(deletedReply.getByTestId('comment-content')).toHaveCount(0); + await expect(deletedReply.getByTestId('reply-button')).toHaveCount(0); + await expect(deletedReply.getByTestId('like-button')).toHaveCount(0); + await expect(frame.getByText('Visible child reply')).toBeVisible(); + }); +});