From f699802b837d7d9fcf150ff2bf00cd3c5475c87a Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Thu, 9 Nov 2023 17:18:46 +0100 Subject: refactor(components): rewrite CommentsList component * use ApprovedCommentProps to make CommentData type * add the author name of the parent on reply form heading * add tests --- .../organisms/comments-list/comments-list.tsx | 147 +++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/components/organisms/comments-list/comments-list.tsx (limited to 'src/components/organisms/comments-list/comments-list.tsx') diff --git a/src/components/organisms/comments-list/comments-list.tsx b/src/components/organisms/comments-list/comments-list.tsx new file mode 100644 index 0000000..0470f99 --- /dev/null +++ b/src/components/organisms/comments-list/comments-list.tsx @@ -0,0 +1,147 @@ +import { + forwardRef, + useCallback, + useState, + type ForwardRefRenderFunction, +} from 'react'; +import { useIntl } from 'react-intl'; +import type { Nullable } from '../../../types'; +import { Heading, List, ListItem, type ListProps } from '../../atoms'; +import { + ApprovedComment, + type ApprovedCommentProps, + PendingComment, + ReplyCommentForm, + type ReplyCommentFormProps, +} from '../comment'; +import styles from './comments-list.module.scss'; + +export type CommentData = Pick< + ApprovedCommentProps, + 'author' | 'content' | 'id' | 'publicationDate' +> & { + isApproved: boolean; + replies?: CommentData[]; +}; + +export type CommentsListProps = Omit< + ListProps, + | 'children' + | 'hideMarker' + | 'isHierarchical' + | 'isInline' + | 'isOrdered' + | 'onSubmit' + | 'spacing' +> & + Pick & { + /** + * The comments. + */ + comments: CommentData[]; + /** + * A positive integer. When depth is set to `0`, replies are not used. + * + * @default 0 + */ + depth?: number; + }; + +const CommentsListWithRef: ForwardRefRenderFunction< + HTMLOListElement, + CommentsListProps +> = ({ comments, depth = 0, onSubmit, ...props }, ref) => { + const [replyingTo, setReplyingTo] = useState>(null); + const intl = useIntl(); + + const toggleReply = useCallback((id: number) => { + setReplyingTo((prevId) => { + if (prevId === id) return null; + return id; + }); + }, []); + + const getComments = useCallback( + (data: CommentData[], currentDepth = 0) => { + const isLastLevel = depth === currentDepth; + + return data.map(({ isApproved, replies, ...comment }) => { + const replyFormHeading = intl.formatMessage( + { + defaultMessage: 'Leave a reply to {name}', + description: 'CommentsList: comment form title', + id: 'c1Ju/q', + }, + { name: comment.author.name } + ); + const replyBtnLabel = + replyingTo === comment.id + ? intl.formatMessage({ + defaultMessage: 'Cancel reply', + description: 'CommentsList: cancel reply button', + id: 'uZj4QI', + }) + : intl.formatMessage({ + defaultMessage: 'Reply', + description: 'CommentsList: reply button', + id: 'Qa9twM', + }); + + return ( + + {isApproved ? ( + <> + + {replyingTo === comment.id ? ( + {replyFormHeading}} + onSubmit={onSubmit} + /> + ) : null} + {replies?.length && !isLastLevel ? ( + + {getComments(replies, currentDepth + 1)} + + ) : null} + + ) : ( + + )} + + ); + }); + }, + [depth, intl, onSubmit, replyingTo, toggleReply] + ); + + return ( + + {getComments(comments)} + + ); +}; + +/** + * CommentsList component + * + * Render a list of comments. + */ +export const CommentsList = forwardRef(CommentsListWithRef); -- cgit v1.2.3