decidim 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim might be problematic. Click here for more details.

Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.json +2 -5
  3. data/.mention-bot +2 -1
  4. data/.yardopts +8 -0
  5. data/Gemfile.lock +64 -62
  6. data/README.md +11 -9
  7. data/decidim-admin/app/assets/javascripts/decidim/admin/sort_steps.js.es6 +3 -3
  8. data/decidim-admin/app/assets/stylesheets/decidim/admin/_email_preview.scss +5 -0
  9. data/decidim-admin/app/assets/stylesheets/decidim/admin/application.scss +1 -0
  10. data/decidim-admin/app/commands/decidim/admin/create_newsletter.rb +30 -0
  11. data/decidim-admin/app/commands/decidim/admin/deliver_newsletter.rb +29 -0
  12. data/decidim-admin/app/commands/decidim/admin/destroy_participatory_process_step.rb +9 -0
  13. data/decidim-admin/app/commands/decidim/admin/update_newsletter.rb +33 -0
  14. data/decidim-admin/app/constraints/decidim/admin/organization_dashboard_constraint.rb +1 -1
  15. data/decidim-admin/app/controllers/decidim/admin/features_controller.rb +2 -2
  16. data/decidim-admin/app/controllers/decidim/admin/newsletters_controller.rb +115 -0
  17. data/decidim-admin/app/controllers/decidim/admin/participatory_processes_controller.rb +4 -4
  18. data/decidim-admin/app/forms/decidim/admin/newsletter_form.rb +15 -0
  19. data/decidim-admin/app/jobs/decidim/admin/newsletter_delivery_job.rb +18 -0
  20. data/decidim-admin/app/jobs/decidim/admin/newsletter_job.rb +32 -0
  21. data/decidim-admin/app/models/decidim/admin/abilities/admin_user.rb +4 -0
  22. data/decidim-admin/app/models/decidim/admin/abilities/base.rb +4 -0
  23. data/decidim-admin/app/models/decidim/admin/abilities/collaborator_user.rb +19 -0
  24. data/decidim-admin/app/models/decidim/admin/abilities/participatory_process_admin.rb +1 -1
  25. data/decidim-admin/app/queries/decidim/admin/manageable_participatory_processes_for_user.rb +1 -1
  26. data/decidim-admin/app/views/decidim/admin/newsletters/_form.html.erb +5 -0
  27. data/decidim-admin/app/views/decidim/admin/newsletters/edit.html.erb +11 -0
  28. data/decidim-admin/app/views/decidim/admin/newsletters/index.html.erb +47 -0
  29. data/decidim-admin/app/views/decidim/admin/newsletters/new.html.erb +11 -0
  30. data/decidim-admin/app/views/decidim/admin/newsletters/show.html.erb +14 -0
  31. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/_form.html.erb +1 -1
  32. data/decidim-admin/app/views/decidim/admin/participatory_processes/_form.html.erb +1 -1
  33. data/decidim-admin/app/views/decidim/admin/static_pages/_form.html.erb +1 -1
  34. data/decidim-admin/app/views/layouts/decidim/admin/_sidebar.html.erb +1 -0
  35. data/decidim-admin/config/locales/ca.yml +40 -0
  36. data/decidim-admin/config/locales/en.yml +40 -0
  37. data/decidim-admin/config/locales/es.yml +40 -0
  38. data/decidim-admin/config/routes.rb +7 -0
  39. data/decidim-admin/decidim-admin.gemspec +0 -1
  40. data/decidim-admin/lib/decidim/admin/engine.rb +1 -0
  41. data/decidim-admin/lib/decidim/admin/features/base_controller.rb +5 -1
  42. data/decidim-admin/spec/commands/create_newsletter_spec.rb +62 -0
  43. data/decidim-admin/spec/commands/deliver_newsletter_spec.rb +45 -0
  44. data/decidim-admin/spec/commands/destroy_participatory_process_step_spec.rb +12 -0
  45. data/decidim-admin/spec/commands/update_newsletter_spec.rb +64 -0
  46. data/decidim-admin/spec/features/admin_manages_newsletters_spec.rb +147 -0
  47. data/decidim-admin/spec/features/admin_manages_participatory_processes_spec.rb +1 -5
  48. data/decidim-admin/spec/features/static_pages_spec.rb +16 -0
  49. data/decidim-admin/spec/forms/newsletter_form_spec.rb +47 -0
  50. data/decidim-admin/spec/jobs/newsletter_delivery_job_spec.rb +25 -0
  51. data/decidim-admin/spec/jobs/newsletter_job_spec.rb +29 -0
  52. data/decidim-admin/spec/models/abilities/collaborator_user_spec.rb +66 -0
  53. data/decidim-admin/spec/organization_dashboard_constraint_spec.rb +55 -0
  54. data/decidim-admin/spec/queries/manageable_participatory_processes_for_user_spec.rb +8 -0
  55. data/decidim-admin/spec/shared/manage_processes_examples.rb +1 -5
  56. data/decidim-api/lib/decidim/api.rb +2 -7
  57. data/decidim-api/lib/decidim/api/{types/mutation.rb → mutation_type.rb} +0 -0
  58. data/decidim-api/lib/decidim/api/{types/query.rb → query_type.rb} +0 -0
  59. data/decidim-budgets/app/assets/javascripts/decidim/budgets/projects.js.es6 +4 -4
  60. data/decidim-budgets/app/commands/decidim/budgets/add_line_item.rb +4 -2
  61. data/decidim-budgets/app/commands/decidim/budgets/checkout.rb +7 -3
  62. data/decidim-budgets/app/models/decidim/budgets/order.rb +16 -1
  63. data/decidim-budgets/app/models/decidim/budgets/project.rb +16 -0
  64. data/decidim-budgets/app/views/decidim/budgets/line_items/update_budget.js.erb +7 -7
  65. data/decidim-budgets/app/views/decidim/budgets/projects/_budget_confirm.html.erb +27 -25
  66. data/decidim-budgets/app/views/decidim/budgets/projects/_budget_summary.html.erb +4 -13
  67. data/decidim-budgets/app/views/decidim/budgets/projects/_order_progress.html.erb +23 -21
  68. data/decidim-budgets/app/views/decidim/budgets/projects/_order_selected_projects.html.erb +25 -23
  69. data/decidim-budgets/app/views/decidim/budgets/projects/_order_total_budget.html.erb +3 -1
  70. data/decidim-budgets/app/views/decidim/budgets/projects/_project.html.erb +32 -30
  71. data/decidim-budgets/app/views/decidim/budgets/projects/_projects.html.erb +1 -3
  72. data/decidim-budgets/app/views/decidim/budgets/projects/index.html.erb +0 -4
  73. data/decidim-budgets/app/views/decidim/budgets/projects/show.html.erb +5 -6
  74. data/decidim-budgets/config/i18n-tasks.yml +1 -0
  75. data/decidim-budgets/config/locales/ca.yml +7 -2
  76. data/decidim-budgets/config/locales/en.yml +17 -6
  77. data/decidim-budgets/config/locales/es.yml +7 -2
  78. data/decidim-budgets/lib/decidim/budgets/feature.rb +2 -2
  79. data/decidim-budgets/spec/commands/add_line_item_spec.rb +16 -3
  80. data/decidim-budgets/spec/commands/cancel_order_spec.rb +7 -1
  81. data/decidim-budgets/spec/commands/checkout_spec.rb +9 -2
  82. data/decidim-budgets/spec/commands/remove_line_item_spec.rb +16 -5
  83. data/decidim-budgets/spec/features/orders_spec.rb +8 -2
  84. data/decidim-budgets/spec/models/order_spec.rb +31 -2
  85. data/decidim-budgets/spec/shared/admin_shared_context.rb +1 -1
  86. data/decidim-comments/README.md +1 -1
  87. data/decidim-comments/app/assets/javascripts/decidim/comments/bundle.js +0 -0
  88. data/decidim-comments/app/frontend/comments/add_comment_form.component.jsx +156 -93
  89. data/decidim-comments/app/frontend/comments/add_comment_form.component.test.jsx +36 -23
  90. data/decidim-comments/app/frontend/comments/add_comment_form.mutation.graphql +5 -3
  91. data/decidim-comments/app/frontend/comments/add_comment_form_commentable.fragment.graphql +4 -0
  92. data/decidim-comments/app/frontend/comments/add_comment_form_session.fragment.graphql +6 -0
  93. data/decidim-comments/app/frontend/comments/comment.component.jsx +11 -13
  94. data/decidim-comments/app/frontend/comments/comment.component.test.jsx +22 -21
  95. data/decidim-comments/app/frontend/comments/comment.fragment.graphql +4 -4
  96. data/decidim-comments/app/frontend/comments/comment_data.fragment.graphql +4 -3
  97. data/decidim-comments/app/frontend/comments/comment_order_selector.component.jsx +1 -1
  98. data/decidim-comments/app/frontend/comments/comment_thread.component.jsx +3 -3
  99. data/decidim-comments/app/frontend/comments/comment_thread.component.test.jsx +3 -3
  100. data/decidim-comments/app/frontend/comments/comment_thread.fragment.graphql +1 -1
  101. data/decidim-comments/app/frontend/comments/comments.component.jsx +47 -39
  102. data/decidim-comments/app/frontend/comments/comments.component.test.jsx +51 -38
  103. data/decidim-comments/app/frontend/comments/comments.query.graphql +10 -4
  104. data/decidim-comments/app/frontend/comments/down_vote_button.component.jsx +6 -3
  105. data/decidim-comments/app/frontend/comments/up_vote_button.component.jsx +7 -4
  106. data/decidim-comments/app/frontend/support/generate_comments_data.js +4 -4
  107. data/decidim-comments/app/models/decidim/comments/comment.rb +7 -9
  108. data/decidim-comments/app/queries/decidim/comments/{comments_with_replies.rb → sorted_comments.rb} +3 -8
  109. data/decidim-comments/app/types/decidim/comments/commentable_interface.rb +44 -0
  110. data/decidim-comments/app/types/decidim/comments/commentable_mutation_type.rb +29 -0
  111. data/decidim-comments/app/types/decidim/comments/commentable_type.rb +14 -0
  112. data/decidim-comments/config/locales/ca.yml +3 -1
  113. data/decidim-comments/config/locales/en.yml +5 -2
  114. data/decidim-comments/config/locales/es.yml +3 -1
  115. data/decidim-comments/lib/decidim/comments.rb +4 -0
  116. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/add_comment_type.rb +0 -0
  117. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/comment_mutation_type.rb +0 -0
  118. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/comment_type.rb +11 -17
  119. data/decidim-comments/lib/decidim/comments/commentable.rb +45 -0
  120. data/decidim-comments/lib/decidim/comments/comments_helper.rb +15 -10
  121. data/decidim-comments/lib/decidim/comments/mutation_extensions.rb +8 -16
  122. data/decidim-comments/lib/decidim/comments/query_extensions.rb +5 -8
  123. data/decidim-comments/lib/decidim/comments/test/factories.rb +3 -3
  124. data/decidim-comments/spec/commands/vote_comment_spec.rb +3 -1
  125. data/decidim-comments/spec/features/comments_spec.rb +28 -14
  126. data/decidim-comments/spec/features/notifications_spec.rb +1 -1
  127. data/decidim-comments/spec/helpers/comments_helper_spec.rb +4 -27
  128. data/decidim-comments/spec/models/comment_spec.rb +7 -11
  129. data/decidim-comments/spec/models/comment_vote_spec.rb +3 -1
  130. data/decidim-comments/spec/models/seed_spec.rb +3 -4
  131. data/decidim-comments/spec/queries/{comments_with_replies_spec.rb → sorted_comments_spec.rb} +12 -29
  132. data/decidim-comments/spec/types/comment_type_spec.rb +23 -17
  133. data/decidim-comments/spec/types/commentable_mutation_type_spec.rb +34 -0
  134. data/decidim-comments/spec/types/commentable_type_spec.rb +48 -0
  135. data/decidim-comments/spec/types/mutation_type_spec.rb +5 -22
  136. data/decidim-comments/spec/types/query_type_spec.rb +0 -24
  137. data/decidim-dev/config/i18n-tasks.yml +4 -0
  138. data/decidim-dev/decidim-dev.gemspec +1 -1
  139. data/decidim-dev/lib/decidim/dev/test/rspec_support/action_mailer.rb +10 -4
  140. data/decidim-dev/lib/decidim/dev/test/rspec_support/feature.rb +6 -5
  141. data/decidim-dev/lib/decidim/dev/test/rspec_support/i18n.rb +0 -1
  142. data/decidim-meetings/app/assets/javascripts/decidim/meetings/map.js.es6.erb +8 -3
  143. data/decidim-meetings/app/assets/stylesheets/decidim/meetings/map.css +3 -1
  144. data/decidim-meetings/app/controllers/decidim/meetings/meetings_controller.rb +2 -2
  145. data/decidim-meetings/app/forms/decidim/meetings/admin/close_meeting_form.rb +2 -1
  146. data/decidim-meetings/app/helpers/decidim/meetings/map_helper.rb +1 -0
  147. data/decidim-meetings/app/services/decidim/meetings/meeting_search.rb +7 -3
  148. data/decidim-meetings/app/views/decidim/meetings/meetings/_datetime.html.erb +1 -1
  149. data/decidim-meetings/app/views/decidim/meetings/meetings/_filters.html.erb +5 -5
  150. data/decidim-meetings/app/views/decidim/meetings/meetings/_linked_meetings.html.erb +1 -1
  151. data/decidim-meetings/app/views/decidim/meetings/meetings/index.html.erb +1 -5
  152. data/decidim-meetings/app/views/decidim/meetings/meetings/show.html.erb +16 -9
  153. data/decidim-meetings/config/locales/ca.yml +2 -3
  154. data/decidim-meetings/config/locales/en.yml +2 -3
  155. data/decidim-meetings/config/locales/es.yml +2 -3
  156. data/decidim-meetings/spec/features/explore_meetings_spec.rb +75 -24
  157. data/decidim-meetings/spec/forms/close_meeting_form_spec.rb +1 -1
  158. data/decidim-meetings/spec/services/meeting_search_spec.rb +15 -12
  159. data/decidim-meetings/vendor/assets/javascripts/leaflet.markercluster.js +7 -0
  160. data/decidim-meetings/vendor/assets/stylesheets/MarkerCluster.Default.css +60 -0
  161. data/decidim-meetings/vendor/assets/stylesheets/MarkerCluster.css +14 -0
  162. data/decidim-pages/app/models/decidim/pages/page.rb +22 -0
  163. data/decidim-pages/app/views/decidim/pages/application/show.html.erb +2 -6
  164. data/decidim-pages/config/locales/ca.yml +2 -2
  165. data/decidim-pages/config/locales/en.yml +2 -2
  166. data/decidim-pages/config/locales/es.yml +2 -2
  167. data/decidim-pages/lib/decidim/pages/feature.rb +2 -2
  168. data/decidim-pages/spec/features/page_show_spec.rb +0 -33
  169. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +2 -0
  170. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposals_controller.rb +2 -0
  171. data/decidim-proposals/app/models/decidim/proposals/abilities/admin_user.rb +45 -0
  172. data/decidim-proposals/app/models/decidim/proposals/abilities/process_admin_user.rb +57 -0
  173. data/decidim-proposals/app/models/decidim/proposals/proposal.rb +21 -0
  174. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
  175. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/index.html.erb +16 -10
  176. data/decidim-proposals/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +4 -4
  177. data/decidim-proposals/app/views/decidim/proposals/proposals/_filters.html.erb +7 -3
  178. data/decidim-proposals/app/views/decidim/proposals/proposals/_proposal.html.erb +2 -6
  179. data/decidim-proposals/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +3 -1
  180. data/decidim-proposals/app/views/decidim/proposals/proposals/_tags.html.erb +1 -1
  181. data/decidim-proposals/app/views/decidim/proposals/proposals/_vote_button.html.erb +23 -21
  182. data/decidim-proposals/app/views/decidim/proposals/proposals/_votes_count.html.erb +9 -6
  183. data/decidim-proposals/app/views/decidim/proposals/proposals/_votes_limit.html.erb +1 -3
  184. data/decidim-proposals/app/views/decidim/proposals/proposals/index.html.erb +0 -4
  185. data/decidim-proposals/app/views/decidim/proposals/proposals/new.html.erb +1 -1
  186. data/decidim-proposals/app/views/decidim/proposals/proposals/show.html.erb +2 -6
  187. data/decidim-proposals/config/locales/ca.yml +8 -1
  188. data/decidim-proposals/config/locales/en.yml +8 -1
  189. data/decidim-proposals/config/locales/es.yml +8 -1
  190. data/decidim-proposals/lib/decidim/proposals/admin_engine.rb +7 -0
  191. data/decidim-proposals/lib/decidim/proposals/feature.rb +6 -2
  192. data/decidim-proposals/spec/features/proposals_spec.rb +226 -40
  193. data/decidim-proposals/spec/models/decidim/proposals/abilities/admin_user_spec.rb +62 -0
  194. data/decidim-proposals/spec/models/decidim/proposals/abilities/process_admin_user_spec.rb +63 -0
  195. data/decidim-proposals/spec/shared/manage_proposals_examples.rb +161 -50
  196. data/decidim-results/app/models/decidim/results/result.rb +21 -0
  197. data/decidim-results/app/views/decidim/results/results/index.html.erb +0 -4
  198. data/decidim-results/app/views/decidim/results/results/show.html.erb +2 -8
  199. data/decidim-results/config/i18n-tasks.yml +1 -0
  200. data/decidim-results/config/locales/ca.yml +5 -2
  201. data/decidim-results/config/locales/en.yml +5 -2
  202. data/decidim-results/config/locales/es.yml +5 -2
  203. data/decidim-results/lib/decidim/results/feature.rb +2 -2
  204. data/decidim-results/spec/services/result_stats_calculator_spec.rb +2 -2
  205. data/decidim-results/spec/shared/admin_shared_context.rb +1 -1
  206. data/decidim-system/config/i18n-tasks.yml +1 -1
  207. data/package.json +50 -51
  208. data/yarn.lock +198 -151
  209. metadata +69 -36
  210. data/decidim-admin/spec/features/admin_access_control.rb +0 -52
  211. data/decidim-api/lib/decidim/api/types/author_interface.rb +0 -13
  212. data/decidim-api/lib/decidim/api/types/localized_string.rb +0 -13
  213. data/decidim-api/lib/decidim/api/types/translated_field.rb +0 -44
  214. data/decidim-api/spec/types/localized_string_type_spec.rb +0 -31
  215. data/decidim-api/spec/types/translated_field_type_spec.rb +0 -69
  216. data/decidim-comments/app/frontend/comments/add_comment_form.fragment.graphql +0 -6
@@ -21,7 +21,7 @@ import commentsQuery from './comments.query.graphql';
21
21
  */
22
22
  export class Comments extends Component {
23
23
  render() {
24
- const { comments, reorderComments, orderBy, loading } = this.props;
24
+ const { commentable: { comments }, reorderComments, orderBy, loading } = this.props;
25
25
  let commentClasses = "comments";
26
26
  let commentHeader = I18n.t("components.comments.title", { count: comments.length });
27
27
 
@@ -42,6 +42,7 @@ export class Comments extends Component {
42
42
  defaultOrderBy={orderBy}
43
43
  />
44
44
  </div>
45
+ {this._renderBlockedCommentsWarning()}
45
46
  {this._renderCommentThreads()}
46
47
  {this._renderAddCommentForm()}
47
48
  </section>
@@ -49,20 +50,39 @@ export class Comments extends Component {
49
50
  );
50
51
  }
51
52
 
53
+ /**
54
+ * Renders a warning message if the commentable doesn't accept new comments.
55
+ * @private
56
+ * @returns {Void|DOMElement} - A warning message or nothing.
57
+ */
58
+ _renderBlockedCommentsWarning() {
59
+ const { commentable: { acceptsNewComments } } = this.props;
60
+
61
+ if (!acceptsNewComments) {
62
+ return (
63
+ <div className="callout warning">
64
+ <p>{ I18n.t("components.comments.blocked_comments_warning") }</p>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ return null;
70
+ }
71
+
52
72
  /**
53
73
  * Iterates the comment's collection and render a CommentThread for each one
54
74
  * @private
55
75
  * @returns {ReactComponent[]} - A collection of CommentThread components
56
76
  */
57
77
  _renderCommentThreads() {
58
- const { comments, session, options: { votable } } = this.props;
78
+ const { session, commentable: { comments, commentsHaveVotes } } = this.props;
59
79
 
60
80
  return comments.map((comment) => (
61
81
  <CommentThread
62
82
  key={comment.id}
63
83
  comment={filter(CommentThread.fragments.comment, comment)}
64
84
  session={session}
65
- votable={votable}
85
+ votable={commentsHaveVotes}
66
86
  />
67
87
  ))
68
88
  }
@@ -73,15 +93,15 @@ export class Comments extends Component {
73
93
  * @returns {Void|ReactComponent} - A AddCommentForm component or nothing
74
94
  */
75
95
  _renderAddCommentForm() {
76
- const { session, commentableId, commentableType, options: { arguable } } = this.props;
96
+ const { session, commentable } = this.props;
97
+ const { acceptsNewComments, commentsHaveAlignment } = commentable;
77
98
 
78
- if (session) {
99
+ if (acceptsNewComments) {
79
100
  return (
80
101
  <AddCommentForm
81
102
  session={session}
82
- commentableId={commentableId}
83
- commentableType={commentableType}
84
- arguable={arguable}
103
+ commentable={commentable}
104
+ arguable={commentsHaveAlignment}
85
105
  />
86
106
  );
87
107
  }
@@ -92,26 +112,27 @@ export class Comments extends Component {
92
112
 
93
113
  Comments.propTypes = {
94
114
  loading: PropTypes.bool,
95
- comments: PropTypes.arrayOf(PropTypes.shape({
96
- id: PropTypes.string.isRequired
97
- })),
98
115
  session: PropTypes.shape({
99
116
  user: PropTypes.any.isRequired
100
117
  }),
101
- commentableId: PropTypes.string.isRequired,
102
- commentableType: PropTypes.string.isRequired,
103
- options: PropTypes.shape({
104
- arguable: PropTypes.bool
105
- }).isRequired,
118
+ commentable: PropTypes.shape({
119
+ acceptsNewComments: PropTypes.bool,
120
+ commentsHaveAlignment: PropTypes.bool,
121
+ commentsHaveVotes: PropTypes.bool,
122
+ comments: PropTypes.arrayOf(PropTypes.shape({
123
+ id: PropTypes.string.isRequired
124
+ }))
125
+ }),
106
126
  orderBy: PropTypes.string.isRequired,
107
127
  reorderComments: PropTypes.func.isRequired
108
128
  };
109
129
 
110
130
  Comments.defaultProps = {
111
131
  loading: false,
112
- comments: null,
113
132
  session: null,
114
- commentableId: null
133
+ commentable: {
134
+ comments: []
135
+ }
115
136
  };
116
137
 
117
138
  /**
@@ -123,20 +144,18 @@ window.Comments = Comments;
123
144
 
124
145
  const CommentsWithData = graphql(gql`
125
146
  ${commentsQuery}
126
- ${AddCommentForm.fragments.user}
147
+ ${AddCommentForm.fragments.session}
148
+ ${AddCommentForm.fragments.commentable}
127
149
  ${CommentThread.fragments.comment}
128
150
  `, {
129
151
  options: {
130
152
  pollInterval: 15000
131
153
  },
132
- props: ({ ownProps, data: { loading, session, comments, refetch }}) => ({
133
- loading: loading,
134
- comments: comments || [],
154
+ props: ({ ownProps, data: { loading, session, commentable, refetch }}) => ({
155
+ loading,
135
156
  session,
136
- commentableId: ownProps.commentableId,
137
- commentableType: ownProps.commentableType,
157
+ commentable,
138
158
  orderBy: ownProps.orderBy,
139
- options: ownProps.options,
140
159
  reorderComments: (orderBy) => {
141
160
  return refetch({
142
161
  orderBy
@@ -150,12 +169,11 @@ const CommentsWithData = graphql(gql`
150
169
  * connect it with Apollo client and store.
151
170
  * @returns {ReactComponent} - A component wrapped within an Application component
152
171
  */
153
- const CommentsApplication = ({ locale, commentableId, commentableType, options }) => (
172
+ const CommentsApplication = ({ locale, commentableId, commentableType }) => (
154
173
  <Application locale={locale}>
155
174
  <CommentsWithData
156
175
  commentableId={commentableId}
157
176
  commentableType={commentableType}
158
- options={options}
159
177
  orderBy="older"
160
178
  />
161
179
  </Application>
@@ -163,18 +181,8 @@ const CommentsApplication = ({ locale, commentableId, commentableType, options }
163
181
 
164
182
  CommentsApplication.propTypes = {
165
183
  locale: PropTypes.string.isRequired,
166
- commentableId: React.PropTypes.oneOfType([
167
- PropTypes.string,
168
- PropTypes.number
169
- ]),
170
- commentableType: PropTypes.string.isRequired,
171
- options: PropTypes.shape({
172
- arguable: PropTypes.bool
173
- }).isRequired
174
- };
175
-
176
- CommentsApplication.defaultProps = {
177
- commentableId: null
184
+ commentableId: PropTypes.string.isRequired,
185
+ commentableType: PropTypes.string.isRequired
178
186
  };
179
187
 
180
188
  export default CommentsApplication;
@@ -15,10 +15,10 @@ import generateUserData from '../support/generate_user_data';
15
15
  import resolveGraphQLQuery from '../support/resolve_graphql_query';
16
16
 
17
17
  describe('<Comments />', () => {
18
- let comments = [];
18
+ let commentable = {};
19
19
  let session = null;
20
20
  const commentableId = "1";
21
- const commentableType = "Decidim::ParticipatoryProcess";
21
+ const commentableType = "Decidim::DummyResource";
22
22
  const orderBy = "older";
23
23
  const reorderComments = () => {};
24
24
 
@@ -28,14 +28,20 @@ describe('<Comments />', () => {
28
28
  }
29
29
  `;
30
30
 
31
- const addCommentFragment = gql`
32
- fragment AddCommentForm on User {
31
+ const addCommentFormSessionFragment = gql`
32
+ fragment AddCommentFormSession on Session {
33
33
  verifiedUserGroups {
34
34
  id
35
35
  }
36
36
  }
37
37
  `;
38
38
 
39
+ const addCommentFormCommentableFragment = gql`
40
+ fragment AddCommentFormCommentable on Commentable {
41
+ id
42
+ }
43
+ `;
44
+
39
45
  stubComponent(CommentOrderSelector)
40
46
 
41
47
  stubComponent(CommentThread, {
@@ -46,7 +52,8 @@ describe('<Comments />', () => {
46
52
 
47
53
  stubComponent(AddCommentForm, {
48
54
  fragments: {
49
- user: addCommentFragment
55
+ session: addCommentFormSessionFragment,
56
+ commentable: addCommentFormCommentableFragment
50
57
  }
51
58
  });
52
59
 
@@ -57,7 +64,8 @@ describe('<Comments />', () => {
57
64
  const query = gql`
58
65
  ${commentsQuery}
59
66
  ${commentThreadFragment}
60
- ${addCommentFragment}
67
+ ${addCommentFormSessionFragment}
68
+ ${addCommentFormCommentableFragment}
61
69
  `;
62
70
 
63
71
  const result = resolveGraphQLQuery(query, {
@@ -66,7 +74,12 @@ describe('<Comments />', () => {
66
74
  session: {
67
75
  user: userData
68
76
  },
69
- comments: commentsData
77
+ commentable: {
78
+ acceptsNewComments: true,
79
+ commentsHaveAlignment: true,
80
+ commentsHaveVotes: true,
81
+ comments: commentsData
82
+ }
70
83
  },
71
84
  variables: {
72
85
  orderBy,
@@ -76,82 +89,82 @@ describe('<Comments />', () => {
76
89
  });
77
90
 
78
91
  session = result.session;
79
- comments = result.comments;
92
+ commentable = result.commentable;
80
93
  });
81
94
 
82
- it("should render loading-comments calss and the respective loading text", () => {
83
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} loading />);
95
+ it("renders loading-comments class and the respective loading text", () => {
96
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} loading />);
84
97
  expect(wrapper.find('.loading-comments')).to.be.present();
85
98
  expect(wrapper.find('h2')).to.have.text("Loading comments ...");
86
99
  });
87
100
 
88
- it("should render a div of id comments", () => {
89
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
101
+ it("renders a div of id comments", () => {
102
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
90
103
  expect(wrapper.find('#comments')).to.be.present();
91
104
  });
92
105
 
93
- describe("should render a CommentThread component for each comment", () => {
106
+ describe("renders a CommentThread component for each comment", () => {
94
107
  it("and pass filter comment data as a prop to it", () => {
95
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
96
- expect(wrapper).to.have.exactly(comments.length).descendants(CommentThread);
108
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
109
+ expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
97
110
  wrapper.find(CommentThread).forEach((node, idx) => {
98
- expect(node).to.have.prop("comment").deep.equal(filter(commentThreadFragment, comments[idx]));
111
+ expect(node).to.have.prop("comment").deep.equal(filter(commentThreadFragment, commentable.comments[idx]));
99
112
  });
100
113
  });
101
114
 
102
115
  it("and pass the session as a prop to it", () => {
103
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
104
- expect(wrapper).to.have.exactly(comments.length).descendants(CommentThread);
116
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
117
+ expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
105
118
  wrapper.find(CommentThread).forEach((node) => {
106
119
  expect(node).to.have.prop("session").deep.equal(session);
107
120
  });
108
121
  });
109
122
 
110
- it("and pass the option votable as a prop to it", () => {
111
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{ votable: true }} reorderComments={reorderComments} orderBy={orderBy} />);
112
- expect(wrapper).to.have.exactly(comments.length).descendants(CommentThread);
123
+ it("and pass the commentable 'commentsHaveVotes' property as a prop to it", () => {
124
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
125
+ expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
113
126
  wrapper.find(CommentThread).forEach((node) => {
114
127
  expect(node).to.have.prop("votable").equal(true);
115
128
  });
116
129
  });
117
130
  });
118
131
 
119
- it("should render comments count", () => {
120
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
121
- const rex = new RegExp(`${comments.length} comments`);
132
+ it("renders comments count", () => {
133
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
134
+ const rex = new RegExp(`${commentable.comments.length} comments`);
122
135
  expect(wrapper.find('h2.section-heading')).to.have.text().match(rex);
123
136
  });
124
137
 
125
- it("should render a AddCommentForm component and pass 'options.arguable' as a prop", () => {
126
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{ arguable: true }} reorderComments={reorderComments} orderBy={orderBy} />);
138
+ it("renders a AddCommentForm component and pass the commentable 'commentsHaveAlignment' as a prop", () => {
139
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
127
140
  expect(wrapper).to.have.exactly(1).descendants(AddCommentForm);
128
141
  expect(wrapper.find(AddCommentForm)).to.have.prop('arguable').equal(true);
129
142
  });
130
143
 
131
- describe("if session is not present", () => {
144
+ describe("when the commentable cannot accept new comments", () => {
132
145
  beforeEach(() => {
133
- session = null;
146
+ commentable.acceptsNewComments = false;
134
147
  });
135
148
 
136
- it("should not render a AddCommentForm component", () => {
137
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
149
+ it("doesn't render an AddCommentForm component", () => {
150
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
138
151
  expect(wrapper.find(AddCommentForm)).not.to.be.present();
139
152
  });
140
- });
141
153
 
142
- describe("should render a CommentOrderSelector component", () => {
143
- it("should render a CommentOrderSelector component", () => {
144
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
145
- expect(wrapper.find(CommentOrderSelector)).to.be.present();
146
- });
154
+ it("renders a callout message to inform the user that comments are blocked", () => {
155
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
156
+ expect(wrapper.find('.callout.warning')).to.include.text("disabled");
157
+ });
158
+ });
147
159
 
160
+ describe("renders a CommentOrderSelector component", () => {
148
161
  it("and pass the reorderComments as a prop to it", () => {
149
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
162
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
150
163
  expect(wrapper.find(CommentOrderSelector)).to.have.prop('reorderComments').deep.equal(reorderComments);
151
164
  });
152
165
 
153
166
  it("and pass the orderBy as a prop to it", () => {
154
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
167
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
155
168
  expect(wrapper.find(CommentOrderSelector)).to.have.prop('defaultOrderBy').equal('older');
156
169
  });
157
170
  });
@@ -4,10 +4,16 @@ query GetComments($commentableId: String!, $commentableType: String!, $orderBy:
4
4
  name
5
5
  avatarUrl
6
6
  }
7
- ...AddCommentForm
7
+ ...AddCommentFormSession
8
8
  }
9
- comments(commentableId: $commentableId, commentableType: $commentableType, orderBy: $orderBy) {
10
- id
11
- ...CommentThread
9
+ commentable(id: $commentableId, type: $commentableType) {
10
+ acceptsNewComments
11
+ commentsHaveAlignment
12
+ commentsHaveVotes
13
+ comments(orderBy: $orderBy) {
14
+ id
15
+ ...CommentThread
16
+ }
17
+ ...AddCommentFormCommentable
12
18
  }
13
19
  }
@@ -71,20 +71,23 @@ const DownVoteButtonWithMutation = graphql(gql`
71
71
  updateQueries: {
72
72
  GetComments: (prev, { mutationResult: { data } }) => {
73
73
  const commentReducer = (comment) => {
74
- const replies = comment.replies || [];
74
+ const replies = comment.comments || [];
75
75
 
76
76
  if (comment.id === ownProps.comment.id) {
77
77
  return data.comment.downVote;
78
78
  }
79
79
  return {
80
80
  ...comment,
81
- replies: replies.map(commentReducer)
81
+ comments: replies.map(commentReducer)
82
82
  };
83
83
  };
84
84
 
85
85
  return {
86
86
  ...prev,
87
- comments: prev.comments.map(commentReducer)
87
+ commentable: {
88
+ ...prev.commentable,
89
+ comments: prev.commentable.comments.map(commentReducer)
90
+ }
88
91
  }
89
92
  }
90
93
  }
@@ -46,7 +46,7 @@ UpVoteButton.propTypes = {
46
46
 
47
47
  const UpVoteButtonWithMutation = graphql(gql`
48
48
  ${upVoteMutation}
49
- ${commentFragment}
49
+ ${commentFragment}
50
50
  ${commentDataFragment}
51
51
  ${upVoteFragment}
52
52
  ${downVoteFragment}
@@ -71,20 +71,23 @@ const UpVoteButtonWithMutation = graphql(gql`
71
71
  updateQueries: {
72
72
  GetComments: (prev, { mutationResult: { data } }) => {
73
73
  const commentReducer = (comment) => {
74
- const replies = comment.replies || [];
74
+ const replies = comment.comments || [];
75
75
 
76
76
  if (comment.id === ownProps.comment.id) {
77
77
  return data.comment.upVote;
78
78
  }
79
79
  return {
80
80
  ...comment,
81
- replies: replies.map(commentReducer)
81
+ comments: replies.map(commentReducer)
82
82
  };
83
83
  };
84
84
 
85
85
  return {
86
86
  ...prev,
87
- comments: prev.comments.map(commentReducer)
87
+ commentable: {
88
+ ...prev.commentable,
89
+ comments: prev.commentable.comments.map(commentReducer)
90
+ }
88
91
  }
89
92
  }
90
93
  }
@@ -1,6 +1,6 @@
1
1
  import { random, name, date, image } from 'faker/locale/en';
2
2
 
3
- /**
3
+ /**
4
4
  * Generate random comment data to emulate a database real content
5
5
  * @param {number} num - The number of comments to generate random data
6
6
  * @returns {Object[]} - An array of objects representing comments data
@@ -17,9 +17,9 @@ const generateCommentsData = (num = 1) => {
17
17
  name: name.findName(),
18
18
  avatarUrl: image.imageUrl()
19
19
  },
20
- hasReplies: false,
21
- replies: [],
22
- canHaveReplies: true,
20
+ hasComments: false,
21
+ comments: [],
22
+ acceptsNewComments: true,
23
23
  alignment: 0,
24
24
  upVotes: random.number(),
25
25
  upVoted: false,