diff --git a/packages/editor/src/components/collab-sidebar/add-comment.js b/packages/editor/src/components/collab-sidebar/add-comment.js
index 2330017ac5db53..f94891ada07109 100644
--- a/packages/editor/src/components/collab-sidebar/add-comment.js
+++ b/packages/editor/src/components/collab-sidebar/add-comment.js
@@ -45,7 +45,7 @@ export function AddComment( {
return (
diff --git a/packages/editor/src/components/collab-sidebar/comment-button.js b/packages/editor/src/components/collab-sidebar/comment-button.js
index 373ee0becd92df..37300be147e1de 100644
--- a/packages/editor/src/components/collab-sidebar/comment-button.js
+++ b/packages/editor/src/components/collab-sidebar/comment-button.js
@@ -17,13 +17,18 @@ const { CommentIconSlotFill } = unlock( blockEditorPrivateApis );
const AddCommentButton = ( { onClick } ) => {
return (
-
+ { ( { onClose } ) => (
+
+ ) }
);
};
diff --git a/packages/editor/src/components/collab-sidebar/comment-form.js b/packages/editor/src/components/collab-sidebar/comment-form.js
index 052fd3cdd26568..bd139a682f5cb7 100644
--- a/packages/editor/src/components/collab-sidebar/comment-form.js
+++ b/packages/editor/src/components/collab-sidebar/comment-form.js
@@ -42,7 +42,10 @@ function CommentForm( { onSubmit, onCancel, thread, submitButtonText } ) {
__next40pxDefaultSize
accessibleWhenDisabled
variant="primary"
- onClick={ () => onSubmit( inputComment ) }
+ onClick={ () => {
+ onSubmit( inputComment );
+ setInputComment( '' );
+ } }
disabled={
0 === sanitizeCommentString( inputComment ).length
}
diff --git a/packages/editor/src/components/collab-sidebar/comments.js b/packages/editor/src/components/collab-sidebar/comments.js
index 7a03068787c81e..d492d233956cb7 100644
--- a/packages/editor/src/components/collab-sidebar/comments.js
+++ b/packages/editor/src/components/collab-sidebar/comments.js
@@ -16,7 +16,7 @@ import {
Tooltip,
} from '@wordpress/components';
import { Icon, check, published, moreVertical } from '@wordpress/icons';
-import { __, _x } from '@wordpress/i18n';
+import { __, _x, sprintf } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
@@ -29,12 +29,14 @@ import CommentForm from './comment-form';
/**
* Renders the Comments component.
*
- * @param {Object} props - The component props.
- * @param {Array} props.threads - The array of comment threads.
- * @param {Function} props.onEditComment - The function to handle comment editing.
- * @param {Function} props.onAddReply - The function to add a reply to a comment.
- * @param {Function} props.onCommentDelete - The function to delete a comment.
- * @param {Function} props.onCommentResolve - The function to mark a comment as resolved.
+ * @param {Object} props - The component props.
+ * @param {Array} props.threads - The array of comment threads.
+ * @param {Function} props.onEditComment - The function to handle comment editing.
+ * @param {Function} props.onAddReply - The function to add a reply to a comment.
+ * @param {Function} props.onCommentDelete - The function to delete a comment.
+ * @param {Function} props.onCommentResolve - The function to mark a comment as resolved.
+ * @param {boolean} props.showCommentBoard - Whether to show the comment board.
+ * @param {Function} props.setShowCommentBoard - The function to set the comment board visibility.
* @return {React.ReactNode} The rendered Comments component.
*/
export function Comments( {
@@ -43,129 +45,28 @@ export function Comments( {
onAddReply,
onCommentDelete,
onCommentResolve,
+ showCommentBoard,
+ setShowCommentBoard,
} ) {
- const [ actionState, setActionState ] = useState( false );
- const [ isConfirmDialogOpen, setIsConfirmDialogOpen ] = useState( false );
-
- const handleConfirmDelete = () => {
- onCommentDelete( actionState.id );
- setActionState( false );
- setIsConfirmDialogOpen( false );
- };
-
- const handleConfirmResolve = () => {
- onCommentResolve( actionState.id );
- setActionState( false );
- setIsConfirmDialogOpen( false );
- };
+ const { blockCommentId } = useSelect( ( select ) => {
+ const { getBlockAttributes, getSelectedBlockClientId } =
+ select( blockEditorStore );
+ const _clientId = getSelectedBlockClientId();
- const handleCancelDelete = () => {
- setActionState( false );
- setIsConfirmDialogOpen( false );
- };
-
- const blockCommentId = useSelect( ( select ) => {
- const clientID = select( blockEditorStore ).getSelectedBlockClientId();
- return (
- select( blockEditorStore ).getBlock( clientID )?.attributes
- ?.blockCommentId ?? false
- );
+ return {
+ blockCommentId: _clientId
+ ? getBlockAttributes( _clientId )?.blockCommentId
+ : null,
+ };
}, [] );
- const CommentBoard = ( { thread, parentThread } ) => {
- return (
- <>
- {
- setActionState( {
- action: 'resolve',
- id: parentThread?.id ?? thread.id,
- } );
- setIsConfirmDialogOpen( true );
- } }
- onEdit={ () =>
- setActionState( { action: 'edit', id: thread.id } )
- }
- onDelete={ () => {
- setActionState( { action: 'delete', id: thread.id } );
- setIsConfirmDialogOpen( true );
- } }
- onReply={
- ! parentThread
- ? () =>
- setActionState( {
- action: 'reply',
- id: thread.id,
- } )
- : undefined
- }
- status={ parentThread?.status ?? thread.status }
- />
-
-
- { 'edit' === actionState?.action &&
- thread.id === actionState?.id && (
- {
- onEditComment( thread.id, value );
- setActionState( false );
- } }
- onCancel={ () => setActionState( false ) }
- thread={ thread }
- submitButtonText={ _x( 'Update', 'verb' ) }
- />
- ) }
- { ( ! actionState ||
- 'edit' !== actionState?.action ) && (
- { thread?.content?.raw }
- ) }
-
-
- { 'resolve' === actionState?.action &&
- thread.id === actionState?.id && (
-
- {
- // translators: message displayed when confirming an action
- __(
- 'Are you sure you want to mark this comment as resolved?'
- )
- }
-
- ) }
- { 'delete' === actionState?.action &&
- thread.id === actionState?.id && (
-
- {
- // translators: message displayed when confirming an action
- __(
- 'Are you sure you want to delete this comment?'
- )
- }
-
- ) }
- >
- );
+ const [ focusThread, setFocusThread ] = useState(
+ showCommentBoard && blockCommentId ? blockCommentId : null
+ );
+
+ const clearThreadFocus = () => {
+ setFocusThread( null );
+ setShowCommentBoard( false );
};
return (
@@ -186,7 +87,6 @@ export function Comments( {
)
}
-
{ Array.isArray( threads ) &&
threads.length > 0 &&
threads.map( ( thread ) => (
@@ -198,145 +98,262 @@ export function Comments( {
'editor-collab-sidebar-panel__active-thread':
blockCommentId &&
blockCommentId === thread.id,
+ 'editor-collab-sidebar-panel__focus-thread':
+ focusThread && focusThread === thread.id,
}
) }
id={ thread.id }
spacing="3"
+ onClick={ () => setFocusThread( thread.id ) }
>
-
- { 0 < thread?.reply?.length &&
- thread.reply.map( ( reply ) => (
-
+
+
+ ) ) }
+ >
+ );
+}
+
+function Thread( {
+ thread,
+ onEditComment,
+ onAddReply,
+ onCommentDelete,
+ onCommentResolve,
+ isFocused,
+ clearThreadFocus,
+} ) {
+ return (
+ <>
+
+ { 0 < thread?.reply?.length && (
+ <>
+ { ! isFocused && (
+
+ { sprintf(
+ // translators: 1: number of replies.
+ _x(
+ '%s more replies..',
+ 'Show replies button'
+ ),
+ thread?.reply?.length
+ ) }
+
+ ) }
+
+ { isFocused &&
+ thread.reply.map( ( reply ) => (
+
+ { 'approved' !== thread.status && (
-
- ) ) }
- { 'reply' === actionState?.action &&
- thread.id === actionState?.id && (
-
-
-
-
-
- {
- onAddReply(
- inputComment,
- thread.id
- );
- setActionState( false );
- } }
- onCancel={ () =>
- setActionState( false )
- }
- submitButtonText={ _x(
- 'Reply',
- 'Add reply comment'
- ) }
- />
-
-
+ ) }
+ { 'approved' === thread.status && (
+
+ ) }
+
+ ) ) }
+ >
+ ) }
+ { 'approved' !== thread.status && isFocused && (
+
+
+
+
+
+ {
+ onAddReply( inputComment, thread.id );
+ } }
+ onCancel={ ( event ) => {
+ event.stopPropagation(); // Prevent the parent onClick from being triggered
+ clearThreadFocus();
+ } }
+ submitButtonText={ _x(
+ 'Reply',
+ 'Add reply comment'
) }
+ />
- ) ) }
+
+ ) }
>
);
}
-/**
- * Renders the header of a comment in the collaboration sidebar.
- *
- * @param {Object} props - The component props.
- * @param {Object} props.thread - The comment thread object.
- * @param {Function} props.onResolve - The function to resolve the comment.
- * @param {Function} props.onEdit - The function to edit the comment.
- * @param {Function} props.onDelete - The function to delete the comment.
- * @param {Function} props.onReply - The function to reply to the comment.
- * @param {string} props.status - The status of the comment.
- * @return {React.ReactNode} The rendered comment header.
- */
-function CommentHeader( {
- thread,
- onResolve,
- onEdit,
- onDelete,
- onReply,
- status,
-} ) {
+const CommentBoard = ( { thread, onResolve, onEdit, onDelete, status } ) => {
+ const [ actionState, setActionState ] = useState( false );
+ const [ showConfirmDialog, setShowConfirmDialog ] = useState( false );
+
+ const handleConfirmDelete = () => {
+ onDelete( thread.id );
+ setActionState( false );
+ setShowConfirmDialog( false );
+ };
+
+ const handleConfirmResolve = () => {
+ onResolve( thread.id );
+ setActionState( false );
+ setShowConfirmDialog( false );
+ };
+
+ const handleCancel = () => {
+ setActionState( false );
+ setShowConfirmDialog( false );
+ };
+
const actions = [
- {
+ onEdit && {
title: _x( 'Edit', 'Edit comment' ),
- onClick: onEdit,
+ onClick: () => {
+ setActionState( 'edit' );
+ },
},
- {
+ onDelete && {
title: _x( 'Delete', 'Delete comment' ),
- onClick: onDelete,
- },
- {
- title: _x( 'Reply', 'Reply on a comment' ),
- onClick: onReply,
+ onClick: () => {
+ setActionState( 'delete' );
+ setShowConfirmDialog( true );
+ },
},
];
- const moreActions = actions.filter( ( item ) => item.onClick );
+ const moreActions = actions.filter( ( item ) => item?.onClick );
return (
-
-
-
- { status !== 'approved' && (
-
- { 0 === thread?.parent && onResolve && (
-
- ) }
-
+
+
+
+ { status !== 'approved' && (
+
+ { 0 === thread?.parent && onResolve && (
+
+ ) }
+ { status === 'approved' && (
+ // translators: tooltip for resolved comment
+
+
+
+ ) }
+
+
+
+
+ { 'edit' === actionState && (
+ {
+ onEdit( thread.id, value );
+ setActionState( false );
+ } }
+ onCancel={ () => handleCancel() }
+ thread={ thread }
+ submitButtonText={ _x( 'Update', 'verb' ) }
/>
-
- ) }
- { status === 'approved' && (
- // translators: tooltip for resolved comment
-
-
-
- ) }
-
-
+ ) }
+ { 'edit' !== actionState && (
+ { thread?.content?.raw }
+ ) }
+
+
+ { 'resolve' === actionState && (
+
+ {
+ // translators: message displayed when confirming an action
+ __(
+ 'Are you sure you want to mark this comment as resolved?'
+ )
+ }
+
+ ) }
+ { 'delete' === actionState && (
+
+ {
+ // translators: message displayed when confirming an action
+ __( 'Are you sure you want to delete this comment?' )
+ }
+
+ ) }
+ >
);
-}
+};
diff --git a/packages/editor/src/components/collab-sidebar/index.js b/packages/editor/src/components/collab-sidebar/index.js
index aa1b9ac6c45609..c53ee9e7e919c6 100644
--- a/packages/editor/src/components/collab-sidebar/index.js
+++ b/packages/editor/src/components/collab-sidebar/index.js
@@ -203,11 +203,14 @@ function CollabSidebarContent( {
setShowCommentBoard={ setShowCommentBoard }
/>
);
@@ -293,17 +296,22 @@ export default function CollabSidebar() {
return { resultComments: [], sortedThreads: [] };
}
+ const updatedResult = result.map( ( item ) => ( {
+ ...item,
+ reply: [ ...item.reply ].reverse(),
+ } ) );
+
const blockCommentIds = getCommentIdsFromBlocks( blocks );
const threadIdMap = new Map(
- result.map( ( thread ) => [ thread.id, thread ] )
+ updatedResult.map( ( thread ) => [ thread.id, thread ] )
);
const sortedComments = blockCommentIds
.map( ( id ) => threadIdMap.get( id ) )
.filter( ( thread ) => thread !== undefined );
- return { resultComments: result, sortedThreads: sortedComments };
+ return { resultComments: updatedResult, sortedThreads: sortedComments };
}, [ threads, blocks ] );
// Get the global styles to set the background color of the sidebar.
diff --git a/packages/editor/src/components/collab-sidebar/style.scss b/packages/editor/src/components/collab-sidebar/style.scss
index 2c1426f1dd75df..29fcc80779f681 100644
--- a/packages/editor/src/components/collab-sidebar/style.scss
+++ b/packages/editor/src/components/collab-sidebar/style.scss
@@ -18,13 +18,17 @@
position: relative;
padding: $grid-unit-20;
border-radius: $radius-large;
- border: 1px solid $gray-300;
+ border: 1.5px solid $gray-300;
background-color: $gray-100;
margin-bottom: $grid-unit-20;
}
&__active-thread {
border: 1.5px solid #3858e9;
+ }
+
+ &__focus-thread {
+ border: 1.5px solid #3858e9;
background-color: $white;
box-shadow: 0 5.5px 7.8px -0.3px rgba(0, 0, 0, 0.102);
}
@@ -121,4 +125,9 @@
}
}
+ &__show-more-reply {
+ font-weight: 500;
+ font-style: italic;
+ padding: 0;
+ }
}