decidim-comments 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/decidim/comments/bundle.js +28 -23
  4. data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -1
  5. data/app/commands/decidim/comments/create_comment.rb +10 -0
  6. data/app/forms/decidim/comments/comment_form.rb +1 -1
  7. data/app/frontend/comments/add_comment_form.component.jsx +171 -87
  8. data/app/frontend/comments/add_comment_form.component.test.jsx +41 -22
  9. data/app/frontend/comments/add_comment_form.mutation.graphql +5 -3
  10. data/app/frontend/comments/add_comment_form_commentable.fragment.graphql +4 -0
  11. data/app/frontend/comments/add_comment_form_session.fragment.graphql +6 -0
  12. data/app/frontend/comments/comment.component.jsx +18 -18
  13. data/app/frontend/comments/comment.component.test.jsx +22 -21
  14. data/app/frontend/comments/comment.fragment.graphql +4 -4
  15. data/app/frontend/comments/comment_data.fragment.graphql +4 -3
  16. data/app/frontend/comments/comment_order_selector.component.jsx +3 -3
  17. data/app/frontend/comments/comment_thread.component.jsx +8 -3
  18. data/app/frontend/comments/comment_thread.component.test.jsx +3 -3
  19. data/app/frontend/comments/comment_thread.fragment.graphql +1 -1
  20. data/app/frontend/comments/comments.component.jsx +52 -33
  21. data/app/frontend/comments/comments.component.test.jsx +51 -38
  22. data/app/frontend/comments/comments.query.graphql +10 -4
  23. data/app/frontend/comments/down_vote_button.component.jsx +6 -3
  24. data/app/frontend/comments/up_vote_button.component.jsx +7 -4
  25. data/app/frontend/comments/vote_button.component.jsx +5 -0
  26. data/app/frontend/comments/vote_button_component.test.jsx +1 -1
  27. data/app/frontend/entry.test.js +2 -0
  28. data/app/frontend/support/generate_comments_data.js +4 -4
  29. data/app/mailers/decidim/comments/comment_notification_mailer.rb +31 -0
  30. data/app/models/decidim/comments/comment.rb +13 -9
  31. data/app/queries/decidim/comments/{comments_with_replies.rb → sorted_comments.rb} +3 -8
  32. data/app/types/decidim/comments/commentable_interface.rb +44 -0
  33. data/app/types/decidim/comments/commentable_mutation_type.rb +29 -0
  34. data/app/types/decidim/comments/commentable_type.rb +14 -0
  35. data/app/views/decidim/comments/comment_notification_mailer/comment_created.html.erb +18 -0
  36. data/app/views/decidim/comments/comment_notification_mailer/reply_created.html.erb +18 -0
  37. data/config/locales/ca.yml +20 -4
  38. data/config/locales/en.yml +22 -5
  39. data/config/locales/es.yml +20 -4
  40. data/config/locales/eu.yml +5 -0
  41. data/lib/decidim/comments.rb +5 -0
  42. data/{app/types/decidim/comments → lib/decidim/comments/api}/add_comment_type.rb +0 -0
  43. data/{app/types/decidim/comments → lib/decidim/comments/api}/comment_mutation_type.rb +0 -0
  44. data/{app/types/decidim/comments → lib/decidim/comments/api}/comment_type.rb +11 -17
  45. data/lib/decidim/comments/commentable.rb +45 -0
  46. data/{app/helpers → lib}/decidim/comments/comments_helper.rb +15 -10
  47. data/lib/decidim/comments/mutation_extensions.rb +8 -16
  48. data/lib/decidim/comments/query_extensions.rb +5 -8
  49. data/lib/decidim/comments/test/factories.rb +3 -3
  50. metadata +21 -12
  51. data/app/frontend/comments/add_comment_form.fragment.graphql +0 -6
@@ -8,8 +8,10 @@ import generateUserGroupData from '../support/generate_user_group_data';
8
8
 
9
9
  describe("<AddCommentForm />", () => {
10
10
  let session = null;
11
- const commentableId = "1";
12
- const commentableType = "Decidim::ParticipatoryProcess";
11
+ const commentable = {
12
+ id: "1",
13
+ type: "Decidim::DummyResource"
14
+ };
13
15
  const addCommentStub = () => {
14
16
  return null;
15
17
  }
@@ -22,44 +24,50 @@ describe("<AddCommentForm />", () => {
22
24
  });
23
25
 
24
26
  it("should render a div with class add-comment", () => {
25
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
27
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
26
28
  expect(wrapper.find('div.add-comment')).to.present();
27
29
  });
28
30
 
29
31
  it("should have a reference to body textarea", () => {
30
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
32
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
31
33
  expect(wrapper.instance().bodyTextArea).to.be.ok;
32
34
  });
33
35
 
34
36
  it("should initialize with a state property disabled as true", () => {
35
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
37
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
36
38
  expect(wrapper).to.have.state('disabled', true);
37
39
  });
38
40
 
39
41
  it("should have a default prop showTitle as true", () => {
40
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
42
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
41
43
  expect(wrapper).to.have.prop('showTitle').equal(true);
42
44
  });
43
45
 
44
46
  it("should not render the title if prop showTitle is false", () => {
45
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} showTitle={false} />);
47
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} showTitle={false} />);
46
48
  expect(wrapper.find('h5.section-heading')).not.to.be.present();
47
49
  });
48
50
 
49
51
  it("should have a default prop submitButtonClassName as 'button button--sc'", () => {
50
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
52
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
51
53
  expect(wrapper).to.have.prop('submitButtonClassName').equal('button button--sc');
52
54
  });
53
55
 
56
+ it("should have a default prop maxLength of 1000", () => {
57
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
58
+ expect(wrapper).to.have.prop('maxLength').equal(1000);
59
+ });
60
+
61
+
54
62
  it("should use prop submitButtonClassName as a className prop for submit button", () => {
55
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} submitButtonClassName="button small hollow" />);
63
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} submitButtonClassName="button small hollow" />);
56
64
  expect(wrapper.find('input[type="submit"]')).to.have.className('button');
57
65
  expect(wrapper.find('input[type="submit"]')).to.have.className('small');
58
66
  expect(wrapper.find('input[type="submit"]')).to.have.className('hollow');
59
67
  });
60
68
 
61
69
  it("should enable the submit button if textarea is not blank", () => {
62
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
70
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
63
71
  wrapper.find('textarea').simulate('change', {
64
72
  target: {
65
73
  value: 'This is a comment'
@@ -69,7 +77,7 @@ describe("<AddCommentForm />", () => {
69
77
  });
70
78
 
71
79
  it("should disable the submit button if textarea is blank", () => {
72
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
80
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
73
81
  wrapper.find('textarea').simulate('change', {
74
82
  target: {
75
83
  value: 'This will be deleted'
@@ -84,7 +92,7 @@ describe("<AddCommentForm />", () => {
84
92
  });
85
93
 
86
94
  it("should not render a div with class 'opinion-toggle'", () => {
87
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
95
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
88
96
  expect(wrapper.find('.opinion-toggle')).not.to.be.present();
89
97
  });
90
98
 
@@ -97,7 +105,7 @@ describe("<AddCommentForm />", () => {
97
105
  beforeEach(() => {
98
106
  addComment = sinon.spy();
99
107
  onCommentAdded = sinon.spy();
100
- wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentableId={commentableId} commentableType={commentableType} onCommentAdded={onCommentAdded} />);
108
+ wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} onCommentAdded={onCommentAdded} />);
101
109
  message = 'This will be submitted';
102
110
  wrapper.instance().bodyTextArea.value = message;
103
111
  });
@@ -125,25 +133,25 @@ describe("<AddCommentForm />", () => {
125
133
  });
126
134
 
127
135
  it("should initialize state with a property alignment and value 0", () => {
128
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} arguable />);
136
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
129
137
  expect(wrapper).to.have.state('alignment').equal(0);
130
138
  });
131
139
 
132
140
  describe("when receiving an optional prop arguable with value true", () => {
133
141
  it("should render a div with class 'opinion-toggle'", () => {
134
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} arguable />);
142
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
135
143
  expect(wrapper.find('.opinion-toggle')).to.be.present();
136
144
  });
137
145
 
138
146
  it("should set state alignment to 1 if user clicks ok button and change its class", () => {
139
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} arguable />);
147
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
140
148
  wrapper.find('.opinion-toggle--ok').simulate('click');
141
149
  expect(wrapper.find('.opinion-toggle--ok')).to.have.className('is-active');
142
150
  expect(wrapper).to.have.state('alignment').equal(1);
143
151
  });
144
152
 
145
153
  it("should set state alignment to -11 if user clicks ko button and change its class", () => {
146
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} arguable />);
154
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
147
155
  wrapper.find('.opinion-toggle--ko').simulate('click');
148
156
  expect(wrapper.find('.opinion-toggle--ko')).to.have.className('is-active');
149
157
  expect(wrapper).to.have.state('alignment').equal(-1);
@@ -156,7 +164,7 @@ describe("<AddCommentForm />", () => {
156
164
 
157
165
  beforeEach(() => {
158
166
  addComment = sinon.spy();
159
- wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentableId={commentableId} commentableType={commentableType} arguable />);
167
+ wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} arguable />);
160
168
  message = 'This will be submitted';
161
169
  wrapper.instance().bodyTextArea.value = message;
162
170
  });
@@ -184,12 +192,12 @@ describe("<AddCommentForm />", () => {
184
192
  });
185
193
 
186
194
  it("should have a reference to user_group_id select", () => {
187
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
195
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
188
196
  expect(wrapper.instance().userGroupIdSelect).to.be.ok;
189
197
  });
190
198
 
191
199
  it("should render a select with option tags for each verified user group", () => {
192
- const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentableId={commentableId} commentableType={commentableType} />);
200
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
193
201
  expect(wrapper.find('select')).to.have.exactly(3).descendants('option');
194
202
  });
195
203
 
@@ -201,7 +209,7 @@ describe("<AddCommentForm />", () => {
201
209
 
202
210
  beforeEach(() => {
203
211
  addComment = sinon.spy();
204
- wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentableId={commentableId} commentableType={commentableType} />);
212
+ wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} />);
205
213
  message = 'This will be submitted';
206
214
  userGroupId = session.verifiedUserGroups[1].id;
207
215
  wrapper.instance().bodyTextArea.value = message;
@@ -224,5 +232,16 @@ describe("<AddCommentForm />", () => {
224
232
  });
225
233
  });
226
234
  });
227
- })
235
+ });
236
+
237
+ describe("when session is null", () => {
238
+ beforeEach(() => {
239
+ session = null;
240
+ });
241
+
242
+ it("display a message to sign in or sign up", () => {
243
+ const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
244
+ expect(wrapper.find('span')).to.include.text("sign up");
245
+ });
246
+ });
228
247
  });
@@ -1,5 +1,7 @@
1
1
  mutation addComment($commentableId: String!, $commentableType: String!, $body: String!, $alignment: Int, $userGroupId: ID) {
2
- addComment(commentableId: $commentableId, commentableType: $commentableType, body: $body, alignment: $alignment, userGroupId: $userGroupId) {
3
- ...CommentThread
2
+ commentable(id: $commentableId, type: $commentableType) {
3
+ addComment(body: $body, alignment: $alignment, userGroupId: $userGroupId) {
4
+ ...CommentThread
5
+ }
4
6
  }
5
- }
7
+ }
@@ -0,0 +1,4 @@
1
+ fragment AddCommentFormCommentable on Commentable {
2
+ id
3
+ type
4
+ }
@@ -0,0 +1,6 @@
1
+ fragment AddCommentFormSession on Session {
2
+ verifiedUserGroups {
3
+ id
4
+ name
5
+ }
6
+ }
@@ -68,10 +68,10 @@ class Comment extends Component {
68
68
  * @returns {Void|DOMElement} - Render the reply button or not if user can reply
69
69
  */
70
70
  _renderReplyButton() {
71
- const { comment: { canHaveReplies }, session } = this.props;
71
+ const { comment: { acceptsNewComments }, session } = this.props;
72
72
  const { showReplyForm } = this.state;
73
73
 
74
- if (session && canHaveReplies) {
74
+ if (session && acceptsNewComments) {
75
75
  return (
76
76
  <button
77
77
  className="comment__reply muted-link"
@@ -92,12 +92,11 @@ class Comment extends Component {
92
92
  * @returns {Void|DOMElement} - Render the reply button or not if user can reply
93
93
  */
94
94
  _renderAdditionalReplyButton() {
95
- const { comment: { canHaveReplies, hasReplies }, session, isRootComment } = this.props;
95
+ const { comment: { acceptsNewComments, hasComments }, session, isRootComment } = this.props;
96
96
  const { showReplyForm } = this.state;
97
97
 
98
- if (session && canHaveReplies) {
99
- if (hasReplies && isRootComment) {
100
-
98
+ if (session && acceptsNewComments) {
99
+ if (hasComments && isRootComment) {
101
100
  return (
102
101
  <div className="comment__additionalreply">
103
102
  <button
@@ -135,23 +134,23 @@ class Comment extends Component {
135
134
  }
136
135
 
137
136
  /**
138
- * Render comment replies alternating the css class
137
+ * Render comment's comments alternating the css class
139
138
  * @private
140
- * @returns {Void|DomElement} - A wrapper element with comment replies inside
139
+ * @returns {Void|DomElement} - A wrapper element with comment's comments inside
141
140
  */
142
141
  _renderReplies() {
143
- const { comment: { id, replies }, session, votable, articleClassName } = this.props;
142
+ const { comment: { id, hasComments, comments }, session, votable, articleClassName } = this.props;
144
143
  let replyArticleClassName = 'comment comment--nested';
145
144
 
146
145
  if (articleClassName === 'comment comment--nested') {
147
146
  replyArticleClassName = `${replyArticleClassName} comment--nested--alt`;
148
147
  }
149
148
 
150
- if (replies) {
149
+ if (hasComments) {
151
150
  return (
152
151
  <div>
153
152
  {
154
- replies.map((reply) => (
153
+ comments.map((reply) => (
155
154
  <Comment
156
155
  key={`comment_${id}_reply_${reply.id}`}
157
156
  comment={reply}
@@ -180,9 +179,8 @@ class Comment extends Component {
180
179
  if (showReplyForm) {
181
180
  return (
182
181
  <AddCommentForm
183
- commentableId={comment.id}
184
- commentableType="Decidim::Comments::Comment"
185
182
  session={session}
183
+ commentable={comment}
186
184
  showTitle={false}
187
185
  submitButtonClassName="button small hollow"
188
186
  onCommentAdded={() => this.setState({ showReplyForm: false })}
@@ -241,11 +239,6 @@ Comment.fragments = {
241
239
  `
242
240
  };
243
241
 
244
- Comment.defaultProps = {
245
- articleClassName: 'comment',
246
- isRootComment: false
247
- };
248
-
249
242
  Comment.propTypes = {
250
243
  comment: PropTypes.oneOfType([
251
244
  propType(Comment.fragments.comment).isRequired,
@@ -259,4 +252,11 @@ Comment.propTypes = {
259
252
  votable: PropTypes.bool
260
253
  };
261
254
 
255
+ Comment.defaultProps = {
256
+ articleClassName: 'comment',
257
+ isRootComment: false,
258
+ session: null,
259
+ votable: false
260
+ };
261
+
262
262
  export default Comment;
@@ -27,7 +27,7 @@ describe("<Comment />", () => {
27
27
 
28
28
  beforeEach(() => {
29
29
  let commentsData = generateCommentsData(1);
30
- commentsData[0].replies = generateCommentsData(3);
30
+ commentsData[0].comments = generateCommentsData(3);
31
31
 
32
32
  const fragment = gql`
33
33
  ${commentFragment}
@@ -77,50 +77,40 @@ describe("<Comment />", () => {
77
77
  expect(wrapper.find(AddCommentForm)).not.to.be.present();
78
78
  wrapper.find('button.comment__reply').simulate('click');
79
79
  expect(wrapper.find(AddCommentForm)).to.have.prop('session').deep.equal(session);
80
- expect(wrapper.find(AddCommentForm)).to.have.prop('commentableId').equal(comment.id);
81
- expect(wrapper.find(AddCommentForm)).to.have.prop('commentableType').equal("Decidim::Comments::Comment");
80
+ expect(wrapper.find(AddCommentForm)).to.have.prop('commentable').deep.equal(comment);
82
81
  expect(wrapper.find(AddCommentForm)).to.have.prop('showTitle').equal(false);
83
82
  expect(wrapper.find(AddCommentForm)).to.have.prop('submitButtonClassName').equal('button small hollow');
84
83
  });
85
84
 
86
- it("should not render the reply button if the comment cannot have replies", () => {
87
- comment.canHaveReplies = false;
88
- const wrapper = shallow(<Comment comment={comment} session={session} />);
89
- expect(wrapper.find('button.comment__reply')).not.to.be.present();
90
- });
91
-
92
- it("should not render the additional reply button if the parent comment has no replies and isRootcomment", () => {
93
- comment.canHaveReplies = true;
94
- comment.hasReplies = false;
85
+ it("should not render the additional reply button if the parent comment has no comments and isRootcomment", () => {
86
+ comment.hasComments = false;
95
87
  const wrapper = shallow(<Comment comment={comment} session={session} isRootComment />);
96
88
  expect(wrapper.find('div.comment__additionalreply')).not.to.be.present();
97
89
  });
98
90
 
99
- it("should not render the additional reply button if the parent comment has replies and not isRootcomment", () => {
100
- comment.canHaveReplies = true;
101
- comment.hasReplies = true;
91
+ it("should not render the additional reply button if the parent comment has comments and not isRootcomment", () => {
92
+ comment.hasComments = true;
102
93
  const wrapper = shallow(<Comment comment={comment} session={session} />);
103
94
  expect(wrapper.find('div.comment__additionalreply')).not.to.be.present();
104
95
  });
105
96
 
106
- it("should render the additional reply button if the parent comment has replies and isRootcomment", () => {
107
- comment.canHaveReplies = true;
108
- comment.hasReplies = true;
97
+ it("should render the additional reply button if the parent comment has comments and isRootcomment", () => {
98
+ comment.hasComments = true;
109
99
  const wrapper = shallow(<Comment comment={comment} session={session} isRootComment />);
110
100
  expect(wrapper.find('div.comment__additionalreply')).to.be.present();
111
101
  });
112
102
 
113
- it("should render comment replies a separate Comment components", () => {
103
+ it("should render comment's comments as a separate Comment components", () => {
114
104
  const wrapper = shallow(<Comment comment={comment} session={session} votable />);
115
105
  wrapper.find(Comment).forEach((node, idx) => {
116
- expect(node).to.have.prop("comment").deep.equal(comment.replies[idx]);
106
+ expect(node).to.have.prop("comment").deep.equal(comment.comments[idx]);
117
107
  expect(node).to.have.prop("session").deep.equal(session);
118
108
  expect(node).to.have.prop("articleClassName").equal("comment comment--nested")
119
109
  expect(node).to.have.prop("votable").equal(true);
120
110
  });
121
111
  });
122
112
 
123
- it("should render comment replies with articleClassName as 'comment comment--nested comment--nested--alt' when articleClassName is 'comment comment--nested'", () => {
113
+ it("should render comment's comments with articleClassName as 'comment comment--nested comment--nested--alt' when articleClassName is 'comment comment--nested'", () => {
124
114
  const wrapper = shallow(<Comment comment={comment} session={session} articleClassName="comment comment--nested" />);
125
115
  wrapper.find(Comment).forEach((node) => {
126
116
  expect(node).to.have.prop("articleClassName").equal("comment comment--nested comment--nested--alt")
@@ -137,6 +127,17 @@ describe("<Comment />", () => {
137
127
  expect(wrapper).to.have.prop("isRootComment").equal(false);
138
128
  });
139
129
 
130
+ describe("when the comment cannot accept new comments", () => {
131
+ beforeEach(() => {
132
+ comment.acceptsNewComments = false;
133
+ });
134
+
135
+ it("should not render the reply button", () => {
136
+ const wrapper = shallow(<Comment comment={comment} session={session} />);
137
+ expect(wrapper.find('button.comment__reply')).not.to.be.present();
138
+ });
139
+ })
140
+
140
141
  describe("when user is not logged in", () => {
141
142
  beforeEach(() => {
142
143
  session = null;
@@ -1,12 +1,12 @@
1
1
  fragment Comment on Comment {
2
2
  ...CommentData
3
- replies {
3
+ comments {
4
4
  ...CommentData
5
- replies {
5
+ comments {
6
6
  ...CommentData
7
- replies {
7
+ comments {
8
8
  ...CommentData
9
- }
9
+ }
10
10
  }
11
11
  }
12
12
  }
@@ -1,13 +1,14 @@
1
1
  fragment CommentData on Comment {
2
2
  id
3
+ type
3
4
  body
4
5
  createdAt
5
6
  author {
6
- name,
7
+ name
7
8
  avatarUrl
8
9
  }
9
- hasReplies
10
- canHaveReplies
10
+ hasComments
11
+ acceptsNewComments
11
12
  alignment
12
13
  ...UpVote
13
14
  ...DownVote
@@ -26,7 +26,7 @@ class CommentOrderSelector extends Component {
26
26
  return (
27
27
  <div className="order-by__dropdown order-by__dropdown--right">
28
28
  <span className="order-by__text">{ I18n.t("components.comment_order_selector.title") }</span>
29
- <ul className="dropdown menu" data-dropdown-menu>
29
+ <ul className="dropdown menu" data-dropdown-menu data-close-on-click-inside="false">
30
30
  <li>
31
31
  <a>{ I18n.t(`components.comment_order_selector.order.${orderBy}`) }</a>
32
32
  <ul className="menu">
@@ -44,7 +44,7 @@ class CommentOrderSelector extends Component {
44
44
  <a href="" onClick={(event) => this._updateOrder(event, "older")} >
45
45
  { I18n.t("components.comment_order_selector.order.older") }
46
46
  </a>
47
- </li>
47
+ </li>
48
48
  <li>
49
49
  <a href="" onClick={(event) => this._updateOrder(event, "most_discussed")} >
50
50
  { I18n.t("components.comment_order_selector.order.most_discussed") }
@@ -57,7 +57,7 @@ class CommentOrderSelector extends Component {
57
57
  );
58
58
  }
59
59
 
60
- _updateOrder(event, orderBy) {
60
+ _updateOrder(event, orderBy) {
61
61
  event.preventDefault();
62
62
  this.setState({ orderBy });
63
63
  this.props.reorderComments(orderBy);
@@ -33,14 +33,14 @@ class CommentThread extends Component {
33
33
  }
34
34
 
35
35
  /**
36
- * Render conversation title if comment has replies
36
+ * Render conversation title if comment has commments
37
37
  * @private
38
38
  * @returns {Void|DOMElement} - The conversation's title
39
39
  */
40
40
  _renderTitle() {
41
- const { comment: { author, hasReplies } } = this.props;
41
+ const { comment: { author, hasComments } } = this.props;
42
42
 
43
- if (hasReplies) {
43
+ if (hasComments) {
44
44
  return (
45
45
  <h6 className="comment-thread__title">
46
46
  { I18n.t("components.comment_thread.title", { authorName: author.name }) }
@@ -67,4 +67,9 @@ CommentThread.propTypes = {
67
67
  votable: PropTypes.bool
68
68
  };
69
69
 
70
+ CommentThread.defaultProps = {
71
+ session: null,
72
+ votable: false
73
+ };
74
+
70
75
  export default CommentThread;