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
@@ -0,0 +1,21 @@
1
+ import { shallow } from "enzyme";
2
+ import * as React from "react";
3
+
4
+ import CommentOrderSelector from "./comment_order_selector.component";
5
+
6
+ describe("<CommentOrderSelector />", () => {
7
+ const orderBy = "older";
8
+ const reorderComments = jasmine.createSpy("reorderComments");
9
+
10
+ it("renders a div with classes order-by__dropdown order-by__dropdown--right", () => {
11
+ const wrapper = shallow(<CommentOrderSelector reorderComments={reorderComments} defaultOrderBy={orderBy} />);
12
+ expect(wrapper.find("div.order-by__dropdown.order-by__dropdown--right")).toBeDefined();
13
+ });
14
+
15
+ it("should set state order to best_rated if user clicks on the first element", () => {
16
+ const preventDefault = jasmine.createSpy("preventDefault");
17
+ const wrapper = shallow(<CommentOrderSelector reorderComments={reorderComments} defaultOrderBy={orderBy} />);
18
+ wrapper.find("a.test").simulate("click", {preventDefault});
19
+ expect(reorderComments).toBeCalledWith("best_rated");
20
+ });
21
+ });
@@ -0,0 +1,88 @@
1
+ import * as React from "react";
2
+
3
+ const { I18n } = require("react-i18nify");
4
+
5
+ interface CommentOrderSelectorProps {
6
+ defaultOrderBy: string;
7
+ reorderComments: (orderBy: string) => void;
8
+ }
9
+
10
+ interface CommentOrderSelectorState {
11
+ orderBy: string;
12
+ }
13
+
14
+ /**
15
+ * A simple static component with the comment's order selector markup
16
+ * @class
17
+ * @augments Component
18
+ * @todo Needs a proper implementation
19
+ */
20
+ class CommentOrderSelector extends React.Component<CommentOrderSelectorProps, CommentOrderSelectorState> {
21
+ private dropdown: HTMLUListElement;
22
+
23
+ constructor(props: CommentOrderSelectorProps) {
24
+ super(props);
25
+
26
+ this.state = {
27
+ orderBy: this.props.defaultOrderBy,
28
+ };
29
+ }
30
+
31
+ public setDropdown = (dropdown: HTMLUListElement) => this.dropdown = dropdown;
32
+
33
+ public componentDidMount() {
34
+ window.$(this.dropdown).foundation();
35
+ }
36
+
37
+ public render() {
38
+ const { orderBy } = this.state;
39
+
40
+ return (
41
+ <div className="order-by__dropdown order-by__dropdown--right">
42
+ <span className="order-by__text">{I18n.t("components.comment_order_selector.title")}</span>
43
+ <ul
44
+ className="dropdown menu"
45
+ data-dropdown-menu="data-dropdown-menu"
46
+ data-close-on-click-inside="false"
47
+ ref={this.setDropdown}
48
+ >
49
+ <li>
50
+ <a>{I18n.t(`components.comment_order_selector.order.${orderBy}`)}</a>
51
+ <ul className="menu">
52
+ <li>
53
+ <a href="" className="test" onClick={this.updateOrder("best_rated")} >
54
+ {I18n.t("components.comment_order_selector.order.best_rated")}
55
+ </a>
56
+ </li>
57
+ <li>
58
+ <a href="" onClick={this.updateOrder("recent")} >
59
+ {I18n.t("components.comment_order_selector.order.recent")}
60
+ </a>
61
+ </li>
62
+ <li>
63
+ <a href="" onClick={this.updateOrder("older")} >
64
+ {I18n.t("components.comment_order_selector.order.older")}
65
+ </a>
66
+ </li>
67
+ <li>
68
+ <a href="" onClick={this.updateOrder("most_discussed")} >
69
+ {I18n.t("components.comment_order_selector.order.most_discussed")}
70
+ </a>
71
+ </li>
72
+ </ul>
73
+ </li>
74
+ </ul>
75
+ </div>
76
+ );
77
+ }
78
+
79
+ private updateOrder = (orderBy: string) => {
80
+ return (event: React.MouseEvent<HTMLAnchorElement>) => {
81
+ event.preventDefault();
82
+ this.setState({ orderBy });
83
+ this.props.reorderComments(orderBy);
84
+ };
85
+ }
86
+ }
87
+
88
+ export default CommentOrderSelector;
@@ -0,0 +1,65 @@
1
+ import { shallow } from "enzyme";
2
+ import * as React from "react";
3
+
4
+ import { CommentFragment } from "../support/schema";
5
+ import Comment from "./comment.component";
6
+ import CommentThread from "./comment_thread.component";
7
+
8
+ import generateCommentsData from "../support/generate_comments_data";
9
+ import generateCUserData from "../support/generate_user_data";
10
+ import { loadLocaleTranslations } from "../support/load_translations";
11
+
12
+ describe("<CommentThread />", () => {
13
+ let comment: CommentFragment;
14
+ let session: any = null;
15
+
16
+ beforeEach(() => {
17
+ loadLocaleTranslations("en");
18
+ const commentsData = generateCommentsData(1);
19
+
20
+ session = {
21
+ user: generateCUserData(),
22
+ };
23
+ comment = commentsData[0];
24
+ });
25
+
26
+ describe("when comment doesn't have comments", () => {
27
+ it("should not render a title with author name", () => {
28
+ const wrapper = shallow(<CommentThread comment={comment} session={session} />);
29
+ expect(wrapper.find("h6.comment-thread__title").exists()).toBeFalsy();
30
+ });
31
+ });
32
+
33
+ describe("when comment does has comments", () => {
34
+ beforeEach(() => {
35
+ comment.hasComments = true;
36
+ });
37
+
38
+ it("should render a h6 comment-thread__title with author name", () => {
39
+ const wrapper = shallow(<CommentThread comment={comment} session={session} />);
40
+ expect(wrapper.find("h6.comment-thread__title").text()).toContain(`Conversation with ${comment.author.name}`);
41
+ });
42
+ });
43
+
44
+ describe("should render a Comment", () => {
45
+ it("and pass the session as a prop to it", () => {
46
+ const wrapper = shallow(<CommentThread comment={comment} session={session} />);
47
+ expect(wrapper.find(Comment).first().props()).toHaveProperty("session", session);
48
+ });
49
+
50
+ it("and pass comment data as a prop to it", () => {
51
+ const wrapper = shallow(<CommentThread comment={comment} session={session} />);
52
+ expect(wrapper.find(Comment).first().props()).toHaveProperty("comment", comment);
53
+ });
54
+
55
+ it("and pass the votable as a prop to it", () => {
56
+ const wrapper = shallow(<CommentThread comment={comment} session={session} votable={true} />);
57
+ expect(wrapper.find(Comment).first().props()).toHaveProperty("votable", true);
58
+ });
59
+
60
+ it("and pass the isRootComment equal true", () => {
61
+ const wrapper = shallow(<CommentThread comment={comment} session={session} votable={true} />);
62
+ expect(wrapper.find(Comment).first().props()).toHaveProperty("isRootComment", true);
63
+ });
64
+ });
65
+ });
@@ -0,0 +1,70 @@
1
+ import * as React from "react";
2
+
3
+ import Comment from "./comment.component";
4
+
5
+ import {
6
+ AddCommentFormSessionFragment,
7
+ CommentFragment,
8
+ } from "../support/schema";
9
+
10
+ const { I18n } = require("react-i18nify");
11
+
12
+ interface CommentThreadProps {
13
+ comment: CommentFragment;
14
+ session: AddCommentFormSessionFragment & {
15
+ user: any;
16
+ } | null;
17
+ votable?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Define a collection of comments. It represents a conversation with multiple users.
22
+ * @class
23
+ * @augments Component
24
+ * @todo It doesn't handle multiple comments yet
25
+ */
26
+ class CommentThread extends React.Component<CommentThreadProps, undefined> {
27
+ public static defaultProps: any = {
28
+ session: null,
29
+ votable: false,
30
+ };
31
+
32
+ public render() {
33
+ const { comment, session, votable } = this.props;
34
+
35
+ return (
36
+ <div>
37
+ {this._renderTitle()}
38
+ <div className="comment-thread">
39
+ <Comment
40
+ comment={comment}
41
+ session={session}
42
+ votable={votable}
43
+ isRootComment={true}
44
+ />
45
+ </div>
46
+ </div>
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Render conversation title if comment has commments
52
+ * @private
53
+ * @returns {Void|DOMElement} - The conversation's title
54
+ */
55
+ private _renderTitle() {
56
+ const { comment: { author, hasComments } } = this.props;
57
+
58
+ if (hasComments) {
59
+ return (
60
+ <h6 className="comment-thread__title">
61
+ {I18n.t("components.comment_thread.title", { authorName: author.name })}
62
+ </h6>
63
+ );
64
+ }
65
+
66
+ return null;
67
+ }
68
+ }
69
+
70
+ export default CommentThread;
@@ -1,91 +1,48 @@
1
- import { shallow } from 'enzyme';
2
- import { filter } from 'graphql-anywhere';
3
- import gql from 'graphql-tag';
1
+ import { shallow } from "enzyme";
2
+ import * as React from "react";
4
3
 
5
- import { Comments } from './comments.component';
6
- import CommentThread from './comment_thread.component';
7
- import AddCommentForm from './add_comment_form.component';
8
- import CommentOrderSelector from './comment_order_selector.component';
4
+ import AddCommentForm from "./add_comment_form.component";
5
+ import CommentOrderSelector from "./comment_order_selector.component";
6
+ import CommentThread from "./comment_thread.component";
7
+ import { Comments, commentsQuery } from "./comments.component";
9
8
 
10
- import commentsQuery from './comments.query.graphql'
9
+ import generateCommentsData from "../support/generate_comments_data";
10
+ import generateUserData from "../support/generate_user_data";
11
+ import resolveGraphQLQuery from "../support/resolve_graphql_query";
11
12
 
12
- import stubComponent from '../support/stub_component';
13
- import generateCommentsData from '../support/generate_comments_data';
14
- import generateUserData from '../support/generate_user_data';
15
- import resolveGraphQLQuery from '../support/resolve_graphql_query';
13
+ import { loadLocaleTranslations } from "../support/load_translations";
16
14
 
17
- describe('<Comments />', () => {
18
- let commentable = {};
19
- let session = null;
15
+ describe("<Comments />", () => {
16
+ let commentable: any = {};
17
+ let session: any = null;
20
18
  const commentableId = "1";
21
19
  const commentableType = "Decidim::DummyResource";
22
20
  const orderBy = "older";
23
- const reorderComments = () => {};
24
-
25
- const commentThreadFragment = gql`
26
- fragment CommentThread on Comment {
27
- author
28
- }
29
- `;
30
-
31
- const addCommentFormSessionFragment = gql`
32
- fragment AddCommentFormSession on Session {
33
- verifiedUserGroups {
34
- id
35
- }
36
- }
37
- `;
38
-
39
- const addCommentFormCommentableFragment = gql`
40
- fragment AddCommentFormCommentable on Commentable {
41
- id
42
- }
43
- `;
44
-
45
- stubComponent(CommentOrderSelector)
46
-
47
- stubComponent(CommentThread, {
48
- fragments: {
49
- comment: commentThreadFragment
50
- }
51
- });
52
-
53
- stubComponent(AddCommentForm, {
54
- fragments: {
55
- session: addCommentFormSessionFragment,
56
- commentable: addCommentFormCommentableFragment
57
- }
58
- });
21
+ const reorderComments = jasmine.createSpy("reorderComments");
59
22
 
60
23
  beforeEach(() => {
24
+ loadLocaleTranslations("en");
61
25
  const userData = generateUserData();
62
26
  const commentsData = generateCommentsData(15);
63
27
 
64
- const query = gql`
65
- ${commentsQuery}
66
- ${commentThreadFragment}
67
- ${addCommentFormSessionFragment}
68
- ${addCommentFormCommentableFragment}
69
- `;
70
-
71
- const result = resolveGraphQLQuery(query, {
28
+ const result = resolveGraphQLQuery(commentsQuery, {
72
29
  filterResult: false,
73
30
  rootValue: {
74
31
  session: {
75
- user: userData
32
+ user: userData,
76
33
  },
77
34
  commentable: {
78
35
  acceptsNewComments: true,
79
36
  commentsHaveAlignment: true,
80
37
  commentsHaveVotes: true,
81
- comments: commentsData
82
- }
38
+ comments: commentsData,
39
+ },
83
40
  },
84
41
  variables: {
85
42
  orderBy,
86
43
  commentableId,
87
- commentableType
88
- }
44
+ commentableType,
45
+ },
89
46
  });
90
47
 
91
48
  session = result.session;
@@ -93,38 +50,38 @@ describe('<Comments />', () => {
93
50
  });
94
51
 
95
52
  it("renders loading-comments class and the respective loading text", () => {
96
- const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} loading />);
97
- expect(wrapper.find('.loading-comments')).to.be.present();
98
- expect(wrapper.find('h2')).to.have.text("Loading comments ...");
53
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} loading={true} />);
54
+ expect(wrapper.find(".loading-comments").exists()).toBeTruthy();
55
+ expect(wrapper.find("h2").text()).toEqual("Loading comments ...");
99
56
  });
100
57
 
101
58
  it("renders a div of id comments", () => {
102
59
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
103
- expect(wrapper.find('#comments')).to.be.present();
60
+ expect(wrapper.find("#comments").exists()).toBeTruthy();
104
61
  });
105
62
 
106
63
  describe("renders a CommentThread component for each comment", () => {
107
64
  it("and pass filter comment data as a prop to it", () => {
108
65
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
109
- expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
66
+ expect(wrapper.find(CommentThread).length).toEqual(commentable.comments.length);
110
67
  wrapper.find(CommentThread).forEach((node, idx) => {
111
- expect(node).to.have.prop("comment").deep.equal(filter(commentThreadFragment, commentable.comments[idx]));
68
+ expect(node.prop("comment")).toEqual(commentable.comments[idx]);
112
69
  });
113
70
  });
114
71
 
115
72
  it("and pass the session as a prop to it", () => {
116
73
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
117
- expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
74
+ expect(wrapper.find(CommentThread).length).toEqual(commentable.comments.length);
118
75
  wrapper.find(CommentThread).forEach((node) => {
119
- expect(node).to.have.prop("session").deep.equal(session);
76
+ expect(node.prop("session")).toEqual(session);
120
77
  });
121
78
  });
122
79
 
123
80
  it("and pass the commentable 'commentsHaveVotes' property as a prop to it", () => {
124
81
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
125
- expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
82
+ expect(wrapper.find(CommentThread).length).toEqual(commentable.comments.length);
126
83
  wrapper.find(CommentThread).forEach((node) => {
127
- expect(node).to.have.prop("votable").equal(true);
84
+ expect(node.prop("votable")).toBeTruthy();
128
85
  });
129
86
  });
130
87
  });
@@ -132,13 +89,13 @@ describe('<Comments />', () => {
132
89
  it("renders comments count", () => {
133
90
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
134
91
  const rex = new RegExp(`${commentable.comments.length} comments`);
135
- expect(wrapper.find('h2.section-heading')).to.have.text().match(rex);
92
+ expect(wrapper.find("h2.section-heading").text()).toMatch(rex);
136
93
  });
137
94
 
138
95
  it("renders a AddCommentForm component and pass the commentable 'commentsHaveAlignment' as a prop", () => {
139
96
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
140
- expect(wrapper).to.have.exactly(1).descendants(AddCommentForm);
141
- expect(wrapper.find(AddCommentForm)).to.have.prop('arguable').equal(true);
97
+ expect(wrapper.find(AddCommentForm).length).toEqual(1);
98
+ expect(wrapper.find(AddCommentForm).prop("arguable")).toBeTruthy();
142
99
  });
143
100
 
144
101
  describe("when the commentable cannot accept new comments", () => {
@@ -148,24 +105,24 @@ describe('<Comments />', () => {
148
105
 
149
106
  it("doesn't render an AddCommentForm component", () => {
150
107
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
151
- expect(wrapper.find(AddCommentForm)).not.to.be.present();
108
+ expect(wrapper.find(AddCommentForm).exists()).toBeFalsy();
152
109
  });
153
110
 
154
111
  it("renders a callout message to inform the user that comments are blocked", () => {
155
112
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
156
- expect(wrapper.find('.callout.warning')).to.include.text("disabled");
113
+ expect(wrapper.find(".callout.warning").text()).toContain("disabled");
157
114
  });
158
115
  });
159
116
 
160
117
  describe("renders a CommentOrderSelector component", () => {
161
118
  it("and pass the reorderComments as a prop to it", () => {
162
119
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
163
- expect(wrapper.find(CommentOrderSelector)).to.have.prop('reorderComments').deep.equal(reorderComments);
120
+ expect(wrapper.find(CommentOrderSelector).prop("reorderComments")).toEqual(reorderComments);
164
121
  });
165
122
 
166
123
  it("and pass the orderBy as a prop to it", () => {
167
124
  const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
168
- expect(wrapper.find(CommentOrderSelector)).to.have.prop('defaultOrderBy').equal('older');
125
+ expect(wrapper.find(CommentOrderSelector).prop("defaultOrderBy")).toEqual("older");
169
126
  });
170
127
  });
171
128
  });