decidim-comments 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -0
  3. data/app/assets/javascripts/decidim/comments/bundle.js +44 -44
  4. data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -1
  5. data/app/assets/javascripts/decidim/comments/comments.js.erb +2 -0
  6. data/app/frontend/application/{apollo_client.js → apollo_client.ts} +5 -5
  7. data/app/frontend/application/application.component.test.tsx +36 -0
  8. data/app/frontend/application/application.component.tsx +37 -0
  9. data/app/frontend/application/icon.component.test.tsx +49 -0
  10. data/app/frontend/application/icon.component.tsx +35 -0
  11. data/app/frontend/comments/{add_comment_form.component.test.jsx → add_comment_form.component.test.tsx} +98 -92
  12. data/app/frontend/comments/{add_comment_form.component.jsx → add_comment_form.component.tsx} +152 -153
  13. data/app/frontend/comments/{comment.component.test.jsx → comment.component.test.tsx} +59 -71
  14. data/app/frontend/comments/{comment.component.jsx → comment.component.tsx} +114 -116
  15. data/app/frontend/comments/comment_order_selector.component.test.tsx +21 -0
  16. data/app/frontend/comments/comment_order_selector.component.tsx +88 -0
  17. data/app/frontend/comments/comment_thread.component.test.tsx +65 -0
  18. data/app/frontend/comments/comment_thread.component.tsx +70 -0
  19. data/app/frontend/comments/{comments.component.test.jsx → comments.component.test.tsx} +38 -81
  20. data/app/frontend/comments/{comments.component.jsx → comments.component.tsx} +49 -63
  21. data/app/frontend/comments/down_vote_button.component.test.tsx +39 -0
  22. data/app/frontend/comments/down_vote_button.component.tsx +89 -0
  23. data/app/frontend/comments/up_vote_button.component.test.tsx +39 -0
  24. data/app/frontend/comments/up_vote_button.component.tsx +89 -0
  25. data/app/frontend/comments/vote_button.component.tsx +36 -0
  26. data/app/frontend/comments/{vote_button_component.test.jsx → vote_button_component.test.tsx} +16 -20
  27. data/app/frontend/entry.ts +19 -0
  28. data/app/frontend/{comments → fragments}/add_comment_form_commentable.fragment.graphql +1 -1
  29. data/app/frontend/{comments → fragments}/add_comment_form_session.fragment.graphql +1 -1
  30. data/app/frontend/{comments → fragments}/comment.fragment.graphql +3 -1
  31. data/app/frontend/{comments → fragments}/comment_data.fragment.graphql +6 -3
  32. data/app/frontend/{comments → fragments}/comment_thread.fragment.graphql +3 -1
  33. data/app/frontend/{comments/down_vote.fragment.graphql → fragments/down_vote_button.fragment.graphql} +2 -2
  34. data/app/frontend/{comments/up_vote.fragment.graphql → fragments/up_vote_button.fragment.graphql} +2 -2
  35. data/app/frontend/{comments/add_comment_form.mutation.graphql → mutations/add_comment.mutation.graphql} +3 -1
  36. data/app/frontend/{comments → mutations}/down_vote.mutation.graphql +3 -1
  37. data/app/frontend/{comments → mutations}/up_vote.mutation.graphql +3 -1
  38. data/app/frontend/{comments → queries}/comments.query.graphql +4 -1
  39. data/app/frontend/support/{asset_url.js → asset_url.ts} +1 -1
  40. data/app/frontend/support/{generate_comments_data.js → generate_comments_data.ts} +11 -6
  41. data/app/frontend/support/{generate_user_data.js → generate_user_data.ts} +2 -2
  42. data/app/frontend/support/{generate_user_group_data.js → generate_user_group_data.ts} +2 -2
  43. data/app/frontend/support/graphql_transformer.js +32 -0
  44. data/app/frontend/support/load_translations.ts +44 -0
  45. data/app/frontend/support/{require_all.js → require_all.ts} +1 -1
  46. data/app/frontend/support/{resolve_graphql_query.js → resolve_graphql_query.ts} +7 -7
  47. data/app/frontend/support/schema.ts +119 -0
  48. data/config/locales/eu.yml +29 -5
  49. metadata +49 -51
  50. data/app/frontend/application/application.component.jsx +0 -37
  51. data/app/frontend/application/application.component.test.jsx +0 -33
  52. data/app/frontend/application/icon.component.jsx +0 -26
  53. data/app/frontend/application/icon.component.test.jsx +0 -53
  54. data/app/frontend/comments/comment_order_selector.component.jsx +0 -72
  55. data/app/frontend/comments/comment_order_selector.component.test.jsx +0 -20
  56. data/app/frontend/comments/comment_thread.component.jsx +0 -75
  57. data/app/frontend/comments/comment_thread.component.test.jsx +0 -83
  58. data/app/frontend/comments/down_vote_button.component.jsx +0 -98
  59. data/app/frontend/comments/down_vote_button.component.test.jsx +0 -48
  60. data/app/frontend/comments/featured_comment.component.jsx +0 -23
  61. data/app/frontend/comments/featured_comment.component.test.jsx +0 -15
  62. data/app/frontend/comments/up_vote_button.component.jsx +0 -98
  63. data/app/frontend/comments/up_vote_button.component.test.jsx +0 -48
  64. data/app/frontend/comments/vote_button.component.jsx +0 -32
  65. data/app/frontend/entry.js +0 -17
  66. data/app/frontend/entry.test.js +0 -31
  67. data/app/frontend/support/load_translations.js +0 -23
  68. data/app/frontend/support/stub_component.js +0 -29
@@ -1,130 +1,118 @@
1
- /* eslint-disable no-unused-expressions */
2
- import { shallow, mount } from 'enzyme';
3
- import { filter } from 'graphql-anywhere';
4
- import gql from 'graphql-tag';
1
+ import { mount, shallow } from "enzyme";
2
+ import * as $ from "jquery";
3
+ import * as React from "react";
5
4
 
6
- import Comment from './comment.component';
7
- import AddCommentForm from './add_comment_form.component';
8
- import UpVoteButton from './up_vote_button.component';
9
- import DownVoteButton from './down_vote_button.component';
5
+ import { CommentFragment } from "../support/schema";
6
+ import AddCommentForm from "./add_comment_form.component";
7
+ import Comment from "./comment.component";
8
+ import DownVoteButton from "./down_vote_button.component";
9
+ import UpVoteButton from "./up_vote_button.component";
10
10
 
11
- import commentFragment from './comment.fragment.graphql';
12
- import commentDataFragment from './comment_data.fragment.graphql';
13
- import upVoteFragment from './up_vote.fragment.graphql';
14
- import downVoteFragment from './down_vote.fragment.graphql';
11
+ import generateCommentsData from "../support/generate_comments_data";
12
+ import generateUserData from "../support/generate_user_data";
15
13
 
16
- import stubComponent from '../support/stub_component';
17
- import generateCommentsData from '../support/generate_comments_data';
18
- import generateUserData from '../support/generate_user_data';
14
+ import { loadLocaleTranslations } from "../support/load_translations";
19
15
 
20
16
  describe("<Comment />", () => {
21
- let comment = {};
22
- let session = null;
23
-
24
- stubComponent(AddCommentForm);
25
- stubComponent(UpVoteButton);
26
- stubComponent(DownVoteButton);
17
+ let comment: CommentFragment;
18
+ let session: any = null;
27
19
 
28
20
  beforeEach(() => {
21
+ loadLocaleTranslations("en");
29
22
  let commentsData = generateCommentsData(1);
30
23
  commentsData[0].comments = generateCommentsData(3);
31
24
 
32
- const fragment = gql`
33
- ${commentFragment}
34
- ${commentDataFragment}
35
- ${upVoteFragment}
36
- ${downVoteFragment}
37
- `;
38
-
39
- comment = filter(fragment, commentsData[0]);
25
+ comment = commentsData[0];
40
26
  session = {
41
- user: generateUserData()
42
- }
27
+ user: generateUserData(),
28
+ };
29
+
30
+ window.$ = $;
43
31
  });
44
32
 
45
33
  it("should render an article with class comment", () => {
46
34
  const wrapper = shallow(<Comment comment={comment} session={session} />);
47
- expect(wrapper.find('article.comment')).to.present();
35
+ expect(wrapper.find("article.comment").exists()).toBeTruthy();
48
36
  });
49
37
 
50
38
  it("should render a time tag with comment's created at", () => {
51
39
  const wrapper = shallow(<Comment comment={comment} session={session} />);
52
- expect(wrapper.find('time')).to.have.text(comment.created_at);
40
+ expect(wrapper.find("time").prop("dateTime")).toEqual(comment.createdAt);
53
41
  });
54
42
 
55
43
  it("should render author's name in a link with class author__name", () => {
56
44
  const wrapper = shallow(<Comment comment={comment} session={session} />);
57
- expect(wrapper.find('a.author__name')).to.have.text(comment.author.name);
45
+ expect(wrapper.find("a.author__name").text()).toEqual(comment.author.name);
58
46
  });
59
47
 
60
48
  it("should render author's avatar as a image tag", () => {
61
49
  const wrapper = shallow(<Comment comment={comment} session={session} />);
62
- expect(wrapper.find('a.author__avatar img')).to.have.attr('src').equal(comment.author.avatarUrl);
50
+ expect(wrapper.find("a.author__avatar img").prop("src")).toEqual(comment.author.avatarUrl);
63
51
  });
64
52
 
65
53
  it("should render comment's body on a div with class comment__content", () => {
66
54
  const wrapper = shallow(<Comment comment={comment} session={session} />);
67
- expect(wrapper.find('div.comment__content')).to.have.text(comment.body);
55
+ expect(wrapper.find("div.comment__content").text()).toEqual(comment.body);
68
56
  });
69
57
 
70
58
  it("should initialize with a state property showReplyForm as false", () => {
71
59
  const wrapper = shallow(<Comment comment={comment} session={session} />);
72
- expect(wrapper).to.have.state('showReplyForm', false);
60
+ expect(wrapper.state()).toHaveProperty("showReplyForm", false);
73
61
  });
74
62
 
75
63
  it("should render a AddCommentForm component with the correct props when clicking the reply button", () => {
76
64
  const wrapper = shallow(<Comment comment={comment} session={session} />);
77
- expect(wrapper.find(AddCommentForm)).not.to.be.present();
78
- wrapper.find('button.comment__reply').simulate('click');
79
- expect(wrapper.find(AddCommentForm)).to.have.prop('session').deep.equal(session);
80
- expect(wrapper.find(AddCommentForm)).to.have.prop('commentable').deep.equal(comment);
81
- expect(wrapper.find(AddCommentForm)).to.have.prop('showTitle').equal(false);
82
- expect(wrapper.find(AddCommentForm)).to.have.prop('submitButtonClassName').equal('button small hollow');
65
+ expect(wrapper.find(AddCommentForm).exists()).toBeFalsy();
66
+ wrapper.find("button.comment__reply").simulate("click");
67
+ expect(wrapper.find(AddCommentForm).prop("session")).toEqual(session);
68
+ expect(wrapper.find(AddCommentForm).prop("commentable")).toEqual(comment);
69
+ expect(wrapper.find(AddCommentForm).prop("showTitle")).toBeFalsy();
70
+ expect(wrapper.find(AddCommentForm).prop("submitButtonClassName")).toEqual("button small hollow");
83
71
  });
84
72
 
85
73
  it("should not render the additional reply button if the parent comment has no comments and isRootcomment", () => {
86
74
  comment.hasComments = false;
87
- const wrapper = shallow(<Comment comment={comment} session={session} isRootComment />);
88
- expect(wrapper.find('div.comment__additionalreply')).not.to.be.present();
75
+ const wrapper = shallow(<Comment comment={comment} session={session} isRootComment={true} />);
76
+ expect(wrapper.find("div.comment__additionalreply").exists()).toBeFalsy();
89
77
  });
90
78
 
91
- it("should not render the additional reply button if the parent comment has comments and not isRootcomment", () => {
79
+ it("should not render the additional reply button if the parent comment has comments and not isRootcomment", () => {
92
80
  comment.hasComments = true;
93
81
  const wrapper = shallow(<Comment comment={comment} session={session} />);
94
- expect(wrapper.find('div.comment__additionalreply')).not.to.be.present();
82
+ expect(wrapper.find("div.comment__additionalreply").exists()).toBeFalsy();
95
83
  });
96
84
 
97
85
  it("should render the additional reply button if the parent comment has comments and isRootcomment", () => {
98
86
  comment.hasComments = true;
99
- const wrapper = shallow(<Comment comment={comment} session={session} isRootComment />);
100
- expect(wrapper.find('div.comment__additionalreply')).to.be.present();
87
+ const wrapper = shallow(<Comment comment={comment} session={session} isRootComment={true} />);
88
+ expect(wrapper.find("div.comment__additionalreply").exists()).toBeTruthy();
101
89
  });
102
90
 
103
91
  it("should render comment's comments as a separate Comment components", () => {
104
- const wrapper = shallow(<Comment comment={comment} session={session} votable />);
92
+ const wrapper = shallow(<Comment comment={comment} session={session} votable={true} />);
105
93
  wrapper.find(Comment).forEach((node, idx) => {
106
- expect(node).to.have.prop("comment").deep.equal(comment.comments[idx]);
107
- expect(node).to.have.prop("session").deep.equal(session);
108
- expect(node).to.have.prop("articleClassName").equal("comment comment--nested")
109
- expect(node).to.have.prop("votable").equal(true);
94
+ expect(node.prop("comment")).toEqual(comment.comments[idx]);
95
+ expect(node.prop("session")).toEqual(session);
96
+ expect(node.prop("articleClassName")).toEqual("comment comment--nested");
97
+ expect(node.prop("votable")).toBeTruthy();
110
98
  });
111
99
  });
112
100
 
113
101
  it("should render comment's comments with articleClassName as 'comment comment--nested comment--nested--alt' when articleClassName is 'comment comment--nested'", () => {
114
102
  const wrapper = shallow(<Comment comment={comment} session={session} articleClassName="comment comment--nested" />);
115
103
  wrapper.find(Comment).forEach((node) => {
116
- expect(node).to.have.prop("articleClassName").equal("comment comment--nested comment--nested--alt")
104
+ expect(node.prop("articleClassName")).toEqual("comment comment--nested comment--nested--alt");
117
105
  });
118
106
  });
119
107
 
120
108
  it("should have a default prop articleClassName with value 'comment'", () => {
121
109
  const wrapper = mount(<Comment comment={comment} session={session} />);
122
- expect(wrapper).to.have.prop("articleClassName").equal("comment");
110
+ expect(wrapper.prop("articleClassName")).toEqual("comment");
123
111
  });
124
112
 
125
113
  it("should have a default prop isRootComment with value false", () => {
126
114
  const wrapper = mount(<Comment comment={comment} session={session} />);
127
- expect(wrapper).to.have.prop("isRootComment").equal(false);
115
+ expect(wrapper.prop("isRootComment")).toBeFalsy();
128
116
  });
129
117
 
130
118
  describe("when the comment cannot accept new comments", () => {
@@ -134,9 +122,9 @@ describe("<Comment />", () => {
134
122
 
135
123
  it("should not render the reply button", () => {
136
124
  const wrapper = shallow(<Comment comment={comment} session={session} />);
137
- expect(wrapper.find('button.comment__reply')).not.to.be.present();
125
+ expect(wrapper.find("button.comment__reply").exists()).toBeFalsy();
138
126
  });
139
- })
127
+ });
140
128
 
141
129
  describe("when user is not logged in", () => {
142
130
  beforeEach(() => {
@@ -145,49 +133,49 @@ describe("<Comment />", () => {
145
133
 
146
134
  it("should not render reply button", () => {
147
135
  const wrapper = shallow(<Comment comment={comment} session={session} />);
148
- expect(wrapper.find('button.comment__reply')).not.to.be.present();
136
+ expect(wrapper.find("button.comment__reply").exists()).toBeFalsy();
149
137
  });
150
138
 
151
139
  it("should not render the flag modal", () => {
152
140
  const wrapper = shallow(<Comment comment={comment} session={session} />);
153
- expect(wrapper.find('.flag-modal')).not.to.be.present();
141
+ expect(wrapper.find(".flag-modal").exists()).toBeFalsy();
154
142
  });
155
143
  });
156
144
 
157
145
  it("should render a 'in favor' badge if comment's alignment is 1", () => {
158
146
  comment.alignment = 1;
159
147
  const wrapper = shallow(<Comment comment={comment} session={session} />);
160
- expect(wrapper.find('span.success.label')).to.have.text('In favor');
148
+ expect(wrapper.find("span.success.label").text()).toEqual("In favor");
161
149
  });
162
150
 
163
151
  it("should render a 'against' badge if comment's alignment is -1", () => {
164
152
  comment.alignment = -1;
165
153
  const wrapper = shallow(<Comment comment={comment} session={session} />);
166
- expect(wrapper.find('span.alert.label')).to.have.text('Against');
154
+ expect(wrapper.find("span.alert.label").text()).toEqual("Against");
167
155
  });
168
156
 
169
157
  it("should render the flag modal", () => {
170
158
  const wrapper = shallow(<Comment comment={comment} session={session} />);
171
- expect(wrapper.find('.flag-modal')).to.be.present();
159
+ expect(wrapper.find(".flag-modal").exists()).toBeTruthy();
172
160
  });
173
161
 
174
162
  describe("when user has already reported the comment", () => {
175
163
  it("should not render the flag form", () => {
176
164
  comment.alreadyReported = true;
177
165
  const wrapper = shallow(<Comment comment={comment} session= {session} />);
178
- expect(wrapper.find('.flag-modal form')).not.to.be.present();
166
+ expect(wrapper.find(".flag-modal form").exists()).toBeFalsy();
179
167
  });
180
168
  });
181
169
 
182
170
  describe("when the comment is votable", () => {
183
171
  it("should render an UpVoteButton component", () => {
184
- const wrapper = shallow(<Comment comment={comment} session={session} votable />);
185
- expect(wrapper.find(UpVoteButton)).to.have.prop("comment").deep.equal(comment);
186
- })
172
+ const wrapper = shallow(<Comment comment={comment} session={session} votable={true} />);
173
+ expect(wrapper.find(UpVoteButton).prop("comment")).toEqual(comment);
174
+ });
187
175
 
188
176
  it("should render an DownVoteButton component", () => {
189
- const wrapper = shallow(<Comment comment={comment} session={session} votable />);
190
- expect(wrapper.find(DownVoteButton)).to.have.prop("comment").deep.equal(comment);
191
- })
177
+ const wrapper = shallow(<Comment comment={comment} session={session} votable={true} />);
178
+ expect(wrapper.find(DownVoteButton).prop("comment")).toEqual(comment);
179
+ });
192
180
  });
193
181
  });
@@ -1,44 +1,66 @@
1
- /* eslint-disable max-lines, camelcase */
2
- import { Component, PropTypes } from 'react';
3
- import { propType } from 'graphql-anywhere';
4
- import gql from 'graphql-tag';
5
- import moment from 'moment';
6
- import { I18n } from 'react-i18nify';
7
- import classnames from 'classnames';
8
-
9
- import AddCommentForm from './add_comment_form.component';
10
- import UpVoteButton from './up_vote_button.component';
11
- import DownVoteButton from './down_vote_button.component';
12
- import Icon from '../application/icon.component';
13
-
14
- import commentFragment from './comment.fragment.graphql';
15
- import commentDataFragment from './comment_data.fragment.graphql';
1
+ import * as classnames from "classnames";
2
+ import * as moment from "moment";
3
+ import * as React from "react";
4
+
5
+ import Icon from "../application/icon.component";
6
+
7
+ import AddCommentForm from "./add_comment_form.component";
8
+ import DownVoteButton from "./down_vote_button.component";
9
+ import UpVoteButton from "./up_vote_button.component";
10
+
11
+ import {
12
+ AddCommentFormSessionFragment,
13
+ CommentFragment,
14
+ } from "../support/schema";
15
+
16
+ const { I18n } = require("react-i18nify");
17
+
18
+ interface CommentProps {
19
+ comment: CommentFragment;
20
+ session: AddCommentFormSessionFragment & {
21
+ user: any;
22
+ } | null;
23
+ articleClassName?: string;
24
+ isRootComment?: boolean;
25
+ votable?: boolean;
26
+ }
27
+
28
+ interface CommentState {
29
+ showReplyForm: boolean;
30
+ }
16
31
 
17
32
  /**
18
33
  * A single comment component with the author info and the comment's body
19
34
  * @class
20
35
  * @augments Component
21
36
  */
22
- class Comment extends Component {
23
- constructor(props) {
37
+ class Comment extends React.Component<CommentProps, CommentState> {
38
+ public static defaultProps: any = {
39
+ articleClassName: "comment",
40
+ isRootComment: false,
41
+ session: null,
42
+ votable: false,
43
+ };
44
+
45
+ constructor(props: CommentProps) {
24
46
  super(props);
25
47
 
26
48
  this.state = {
27
- showReplyForm: false
49
+ showReplyForm: false,
28
50
  };
29
51
  }
30
52
 
31
- componentDidMount() {
32
- if ($(document).foundation) {
53
+ public componentDidMount() {
54
+ if (window.$(document).foundation) {
33
55
  const { comment: { id } } = this.props;
34
- $(`#flagModalComment${id}`).foundation();
56
+ window.$(`#flagModalComment${id}`).foundation();
35
57
  }
36
58
  }
37
59
 
38
- render() {
60
+ public render(): JSX.Element {
39
61
  const { session, comment: { id, author, body, createdAt }, articleClassName } = this.props;
40
62
  const formattedCreatedAt = ` ${moment(createdAt).format("LLL")}`;
41
- let modalName = 'loginModal';
63
+ let modalName = "loginModal";
42
64
 
43
65
  if (session && session.user) {
44
66
  modalName = `flagModalComment${id}`;
@@ -58,8 +80,8 @@ class Comment extends Component {
58
80
  </div>
59
81
  </div>
60
82
  <div className="author-data__extra">
61
- <button type="button" title={ I18n.t("components.comment.report.title") } data-open={modalName}>
62
- <Icon name="icon-flag" iconExtraClassName="icon--small"></Icon>
83
+ <button type="button" title={I18n.t("components.comment.report.title")} data-open={modalName}>
84
+ <Icon name="icon-flag" iconExtraClassName="icon--small" />
63
85
  </button>
64
86
  {this._renderFlagModal()}
65
87
  </div>
@@ -67,8 +89,8 @@ class Comment extends Component {
67
89
  </div>
68
90
  <div className="comment__content">
69
91
  <p>
70
- { this._renderAlignmentBadge() }
71
- { body }
92
+ {this._renderAlignmentBadge()}
93
+ {body}
72
94
  </p>
73
95
  </div>
74
96
  <div className="comment__footer">
@@ -82,23 +104,27 @@ class Comment extends Component {
82
104
  );
83
105
  }
84
106
 
107
+ private toggleReplyForm = () => {
108
+ const { showReplyForm } = this.state;
109
+ this.setState({ showReplyForm: !showReplyForm });
110
+ }
111
+
85
112
  /**
86
113
  * Render reply button if user can reply the comment
87
114
  * @private
88
115
  * @returns {Void|DOMElement} - Render the reply button or not if user can reply
89
116
  */
90
- _renderReplyButton() {
117
+ private _renderReplyButton() {
91
118
  const { comment: { acceptsNewComments }, session } = this.props;
92
- const { showReplyForm } = this.state;
93
119
 
94
120
  if (session && acceptsNewComments) {
95
121
  return (
96
122
  <button
97
123
  className="comment__reply muted-link"
98
124
  aria-controls="comment1-reply"
99
- onClick={() => this.setState({ showReplyForm: !showReplyForm })}
125
+ onClick={this.toggleReplyForm}
100
126
  >
101
- { I18n.t("components.comment.reply") }
127
+ {I18n.t("components.comment.reply")}
102
128
  </button>
103
129
  );
104
130
  }
@@ -106,14 +132,13 @@ class Comment extends Component {
106
132
  return <span>&nbsp;</span>;
107
133
  }
108
134
 
109
- /**
135
+ /**
110
136
  * Render additional reply button if user can reply the comment at the bottom of a conversation
111
137
  * @private
112
138
  * @returns {Void|DOMElement} - Render the reply button or not if user can reply
113
139
  */
114
- _renderAdditionalReplyButton() {
140
+ private _renderAdditionalReplyButton() {
115
141
  const { comment: { acceptsNewComments, hasComments }, session, isRootComment } = this.props;
116
- const { showReplyForm } = this.state;
117
142
 
118
143
  if (session && acceptsNewComments) {
119
144
  if (hasComments && isRootComment) {
@@ -122,9 +147,9 @@ class Comment extends Component {
122
147
  <button
123
148
  className="comment__reply muted-link"
124
149
  aria-controls="comment1-reply"
125
- onClick={() => this.setState({ showReplyForm: !showReplyForm })}
150
+ onClick={this.toggleReplyForm}
126
151
  >
127
- { I18n.t("components.comment.reply") }
152
+ {I18n.t("components.comment.reply")}
128
153
  </button>
129
154
  </div>
130
155
  );
@@ -138,7 +163,7 @@ class Comment extends Component {
138
163
  * @private
139
164
  * @returns {Void|DOMElement} - Render the upVote and downVote buttons or not
140
165
  */
141
- _renderVoteButtons() {
166
+ private _renderVoteButtons() {
142
167
  const { comment, votable } = this.props;
143
168
 
144
169
  if (votable) {
@@ -158,11 +183,11 @@ class Comment extends Component {
158
183
  * @private
159
184
  * @returns {Void|DomElement} - A wrapper element with comment's comments inside
160
185
  */
161
- _renderReplies() {
186
+ private _renderReplies() {
162
187
  const { comment: { id, hasComments, comments }, session, votable, articleClassName } = this.props;
163
- let replyArticleClassName = 'comment comment--nested';
188
+ let replyArticleClassName = "comment comment--nested";
164
189
 
165
- if (articleClassName === 'comment comment--nested') {
190
+ if (articleClassName === "comment comment--nested") {
166
191
  replyArticleClassName = `${replyArticleClassName} comment--nested--alt`;
167
192
  }
168
193
 
@@ -170,7 +195,7 @@ class Comment extends Component {
170
195
  return (
171
196
  <div>
172
197
  {
173
- comments.map((reply) => (
198
+ comments.map((reply: CommentFragment) => (
174
199
  <Comment
175
200
  key={`comment_${id}_reply_${reply.id}`}
176
201
  comment={reply}
@@ -192,19 +217,19 @@ class Comment extends Component {
192
217
  * @private
193
218
  * @returns {Void|ReactElement} - Render the AddCommentForm component or not
194
219
  */
195
- _renderReplyForm() {
220
+ private _renderReplyForm() {
196
221
  const { session, comment } = this.props;
197
222
  const { showReplyForm } = this.state;
198
223
 
199
- if (showReplyForm) {
224
+ if (session && showReplyForm) {
200
225
  return (
201
226
  <AddCommentForm
202
227
  session={session}
203
228
  commentable={comment}
204
229
  showTitle={false}
205
230
  submitButtonClassName="button small hollow"
206
- onCommentAdded={() => this.setState({ showReplyForm: false })}
207
- autoFocus
231
+ onCommentAdded={this.toggleReplyForm}
232
+ autoFocus={true}
208
233
  />
209
234
  );
210
235
  }
@@ -217,25 +242,25 @@ class Comment extends Component {
217
242
  * @private
218
243
  * @returns {Void|DOMElement} - The alignment's badge or not
219
244
  */
220
- _renderAlignmentBadge() {
245
+ private _renderAlignmentBadge() {
221
246
  const { comment: { alignment } } = this.props;
222
- const spanClassName = classnames('label', {
247
+ const spanClassName = classnames("label", {
223
248
  success: alignment === 1,
224
- alert: alignment === -1
249
+ alert: alignment === -1,
225
250
  });
226
251
 
227
- let label = '';
252
+ let label = "";
228
253
 
229
254
  if (alignment === 1) {
230
- label = I18n.t('components.comment.alignment.in_favor');
255
+ label = I18n.t("components.comment.alignment.in_favor");
231
256
  } else {
232
- label = I18n.t('components.comment.alignment.against');
257
+ label = I18n.t("components.comment.alignment.against");
233
258
  }
234
259
 
235
260
  if (alignment === 1 || alignment === -1) {
236
261
  return (
237
262
  <span>
238
- <span className={spanClassName}>{ label }</span>
263
+ <span className={spanClassName}>{label}</span>
239
264
  &nbsp;
240
265
  </span>
241
266
  );
@@ -249,20 +274,25 @@ class Comment extends Component {
249
274
  * @private
250
275
  * @return {Void|DOMElement} - The comment's report modal or not.
251
276
  */
252
- _renderFlagModal() {
277
+ private _renderFlagModal() {
253
278
  const { session, comment: { id, sgid, alreadyReported } } = this.props;
254
279
  const authenticityToken = this._getAuthenticityToken();
255
280
 
281
+ const closeModal = () => {
282
+ window.$(`#flagModalComment${id}`).foundation("close");
283
+ };
284
+
256
285
  if (session && session.user) {
257
286
  return (
258
- <div className="reveal flag-modal" id={`flagModalComment${id}`} data-reveal>
287
+ <div className="reveal flag-modal" id={`flagModalComment${id}`} data-reveal={true}>
259
288
  <div className="reveal__header">
260
- <h3 className="reveal__title">{ I18n.t("components.comment.report.title") }</h3>
289
+ <h3 className="reveal__title">{I18n.t("components.comment.report.title")}</h3>
261
290
  <button
262
291
  className="close-button"
263
- aria-label={ I18n.t("components.comment.report.close") }
292
+ aria-label={I18n.t("components.comment.report.close")}
264
293
  type="button"
265
- onClick={() => $(`#flagModalComment${id}`).foundation('close')}>
294
+ onClick={closeModal}
295
+ >
266
296
  <span aria-hidden="true">&times;</span>
267
297
  </button>
268
298
  </div>
@@ -270,31 +300,33 @@ class Comment extends Component {
270
300
  (() => {
271
301
  if (alreadyReported) {
272
302
  return (
273
- <p key={`already-reported-comment-${id}`}>{ I18n.t("components.comment.report.already_reported") }</p>
303
+ <p key={`already-reported-comment-${id}`}>{I18n.t("components.comment.report.already_reported")}</p>
274
304
  );
275
305
  }
276
306
  return [
277
- <p key={`report-description-comment-${id}`}>{ I18n.t("components.comment.report.description") }</p>,
278
- <form key={`report-form-comment-${id}`} method="post" action={`/report?sgid=${sgid}`}>
279
- <input type="hidden" name="authenticity_token" value={authenticityToken} />
280
- <label htmlFor={`report_comment_${id}_reason_spam`}>
281
- <input type="radio" value="spam" name="report[reason]" id={`report_comment_${id}_reason_spam`} defaultChecked />
282
- { I18n.t("components.comment.report.reasons.spam") }
283
- </label>
284
- <label htmlFor={`report_comment_${id}_reason_offensive`}>
285
- <input type="radio" value="offensive" name="report[reason]" id={`report_comment_${id}_reason_offensive`} />
286
- { I18n.t("components.comment.report.reasons.offensive") }
287
- </label>
288
- <label htmlFor={`report_comment_${id}_reason_does_not_belong`}>
289
- <input type="radio" value="does_not_belong" name="report[reason]" id={`report_comment_${id}_reason_does_not_belong`} />
290
- { I18n.t("components.comment.report.reasons.does_not_belong", { organization_name: session.user.organizationName }) }
291
- </label>
292
- <label htmlFor={`report_comment_${id}_details`}>
293
- { I18n.t("components.comment.report.details") }
294
- <textarea rows="4" name="report[details]" id={`report_comment_${id}_details`} />
295
- </label>
296
- <button type="submit" name="commit" className="button">{ I18n.t("components.comment.report.action") }</button>
297
- </form>
307
+ <p key={`report-description-comment-${id}`}>{I18n.t("components.comment.report.description")}</p>,
308
+ (
309
+ <form key={`report-form-comment-${id}`} method="post" action={`/report?sgid=${sgid}`}>
310
+ <input type="hidden" name="authenticity_token" value={authenticityToken} />
311
+ <label htmlFor={`report_comment_${id}_reason_spam`}>
312
+ <input type="radio" value="spam" name="report[reason]" id={`report_comment_${id}_reason_spam`} defaultChecked={true} />
313
+ {I18n.t("components.comment.report.reasons.spam")}
314
+ </label>
315
+ <label htmlFor={`report_comment_${id}_reason_offensive`}>
316
+ <input type="radio" value="offensive" name="report[reason]" id={`report_comment_${id}_reason_offensive`} />
317
+ {I18n.t("components.comment.report.reasons.offensive")}
318
+ </label>
319
+ <label htmlFor={`report_comment_${id}_reason_does_not_belong`}>
320
+ <input type="radio" value="does_not_belong" name="report[reason]" id={`report_comment_${id}_reason_does_not_belong`} />
321
+ {I18n.t("components.comment.report.reasons.does_not_belong", { organization_name: session.user.organizationName })}
322
+ </label>
323
+ <label htmlFor={`report_comment_${id}_details`}>
324
+ {I18n.t("components.comment.report.details")}
325
+ <textarea rows={4} name="report[details]" id={`report_comment_${id}_details`} />
326
+ </label>
327
+ <button type="submit" name="commit" className="button">{I18n.t("components.comment.report.action")}</button>
328
+ </form>
329
+ ),
298
330
  ];
299
331
  })()
300
332
  }
@@ -309,44 +341,10 @@ class Comment extends Component {
309
341
  * Get Rails authenticity token so we can send requests through the report forms.
310
342
  * @private
311
343
  * @return {string} - The current authenticity token.
312
- */
313
- _getAuthenticityToken() {
314
- return $('meta[name="csrf-token"]').attr('content');
344
+ */
345
+ private _getAuthenticityToken() {
346
+ return window.$('meta[name="csrf-token"]').attr("content");
315
347
  }
316
348
  }
317
349
 
318
- Comment.fragments = {
319
- comment: gql`
320
- ${commentFragment}
321
- ${commentDataFragment}
322
- ${UpVoteButton.fragments.comment}
323
- ${DownVoteButton.fragments.comment}
324
- `,
325
- commentData: gql`
326
- ${commentDataFragment}
327
- ${UpVoteButton.fragments.comment}
328
- ${DownVoteButton.fragments.comment}
329
- `
330
- };
331
-
332
- Comment.propTypes = {
333
- comment: PropTypes.oneOfType([
334
- propType(Comment.fragments.comment).isRequired,
335
- propType(Comment.fragments.commentData).isRequired
336
- ]).isRequired,
337
- session: PropTypes.shape({
338
- user: PropTypes.any.isRequired
339
- }),
340
- articleClassName: PropTypes.string.isRequired,
341
- isRootComment: PropTypes.bool,
342
- votable: PropTypes.bool
343
- };
344
-
345
- Comment.defaultProps = {
346
- articleClassName: 'comment',
347
- isRootComment: false,
348
- session: null,
349
- votable: false
350
- };
351
-
352
350
  export default Comment;