@wordpress/editor 14.31.0 → 14.31.1-next.233ccab9b.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/build/components/collab-sidebar/add-comment.js +6 -2
  2. package/build/components/collab-sidebar/add-comment.js.map +1 -1
  3. package/build/components/collab-sidebar/comment-author-info.js +39 -11
  4. package/build/components/collab-sidebar/comment-author-info.js.map +1 -1
  5. package/build/components/collab-sidebar/comment-form.js +23 -19
  6. package/build/components/collab-sidebar/comment-form.js.map +1 -1
  7. package/build/components/collab-sidebar/comment-indicator-toolbar.js +7 -4
  8. package/build/components/collab-sidebar/comment-indicator-toolbar.js.map +1 -1
  9. package/build/components/collab-sidebar/comments.js +141 -86
  10. package/build/components/collab-sidebar/comments.js.map +1 -1
  11. package/build/components/collab-sidebar/hooks.js +96 -0
  12. package/build/components/collab-sidebar/hooks.js.map +1 -0
  13. package/build/components/collab-sidebar/index.js +44 -135
  14. package/build/components/collab-sidebar/index.js.map +1 -1
  15. package/build/components/collab-sidebar/utils.js +27 -27
  16. package/build/components/collab-sidebar/utils.js.map +1 -1
  17. package/build/components/header/index.js +5 -1
  18. package/build/components/header/index.js.map +1 -1
  19. package/build/components/post-type-support-check/index.js +12 -1
  20. package/build/components/post-type-support-check/index.js.map +1 -1
  21. package/build-module/components/collab-sidebar/add-comment.js +7 -3
  22. package/build-module/components/collab-sidebar/add-comment.js.map +1 -1
  23. package/build-module/components/collab-sidebar/comment-author-info.js +43 -15
  24. package/build-module/components/collab-sidebar/comment-author-info.js.map +1 -1
  25. package/build-module/components/collab-sidebar/comment-form.js +26 -22
  26. package/build-module/components/collab-sidebar/comment-form.js.map +1 -1
  27. package/build-module/components/collab-sidebar/comment-indicator-toolbar.js +8 -5
  28. package/build-module/components/collab-sidebar/comment-indicator-toolbar.js.map +1 -1
  29. package/build-module/components/collab-sidebar/comments.js +145 -91
  30. package/build-module/components/collab-sidebar/comments.js.map +1 -1
  31. package/build-module/components/collab-sidebar/hooks.js +89 -0
  32. package/build-module/components/collab-sidebar/hooks.js.map +1 -0
  33. package/build-module/components/collab-sidebar/index.js +47 -138
  34. package/build-module/components/collab-sidebar/index.js.map +1 -1
  35. package/build-module/components/collab-sidebar/utils.js +26 -26
  36. package/build-module/components/collab-sidebar/utils.js.map +1 -1
  37. package/build-module/components/header/index.js +5 -1
  38. package/build-module/components/header/index.js.map +1 -1
  39. package/build-module/components/post-type-support-check/index.js +11 -1
  40. package/build-module/components/post-type-support-check/index.js.map +1 -1
  41. package/build-style/style-rtl.css +66 -9
  42. package/build-style/style.css +66 -9
  43. package/build-types/components/collab-sidebar/add-comment.d.ts.map +1 -1
  44. package/build-types/components/collab-sidebar/comment-author-info.d.ts +3 -1
  45. package/build-types/components/collab-sidebar/comment-author-info.d.ts.map +1 -1
  46. package/build-types/components/collab-sidebar/comment-form.d.ts +3 -5
  47. package/build-types/components/collab-sidebar/comment-form.d.ts.map +1 -1
  48. package/build-types/components/collab-sidebar/comment-indicator-toolbar.d.ts.map +1 -1
  49. package/build-types/components/collab-sidebar/comments.d.ts +1 -7
  50. package/build-types/components/collab-sidebar/comments.d.ts.map +1 -1
  51. package/build-types/components/collab-sidebar/hooks.d.ts +6 -0
  52. package/build-types/components/collab-sidebar/hooks.d.ts.map +1 -0
  53. package/build-types/components/collab-sidebar/index.d.ts.map +1 -1
  54. package/build-types/components/collab-sidebar/utils.d.ts +4 -7
  55. package/build-types/components/collab-sidebar/utils.d.ts.map +1 -1
  56. package/build-types/components/header/index.d.ts.map +1 -1
  57. package/build-types/components/post-type-support-check/index.d.ts.map +1 -1
  58. package/package.json +37 -37
  59. package/src/components/collab-sidebar/add-comment.js +5 -2
  60. package/src/components/collab-sidebar/comment-author-info.js +53 -27
  61. package/src/components/collab-sidebar/comment-form.js +25 -24
  62. package/src/components/collab-sidebar/comment-indicator-toolbar.js +16 -5
  63. package/src/components/collab-sidebar/comments.js +203 -164
  64. package/src/components/collab-sidebar/hooks.js +94 -0
  65. package/src/components/collab-sidebar/index.js +63 -174
  66. package/src/components/collab-sidebar/style.scss +98 -83
  67. package/src/components/collab-sidebar/utils.js +20 -31
  68. package/src/components/header/index.js +6 -3
  69. package/src/components/post-publish-panel/test/__snapshots__/index.js.snap +2 -2
  70. package/src/components/post-type-support-check/index.js +18 -1
  71. package/src/components/post-type-support-check/test/index.js +37 -0
  72. package/tsconfig.tsbuildinfo +1 -1
@@ -8,11 +8,13 @@ import TextareaAutosize from 'react-autosize-textarea';
8
8
  */
9
9
  import { useState } from '@wordpress/element';
10
10
  import {
11
+ __experimentalVStack as VStack,
11
12
  __experimentalHStack as HStack,
13
+ __experimentalTruncate as Truncate,
12
14
  Button,
13
15
  VisuallyHidden,
14
16
  } from '@wordpress/components';
15
- import { _x, __ } from '@wordpress/i18n';
17
+ import { __ } from '@wordpress/i18n';
16
18
  import { useInstanceId } from '@wordpress/compose';
17
19
 
18
20
  /**
@@ -28,8 +30,7 @@ import { sanitizeCommentString } from './utils';
28
30
  * @param {Function} props.onCancel - The function to call when canceling the comment update.
29
31
  * @param {Object} props.thread - The comment thread object.
30
32
  * @param {string} props.submitButtonText - The text to display on the submit button.
31
- * @param {string?} props.placeholderText - The placeholder text for the comment input.
32
- * @param {number?} props.rows - The number of rows for the comment input.
33
+ * @param {string?} props.labelText - The label text for the comment input.
33
34
  * @return {React.ReactNode} The CommentForm component.
34
35
  */
35
36
  function CommentForm( {
@@ -37,19 +38,24 @@ function CommentForm( {
37
38
  onCancel,
38
39
  thread,
39
40
  submitButtonText,
40
- placeholderText,
41
- rows = 4,
41
+ labelText,
42
42
  } ) {
43
43
  const [ inputComment, setInputComment ] = useState(
44
44
  thread?.content?.raw ?? ''
45
45
  );
46
46
 
47
47
  const inputId = useInstanceId( CommentForm, 'comment-input' );
48
+ const isDisabled =
49
+ inputComment === thread?.content?.raw ||
50
+ ! sanitizeCommentString( inputComment ).length;
48
51
 
49
52
  return (
50
- <>
53
+ <VStack
54
+ className="editor-collab-sidebar-panel__comment-form"
55
+ spacing="4"
56
+ >
51
57
  <VisuallyHidden as="label" htmlFor={ inputId }>
52
- { __( 'Comment' ) }
58
+ { labelText ?? __( 'Comment' ) }
53
59
  </VisuallyHidden>
54
60
  <TextareaAutosize
55
61
  id={ inputId }
@@ -57,32 +63,27 @@ function CommentForm( {
57
63
  onChange={ ( comment ) =>
58
64
  setInputComment( comment.target.value )
59
65
  }
60
- rows={ rows }
66
+ rows={ 1 }
61
67
  maxRows={ 20 }
62
- placeholder={ placeholderText || '' }
63
- ></TextareaAutosize>
64
- <HStack spacing="3" justify="flex-start" wrap>
68
+ />
69
+ <HStack spacing="2" justify="flex-end" wrap>
70
+ <Button size="compact" variant="tertiary" onClick={ onCancel }>
71
+ <Truncate>{ __( 'Cancel' ) }</Truncate>
72
+ </Button>
65
73
  <Button
66
- __next40pxDefaultSize
74
+ size="compact"
67
75
  accessibleWhenDisabled
68
76
  variant="primary"
69
77
  onClick={ () => {
70
78
  onSubmit( inputComment );
71
79
  setInputComment( '' );
72
80
  } }
73
- disabled={
74
- 0 === sanitizeCommentString( inputComment ).length
75
- }
76
- text={ submitButtonText }
77
- />
78
- <Button
79
- __next40pxDefaultSize
80
- variant="tertiary"
81
- onClick={ onCancel }
82
- text={ _x( 'Cancel', 'Cancel comment button' ) }
83
- />
81
+ disabled={ isDisabled }
82
+ >
83
+ <Truncate>{ submitButtonText }</Truncate>
84
+ </Button>
84
85
  </HStack>
85
- </>
86
+ </VStack>
86
87
  );
87
88
  }
88
89
 
@@ -2,7 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { ToolbarButton } from '@wordpress/components';
5
- import { _x, __, sprintf } from '@wordpress/i18n';
5
+ import { _x, __, _n, sprintf } from '@wordpress/i18n';
6
6
  import { useMemo } from '@wordpress/element';
7
7
  import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
8
8
 
@@ -15,6 +15,7 @@ import clsx from 'clsx';
15
15
  * Internal dependencies
16
16
  */
17
17
  import { unlock } from '../../lock-unlock';
18
+ import { getAvatarBorderColor } from './utils';
18
19
 
19
20
  const { CommentIconToolbarSlotFill } = unlock( blockEditorPrivateApis );
20
21
 
@@ -40,6 +41,7 @@ const CommentAvatarIndicator = ( { onClick, thread, hasMoreComments } ) => {
40
41
  avatar:
41
42
  comment.author_avatar_urls?.[ '48' ] ||
42
43
  comment.author_avatar_urls?.[ '96' ],
44
+ id: comment.author,
43
45
  isOriginalCommenter: comment.id === thread.id,
44
46
  date: comment.date,
45
47
  } );
@@ -72,7 +74,7 @@ const CommentAvatarIndicator = ( { onClick, thread, hasMoreComments } ) => {
72
74
  threadHasMoreParticipants && overflowCount > 0
73
75
  ? __( '100+' )
74
76
  : sprintf(
75
- // translators: %s: Number of comments.
77
+ // translators: %s: Number of participants.
76
78
  __( '+%s' ),
77
79
  overflowCount
78
80
  );
@@ -81,8 +83,12 @@ const CommentAvatarIndicator = ( { onClick, thread, hasMoreComments } ) => {
81
83
  threadHasMoreParticipants && overflowCount > 0
82
84
  ? __( '100+ participants' )
83
85
  : sprintf(
84
- // translators: %s: Number of comments.
85
- __( '+%s more participants' ),
86
+ // translators: %s: Number of participants.
87
+ _n(
88
+ '+%s more participant',
89
+ '+%s more participants',
90
+ overflowCount
91
+ ),
86
92
  overflowCount
87
93
  );
88
94
 
@@ -103,7 +109,12 @@ const CommentAvatarIndicator = ( { onClick, thread, hasMoreComments } ) => {
103
109
  src={ participant.avatar }
104
110
  alt={ participant.name }
105
111
  className="comment-avatar"
106
- style={ { zIndex: maxAvatars - index } }
112
+ style={ {
113
+ zIndex: maxAvatars - index,
114
+ borderColor: getAvatarBorderColor(
115
+ participant.id
116
+ ),
117
+ } }
107
118
  />
108
119
  ) ) }
109
120
  { overflowCount > 0 && (
@@ -12,19 +12,27 @@ import {
12
12
  __experimentalVStack as VStack,
13
13
  __experimentalConfirmDialog as ConfirmDialog,
14
14
  Button,
15
- DropdownMenu,
15
+ privateApis as componentsPrivateApis,
16
16
  } from '@wordpress/components';
17
+
17
18
  import { published, moreVertical } from '@wordpress/icons';
18
- import { __, _x, _n, sprintf } from '@wordpress/i18n';
19
- import { useSelect } from '@wordpress/data';
20
- import { store as blockEditorStore } from '@wordpress/block-editor';
19
+ import { __, _x, sprintf, _n } from '@wordpress/i18n';
20
+ import { useSelect, useDispatch } from '@wordpress/data';
21
+ import {
22
+ store as blockEditorStore,
23
+ privateApis as blockEditorPrivateApis,
24
+ } from '@wordpress/block-editor';
21
25
 
22
26
  /**
23
27
  * Internal dependencies
24
28
  */
29
+ import { unlock } from '../../lock-unlock';
25
30
  import CommentAuthorInfo from './comment-author-info';
26
31
  import CommentForm from './comment-form';
27
32
 
33
+ const { useBlockElement } = unlock( blockEditorPrivateApis );
34
+ const { Menu } = unlock( componentsPrivateApis );
35
+
28
36
  /**
29
37
  * Renders the Comments component.
30
38
  *
@@ -33,9 +41,6 @@ import CommentForm from './comment-form';
33
41
  * @param {Function} props.onEditComment - The function to handle comment editing.
34
42
  * @param {Function} props.onAddReply - The function to add a reply to a comment.
35
43
  * @param {Function} props.onCommentDelete - The function to delete a comment.
36
- * @param {Function} props.onCommentResolve - The function to mark a comment as resolved.
37
- * @param {Function} props.onCommentReopen - The function to reopen a resolved comment.
38
- * @param {boolean} props.showCommentBoard - Whether to show the comment board.
39
44
  * @param {Function} props.setShowCommentBoard - The function to set the comment board visibility.
40
45
  * @return {React.ReactNode} The rendered Comments component.
41
46
  */
@@ -44,84 +49,49 @@ export function Comments( {
44
49
  onEditComment,
45
50
  onAddReply,
46
51
  onCommentDelete,
47
- onCommentResolve,
48
- onCommentReopen,
49
- showCommentBoard,
50
52
  setShowCommentBoard,
51
53
  } ) {
52
54
  const { blockCommentId } = useSelect( ( select ) => {
53
55
  const { getBlockAttributes, getSelectedBlockClientId } =
54
56
  select( blockEditorStore );
55
- const _clientId = getSelectedBlockClientId();
56
-
57
+ const clientId = getSelectedBlockClientId();
57
58
  return {
58
- blockCommentId: _clientId
59
- ? getBlockAttributes( _clientId )?.blockCommentId
59
+ blockCommentId: clientId
60
+ ? getBlockAttributes( clientId )?.blockCommentId
60
61
  : null,
61
62
  };
62
63
  }, [] );
64
+ const [ focusThread = blockCommentId, setFocusThread ] = useState();
63
65
 
64
- const [ focusThread, setFocusThread ] = useState(
65
- showCommentBoard && blockCommentId ? blockCommentId : null
66
- );
67
-
68
- const clearThreadFocus = () => {
69
- setFocusThread( null );
70
- setShowCommentBoard( false );
71
- };
66
+ const hasThreads = Array.isArray( threads ) && threads.length > 0;
67
+ if ( ! hasThreads ) {
68
+ return (
69
+ <VStack
70
+ alignment="left"
71
+ className="editor-collab-sidebar-panel__thread"
72
+ justify="flex-start"
73
+ spacing="2"
74
+ >
75
+ {
76
+ // translators: message displayed when there are no comments available
77
+ __( 'No comments available' )
78
+ }
79
+ </VStack>
80
+ );
81
+ }
72
82
 
73
- return (
74
- <>
75
- {
76
- // If there are no comments, show a message indicating no comments are available.
77
- ( ! Array.isArray( threads ) || threads.length === 0 ) && (
78
- <VStack
79
- alignment="left"
80
- className="editor-collab-sidebar-panel__thread"
81
- justify="flex-start"
82
- spacing="3"
83
- >
84
- {
85
- // translators: message displayed when there are no comments available
86
- __( 'No comments available' )
87
- }
88
- </VStack>
89
- )
90
- }
91
- { Array.isArray( threads ) &&
92
- threads.length > 0 &&
93
- threads.map( ( thread ) => (
94
- <VStack
95
- key={ thread.id }
96
- className={ clsx(
97
- 'editor-collab-sidebar-panel__thread',
98
- {
99
- 'editor-collab-sidebar-panel__active-thread':
100
- blockCommentId &&
101
- blockCommentId === thread.id,
102
- 'editor-collab-sidebar-panel__focus-thread':
103
- focusThread && focusThread === thread.id,
104
- }
105
- ) }
106
- id={ thread.id }
107
- spacing="3"
108
- onClick={ () => setFocusThread( thread.id ) }
109
- >
110
- <Thread
111
- thread={ thread }
112
- onAddReply={ onAddReply }
113
- onCommentDelete={ onCommentDelete }
114
- onCommentResolve={ onCommentResolve }
115
- onCommentReopen={ onCommentReopen }
116
- onEditComment={ onEditComment }
117
- isFocused={ focusThread === thread.id }
118
- clearThreadFocus={ clearThreadFocus }
119
- setFocusThread={ setFocusThread }
120
- />
121
- </VStack>
122
- ) ) }
123
- </>
124
- );
83
+ return threads.map( ( thread ) => (
84
+ <Thread
85
+ key={ thread.id }
86
+ thread={ thread }
87
+ onAddReply={ onAddReply }
88
+ onCommentDelete={ onCommentDelete }
89
+ onEditComment={ onEditComment }
90
+ isFocused={ focusThread === thread.id }
91
+ setFocusThread={ setFocusThread }
92
+ setShowCommentBoard={ setShowCommentBoard }
93
+ />
94
+ ) );
125
95
  }
126
96
 
127
97
  function Thread( {
@@ -129,64 +99,106 @@ function Thread( {
129
99
  onEditComment,
130
100
  onAddReply,
131
101
  onCommentDelete,
132
- onCommentResolve,
133
- onCommentReopen,
134
102
  isFocused,
135
- clearThreadFocus,
136
103
  setFocusThread,
104
+ setShowCommentBoard,
137
105
  } ) {
106
+ const { flashBlock } = useDispatch( blockEditorStore );
107
+ const relatedBlockElement = useBlockElement( thread.blockClientId );
108
+
109
+ const handleCommentSelect = ( { id, blockClientId } ) => {
110
+ setShowCommentBoard( false );
111
+ setFocusThread( id );
112
+ if ( blockClientId && relatedBlockElement ) {
113
+ relatedBlockElement.scrollIntoView( {
114
+ behavior: 'instant',
115
+ block: 'center',
116
+ } );
117
+ flashBlock( blockClientId );
118
+ }
119
+ };
120
+
121
+ const clearThreadFocus = () => {
122
+ setFocusThread( null );
123
+ setShowCommentBoard( false );
124
+ };
125
+
126
+ const replies = thread?.reply;
127
+ const lastReply = !! replies.length
128
+ ? replies[ replies.length - 1 ]
129
+ : undefined;
130
+ const restReplies = !! replies.length ? replies.slice( 0, -1 ) : [];
131
+
138
132
  return (
139
- <>
133
+ <VStack
134
+ className={ clsx( 'editor-collab-sidebar-panel__thread', {
135
+ 'editor-collab-sidebar-panel__focus-thread': isFocused,
136
+ } ) }
137
+ id={ thread.id }
138
+ spacing="2"
139
+ onClick={ () => handleCommentSelect( thread ) }
140
+ >
140
141
  <CommentBoard
141
142
  thread={ thread }
142
- onResolve={ onCommentResolve }
143
- onReopen={ onCommentReopen }
144
143
  onEdit={ onEditComment }
145
144
  onDelete={ onCommentDelete }
146
145
  status={ thread.status }
147
146
  />
148
- { 0 < thread?.reply?.length && (
149
- <>
150
- { ! isFocused && (
151
- <Button
152
- __next40pxDefaultSize
153
- variant="link"
154
- className="editor-collab-sidebar-panel__show-more-reply"
155
- onClick={ () => setFocusThread( thread.id ) }
156
- >
157
- { sprintf(
158
- // translators: %s: number of replies.
159
- _n(
160
- '%s more reply',
161
- '%s more replies',
162
- thread?.reply?.length
163
- ),
164
- thread?.reply?.length
165
- ) }
166
- </Button>
167
- ) }
168
-
169
- { isFocused &&
170
- thread.reply.map( ( reply ) => (
171
- <VStack
172
- key={ reply.id }
173
- className="editor-collab-sidebar-panel__child-thread"
174
- id={ reply.id }
175
- spacing="2"
176
- >
177
- { 'approved' !== thread.status && (
178
- <CommentBoard
179
- thread={ reply }
180
- onEdit={ onEditComment }
181
- onDelete={ onCommentDelete }
182
- />
183
- ) }
184
- { 'approved' === thread.status && (
185
- <CommentBoard thread={ reply } />
186
- ) }
187
- </VStack>
188
- ) ) }
189
- </>
147
+ { isFocused &&
148
+ replies.map( ( reply ) => (
149
+ <VStack
150
+ key={ reply.id }
151
+ className="editor-collab-sidebar-panel__child-thread"
152
+ id={ reply.id }
153
+ spacing="2"
154
+ >
155
+ <CommentBoard
156
+ thread={ reply }
157
+ onEdit={
158
+ 'approved' !== thread.status
159
+ ? onEditComment
160
+ : undefined
161
+ }
162
+ onDelete={
163
+ 'approved' !== thread.status
164
+ ? onCommentDelete
165
+ : undefined
166
+ }
167
+ />
168
+ </VStack>
169
+ ) ) }
170
+ { ! isFocused && restReplies.length > 0 && (
171
+ <HStack className="editor-collab-sidebar-panel__more-reply-separator">
172
+ <Button
173
+ size="compact"
174
+ variant="tertiary"
175
+ className="editor-collab-sidebar-panel__more-reply-button"
176
+ onClick={ () => setFocusThread( thread.id ) }
177
+ >
178
+ { sprintf(
179
+ // translators: %s: number of replies.
180
+ _n(
181
+ '%s more reply',
182
+ '%s more replies',
183
+ restReplies.length
184
+ ),
185
+ restReplies.length
186
+ ) }
187
+ </Button>
188
+ </HStack>
189
+ ) }
190
+ { ! isFocused && lastReply && (
191
+ <CommentBoard
192
+ thread={ lastReply }
193
+ onEdit={
194
+ 'approved' !== thread.status ? onEditComment : undefined
195
+ }
196
+ onDelete={
197
+ 'approved' !== thread.status
198
+ ? onCommentDelete
199
+ : undefined
200
+ }
201
+ />
190
202
  ) }
191
203
  { isFocused && (
192
204
  <VStack
@@ -196,57 +208,50 @@ function Thread( {
196
208
  <HStack alignment="left" spacing="3" justify="flex-start">
197
209
  <CommentAuthorInfo />
198
210
  </HStack>
199
- <VStack
200
- spacing="3"
201
- className="editor-collab-sidebar-panel__comment-field"
202
- >
211
+ <VStack spacing="2">
203
212
  <CommentForm
204
213
  onSubmit={ ( inputComment ) => {
205
214
  if ( 'approved' === thread.status ) {
206
- onCommentReopen( thread.id );
215
+ onEditComment( {
216
+ id: thread.id,
217
+ status: 'hold',
218
+ } );
207
219
  }
208
- onAddReply( inputComment, thread.id );
220
+ onAddReply( {
221
+ content: inputComment,
222
+ parent: thread.id,
223
+ } );
209
224
  } }
210
225
  onCancel={ ( event ) => {
211
226
  event.stopPropagation(); // Prevent the parent onClick from being triggered
212
227
  clearThreadFocus();
213
228
  } }
214
- placeholderText={
215
- 'approved' === thread.status &&
216
- __(
217
- 'Adding a comment will re-open this discussion….'
218
- )
219
- }
220
229
  submitButtonText={
221
230
  'approved' === thread.status
222
- ? _x(
223
- 'Reopen & Reply',
224
- 'Reopen comment and add reply'
225
- )
226
- : _x( 'Reply', 'Add reply comment' )
231
+ ? __( 'Reopen & Reply' )
232
+ : __( 'Reply' )
227
233
  }
228
234
  rows={ 'approved' === thread.status ? 2 : 4 }
235
+ labelText={ sprintf(
236
+ // translators: %1$s: comment identifier, %2$s: author name
237
+ __( 'Reply to Comment %1$s by %2$s' ),
238
+ thread.id,
239
+ thread?.author_name || 'Unknown'
240
+ ) }
229
241
  />
230
242
  </VStack>
231
243
  </VStack>
232
244
  ) }
233
- </>
245
+ </VStack>
234
246
  );
235
247
  }
236
248
 
237
- const CommentBoard = ( {
238
- thread,
239
- onResolve,
240
- onReopen,
241
- onEdit,
242
- onDelete,
243
- status,
244
- } ) => {
249
+ const CommentBoard = ( { thread, onEdit, onDelete, status } ) => {
245
250
  const [ actionState, setActionState ] = useState( false );
246
251
  const [ showConfirmDialog, setShowConfirmDialog ] = useState( false );
247
252
 
248
253
  const handleConfirmDelete = () => {
249
- onDelete( thread.id );
254
+ onDelete( thread );
250
255
  setActionState( false );
251
256
  setShowConfirmDialog( false );
252
257
  };
@@ -259,27 +264,31 @@ const CommentBoard = ( {
259
264
  const actions = [
260
265
  onEdit &&
261
266
  status !== 'approved' && {
267
+ id: 'edit',
262
268
  title: _x( 'Edit', 'Edit comment' ),
263
269
  onClick: () => {
264
270
  setActionState( 'edit' );
265
271
  },
266
272
  },
267
273
  onDelete && {
274
+ id: 'delete',
268
275
  title: _x( 'Delete', 'Delete comment' ),
269
276
  onClick: () => {
270
277
  setActionState( 'delete' );
271
278
  setShowConfirmDialog( true );
272
279
  },
273
280
  },
274
- onReopen &&
281
+ onEdit &&
275
282
  status === 'approved' && {
283
+ id: 'reopen',
276
284
  title: _x( 'Reopen', 'Reopen comment' ),
277
285
  onClick: () => {
278
- onReopen( thread.id );
286
+ onEdit( { id: thread.id, status: 'hold' } );
279
287
  },
280
288
  },
281
289
  ];
282
290
 
291
+ const canResolve = thread?.parent === 0;
283
292
  const moreActions = actions.filter( ( item ) => item?.onClick );
284
293
 
285
294
  return (
@@ -289,10 +298,11 @@ const CommentBoard = ( {
289
298
  avatar={ thread?.author_avatar_urls?.[ 48 ] }
290
299
  name={ thread?.author_name }
291
300
  date={ thread?.date }
301
+ userId={ thread?.author }
292
302
  />
293
303
  <span className="editor-collab-sidebar-panel__comment-status">
294
304
  <HStack alignment="right" justify="flex-end" spacing="0">
295
- { 0 === thread?.parent && onResolve && (
305
+ { canResolve && (
296
306
  <Button
297
307
  label={ _x(
298
308
  'Resolve',
@@ -303,33 +313,62 @@ const CommentBoard = ( {
303
313
  disabled={ status === 'approved' }
304
314
  accessibleWhenDisabled={ status === 'approved' }
305
315
  onClick={ () => {
306
- onResolve( thread.id );
316
+ onEdit( {
317
+ id: thread.id,
318
+ status: 'approved',
319
+ } );
307
320
  } }
308
321
  />
309
322
  ) }
310
- { 0 < moreActions.length && (
311
- <DropdownMenu
312
- icon={ moreVertical }
313
- label={ _x(
314
- 'Select an action',
315
- 'Select comment action'
316
- ) }
317
- className="editor-collab-sidebar-panel__comment-dropdown-menu"
318
- controls={ moreActions }
323
+ <Menu placement="bottom-end">
324
+ <Menu.TriggerButton
325
+ render={
326
+ <Button
327
+ size="small"
328
+ icon={ moreVertical }
329
+ label={ __( 'Actions' ) }
330
+ disabled={ ! moreActions.length }
331
+ accessibleWhenDisabled
332
+ />
333
+ }
319
334
  />
320
- ) }
335
+ <Menu.Popover>
336
+ { moreActions.map( ( action ) => (
337
+ <Menu.Item
338
+ key={ action.id }
339
+ onClick={ ( event ) => {
340
+ event.stopPropagation();
341
+ action.onClick();
342
+ } }
343
+ >
344
+ <Menu.ItemLabel>
345
+ { action.title }
346
+ </Menu.ItemLabel>
347
+ </Menu.Item>
348
+ ) ) }
349
+ </Menu.Popover>
350
+ </Menu>
321
351
  </HStack>
322
352
  </span>
323
353
  </HStack>
324
354
  { 'edit' === actionState ? (
325
355
  <CommentForm
326
356
  onSubmit={ ( value ) => {
327
- onEdit( thread.id, value );
357
+ onEdit( {
358
+ id: thread.id,
359
+ content: value,
360
+ } );
328
361
  setActionState( false );
329
362
  } }
330
363
  onCancel={ () => handleCancel() }
331
364
  thread={ thread }
332
365
  submitButtonText={ _x( 'Update', 'verb' ) }
366
+ labelText={ sprintf(
367
+ // translators: %1$s: comment identifier, %2$s: author name.
368
+ __( 'Edit Comment %1$s by %2$s' ),
369
+ thread.id,
370
+ thread?.author_name || 'Unknown'
371
+ ) }
333
372
  />
334
373
  ) : (
335
374
  <RawHTML className="editor-collab-sidebar-panel__user-comment">