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 ( - - { _x( 'Comment', 'Add comment button' ) } - + { ( { onClose } ) => ( + { + onClick(); + onClose(); + } } + aria-haspopup="dialog" + > + { _x( 'Comment', 'Add comment button' ) } + + ) } ); }; 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 && ( -