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.
- checksums.yaml +4 -4
- data/README.md +33 -0
- data/app/assets/javascripts/decidim/comments/bundle.js +44 -44
- data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -1
- data/app/assets/javascripts/decidim/comments/comments.js.erb +2 -0
- data/app/frontend/application/{apollo_client.js → apollo_client.ts} +5 -5
- data/app/frontend/application/application.component.test.tsx +36 -0
- data/app/frontend/application/application.component.tsx +37 -0
- data/app/frontend/application/icon.component.test.tsx +49 -0
- data/app/frontend/application/icon.component.tsx +35 -0
- data/app/frontend/comments/{add_comment_form.component.test.jsx → add_comment_form.component.test.tsx} +98 -92
- data/app/frontend/comments/{add_comment_form.component.jsx → add_comment_form.component.tsx} +152 -153
- data/app/frontend/comments/{comment.component.test.jsx → comment.component.test.tsx} +59 -71
- data/app/frontend/comments/{comment.component.jsx → comment.component.tsx} +114 -116
- data/app/frontend/comments/comment_order_selector.component.test.tsx +21 -0
- data/app/frontend/comments/comment_order_selector.component.tsx +88 -0
- data/app/frontend/comments/comment_thread.component.test.tsx +65 -0
- data/app/frontend/comments/comment_thread.component.tsx +70 -0
- data/app/frontend/comments/{comments.component.test.jsx → comments.component.test.tsx} +38 -81
- data/app/frontend/comments/{comments.component.jsx → comments.component.tsx} +49 -63
- data/app/frontend/comments/down_vote_button.component.test.tsx +39 -0
- data/app/frontend/comments/down_vote_button.component.tsx +89 -0
- data/app/frontend/comments/up_vote_button.component.test.tsx +39 -0
- data/app/frontend/comments/up_vote_button.component.tsx +89 -0
- data/app/frontend/comments/vote_button.component.tsx +36 -0
- data/app/frontend/comments/{vote_button_component.test.jsx → vote_button_component.test.tsx} +16 -20
- data/app/frontend/entry.ts +19 -0
- data/app/frontend/{comments → fragments}/add_comment_form_commentable.fragment.graphql +1 -1
- data/app/frontend/{comments → fragments}/add_comment_form_session.fragment.graphql +1 -1
- data/app/frontend/{comments → fragments}/comment.fragment.graphql +3 -1
- data/app/frontend/{comments → fragments}/comment_data.fragment.graphql +6 -3
- data/app/frontend/{comments → fragments}/comment_thread.fragment.graphql +3 -1
- data/app/frontend/{comments/down_vote.fragment.graphql → fragments/down_vote_button.fragment.graphql} +2 -2
- data/app/frontend/{comments/up_vote.fragment.graphql → fragments/up_vote_button.fragment.graphql} +2 -2
- data/app/frontend/{comments/add_comment_form.mutation.graphql → mutations/add_comment.mutation.graphql} +3 -1
- data/app/frontend/{comments → mutations}/down_vote.mutation.graphql +3 -1
- data/app/frontend/{comments → mutations}/up_vote.mutation.graphql +3 -1
- data/app/frontend/{comments → queries}/comments.query.graphql +4 -1
- data/app/frontend/support/{asset_url.js → asset_url.ts} +1 -1
- data/app/frontend/support/{generate_comments_data.js → generate_comments_data.ts} +11 -6
- data/app/frontend/support/{generate_user_data.js → generate_user_data.ts} +2 -2
- data/app/frontend/support/{generate_user_group_data.js → generate_user_group_data.ts} +2 -2
- data/app/frontend/support/graphql_transformer.js +32 -0
- data/app/frontend/support/load_translations.ts +44 -0
- data/app/frontend/support/{require_all.js → require_all.ts} +1 -1
- data/app/frontend/support/{resolve_graphql_query.js → resolve_graphql_query.ts} +7 -7
- data/app/frontend/support/schema.ts +119 -0
- data/config/locales/eu.yml +29 -5
- metadata +49 -51
- data/app/frontend/application/application.component.jsx +0 -37
- data/app/frontend/application/application.component.test.jsx +0 -33
- data/app/frontend/application/icon.component.jsx +0 -26
- data/app/frontend/application/icon.component.test.jsx +0 -53
- data/app/frontend/comments/comment_order_selector.component.jsx +0 -72
- data/app/frontend/comments/comment_order_selector.component.test.jsx +0 -20
- data/app/frontend/comments/comment_thread.component.jsx +0 -75
- data/app/frontend/comments/comment_thread.component.test.jsx +0 -83
- data/app/frontend/comments/down_vote_button.component.jsx +0 -98
- data/app/frontend/comments/down_vote_button.component.test.jsx +0 -48
- data/app/frontend/comments/featured_comment.component.jsx +0 -23
- data/app/frontend/comments/featured_comment.component.test.jsx +0 -15
- data/app/frontend/comments/up_vote_button.component.jsx +0 -98
- data/app/frontend/comments/up_vote_button.component.test.jsx +0 -48
- data/app/frontend/comments/vote_button.component.jsx +0 -32
- data/app/frontend/entry.js +0 -17
- data/app/frontend/entry.test.js +0 -31
- data/app/frontend/support/load_translations.js +0 -23
- data/app/frontend/support/stub_component.js +0 -29
@@ -1,130 +1,118 @@
|
|
1
|
-
|
2
|
-
import
|
3
|
-
import
|
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
|
7
|
-
import AddCommentForm from
|
8
|
-
import
|
9
|
-
import DownVoteButton from
|
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
|
12
|
-
import
|
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
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
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)).
|
78
|
-
wrapper.find(
|
79
|
-
expect(wrapper.find(AddCommentForm)
|
80
|
-
expect(wrapper.find(AddCommentForm)
|
81
|
-
expect(wrapper.find(AddCommentForm)
|
82
|
-
expect(wrapper.find(AddCommentForm)
|
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(
|
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
|
-
|
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(
|
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(
|
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
|
107
|
-
expect(node
|
108
|
-
expect(node
|
109
|
-
expect(node
|
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
|
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
|
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
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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)
|
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)
|
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
|
-
|
2
|
-
import
|
3
|
-
import
|
4
|
-
|
5
|
-
import
|
6
|
-
|
7
|
-
import
|
8
|
-
|
9
|
-
import
|
10
|
-
|
11
|
-
import
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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 (
|
53
|
+
public componentDidMount() {
|
54
|
+
if (window.$(document).foundation) {
|
33
55
|
const { comment: { id } } = this.props;
|
34
|
-
|
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 =
|
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={
|
62
|
-
<Icon name="icon-flag" iconExtraClassName="icon--small"
|
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
|
-
{
|
71
|
-
{
|
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={
|
125
|
+
onClick={this.toggleReplyForm}
|
100
126
|
>
|
101
|
-
{
|
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> </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={
|
150
|
+
onClick={this.toggleReplyForm}
|
126
151
|
>
|
127
|
-
{
|
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 =
|
188
|
+
let replyArticleClassName = "comment comment--nested";
|
164
189
|
|
165
|
-
if (articleClassName ===
|
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={
|
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(
|
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(
|
255
|
+
label = I18n.t("components.comment.alignment.in_favor");
|
231
256
|
} else {
|
232
|
-
label = I18n.t(
|
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}>{
|
263
|
+
<span className={spanClassName}>{label}</span>
|
239
264
|
|
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">{
|
289
|
+
<h3 className="reveal__title">{I18n.t("components.comment.report.title")}</h3>
|
261
290
|
<button
|
262
291
|
className="close-button"
|
263
|
-
aria-label={
|
292
|
+
aria-label={I18n.t("components.comment.report.close")}
|
264
293
|
type="button"
|
265
|
-
onClick={
|
294
|
+
onClick={closeModal}
|
295
|
+
>
|
266
296
|
<span aria-hidden="true">×</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}`}>{
|
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}`}>{
|
278
|
-
|
279
|
-
<
|
280
|
-
|
281
|
-
<
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
<
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
<
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
{
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
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
|
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;
|