Просмотр исходного кода

Merge pull request #13687 from overleaf/ii-review-panel-migration-add-comment-entry

[web] Create add comment entry

GitOrigin-RevId: 019f508eeb3982fce082df153e56d6c3c3e3bae5
ilkin-overleaf 3 лет назад
Родитель
Сommit
225de683c7

+ 3 - 0
services/web/frontend/extracted-translations.json

@@ -28,6 +28,7 @@
   "add_another_email": "",
   "add_another_token": "",
   "add_comma_separated_emails_help": "",
+  "add_comment": "",
   "add_company_details": "",
   "add_email_to_claim_features": "",
   "add_files": "",
@@ -36,6 +37,7 @@
   "add_or_remove_project_from_tag": "",
   "add_role_and_department": "",
   "add_to_tag": "",
+  "add_your_comment_here": "",
   "add_your_first_group_member_now": "",
   "added_by_on": "",
   "adding": "",
@@ -137,6 +139,7 @@
   "collabs_per_proj": "",
   "collabs_per_proj_single": "",
   "collapse": "",
+  "comment": "",
   "commit": "",
   "common": "",
   "commons_plan_tooltip": "",

+ 1 - 1
services/web/frontend/js/features/source-editor/components/review-panel/current-file-container.tsx

@@ -110,7 +110,7 @@ function CurrentFileContainer() {
               }
 
               if (entry.type === 'add-comment' && permissions.comment) {
-                return <AddCommentEntry key={id} />
+                return <AddCommentEntry key={id} entry={entry} />
               }
 
               if (entry.type === 'bulk-actions') {

+ 107 - 2
services/web/frontend/js/features/source-editor/components/review-panel/entries/add-comment-entry.tsx

@@ -1,7 +1,112 @@
+import { useTranslation } from 'react-i18next'
+import { useState } from 'react'
 import EntryContainer from './entry-container'
+import EntryCallout from './entry-callout'
+import EntryActions from './entry-actions'
+import AutoExpandingTextArea from '../../../../../shared/components/auto-expanding-text-area'
+import Icon from '../../../../../shared/components/icon'
+import { useReviewPanelValueContext } from '../../../context/review-panel/review-panel-context'
+import classnames from 'classnames'
+import { ReviewPanelAddCommentEntry } from '../../../../../../../types/review-panel/entry'
 
-function AddCommentEntry() {
-  return <EntryContainer>Add comment entry</EntryContainer>
+type AddCommentEntryProps = {
+  entry: ReviewPanelAddCommentEntry
+}
+
+function AddCommentEntry({ entry }: AddCommentEntryProps) {
+  const { t } = useTranslation()
+  const { submitNewComment, handleLayoutChange } = useReviewPanelValueContext()
+
+  const [content, setContent] = useState('')
+  const [isAdding, setIsAdding] = useState(false)
+
+  const handleStartNewComment = () => {
+    setIsAdding(true)
+    handleLayoutChange()
+  }
+
+  const handleSubmitNewComment = () => {
+    submitNewComment(content)
+    setIsAdding(false)
+    setContent('')
+  }
+
+  const handleCancelNewComment = () => {
+    setIsAdding(false)
+    setContent('')
+    handleLayoutChange()
+  }
+
+  const handleCommentKeyPress = (
+    e: React.KeyboardEvent<HTMLTextAreaElement>
+  ) => {
+    const target = e.target as HTMLTextAreaElement
+
+    if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.metaKey) {
+      e.preventDefault()
+      if (content.length) {
+        handleSubmitNewComment()
+      }
+    }
+
+    if (['PageDown', 'PageUp'].includes(e.key)) {
+      if (target.closest('textarea')) {
+        e.preventDefault()
+      }
+    }
+  }
+
+  return (
+    <EntryContainer>
+      <EntryCallout className="rp-entry-callout-add-comment" />
+      <div
+        className={classnames('rp-entry', 'rp-entry-add-comment', {
+          'rp-entry-adding-comment': isAdding,
+        })}
+        style={{
+          top: entry.screenPos.y + 'px',
+          visibility: entry.visible ? 'visible' : 'hidden',
+        }}
+      >
+        {isAdding ? (
+          <>
+            <div className="rp-new-comment">
+              <AutoExpandingTextArea
+                className="rp-comment-input"
+                onChange={e => setContent(e.target.value)}
+                onKeyPress={handleCommentKeyPress}
+                onResize={handleLayoutChange}
+                placeholder={t('add_your_comment_here')}
+                value={content}
+                autoFocus // eslint-disable-line jsx-a11y/no-autofocus
+              />
+            </div>
+            <EntryActions>
+              <EntryActions.Button
+                className="rp-entry-button-cancel"
+                onClick={handleCancelNewComment}
+              >
+                <Icon type="times" /> {t('cancel')}
+              </EntryActions.Button>
+              <EntryActions.Button
+                onClick={handleSubmitNewComment}
+                disabled={!content.length}
+              >
+                <Icon type="comment" /> {t('comment')}
+              </EntryActions.Button>
+            </EntryActions>
+          </>
+        ) : (
+          <button
+            className="rp-add-comment-btn"
+            onClick={handleStartNewComment}
+          >
+            <Icon type="comment" /> {t('add_comment')}
+          </button>
+        )}
+      </div>
+    </EntryContainer>
+  )
 }
 
 export default AddCommentEntry

+ 3 - 2
services/web/frontend/js/features/source-editor/components/review-panel/entries/aggregate-change-entry.tsx

@@ -1,6 +1,7 @@
 import { useTranslation } from 'react-i18next'
 import { useState } from 'react'
 import EntryContainer from './entry-container'
+import EntryCallout from './entry-callout'
 import EntryActions from './entry-actions'
 import Icon from '../../../../../shared/components/icon'
 import { useReviewPanelValueContext } from '../../../context/review-panel/review-panel-context'
@@ -85,8 +86,8 @@ function AggregateChangeEntry({
       onMouseEnter={onMouseEnter}
       onMouseLeave={onMouseLeave}
     >
-      <div
-        className="rp-entry-callout rp-entry-callout-aggregate"
+      <EntryCallout
+        className="rp-entry-callout-aggregate"
         style={{
           top: entry.screenPos
             ? entry.screenPos.y + entry.screenPos.height - 1 + 'px'

+ 3 - 5
services/web/frontend/js/features/source-editor/components/review-panel/entries/change-entry.tsx

@@ -1,6 +1,7 @@
 import { useTranslation } from 'react-i18next'
 import { useState } from 'react'
 import EntryContainer from './entry-container'
+import EntryCallout from './entry-callout'
 import EntryActions from './entry-actions'
 import Icon from '../../../../../shared/components/icon'
 import { useReviewPanelValueContext } from '../../../context/review-panel/review-panel-context'
@@ -75,11 +76,8 @@ function ChangeEntry({
       onMouseEnter={onMouseEnter}
       onMouseLeave={onMouseLeave}
     >
-      <div
-        className={classnames(
-          'rp-entry-callout',
-          `rp-entry-callout-${entry.type}`
-        )}
+      <EntryCallout
+        className={`rp-entry-callout-${entry.type}`}
         style={{
           top: entry.screenPos
             ? entry.screenPos.y + entry.screenPos.height - 1 + 'px'

+ 4 - 3
services/web/frontend/js/features/source-editor/components/review-panel/entries/comment-entry.tsx

@@ -1,8 +1,9 @@
 import { useState, useRef } from 'react'
 import { useTranslation } from 'react-i18next'
 import EntryContainer from './entry-container'
-import Comment from './comment'
+import EntryCallout from './entry-callout'
 import EntryActions from './entry-actions'
+import Comment from './comment'
 import AutoExpandingTextArea, {
   resetHeight,
 } from '../../../../../shared/components/auto-expanding-text-area'
@@ -118,8 +119,8 @@ function CommentEntry({
           'rp-comment-wrapper-resolving': animating,
         })}
       >
-        <div
-          className="rp-entry-callout rp-entry-callout-comment"
+        <EntryCallout
+          className="rp-entry-callout-comment"
           style={{
             top: entry.screenPos
               ? entry.screenPos.y + entry.screenPos.height - 1 + 'px'

+ 1 - 0
services/web/frontend/js/features/source-editor/components/review-panel/entries/comment.tsx

@@ -71,6 +71,7 @@ function Comment({ thread, threadId, comment }: CommentProps) {
             onBlur={handleSaveEdit}
             onClick={e => e.stopPropagation()}
             onResize={handleLayoutChange}
+            autoFocus // eslint-disable-line jsx-a11y/no-autofocus
           />
         ) : (
           <>

+ 7 - 0
services/web/frontend/js/features/source-editor/components/review-panel/entries/entry-callout.tsx

@@ -0,0 +1,7 @@
+import classnames from 'classnames'
+
+function EntryCallout({ className, ...rest }: React.ComponentProps<'div'>) {
+  return <div className={classnames('rp-entry-callout', className)} {...rest} />
+}
+
+export default EntryCallout

+ 4 - 0
services/web/frontend/js/features/source-editor/context/review-panel/hooks/use-angular-review-panel-state.ts

@@ -71,6 +71,8 @@ function useAngularReviewPanelState(): ReviewPanelState {
   >('reviewPanel.trackChangesForGuestsAvailable')
   const [resolveComment] =
     useScopeValue<ReviewPanel.Value<'resolveComment'>>('resolveComment')
+  const [submitNewComment] =
+    useScopeValue<ReviewPanel.Value<'submitNewComment'>>('submitNewComment')
   const [deleteComment] =
     useScopeValue<ReviewPanel.Value<'deleteComment'>>('deleteComment')
   const [gotoEntry] = useScopeValue<ReviewPanel.Value<'gotoEntry'>>('gotoEntry')
@@ -155,6 +157,7 @@ function useAngularReviewPanelState(): ReviewPanelState {
       refreshResolvedCommentsDropdown,
       acceptChanges,
       rejectChanges,
+      submitNewComment,
     }),
     [
       collapsed,
@@ -191,6 +194,7 @@ function useAngularReviewPanelState(): ReviewPanelState {
       refreshResolvedCommentsDropdown,
       acceptChanges,
       rejectChanges,
+      submitNewComment,
     ]
   )
 

+ 1 - 0
services/web/frontend/js/features/source-editor/context/review-panel/types/review-panel-state.ts

@@ -59,6 +59,7 @@ export interface ReviewPanelState {
     refreshResolvedCommentsDropdown: () => Promise<void>
     acceptChanges: (entryIds: unknown) => void
     rejectChanges: (entryIds: unknown) => void
+    submitNewComment: (content: string) => void
   }
   updaterFns: {
     handleSetSubview: (subView: SubView) => void

+ 11 - 0
services/web/test/frontend/features/review-panel/review-panel.spec.tsx

@@ -221,6 +221,17 @@ describe('<ReviewPanel />', function () {
     it.skip('renders changed entries in overview mode', function () {})
   })
 
+  describe('add comment entry', function () {
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('renders `add comment button`', function () {})
+
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('cancels adding comment', function () {})
+
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('adds comment', function () {})
+  })
+
   describe('overview mode', function () {
     // eslint-disable-next-line mocha/no-skipped-tests
     it.skip('shows list of files changed', function () {})

+ 1 - 1
services/web/types/review-panel/entry.ts

@@ -53,7 +53,7 @@ export interface ReviewPanelAggregateChangeEntry extends ReviewPanelBaseEntry {
   }
 }
 
-interface ReviewPanelAddCommentEntry extends ReviewPanelBaseEntry {
+export interface ReviewPanelAddCommentEntry extends ReviewPanelBaseEntry {
   type: 'add-comment'
 }