Skip to content

Commit 80073e3

Browse files
authored
feat: updated redux structure using updated comments api (#670)
* feat: updated redux structure and commentsview component * test: fixed test cases * fix: fixed lint error
1 parent 3aacdda commit 80073e3

File tree

13 files changed

+149
-289
lines changed

13 files changed

+149
-289
lines changed

src/discussions/post-comments/PostCommentsView.jsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
99
import { useIntl } from '@edx/frontend-platform/i18n';
1010

1111
import Spinner from '../../components/Spinner';
12-
import {
13-
EndorsementStatus, PostsPages, ThreadType,
14-
} from '../../data/constants';
12+
import { PostsPages } from '../../data/constants';
1513
import useDispatchWithState from '../../data/hooks';
1614
import DiscussionContext from '../common/context';
1715
import { useIsOnDesktop } from '../data/hooks';
@@ -127,15 +125,7 @@ const PostCommentsView = () => {
127125
</div>
128126
<Suspense fallback={(<Spinner />)}>
129127
{!!commentsCount && <CommentsSort />}
130-
{type === ThreadType.DISCUSSION && (
131-
<CommentsView endorsed={EndorsementStatus.DISCUSSION} />
132-
)}
133-
{type === ThreadType.QUESTION && (
134-
<>
135-
<CommentsView endorsed={EndorsementStatus.ENDORSED} />
136-
<CommentsView endorsed={EndorsementStatus.UNENDORSED} />
137-
</>
138-
)}
128+
<CommentsView threadType={type} />
139129
</Suspense>
140130
</PostCommentsContext.Provider>
141131
);

src/discussions/post-comments/PostCommentsView.test.jsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { camelCaseObject, initializeMockApp } from '@edx/frontend-platform';
1212
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
1313
import { AppProvider } from '@edx/frontend-platform/react';
1414

15-
import { getApiBaseUrl } from '../../data/constants';
15+
import { getApiBaseUrl, ThreadType } from '../../data/constants';
1616
import { initializeStore } from '../../store';
1717
import executeThunk from '../../test-utils';
1818
import { getCohortsApiUrl } from '../cohorts/data/api';
@@ -50,10 +50,10 @@ let testLocation;
5050
let container;
5151
let unmount;
5252

53-
async function mockAxiosReturnPagedComments(threadId, endorsed = false, page = 1, count = 2) {
53+
async function mockAxiosReturnPagedComments(threadId, threadType = ThreadType.DISCUSSION, page = 1, count = 2) {
5454
axiosMock.onGet(commentsApiUrl).reply(200, Factory.build('commentsResult', { can_delete: true }, {
5555
threadId,
56-
endorsed,
56+
threadType,
5757
pageSize: 1,
5858
count,
5959
childCount: page === 1 ? 2 : 0,
@@ -76,6 +76,7 @@ async function mockAxiosReturnPagedCommentsResponses() {
7676
Factory.build('commentsResult', null, {
7777
threadId: discussionPostId,
7878
parentId,
79+
endorsed: false,
7980
page,
8081
pageSize: 1,
8182
count: 2,
@@ -201,6 +202,7 @@ describe('ThreadView', () => {
201202
id: commentId,
202203
rendered_body: rawBody,
203204
raw_body: rawBody,
205+
endorsed: false,
204206
})];
205207
});
206208
axiosMock.onPost(commentsApiUrl).reply(({ data }) => {
@@ -209,6 +211,7 @@ describe('ThreadView', () => {
209211
rendered_body: rawBody,
210212
raw_body: rawBody,
211213
thread_id: threadId,
214+
endorsed: false,
212215
})];
213216
});
214217
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, { isPostingEnabled: true });
@@ -230,9 +233,9 @@ describe('ThreadView', () => {
230233
expect(JSON.parse(axiosMock.history.patch[axiosMock.history.patch.length - 1].data)).toMatchObject(data);
231234
}
232235

233-
it('should not allow posting a comment on a closed post', async () => {
236+
it('should not allow posting a reply on a closed post', async () => {
234237
axiosMock.reset();
235-
await mockAxiosReturnPagedComments(closedPostId, true);
238+
await mockAxiosReturnPagedComments(closedPostId, ThreadType.QUESTION);
236239
await waitFor(() => renderComponent(closedPostId, true));
237240
const comments = await waitFor(() => screen.findAllByTestId('comment-comment-4'));
238241
const hoverCard = within(comments[0]).getByTestId('hover-card-comment-4');
@@ -288,7 +291,7 @@ describe('ThreadView', () => {
288291
expect(screen.queryByTestId('tinymce-editor')).not.toBeInTheDocument();
289292
});
290293

291-
it('should allow posting a response', async () => {
294+
it('should allow posting a comment', async () => {
292295
await waitFor(() => renderComponent(discussionPostId));
293296

294297
const post = await screen.findByTestId('post-thread-1');
@@ -540,8 +543,11 @@ describe('ThreadView', () => {
540543
// Wait for the content to load
541544
const comment = await waitFor(() => screen.findByTestId('comment-comment-1'));
542545
const hoverCard = within(comment).getByTestId('hover-card-comment-1');
546+
547+
const endorseButton = await waitFor(() => within(hoverCard).getByRole('button', { name: /Endorse/i }));
548+
543549
await act(async () => {
544-
fireEvent.click(within(hoverCard).getByRole('button', { name: /Endorse/i }));
550+
fireEvent.click(endorseButton);
545551
});
546552
expect(axiosMock.history.patch).toHaveLength(2);
547553
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ endorsed: true });
@@ -591,7 +597,7 @@ describe('ThreadView', () => {
591597

592598
it('pressing load more button will load next page of comments', async () => {
593599
await waitFor(() => renderComponent(discussionPostId));
594-
await mockAxiosReturnPagedComments(discussionPostId, false, 2);
600+
await mockAxiosReturnPagedComments(discussionPostId, ThreadType.DISCUSSION, 2);
595601

596602
const loadMoreButton = await findLoadMoreCommentsButton();
597603
await act(async () => {
@@ -604,7 +610,7 @@ describe('ThreadView', () => {
604610

605611
it('newly loaded comments are appended to the old ones', async () => {
606612
await waitFor(() => renderComponent(discussionPostId));
607-
await mockAxiosReturnPagedComments(discussionPostId, false, 2);
613+
await mockAxiosReturnPagedComments(discussionPostId, ThreadType.DISCUSSION, 2);
608614

609615
const loadMoreButton = await findLoadMoreCommentsButton();
610616
await act(async () => {
@@ -622,7 +628,7 @@ describe('ThreadView', () => {
622628
const findLoadMoreCommentsButtons = () => screen.findByTestId('load-more-comments');
623629

624630
it('initially loads only the first page', async () => {
625-
await mockAxiosReturnPagedComments(questionPostId);
631+
await mockAxiosReturnPagedComments(questionPostId, ThreadType.QUESTION);
626632
act(() => renderComponent(questionPostId));
627633

628634
expect(await screen.findByTestId('comment-comment-4'))
@@ -633,7 +639,7 @@ describe('ThreadView', () => {
633639
});
634640

635641
it('pressing load more button will load next page of comments', async () => {
636-
await mockAxiosReturnPagedComments(questionPostId);
642+
await mockAxiosReturnPagedComments(questionPostId, ThreadType.QUESTION);
637643
await waitFor(() => renderComponent(questionPostId));
638644

639645
const loadMoreButton = await findLoadMoreCommentsButtons();
@@ -644,7 +650,7 @@ describe('ThreadView', () => {
644650
expect(await screen.queryByTestId('comment-comment-5'))
645651
.not
646652
.toBeInTheDocument();
647-
await mockAxiosReturnPagedComments(questionPostId, false, 2, 1);
653+
await mockAxiosReturnPagedComments(questionPostId, ThreadType.QUESTION, 2, 1);
648654
await act(async () => {
649655
fireEvent.click(loadMoreButton);
650656
});
@@ -664,7 +670,7 @@ describe('ThreadView', () => {
664670
expect(screen.queryByTestId('reply-comment-3')).not.toBeInTheDocument();
665671
});
666672

667-
it('pressing load more button will load next page of responses', async () => {
673+
it('pressing load more button will load next page of replies', async () => {
668674
await waitFor(() => renderComponent(discussionPostId));
669675

670676
const loadMoreButton = await findLoadMoreCommentsResponsesButton();
@@ -674,7 +680,7 @@ describe('ThreadView', () => {
674680
await screen.findByTestId('reply-comment-3');
675681
});
676682

677-
it('newly loaded responses are appended to the old ones', async () => {
683+
it('newly loaded replies are appended to the old ones', async () => {
678684
await waitFor(() => renderComponent(discussionPostId));
679685

680686
const loadMoreButton = await findLoadMoreCommentsResponsesButton();
@@ -687,7 +693,7 @@ describe('ThreadView', () => {
687693
expect(screen.queryByTestId('reply-comment-2')).toBeInTheDocument();
688694
});
689695

690-
it('load more button is hidden when no more responses pages to load', async () => {
696+
it('load more button is hidden when no more replies pages to load', async () => {
691697
await waitFor(() => renderComponent(discussionPostId));
692698

693699
const loadMoreButton = await findLoadMoreCommentsResponsesButton();

src/discussions/post-comments/comments/CommentsView.jsx

Lines changed: 52 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { Button, Spinner } from '@openedx/paragon';
55

66
import { useIntl } from '@edx/frontend-platform/i18n';
77

8-
import { EndorsementStatus } from '../../../data/constants';
8+
import { ThreadType } from '../../../data/constants';
99
import { useUserPostingEnabled } from '../../data/hooks';
1010
import { isLastElementOfList } from '../../utils';
1111
import { usePostComments } from '../data/hooks';
1212
import messages from '../messages';
1313
import PostCommentsContext from '../postCommentsContext';
1414
import { Comment, ResponseEditor } from './comment';
1515

16-
const CommentsView = ({ endorsed }) => {
16+
const CommentsView = ({ threadType }) => {
1717
const intl = useIntl();
1818
const [addingResponse, setAddingResponse] = useState(false);
1919
const { isClosed } = useContext(PostCommentsContext);
@@ -25,7 +25,7 @@ const CommentsView = ({ endorsed }) => {
2525
hasMorePages,
2626
isLoading,
2727
handleLoadMoreResponses,
28-
} = usePostComments(endorsed);
28+
} = usePostComments(threadType);
2929

3030
const handleAddResponse = useCallback(() => {
3131
setAddingResponse(true);
@@ -45,7 +45,7 @@ const CommentsView = ({ endorsed }) => {
4545
</div>
4646
), []);
4747

48-
const handleComments = useCallback((postCommentsIds, showLoadMoreResponses = false) => (
48+
const handleComments = useCallback((postCommentsIds) => (
4949
<div className="mx-4" role="list">
5050
{postCommentsIds.map((commentId) => (
5151
<Comment
@@ -54,72 +54,66 @@ const CommentsView = ({ endorsed }) => {
5454
marginBottom={isLastElementOfList(postCommentsIds, commentId)}
5555
/>
5656
))}
57-
{hasMorePages && !isLoading && !showLoadMoreResponses && (
58-
<Button
59-
onClick={handleLoadMoreResponses}
60-
variant="link"
61-
block="true"
62-
className="px-4 mt-3 border-0 line-height-24 py-0 mb-2 font-style font-weight-500 font-size-14"
63-
data-testid="load-more-comments"
64-
>
65-
{intl.formatMessage(messages.loadMoreResponses)}
66-
</Button>
67-
)}
68-
{isLoading && !showLoadMoreResponses && (
69-
<div className="mb-2 mt-3 d-flex justify-content-center">
70-
<Spinner animation="border" variant="primary" className="spinner-dimensions" />
71-
</div>
72-
)}
7357
</div>
7458
), [hasMorePages, isLoading, handleLoadMoreResponses]);
7559

7660
return (
7761
((hasMorePages && isLoading) || !isLoading) && (
62+
<>
63+
{endorsedCommentsIds.length > 0 && (
7864
<>
79-
{endorsedCommentsIds.length > 0 && (
80-
<>
81-
{handleDefinition(messages.endorsedResponseCount, endorsedCommentsIds.length)}
82-
{endorsed === EndorsementStatus.DISCUSSION
83-
? handleComments(endorsedCommentsIds, true)
84-
: handleComments(endorsedCommentsIds, false)}
85-
</>
86-
)}
87-
{endorsed !== EndorsementStatus.ENDORSED && (
88-
<>
89-
{handleDefinition(messages.responseCount, unEndorsedCommentsIds.length)}
90-
{unEndorsedCommentsIds.length === 0 && <br />}
91-
{handleComments(unEndorsedCommentsIds, false)}
92-
{(isUserPrivilegedInPostingRestriction && !!unEndorsedCommentsIds.length && !isClosed) && (
93-
<div className="mx-4">
94-
{!addingResponse && (
95-
<Button
96-
variant="plain"
97-
block="true"
98-
className="card mb-4 px-0 border-0 py-10px mt-2 font-style font-weight-500
99-
line-height-24 font-size-14 text-primary-500"
100-
onClick={handleAddResponse}
101-
data-testid="add-response"
102-
>
103-
{intl.formatMessage(messages.addResponse)}
104-
</Button>
105-
)}
106-
<ResponseEditor
107-
addWrappingDiv
108-
addingResponse={addingResponse}
109-
handleCloseEditor={handleCloseResponseEditor}
110-
/>
111-
</div>
112-
)}
113-
</>
114-
)}
65+
{handleDefinition(messages.endorsedResponseCount, endorsedCommentsIds.length)}
66+
{handleComments(endorsedCommentsIds)}
11567
</>
68+
)}
69+
{handleDefinition(messages.responseCount, unEndorsedCommentsIds.length)}
70+
{unEndorsedCommentsIds.length > 0 && handleComments(unEndorsedCommentsIds)}
71+
{hasMorePages && !isLoading && (!!unEndorsedCommentsIds.length || !!endorsedCommentsIds.length) && (
72+
<Button
73+
onClick={handleLoadMoreResponses}
74+
variant="link"
75+
block="true"
76+
className="px-4 mt-3 border-0 line-height-24 py-0 mb-2 font-style font-weight-500 font-size-14"
77+
data-testid="load-more-comments"
78+
>
79+
{intl.formatMessage(messages.loadMoreResponses)}
80+
</Button>
81+
)}
82+
{isLoading && (
83+
<div className="mb-2 mt-3 d-flex justify-content-center">
84+
<Spinner animation="border" variant="primary" className="spinner-dimensions" />
85+
</div>
86+
)}
87+
{(isUserPrivilegedInPostingRestriction && (!!unEndorsedCommentsIds.length || !!endorsedCommentsIds.length)
88+
&& !isClosed) && (
89+
<div className="mx-4">
90+
{!addingResponse && (
91+
<Button
92+
variant="plain"
93+
block="true"
94+
className="card mb-4 px-0 border-0 py-10px mt-2 font-style font-weight-500
95+
line-height-24 font-size-14 text-primary-500"
96+
onClick={handleAddResponse}
97+
data-testid="add-response"
98+
>
99+
{intl.formatMessage(messages.addResponse)}
100+
</Button>
101+
)}
102+
<ResponseEditor
103+
addWrappingDiv
104+
addingResponse={addingResponse}
105+
handleCloseEditor={handleCloseResponseEditor}
106+
/>
107+
</div>
108+
)}
109+
</>
116110
)
117111
);
118112
};
119113

120114
CommentsView.propTypes = {
121-
endorsed: PropTypes.oneOf([
122-
EndorsementStatus.ENDORSED, EndorsementStatus.UNENDORSED, EndorsementStatus.DISCUSSION,
115+
threadType: PropTypes.oneOf([
116+
ThreadType.DISCUSSION, ThreadType.QUESTION,
123117
]).isRequired,
124118
};
125119

src/discussions/post-comments/comments/comment/Comment.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const Comment = ({
8282
}, []);
8383

8484
const handleCommentEndorse = useCallback(async () => {
85-
await dispatch(editComment(id, { endorsed: !endorsed }, ContentActions.ENDORSE));
85+
await dispatch(editComment(id, { endorsed: !endorsed }));
8686
await dispatch(fetchThread(threadId, courseId));
8787
}, [id, endorsed, threadId]);
8888

src/discussions/post-comments/comments/comment/Reply.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const Reply = ({ responseId }) => {
5454
}, []);
5555

5656
const handleReplyEndorse = useCallback(() => {
57-
dispatch(editComment(id, { endorsed: !endorsed }, ContentActions.ENDORSE));
57+
dispatch(editComment(id, { endorsed: !endorsed }));
5858
}, [endorsed, id]);
5959

6060
const handleAbusedFlag = useCallback(() => {

src/discussions/post-comments/data/__factories__/comments.factory.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Factory.define('comment')
66
.sequence('rendered_body', ['endorsed'], (idx, endorsed) => `Some contents for <b>${endorsed ? 'endorsed ' : 'unendorsed '}comment number ${idx}</b>.`)
77
.attr('thread_id', null, 'test-thread')
88
.option('endorsedBy', null, null)
9-
.attr('endorsed', ['endorsedBy'], (endorsedBy) => !!endorsedBy)
9+
.attr('endorsed', ['endorsed'], (endorsed) => endorsed)
1010
.attr('endorsed_by', ['endorsedBy'], (endorsedBy) => endorsedBy)
1111
.attr('endorsed_by_label', ['endorsedBy'], (endorsedBy) => (endorsedBy ? 'Staff' : null))
1212
.attr('endorsed_at', ['endorsedBy'], (endorsedBy) => (endorsedBy ? (new Date()).toISOString() : null))
@@ -38,7 +38,7 @@ Factory.define('commentsResult')
3838
.option('pageSize', null, 5)
3939
.option('threadId', null, 'test-thread')
4040
.option('parentId', null, null)
41-
.option('endorsed', null, null)
41+
.option('endorsed', false, false)
4242
.option('childCount', null, 0)
4343
.attr('pagination', ['threadId', 'count', 'page', 'pageSize'], (threadId, count, page, pageSize) => {
4444
const numPages = Math.ceil(count / pageSize);

0 commit comments

Comments
 (0)