Skip to content

Commit 044a336

Browse files
authored
feat(threaded-replies): Implement Create Replies and Replies Toggle (#3304)
* feat(threaded-replies): Implement Create Replies * feat(threaded-replies): Implement RepliesToggle * feat(threaded-replies): fix linting errors * feat(threaded-replies): fix reply pending state * feat(threaded-replies): fix no reply spacing * feat(threaded-replies): destructure item, sort props * feat(threaded-replies): fix lint errors * feat(threaded-replies): Add conditionals for toggle and create replies
1 parent 0d7ef97 commit 044a336

File tree

8 files changed

+337
-58
lines changed

8 files changed

+337
-58
lines changed

src/elements/content-sidebar/activity-feed/activity-feed/ActiveState.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,17 @@ const ActiveState = ({
183183
mentionSelectorContacts={mentionSelectorContacts}
184184
onDelete={onCommentDelete}
185185
onEdit={onCommentEdit}
186+
onReplyCreate={reply => onReplyCreate(item.id, FEED_ITEM_TYPE_COMMENT, reply)}
186187
onSelect={onCommentSelectHandler(item.id)}
188+
onShowReplies={() => onShowReplies(item.id, FEED_ITEM_TYPE_COMMENT)}
189+
onHideReplies={shownReplies => onHideReplies(item.id, shownReplies)}
187190
permissions={{
188191
can_delete: getProp(item.permissions, 'can_delete', false),
189192
can_edit: getProp(item.permissions, 'can_edit', false),
190193
can_reply: getProp(item.permissions, 'can_reply', false),
191194
can_resolve: getProp(item.permissions, 'can_resolve', false),
192195
}}
196+
repliesTotalCount={item.total_reply_count}
193197
translations={translations}
194198
/>
195199
) : (

src/elements/content-sidebar/activity-feed/comment/BaseComment.js

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import ActivityTimestamp from '../common/activity-timestamp';
1111
import Avatar from '../Avatar';
1212
import Checkmark16 from '../../../../icon/line/Checkmark16';
1313
import CommentForm from '../comment-form';
14+
import CreateReply from './CreateReply';
1415
import DeleteConfirmation from '../common/delete-confirmation';
1516
import LoadingIndicator from '../../../../components/loading-indicator';
1617
import Media from '../../../../components/media';
1718
import messages from './messages';
1819
import Pencil16 from '../../../../icon/line/Pencil16';
20+
import RepliesToggle from './RepliesToggle';
1921
import Trash16 from '../../../../icon/line/Trash16';
2022
import UserLink from '../common/user-link';
2123
import X16 from '../../../../icon/fill/X16';
@@ -61,10 +63,13 @@ type BaseCommentProps = {
6163
onSuccess: ?Function,
6264
onError: ?Function,
6365
) => void,
64-
onReplySelect?: (isSelected: boolean) => void,
66+
onHideReplies?: (shownReplies: CommentType[]) => void,
67+
onReplyCreate?: (reply: string) => void,
6568
onSelect: (isSelected: boolean) => void,
69+
onShowReplies?: () => void,
6670
permissions: BoxCommentPermission,
6771
replies?: CommentType[],
72+
repliesTotalCount?: number,
6873
status?: FeedItemStatus,
6974
tagged_message: string,
7075
translatedTaggedMessage?: string,
@@ -78,7 +83,7 @@ const BaseComment = (props: BaseCommentProps) => {
7883
created_at,
7984
permissions = {},
8085
id,
81-
isPending,
86+
isPending = false,
8287
isRepliesLoading = false,
8388
error,
8489
tagged_message = '',
@@ -95,66 +100,61 @@ const BaseComment = (props: BaseCommentProps) => {
95100
onDelete,
96101
onEdit,
97102
onSelect,
98-
onReplySelect = noop,
103+
onReplyCreate,
104+
onShowReplies,
105+
onHideReplies,
99106
replies = [],
107+
repliesTotalCount = 0,
100108
status,
101109
} = props;
102110

103111
const [isConfirmingDelete, setIsConfirmingDelete] = React.useState<boolean>(false);
104112
const [isEditing, setIsEditing] = React.useState<boolean>(false);
105113
const [isInputOpen, setIsInputOpen] = React.useState<boolean>(false);
106114

107-
const selectComment = (isSelected: boolean = true): void => {
108-
onSelect(isSelected);
109-
};
110-
111115
const handleDeleteConfirm = (): void => {
112116
onDelete({ id, permissions });
113-
selectComment(false);
117+
onSelect(false);
114118
};
115119

116120
const handleDeleteCancel = (): void => {
117121
setIsConfirmingDelete(false);
118-
selectComment(false);
122+
onSelect(false);
119123
};
120124

121125
const handleDeleteClick = (): void => {
122126
setIsConfirmingDelete(true);
123-
selectComment();
127+
onSelect(true);
124128
};
125129

126130
const handleEditClick = (): void => {
127131
setIsEditing(true);
128132
setIsInputOpen(true);
129-
selectComment();
133+
onSelect(true);
130134
};
131135

132136
const handleMenuClose = (): void => {
133137
if (isConfirmingDelete || isEditing || isInputOpen) {
134138
return;
135139
}
136-
selectComment(false);
137-
};
138-
139-
const handleMenuOpen = (): void => {
140-
selectComment();
140+
onSelect(false);
141141
};
142142

143143
const commentFormFocusHandler = (): void => {
144144
setIsInputOpen(true);
145-
selectComment();
145+
onSelect(true);
146146
};
147147

148148
const commentFormCancelHandler = (): void => {
149149
setIsInputOpen(false);
150150
setIsEditing(false);
151-
selectComment(false);
151+
onSelect(false);
152152
};
153153

154154
const commentFormSubmitHandler = (): void => {
155155
setIsInputOpen(false);
156156
setIsEditing(false);
157-
selectComment(false);
157+
onSelect(false);
158158
};
159159

160160
const handleMessageUpdate = ({
@@ -214,7 +214,7 @@ const BaseComment = (props: BaseCommentProps) => {
214214
isDisabled={isConfirmingDelete}
215215
data-testid="comment-actions-menu"
216216
dropdownProps={{
217-
onMenuOpen: handleMenuOpen,
217+
onMenuOpen: () => onSelect(true),
218218
onMenuClose: handleMenuClose,
219219
}}
220220
menuProps={{
@@ -323,9 +323,14 @@ const BaseComment = (props: BaseCommentProps) => {
323323
{hasReplies && (
324324
<Replies
325325
{...commentProps}
326+
isParentPending={isPending}
326327
isRepliesLoading={isRepliesLoading}
327-
onReplySelect={onReplySelect}
328+
onHideReplies={onHideReplies}
329+
onReplyCreate={onReplyCreate}
330+
onReplySelect={onSelect}
331+
onShowReplies={onShowReplies}
328332
replies={replies}
333+
repliesTotalCount={repliesTotalCount}
329334
/>
330335
)}
331336
</div>
@@ -338,10 +343,15 @@ type RepliesProps = {
338343
getAvatarUrl: GetAvatarUrlCallback,
339344
getMentionWithQuery?: (searchStr: string) => void,
340345
getUserProfileUrl?: GetProfileUrlCallback,
346+
isParentPending?: boolean,
341347
isRepliesLoading?: boolean,
342348
mentionSelectorContacts?: SelectorItems<>,
349+
onHideReplies?: (shownReplies: CommentType[]) => void,
350+
onReplyCreate?: (reply: string) => void,
343351
onReplySelect?: (isSelected: boolean) => void,
352+
onShowReplies?: () => void,
344353
replies: CommentType[],
354+
repliesTotalCount?: number,
345355
translations?: Translations,
346356
};
347357

@@ -350,12 +360,18 @@ const Replies = ({
350360
getAvatarUrl,
351361
getMentionWithQuery,
352362
getUserProfileUrl,
363+
isParentPending = false,
353364
isRepliesLoading = false,
354365
mentionSelectorContacts,
366+
onReplyCreate,
355367
onReplySelect = noop,
368+
onShowReplies,
369+
onHideReplies,
356370
replies,
371+
repliesTotalCount = 0,
357372
translations,
358373
}: RepliesProps) => {
374+
const [showReplyForm, setShowReplyForm] = React.useState(false);
359375
const getReplyPermissions = (reply: CommentType): BoxCommentPermission => {
360376
const { permissions: { can_delete = false, can_edit = false, can_resolve = false } = {} } = reply;
361377
return {
@@ -365,8 +381,31 @@ const Replies = ({
365381
};
366382
};
367383

384+
const handleNewReplyButton = () => {
385+
setShowReplyForm(true);
386+
onReplySelect(true);
387+
};
388+
389+
const handleCancelNewReply = () => {
390+
setShowReplyForm(false);
391+
onReplySelect(false);
392+
};
393+
394+
const handleSubmitNewReply = (reply: string, replyCreate: (reply: string) => void) => {
395+
setShowReplyForm(false);
396+
replyCreate(reply);
397+
};
398+
368399
return (
369400
<div className="bcs-Replies">
401+
{!!onShowReplies && !!onHideReplies && (
402+
<RepliesToggle
403+
onHideReplies={index => onHideReplies([replies[index]])}
404+
onShowReplies={onShowReplies}
405+
repliesShownCount={replies.length}
406+
repliesTotalCount={repliesTotalCount}
407+
/>
408+
)}
370409
<div className="bcs-Replies-content">
371410
{isRepliesLoading && (
372411
<div className="bcs-Replies-loading" data-testid="replies-loading">
@@ -385,6 +424,7 @@ const Replies = ({
385424
getAvatarUrl={getAvatarUrl}
386425
getMentionWithQuery={getMentionWithQuery}
387426
getUserProfileUrl={getUserProfileUrl}
427+
isPending={isParentPending || reply.isPending}
388428
mentionSelectorContacts={mentionSelectorContacts}
389429
onSelect={onReplySelect}
390430
onDelete={noop}
@@ -397,6 +437,18 @@ const Replies = ({
397437
})}
398438
</ol>
399439
</div>
440+
{!!onReplyCreate && (
441+
<CreateReply
442+
getMentionWithQuery={getMentionWithQuery}
443+
isDisabled={isParentPending}
444+
mentionSelectorContacts={mentionSelectorContacts}
445+
onCancel={handleCancelNewReply}
446+
onClick={handleNewReplyButton}
447+
onFocus={() => onReplySelect(true)}
448+
onSubmit={reply => handleSubmitNewReply(reply, onReplyCreate)}
449+
showReplyForm={showReplyForm}
450+
/>
451+
)}
400452
</div>
401453
);
402454
};

src/elements/content-sidebar/activity-feed/comment/BaseComment.scss

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ $repliesPaddingLeft: 40px;
44

55
.bcs-BaseComment {
66
.bcs-Comment-timestamp {
7-
margin-bottom: 16px;
7+
margin-bottom: $bdl-grid-unit * 4;
88
}
99

1010
.bcs-ActivityTimestamp {
11-
color: $bdl-gray;
11+
color: $bdl-gray-65;
1212
}
1313

1414
.bcs-CommentForm-input {
@@ -20,7 +20,11 @@ $repliesPaddingLeft: 40px;
2020
}
2121

2222
.bcs-Replies {
23-
margin: $bdl-grid-unit * 4 0;
23+
margin: $bdl-grid-unit * 3 0;
2424
padding-left: $repliesPaddingLeft;
2525
}
26+
27+
.bcs-Comment-media {
28+
margin-bottom: $bdl-grid-unit * 3;
29+
}
2630
}
Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,41 @@
11
@import '../../../common/variables';
22

3-
.bcs-Replies-content {
4-
position: relative;
5-
padding-left: $bdl-grid-unit * 5;
3+
.bcs-Replies {
4+
.bcs-Replies-content {
5+
position: relative;
6+
margin-bottom: $bdl-grid-unit * 6;
7+
padding-left: $bdl-grid-unit * 5;
68

7-
.bcs-Replies-list {
8-
// Override default ol margins
9-
margin: 0;
9+
.bcs-Replies-list {
10+
// Override default ol margins
11+
margin: 0;
1012

11-
ol {
12-
list-style: none;
13+
ol {
14+
list-style: none;
15+
}
16+
}
17+
18+
li:not(:last-child) {
19+
margin-bottom: $bdl-grid-unit * 7;
1320
}
1421
}
1522

16-
li:not(:last-child) {
17-
margin-bottom: $bdl-grid-unit * 3;
23+
.bcs-Replies-content::before {
24+
position: absolute;
25+
top: 0;
26+
bottom: 0;
27+
left: 0;
28+
width: $bdl-grid-unit;
29+
background: $bdl-gray-10;
30+
border-radius: $bdl-border-radius-size;
31+
content: '';
1832
}
19-
}
2033

21-
.bcs-Replies-content::before {
22-
position: absolute;
23-
top: 0;
24-
bottom: 0;
25-
left: 0;
26-
width: $bdl-grid-unit;
27-
background: $bdl-gray-10;
28-
border-radius: $bdl-border-radius-size;
29-
content: '';
30-
}
34+
.bcs-Replies-loading {
35+
padding-bottom: $bdl-grid-unit * 5;
36+
}
3137

32-
.bcs-Replies-loading {
33-
padding-bottom: $bdl-grid-unit * 5;
38+
.bcs-RepliesToggle {
39+
margin-bottom: $bdl-grid-unit * 4;
40+
}
3441
}

src/elements/content-sidebar/activity-feed/comment/RepliesToggle.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const RepliesToggle = ({ onShowReplies, onHideReplies, repliesShownCount, replie
3232
};
3333

3434
return (
35-
<PlainButton className="bcs-RepliesToggle" onClick={handleToggle} type="button">
35+
<PlainButton className="bcs-RepliesToggle" onClick={handleToggle} type="button" data-testid="replies-toggle">
3636
<FormattedMessage values={{ repliesToLoadCount }} {...toggleMessage} />
3737
</PlainButton>
3838
);
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@import '../../../common/variables';
22

3-
.bcs-RepliesToggle {
4-
margin-bottom: $bdl-grid-unit * 4;
3+
.btn-plain.bcs-RepliesToggle {
54
color: $bdl-box-blue;
65
font-weight: bold;
76
}

0 commit comments

Comments
 (0)