diff --git a/src/features/Posts/Components/Comments/AddComment/AddComment.tsx b/src/features/Posts/Components/Comments/AddComment/AddComment.tsx
new file mode 100644
index 0000000..669cbd5
--- /dev/null
+++ b/src/features/Posts/Components/Comments/AddComment/AddComment.tsx
@@ -0,0 +1,33 @@
+import { FormEvent } from "react";
+import Button from "src/Components/Button/Button";
+import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
+import { useAutoResizableTextArea } from "src/utils/hooks";
+
+
+
+export default function AddComment() {
+
+ const textAreaRef = useAutoResizableTextArea();
+
+ const submitComment = (e: FormEvent) => {
+ e.preventDefault();
+ alert('submitted')
+ }
+
+ return (
+
+ )
+}
diff --git a/src/features/Posts/Components/Comments/Comment/Comment.stories.tsx b/src/features/Posts/Components/Comments/Comment/Comment.stories.tsx
new file mode 100644
index 0000000..0ec8f23
--- /dev/null
+++ b/src/features/Posts/Components/Comments/Comment/Comment.stories.tsx
@@ -0,0 +1,28 @@
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { MOCK_DATA } from 'src/mocks/data';
+
+import Comment from './Comment';
+
+export default {
+ title: 'Posts/Components/Comments/Comment with Replies',
+ component: Comment,
+ argTypes: {
+ backgroundColor: { control: 'color' },
+ },
+} as ComponentMeta;
+
+
+const Template: ComponentStory = (args) =>
+
+export const Default = Template.bind({});
+Default.args = {
+ comment: {
+ ...MOCK_DATA.generatePostComments(1)[0],
+ replies: [
+ { ...MOCK_DATA.generatePostComments(1)[0], replies: [] },
+ { ...MOCK_DATA.generatePostComments(1)[0], replies: [] }
+ ]
+ }
+}
+
+
diff --git a/src/features/Posts/Components/Comments/Comment/Comment.tsx b/src/features/Posts/Components/Comments/Comment/Comment.tsx
new file mode 100644
index 0000000..1cfb6ea
--- /dev/null
+++ b/src/features/Posts/Components/Comments/Comment/Comment.tsx
@@ -0,0 +1,22 @@
+
+import CommentCard from "../CommentCard/CommentCard";
+import { CommentWithReplies } from "../types";
+
+
+interface Props {
+ comment: CommentWithReplies
+}
+
+export default function Comment({ comment }: Props) {
+ return (
+
+
+ {comment.replies.length > 0 &&
+
+
+ {comment.replies.map(reply => )}
+
+
}
+
+ )
+}
diff --git a/src/features/Posts/Components/CommentCard/CommentCard.stories.tsx b/src/features/Posts/Components/Comments/CommentCard/CommentCard.stories.tsx
similarity index 80%
rename from src/features/Posts/Components/CommentCard/CommentCard.stories.tsx
rename to src/features/Posts/Components/Comments/CommentCard/CommentCard.stories.tsx
index e095e04..1e73b25 100644
--- a/src/features/Posts/Components/CommentCard/CommentCard.stories.tsx
+++ b/src/features/Posts/Components/Comments/CommentCard/CommentCard.stories.tsx
@@ -4,7 +4,7 @@ import { MOCK_DATA } from 'src/mocks/data';
import CommentCard from './CommentCard';
export default {
- title: 'Posts/Components/CommentCard',
+ title: 'Posts/Components/Comments/CommentCard',
component: CommentCard,
argTypes: {
backgroundColor: { control: 'color' },
@@ -16,7 +16,9 @@ const Template: ComponentStory = (args) => ;
+
+
+const Template: ComponentStory
= (args) =>
+
+export const Default = Template.bind({});
+Default.args = {
+ comments: MOCK_DATA.generatePostComments(15)
+}
+
+
diff --git a/src/features/Posts/Components/Comments/CommentsSection/CommentsSection.tsx b/src/features/Posts/Components/Comments/CommentsSection/CommentsSection.tsx
new file mode 100644
index 0000000..4194545
--- /dev/null
+++ b/src/features/Posts/Components/Comments/CommentsSection/CommentsSection.tsx
@@ -0,0 +1,27 @@
+import React, { useMemo } from 'react'
+import Comment from '../Comment/Comment'
+import AddComment from '../AddComment/AddComment'
+import { convertCommentsToTree } from '../helpers'
+import { Comment as IComment } from '../types'
+
+interface Props {
+ comments: IComment[]
+}
+
+export default function CommentsSection({ comments }: Props) {
+ const commentsTree = useMemo(() => convertCommentsToTree(comments), [comments])
+
+ return (
+
+
Discussion ({comments.length})
+
+
+
+ {commentsTree.map(comment => )}
+
+
+
+ )
+}
diff --git a/src/features/Posts/Components/Comments/helpers.tsx b/src/features/Posts/Components/Comments/helpers.tsx
new file mode 100644
index 0000000..12bf818
--- /dev/null
+++ b/src/features/Posts/Components/Comments/helpers.tsx
@@ -0,0 +1,19 @@
+import { Comment, CommentWithReplies } from "./types";
+
+
+export function convertCommentsToTree(comments: Comment[]) {
+ let tree: Record = {};
+
+ for (const comment of comments)
+ tree[comment.id] = { ...comment, replies: [] }
+
+ for (const comment of Object.values(tree)) {
+ if (comment.parentId)
+ tree[comment.parentId].replies = [...tree[comment.parentId].replies, comment]
+ }
+
+ // TODO
+ // Sort the comments according to date
+
+ return Object.values(tree).filter(node => !node.parentId);
+}
\ No newline at end of file
diff --git a/src/features/Posts/Components/Comments/index.tsx b/src/features/Posts/Components/Comments/index.tsx
new file mode 100644
index 0000000..3deda80
--- /dev/null
+++ b/src/features/Posts/Components/Comments/index.tsx
@@ -0,0 +1 @@
+export { }
\ No newline at end of file
diff --git a/src/features/Posts/Components/Comments/types.ts b/src/features/Posts/Components/Comments/types.ts
new file mode 100644
index 0000000..98d973a
--- /dev/null
+++ b/src/features/Posts/Components/Comments/types.ts
@@ -0,0 +1,15 @@
+
+import { Author } from "src/features/Posts/types";
+
+export interface Comment {
+ id: number
+ author: Author
+ created_at: string
+ body: string
+ votes_count: number
+ parentId: number
+}
+
+export interface CommentWithReplies extends Comment {
+ replies: CommentWithReplies[]
+}
\ No newline at end of file
diff --git a/src/mocks/data.ts b/src/mocks/data.ts
index 269a6f6..42e78c7 100644
--- a/src/mocks/data.ts
+++ b/src/mocks/data.ts
@@ -1,9 +1,10 @@
-import { posts, feed } from "./data/posts";
+import { posts, feed, generatePostComments } from "./data/posts";
import { categories, projects } from "./data/projects";
export const MOCK_DATA = {
projects,
categories,
posts,
- feed
+ feed,
+ generatePostComments: generatePostComments
}
\ No newline at end of file
diff --git a/src/mocks/data/posts.ts b/src/mocks/data/posts.ts
index 43727d6..4e2bebc 100644
--- a/src/mocks/data/posts.ts
+++ b/src/mocks/data/posts.ts
@@ -14,7 +14,7 @@ const getAuthor = () => ({
join_date: getDate()
})
-const getPostComments = (cnt: number = 1): Story['comments'] => {
+export const generatePostComments = (cnt: number = 1): Story['comments'] => {
let comments = [];
const rootCommentsIds: any[] = []
@@ -95,7 +95,7 @@ export let posts = {
{ id: 3, title: "guide" },
],
author: getAuthor(),
- comments: getPostComments(),
+ comments: generatePostComments(),
},
] as Story[],
@@ -137,7 +137,7 @@ export let posts = {
{ id: 2, title: "webln" },
],
author: getAuthor(),
- comments: getPostComments(3)
+ comments: generatePostComments(3)
},
] as Question[]
}
diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts
index 498027e..8e6c79e 100644
--- a/src/utils/hooks/index.ts
+++ b/src/utils/hooks/index.ts
@@ -3,3 +3,4 @@ export * from "./useResizeListener";
export * from "./usePressHolder";
export * from "./useInfiniteQuery";
export * from "./useReachedBottom";
+export * from "./useAutoResizableTextArea";
diff --git a/src/utils/hooks/useAutoResizableTextArea.ts b/src/utils/hooks/useAutoResizableTextArea.ts
new file mode 100644
index 0000000..be3d5df
--- /dev/null
+++ b/src/utils/hooks/useAutoResizableTextArea.ts
@@ -0,0 +1,20 @@
+import { useEffect, useRef } from "react";
+
+export const useAutoResizableTextArea = () => {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ function OnInput() {
+ if (ref.current) {
+ ref.current.style.height = "auto";
+ ref.current.style.height = (ref.current.scrollHeight) + "px";
+ }
+ }
+ ref.current?.setAttribute("style", "height:" + (ref.current?.scrollHeight) + "px;overflow-y:hidden;");
+ ref.current?.addEventListener("input", OnInput, false);
+
+ }, [])
+
+ return ref
+
+}
\ No newline at end of file