Explorar o código

WIP refactoring

shinoka7 %!s(int64=6) %!d(string=hai) anos
pai
achega
12f3fa1a12

+ 16 - 14
src/client/js/app.js

@@ -311,16 +311,6 @@ const componentMappings = {
 
 };
 
-const data = {
-  pageId,
-  pagePath,
-  editorOptions: pageEditorOptions,
-  slackChannels,
-  crowi,
-  crowiOriginRenderer: crowiRenderer,
-  revisionId: pageRevisionId,
-};
-
 // additional definitions if data exists
 let pageComments = null;
 if (pageId) {
@@ -333,7 +323,13 @@ if (pageId) {
           }
         }}
         revisionCreatedAt={pageRevisionCreatedAt}
-        data={data}
+        pageId={pageId}
+        pagePath={pagePath}
+        editorOptions={pageEditorOptions}
+        slackChannels={slackChannels}
+        crowi={crowi}
+        crowiOriginRenderer={crowiRenderer}
+        revisionId={pageRevisionId}
       />
     </I18nextProvider>
   );
@@ -523,11 +519,17 @@ if (writeCommentElem) {
       <CommentForm
         onPostComplete={postCompleteHandler}
         replyTo={undefined}
-        data={data}
+        pageId={pageId}
+        pagePath={pagePath}
+        slackChannels={slackChannels}
+        crowi={crowi}
+        revisionId={pageRevisionId}
       >
         <CommentEditor
-          replyTo={undefined}
-          data={data}
+          editorOptions={pageEditorOptions}
+          crowi={crowi}
+          crowiOriginRenderer={crowiRenderer}
+          showCommentEditor
         >
         </CommentEditor>
       </CommentForm>

+ 41 - 60
src/client/js/components/PageComment/Comment.jsx

@@ -3,8 +3,6 @@ import PropTypes from 'prop-types';
 
 import Button from 'react-bootstrap/es/Button';
 import dateFnsFormat from 'date-fns/format';
-import CommentForm from './CommentForm';
-import CommentEditor from './CommentEditor';
 
 import RevisionBody from '../Page/RevisionBody';
 
@@ -27,7 +25,6 @@ export default class Comment extends React.Component {
 
     this.state = {
       html: '',
-      showCommentForm: false,
     };
 
     this.isCurrentUserIsAuthor = this.isCurrentUserEqualsToAuthor.bind(this);
@@ -36,15 +33,7 @@ export default class Comment extends React.Component {
     this.getRevisionLabelClassName = this.getRevisionLabelClassName.bind(this);
     this.deleteBtnClickedHandler = this.deleteBtnClickedHandler.bind(this);
     this.renderHtml = this.renderHtml.bind(this);
-    this.showForm = this.showForm.bind(this);
-  }
-
-  showForm() {
-    this.setState((prevState) => {
-      return {
-        showCommentForm: !prevState.showCommentForm,
-      };
-    });
+    this.replyBtnClickedHandler = this.replyBtnClickedHandler.bind(this);
   }
 
   componentWillMount() {
@@ -61,11 +50,11 @@ export default class Comment extends React.Component {
   }
 
   isCurrentUserEqualsToAuthor() {
-    return this.props.comment.creator.username === this.props.data.crowi.me;
+    return this.props.comment.creator.username === this.props.crowi.me;
   }
 
   isCurrentRevision() {
-    return this.props.comment.revision === this.props.data.revisionId;
+    return this.props.comment.revision === this.props.revisionId;
   }
 
   getRootClassName() {
@@ -82,8 +71,12 @@ export default class Comment extends React.Component {
     this.props.deleteBtnClicked(this.props.comment);
   }
 
+  replyBtnClickedHandler() {
+    this.props.onReplyButtonClicked(this.props.comment);
+  }
+
   renderRevisionBody() {
-    const config = this.props.data.crowi.getConfig();
+    const config = this.props.crowi.getConfig();
     const isMathJaxEnabled = !!config.env.MATHJAX;
     return (
       <RevisionBody
@@ -101,7 +94,7 @@ export default class Comment extends React.Component {
     };
 
     const crowiRenderer = this.props.crowiRenderer;
-    const interceptorManager = this.props.data.crowi.interceptorManager;
+    const interceptorManager = this.props.crowi.interceptorManager;
     interceptorManager.process('preRenderComment', context)
       .then(() => { return interceptorManager.process('prePreProcess', context) })
       .then(() => {
@@ -139,49 +132,37 @@ export default class Comment extends React.Component {
     const revisionLavelClassName = this.getRevisionLabelClassName();
 
     return (
-      <div>
-        <div className={rootClassName}>
-          <UserPicture user={creator} />
-          <div className="page-comment-main">
-            <div className="page-comment-creator">
-              <Username user={creator} />
-            </div>
-            <div className="page-comment-body">{commentBody}</div>
-            <div className="page-comment-reply text-right">
-              {
-                comment.replyTo === undefined
-                && (
-                  <Button
-                    type="button"
-                    className="fcbtn btn btn-primary btn-sm btn-success btn-rounded btn-1b"
-                    onClick={this.showForm}
-                  >
-                    Reply
-                  </Button>
-                )
-              }
-            </div>
-            <div className="page-comment-meta">
-              {commentDate}&nbsp;
-              <a className={revisionLavelClassName} href={revHref}>{revFirst8Letters}</a>
-            </div>
-            <div className="page-comment-control">
-              <button type="button" className="btn btn-link" onClick={this.deleteBtnClickedHandler}>
-                <i className="ti-close"></i>
-              </button>
-            </div>
+      <div className={rootClassName}>
+        <UserPicture user={creator} />
+        <div className="page-comment-main">
+          <div className="page-comment-creator">
+            <Username user={creator} />
+          </div>
+          <div className="page-comment-body">{commentBody}</div>
+          <div className="page-comment-reply text-right">
+            {
+              comment.replyTo === undefined
+              && (
+                <Button
+                  type="button"
+                  className="fcbtn btn btn-primary btn-sm btn-success btn-rounded btn-1b"
+                  onClick={this.replyBtnClickedHandler}
+                >
+                  Reply
+                </Button>
+              )
+            }
+          </div>
+          <div className="page-comment-meta">
+            {commentDate}&nbsp;
+            <a className={revisionLavelClassName} href={revHref}>{revFirst8Letters}</a>
+          </div>
+          <div className="page-comment-control">
+            <button type="button" className="btn btn-link" onClick={this.deleteBtnClickedHandler}>
+              <i className="ti-close"></i>
+            </button>
           </div>
         </div>
-        {
-          this.state.showCommentForm
-          && (
-          <CommentEditor
-            onPostComplete={this.props.onPostComplete}
-            data={this.props.data}
-            replyTo={comment._id.toString()}
-          />
-        )
-        }
       </div>
     );
   }
@@ -192,7 +173,7 @@ Comment.propTypes = {
   comment: PropTypes.object.isRequired,
   crowiRenderer: PropTypes.object.isRequired,
   deleteBtnClicked: PropTypes.func.isRequired,
-  data: PropTypes.object.isRequired,
-  replyTo: PropTypes.string,
-  onPostComplete: PropTypes.func.isRequired,
+  onReplyButtonClicked: PropTypes.func.isRequired,
+  crowi: PropTypes.object.isRequired,
+  revisionId: PropTypes.string,
 };

+ 129 - 108
src/client/js/components/PageComment/CommentEditor.jsx

@@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
 import Button from 'react-bootstrap/es/Button';
 import Tab from 'react-bootstrap/es/Tab';
 import Tabs from 'react-bootstrap/es/Tabs';
-import * as toastr from 'toastr';
 import UserPicture from '../User/UserPicture';
 import ReactUtils from '../ReactUtils';
 
@@ -28,7 +27,7 @@ export default class CommentEditor extends React.Component {
   constructor(props) {
     super(props);
 
-    const config = this.props.data.crowi.getConfig();
+    const config = this.props.crowi.getConfig();
     const isUploadable = config.upload.image || config.upload.file;
     const isUploadableFile = config.upload.file;
 
@@ -44,10 +43,11 @@ export default class CommentEditor extends React.Component {
       errorMessage: undefined,
       hasSlackConfig: config.hasSlackConfig,
       isSlackEnabled: false,
-      slackChannels: this.props.data.slackChannels,
+      slackChannels: this.props.slackChannels,
+      showCommentEditor: this.props.showCommentEditor,
     };
 
-    this.growiRenderer = new GrowiRenderer(this.props.data.crowi, this.props.data.crowiOriginRenderer, { mode: 'comment' });
+    this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, { mode: 'comment' });
 
     this.updateState = this.updateState.bind(this);
     this.updateStateCheckbox = this.updateStateCheckbox.bind(this);
@@ -55,6 +55,7 @@ export default class CommentEditor extends React.Component {
     this.onSubmit = this.onSubmit.bind(this);
     this.onUpload = this.onUpload.bind(this);
 
+    this.toggleEditor = this.toggleEditor.bind(this);
     this.renderHtml = this.renderHtml.bind(this);
     this.handleSelect = this.handleSelect.bind(this);
     this.onSlackEnabledFlagChange = this.onSlackEnabledFlagChange.bind(this);
@@ -67,11 +68,11 @@ export default class CommentEditor extends React.Component {
   }
 
   init() {
-    if (!this.props.data.pageId) {
+    if (!this.props.pageId) {
       return;
     }
 
-    const layoutType = this.props.data.crowi.getConfig().layoutType;
+    const layoutType = this.props.crowi.getConfig().layoutType;
     this.setState({ isLayoutTypeGrowi: layoutType === 'crowi-plus' || layoutType === 'growi' });
   }
 
@@ -117,13 +118,21 @@ export default class CommentEditor extends React.Component {
     );
   }
 
+  toggleEditor() {
+    this.setState((prevState) => {
+      return {
+        showCommentEditor: !prevState.showCommentEditor,
+      };
+    });
+  }
+
   renderHtml(markdown) {
     const context = {
       markdown,
     };
 
     const growiRenderer = this.growiRenderer;
-    const interceptorManager = this.props.data.crowi.interceptorManager;
+    const interceptorManager = this.props.crowi.interceptorManager;
     interceptorManager.process('preRenderCommnetPreview', context)
       .then(() => { return interceptorManager.process('prePreProcess', context) })
       .then(() => {
@@ -204,12 +213,12 @@ export default class CommentEditor extends React.Component {
   }
 
   render() {
-    const crowi = this.props.data.crowi;
+    const crowi = this.props.crowi;
     const username = crowi.me;
     const user = crowi.findUser(username);
     const comment = this.state.comment;
     const commentPreview = this.state.isMarkdown ? this.getCommentHtml() : ReactUtils.nl2br(comment);
-    const emojiStrategy = this.props.data.crowi.getEmojiStrategy();
+    const emojiStrategy = this.props.crowi.getEmojiStrategy();
 
     const isLayoutTypeGrowi = this.state.isLayoutTypeGrowi;
 
@@ -222,112 +231,120 @@ export default class CommentEditor extends React.Component {
 
     return (
       <div>
-
-        { username
+        {
+          this.state.showCommentEditor
           && (
-          <div className="comment-form">
-            { isLayoutTypeGrowi
+
+          <div>
+
+            { username
               && (
-              <div className="comment-form-user">
-                <UserPicture user={user} />
-              </div>
-              )
-            }
-            <div className="comment-form-main">
-              {/* Add Comment Button */}
-              { !this.state.isFormShown
-                && (
-                <button
-                  type="button"
-                  className={`btn btn-lg ${isLayoutTypeGrowi ? 'btn-link' : 'btn-primary'} center-block`}
-                  onClick={this.showCommentFormBtnClickHandler}
-                >
-                  <i className="icon-bubble"></i> Add Comment
-                </button>
-                )
-              }
-              {/* Editor */}
-              { this.state.isFormShown
-                && (
-                <React.Fragment>
-                  <div className="comment-write">
-                    <Tabs activeKey={this.state.key} id="comment-form-tabs" onSelect={this.handleSelect} animation={false}>
-                      <Tab eventKey={1} title="Write">
-                        <Editor
-                          ref={(c) => { this.editor = c }}
-                          value={this.state.comment}
-                          isGfmMode={this.state.isMarkdown}
-                          editorOptions={this.props.data.editorOptions}
-                          lineNumbers={false}
-                          isMobile={this.props.data.crowi.isMobile}
-                          isUploadable={this.state.isUploadable && this.state.isLayoutTypeGrowi} // enable only when GROWI layout
-                          isUploadableFile={this.state.isUploadableFile}
-                          emojiStrategy={emojiStrategy}
-                          onChange={this.updateState}
-                          onUpload={this.onUpload}
-                          onCtrlEnter={this.onSubmit}
-                        />
-                      </Tab>
-                      { this.state.isMarkdown
-                        && (
-                        <Tab eventKey={2} title="Preview">
-                          <div className="comment-form-preview">
-                            {commentPreview}
-                          </div>
-                        </Tab>
-                        )
-                      }
-                    </Tabs>
+              <div className="comment-form">
+                { isLayoutTypeGrowi
+                  && (
+                  <div className="comment-form-user">
+                    <UserPicture user={user} />
                   </div>
-                  <div className="comment-submit">
-                    <div className="d-flex">
-                      <label style={{ flex: 1 }}>
-                        { isLayoutTypeGrowi && this.state.key === 1
-                          && (
-                          <span>
-                            <input
-                              type="checkbox"
-                              id="comment-form-is-markdown"
-                              name="isMarkdown"
-                              checked={this.state.isMarkdown}
-                              value="1"
-                              onChange={this.updateStateCheckbox}
+                  )
+                }
+                <div className="comment-form-main">
+                  {/* Add Comment Button */}
+                  { !this.state.isFormShown
+                    && (
+                    <button
+                      type="button"
+                      className={`btn btn-lg ${isLayoutTypeGrowi ? 'btn-link' : 'btn-primary'} center-block`}
+                      onClick={this.showCommentFormBtnClickHandler}
+                    >
+                      <i className="icon-bubble"></i> Add Comment
+                    </button>
+                    )
+                  }
+                  {/* Editor */}
+                  { this.state.isFormShown
+                    && (
+                    <React.Fragment>
+                      <div className="comment-write">
+                        <Tabs activeKey={this.state.key} id="comment-form-tabs" onSelect={this.handleSelect} animation={false}>
+                          <Tab eventKey={1} title="Write">
+                            <Editor
+                              ref={(c) => { this.editor = c }}
+                              value={this.state.comment}
+                              isGfmMode={this.state.isMarkdown}
+                              editorOptions={this.props.editorOptions}
+                              lineNumbers={false}
+                              isMobile={this.props.crowi.isMobile}
+                              isUploadable={this.state.isUploadable && this.state.isLayoutTypeGrowi} // enable only when GROWI layout
+                              isUploadableFile={this.state.isUploadableFile}
+                              emojiStrategy={emojiStrategy}
+                              onChange={this.updateState}
+                              onUpload={this.onUpload}
+                              onCtrlEnter={this.onSubmit}
                             />
-                            <span className="ml-2">Markdown</span>
-                          </span>
-                          )
-                      }
-                      </label>
-                      <span className="hidden-xs">{ this.state.errorMessage && errorMessage }</span>
-                      { this.state.hasSlackConfig
-                        && (
-                        <div className="form-inline align-self-center mr-md-2">
-                          <SlackNotification
-                            isSlackEnabled={this.state.isSlackEnabled}
-                            slackChannels={this.state.slackChannels}
-                            onEnabledFlagChange={this.onSlackEnabledFlagChange}
-                            onChannelChange={this.onSlackChannelsChange}
-                          />
+                          </Tab>
+                          { this.state.isMarkdown
+                            && (
+                            <Tab eventKey={2} title="Preview">
+                              <div className="comment-form-preview">
+                                {commentPreview}
+                              </div>
+                            </Tab>
+                            )
+                          }
+                        </Tabs>
+                      </div>
+                      <div className="comment-submit">
+                        <div className="d-flex">
+                          <label style={{ flex: 1 }}>
+                            { isLayoutTypeGrowi && this.state.key === 1
+                              && (
+                              <span>
+                                <input
+                                  type="checkbox"
+                                  id="comment-form-is-markdown"
+                                  name="isMarkdown"
+                                  checked={this.state.isMarkdown}
+                                  value="1"
+                                  onChange={this.updateStateCheckbox}
+                                />
+                                <span className="ml-2">Markdown</span>
+                              </span>
+                              )
+                          }
+                          </label>
+                          <span className="hidden-xs">{ this.state.errorMessage && errorMessage }</span>
+                          { this.state.hasSlackConfig
+                            && (
+                            <div className="form-inline align-self-center mr-md-2">
+                              <SlackNotification
+                                isSlackEnabled={this.state.isSlackEnabled}
+                                slackChannels={this.state.slackChannels}
+                                onEnabledFlagChange={this.onSlackEnabledFlagChange}
+                                onChannelChange={this.onSlackChannelsChange}
+                              />
+                            </div>
+                            )
+                          }
+                          <div className="hidden-xs">{submitButton}</div>
+                        </div>
+                        <div className="visible-xs mt-2">
+                          <div className="d-flex justify-content-end">
+                            { this.state.errorMessage && errorMessage }
+                            <div>{submitButton}</div>
+                          </div>
                         </div>
-                        )
-                      }
-                      <div className="hidden-xs">{submitButton}</div>
-                    </div>
-                    <div className="visible-xs mt-2">
-                      <div className="d-flex justify-content-end">
-                        { this.state.errorMessage && errorMessage }
-                        <div>{submitButton}</div>
                       </div>
-                    </div>
-                  </div>
-                </React.Fragment>
-                )
-              }
-            </div>
+                    </React.Fragment>
+                    )
+                  }
+                </div>
+              </div>
+              )
+            }
+
           </div>
           )
         }
-
       </div>
     );
   }
@@ -335,6 +352,10 @@ export default class CommentEditor extends React.Component {
 }
 
 CommentEditor.propTypes = {
-  replyTo: PropTypes.string,
-  data: PropTypes.object.isRequired,
+  pageId: PropTypes.string,
+  editorOptions: PropTypes.object,
+  slackChannels: PropTypes.string,
+  crowi: PropTypes.object.isRequired,
+  crowiOriginRenderer: PropTypes.object.isRequired,
+  showCommentEditor: PropTypes.bool,
 };

+ 17 - 3
src/client/js/components/PageComment/CommentForm.jsx

@@ -42,7 +42,7 @@ export default class CommentForm extends React.Component {
   }
 
   init() {
-    if (!this.props.data.pageId) {
+    if (!this.props.pageId) {
       return;
     }
   }
@@ -133,9 +133,19 @@ export default class CommentForm extends React.Component {
   }
 
   render() {
+    const children = React.Children.map(this.props.children, (child) => {
+      return React.cloneElement(child, {
+        onPostComplete: this.props.onPostComplete,
+        replyTo: this.props.replyTo,
+        pageId: this.props.pageId,
+        pagePath: this.props.pagePath,
+        slackChannels: this.props.slackChannels,
+        revisionId: this.props.revisionId,
+      });
+    });
     return (
       <div>
-        {this.props.children}
+        {children}
       </div>
     );
   }
@@ -146,5 +156,9 @@ CommentForm.propTypes = {
   children: PropTypes.node.isRequired,
   onPostComplete: PropTypes.func,
   replyTo: PropTypes.string,
-  data: PropTypes.object.isRequired,
+  pageId: PropTypes.string,
+  pagePath: PropTypes.string,
+  slackChannels: PropTypes.string,
+  revisionId: PropTypes.string,
+  crowi: PropTypes.object.isRequired,
 };

+ 50 - 13
src/client/js/components/PageComments.js

@@ -5,6 +5,9 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import GrowiRenderer from '../util/GrowiRenderer';
 
+import CommentForm from './PageComment/CommentForm';
+import CommentEditor from './PageComment/CommentEditor';
+
 import Comment from './PageComment/Comment';
 import DeleteCommentModal from './PageComment/DeleteCommentModal';
 
@@ -26,6 +29,8 @@ class PageComments extends React.Component {
       // desc order array
       comments: [],
 
+      children: {},
+
       isLayoutTypeGrowi: false,
 
       // for deleting comment
@@ -34,13 +39,14 @@ class PageComments extends React.Component {
       errorMessageForDeleting: undefined,
     };
 
-    this.growiRenderer = new GrowiRenderer(this.props.data.crowi, this.props.data.crowiOriginRenderer, { mode: 'comment' });
+    this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, { mode: 'comment' });
 
     this.init = this.init.bind(this);
     this.confirmToDeleteComment = this.confirmToDeleteComment.bind(this);
     this.deleteComment = this.deleteComment.bind(this);
     this.showDeleteConfirmModal = this.showDeleteConfirmModal.bind(this);
     this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
+    this.replyToComment = this.replyToComment.bind(this);
   }
 
   componentWillMount() {
@@ -49,11 +55,11 @@ class PageComments extends React.Component {
   }
 
   init() {
-    if (!this.props.data.pageId) {
+    if (!this.props.pageId) {
       return;
     }
 
-    const layoutType = this.props.data.crowi.getConfig().layoutType;
+    const layoutType = this.props.crowi.getConfig().layoutType;
     this.setState({ isLayoutTypeGrowi: layoutType === 'crowi-plus' || layoutType === 'growi' });
 
     this.retrieveData();
@@ -64,10 +70,15 @@ class PageComments extends React.Component {
    */
   retrieveData() {
     // get data (desc order array)
-    this.props.data.crowi.apiGet('/comments.get', { page_id: this.props.data.pageId })
+    this.props.crowi.apiGet('/comments.get', { page_id: this.props.pageId })
       .then((res) => {
         if (res.ok) {
           this.setState({ comments: res.comments });
+          const tempChildren = {};
+          res.comments.forEach((comment) => {
+            tempChildren[comment._id] = React.createRef();
+          });
+          this.setState({ children: tempChildren });
         }
       });
   }
@@ -77,10 +88,14 @@ class PageComments extends React.Component {
     this.showDeleteConfirmModal();
   }
 
+  replyToComment(comment) {
+    this.state.children[comment._id].toggleEditor();
+  }
+
   deleteComment() {
     const comment = this.state.commentToDelete;
 
-    this.props.data.crowi.apiPost('/comments.remove', { comment_id: comment._id })
+    this.props.crowi.apiPost('/comments.remove', { comment_id: comment._id })
       .then((res) => {
         if (res.ok) {
           this.findAndSplice(comment);
@@ -142,15 +157,31 @@ class PageComments extends React.Component {
     const commentsWithReplies = this.reorderBasedOnReplies(comments, replies);
     return commentsWithReplies.map((comment) => {
       return (
-        <Comment
+        <CommentForm
           key={comment._id}
-          comment={comment}
-          deleteBtnClicked={this.confirmToDeleteComment}
-          crowiRenderer={this.growiRenderer}
           onPostComplete={this.retrieveData}
-          data={this.props.data}
           replyTo={comment.replyTo}
-        />
+          pageId={this.props.pageId}
+          pagePath={this.props.pagePath}
+          slackChannels={this.props.slackChannels}
+          crowi={this.props.crowi}
+          revisionId={this.props.revisionId}
+        >
+          <Comment
+            comment={comment}
+            deleteBtnClicked={this.confirmToDeleteComment}
+            crowiRenderer={this.growiRenderer}
+            onReplyButtonClicked={this.replyToComment}
+            crowi={this.props.crowi}
+          />
+          <CommentEditor
+            ref={(instance) => { this.state.children[comment._id] = instance }}
+            editorOptions={this.props.editorOptions}
+            crowiOriginRenderer={this.props.crowiOriginRenderer}
+            showCommentEditor={false}
+            crowi={this.props.crowi}
+          />
+        </CommentForm>
       );
     });
   }
@@ -170,7 +201,7 @@ class PageComments extends React.Component {
     }
 
     // divide by revisionId and createdAt
-    const revisionId = this.props.data.revisionId;
+    const revisionId = this.props.revisionId;
     const revisionCreatedAt = this.props.revisionCreatedAt;
     comments.forEach((comment) => {
       // comparing ObjectId
@@ -282,8 +313,14 @@ class PageComments extends React.Component {
 }
 
 PageComments.propTypes = {
-  data: PropTypes.object.isRequired,
   revisionCreatedAt: PropTypes.number,
+  pageId: PropTypes.string,
+  pagePath: PropTypes.string,
+  editorOptions: PropTypes.object,
+  slackChannels: PropTypes.string,
+  crowi: PropTypes.object.isRequired,
+  crowiOriginRenderer: PropTypes.object.isRequired,
+  revisionId: PropTypes.string,
 };
 
 export default withTranslation(null, { withRef: true })(PageComments);