# 7275 MINNested Comments
Overview
Build a threaded comments UI: render comments recursively and add reply, edit, delete, and collapse with immutable tree updates.
Solution preview
Here's the result you'll build by completing this challenge.
Requirements
Render
- Render the full thread from
initialCommentsinApp.tsx; the providedCommentCard.tsxanddata.tsare read-only. - Root comments appear in array order and replies are nested inside their parent, so a parent comment container also contains its descendants in the DOM.
- Each comment container has
data-testid="comment-<id>". The seed order isc1,c2,c3,c4,c5, wherec1containsc2andc4, andc2containsc3. - Each comment shows its author and text. The text element has
data-testid="comment-text-<id>".
Collapse
- Only comments that have replies render a collapse control
data-testid="toggle-<id>"with anaria-expandedattribute. - Clicking the collapse control hides or shows only that comment's replies. The comment itself stays visible.
Reply
- Every comment renders
data-testid="reply-<id>",data-testid="edit-<id>", anddata-testid="delete-<id>"controls. - Clicking
reply-<id>opens a reply form under that comment withdata-testid="reply-input-<id>",data-testid="reply-submit-<id>", anddata-testid="reply-cancel-<id>". - Submitting a non-empty reply appends it to that comment's replies with author
"You", and closes the form. - Reply text is trimmed, and empty or whitespace-only submissions are rejected: the form stays open and nothing is added.
Edit
- Clicking
edit-<id>replaces the comment text with a prefilled editor:data-testid="edit-input-<id>",data-testid="edit-save-<id>", anddata-testid="edit-cancel-<id>". - Saving an edit trims the text and updates only that comment, then closes the editor.
- Cancelling an edit restores the original text and closes the editor.
Delete
- Deleting a comment removes it together with its entire subtree while leaving siblings intact.
- Deleting every comment renders
data-testid="empty-state".
Immutability
- All updates are immutable: neither
initialCommentsnor any node'srepliesarray is mutated.
Notes
-
The card UI and its recursive rendering are already provided by the read-only
CommentCard; you own the state, the handlers, and the two tree helpers. -
The single most important idea is a recursive tree update.
mapover the current level, recurse intoreplieswhen the target is not the current node, and rebuild only the nodes on the path:Use it for both reply (the
updatefn appends a new reply) and edit (theupdatefn returns{ ...comment, text }). -
Never mutate
initialCommentsor a node'srepliesarray. Create new arrays with spread; the seed is shared across renders and across tests. -
Collapse is UI state, separate from the tree. Keep the collapsed ids in a
Set;CommentCardreads it to hide replies. -
Delete drops the whole subtree:
filterout the node and recurse into the remaining replies so the deleted branch never renders. -
Make the reply and edit textareas controlled and
trim()before saving; reject empty or whitespace-only text so the form stays open. -
A reply added to a collapsed parent stays hidden until you expand it — that is expected and fine.
-
APIs: Updating objects in state, Rendering lists,
aria-expanded.
Tests
- renders all seed comments with replies nested under their parent
- shows the author and text of every comment
- collapses a comment's replies and keeps the comment visible
- expands a collapsed comment again
- opens the reply form under the correct comment
- adds a reply with the author You and closes the form
- rejects an empty or whitespace-only reply and keeps the form open
- prefills the editor with the current comment text
- saves an edited comment and closes the editor
- cancels an edit and restores the original text
- deletes a comment and its replies, keeping siblings intact
- shows the empty state when every comment is deleted
- never mutates the imported seed tree