decidim 0.0.6 → 0.0.7

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 (308) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +2 -1
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +4 -6
  5. data/Dockerfile +2 -2
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +48 -48
  8. data/README.md +6 -1
  9. data/decidim-admin/app/assets/stylesheets/decidim/admin/_decidim.scss +9 -0
  10. data/decidim-admin/app/assets/stylesheets/decidim/admin/_variables.scss +7 -0
  11. data/decidim-admin/app/assets/stylesheets/decidim/admin/application.scss +2 -19
  12. data/decidim-admin/app/assets/stylesheets/decidim/admin/extra/_categories.scss +5 -0
  13. data/decidim-admin/app/assets/stylesheets/decidim/admin/{_email_preview.scss → extra/_email_preview.scss} +0 -0
  14. data/decidim-admin/app/assets/stylesheets/decidim/admin/{_login.scss → extra/_login.scss} +0 -0
  15. data/decidim-admin/app/assets/stylesheets/decidim/admin/extra/_select_multiple.scss +3 -0
  16. data/decidim-admin/app/assets/stylesheets/decidim/admin/extra/_title_bar.scss +5 -0
  17. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_action-icon.scss +13 -0
  18. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_buttons.scss +44 -0
  19. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_callouts.scss +32 -0
  20. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_cards.scss +36 -0
  21. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_char-counter.scss +20 -0
  22. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_component-counter.scss +17 -0
  23. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_forms.scss +80 -0
  24. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_icons.scss +65 -0
  25. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_layout.scss +67 -0
  26. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_main-nav.scss +41 -0
  27. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_modules.scss +24 -0
  28. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_process-header.scss +11 -0
  29. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_secondary-nav.scss +95 -0
  30. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_table-list.scss +78 -0
  31. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_tabs.scss +49 -0
  32. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_title-bar.scss +32 -0
  33. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_typography.scss +15 -0
  34. data/decidim-admin/app/assets/stylesheets/decidim/admin/modules/_user-login.scss +23 -0
  35. data/decidim-admin/app/assets/stylesheets/decidim/admin/utils/_fontface.scss +27 -0
  36. data/decidim-admin/app/assets/stylesheets/decidim/admin/utils/_helpers.scss +25 -0
  37. data/decidim-admin/app/assets/stylesheets/decidim/admin/utils/_keyframes.scss +21 -0
  38. data/decidim-admin/app/assets/stylesheets/decidim/admin/utils/_mixins.scss +20 -0
  39. data/decidim-admin/app/assets/stylesheets/decidim/admin/utils/_settings.scss +590 -0
  40. data/decidim-admin/app/assets/stylesheets/decidim/admin/utils/_toggle-expand.scss +8 -0
  41. data/decidim-admin/app/commands/decidim/admin/create_attachment.rb +13 -4
  42. data/decidim-admin/app/commands/decidim/admin/update_participatory_process.rb +3 -3
  43. data/decidim-admin/app/commands/decidim/admin/update_participatory_process_admin.rb +40 -0
  44. data/decidim-admin/app/controllers/decidim/admin/application_controller.rb +7 -1
  45. data/decidim-admin/app/controllers/decidim/admin/newsletters_controller.rb +1 -1
  46. data/decidim-admin/app/controllers/decidim/admin/organization_controller.rb +2 -0
  47. data/decidim-admin/app/controllers/decidim/admin/participatory_process_user_roles_controller.rb +29 -0
  48. data/decidim-admin/app/controllers/decidim/admin/participatory_processes_controller.rb +2 -2
  49. data/decidim-admin/app/controllers/decidim/admin/scopes_controller.rb +2 -0
  50. data/decidim-admin/app/controllers/decidim/admin/static_pages_controller.rb +2 -0
  51. data/decidim-admin/app/controllers/decidim/admin/user_groups_controller.rb +3 -1
  52. data/decidim-admin/app/controllers/decidim/admin/users_controller.rb +1 -1
  53. data/decidim-admin/app/helpers/decidim/admin/application_helper.rb +8 -0
  54. data/decidim-admin/app/helpers/decidim/admin/icon_link_helper.rb +30 -0
  55. data/decidim-admin/app/jobs/decidim/admin/newsletter_job.rb +1 -1
  56. data/decidim-admin/app/queries/decidim/admin/process_admin_roles_for_process.rb +1 -1
  57. data/decidim-admin/app/views/decidim/admin/attachments/_form.html.erb +29 -8
  58. data/decidim-admin/app/views/decidim/admin/attachments/edit.html.erb +3 -5
  59. data/decidim-admin/app/views/decidim/admin/attachments/index.html.erb +45 -35
  60. data/decidim-admin/app/views/decidim/admin/attachments/new.html.erb +3 -5
  61. data/decidim-admin/app/views/decidim/admin/categories/_form.html.erb +18 -10
  62. data/decidim-admin/app/views/decidim/admin/categories/edit.html.erb +3 -5
  63. data/decidim-admin/app/views/decidim/admin/categories/index.html.erb +57 -42
  64. data/decidim-admin/app/views/decidim/admin/categories/new.html.erb +3 -5
  65. data/decidim-admin/app/views/decidim/admin/dashboard/show.html.erb +6 -3
  66. data/decidim-admin/app/views/decidim/admin/features/_feature.html.erb +11 -11
  67. data/decidim-admin/app/views/decidim/admin/features/_form.html.erb +56 -48
  68. data/decidim-admin/app/views/decidim/admin/features/edit.html.erb +3 -5
  69. data/decidim-admin/app/views/decidim/admin/features/index.html.erb +21 -19
  70. data/decidim-admin/app/views/decidim/admin/features/new.html.erb +3 -4
  71. data/decidim-admin/app/views/decidim/admin/moderations/index.html.erb +71 -61
  72. data/decidim-admin/app/views/decidim/admin/newsletters/_form.html.erb +7 -1
  73. data/decidim-admin/app/views/decidim/admin/newsletters/edit.html.erb +10 -8
  74. data/decidim-admin/app/views/decidim/admin/newsletters/index.html.erb +50 -43
  75. data/decidim-admin/app/views/decidim/admin/newsletters/new.html.erb +10 -8
  76. data/decidim-admin/app/views/decidim/admin/newsletters/show.html.erb +20 -12
  77. data/decidim-admin/app/views/decidim/admin/organization/_form.html.erb +98 -62
  78. data/decidim-admin/app/views/decidim/admin/organization/edit.html.erb +11 -8
  79. data/decidim-admin/app/views/decidim/admin/participatory_process_groups/_form.html.erb +4 -4
  80. data/decidim-admin/app/views/decidim/admin/participatory_process_groups/edit.html.erb +15 -9
  81. data/decidim-admin/app/views/decidim/admin/participatory_process_groups/index.html.erb +37 -35
  82. data/decidim-admin/app/views/decidim/admin/participatory_process_groups/new.html.erb +10 -8
  83. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/_form.html.erb +21 -11
  84. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/edit.html.erb +3 -4
  85. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/index.html.erb +60 -51
  86. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/new.html.erb +4 -5
  87. data/decidim-admin/app/views/decidim/admin/participatory_process_user_roles/_form.html.erb +23 -0
  88. data/decidim-admin/app/views/decidim/admin/participatory_process_user_roles/edit.html.erb +7 -0
  89. data/decidim-admin/app/views/decidim/admin/participatory_process_user_roles/index.html.erb +46 -34
  90. data/decidim-admin/app/views/decidim/admin/participatory_process_user_roles/new.html.erb +7 -0
  91. data/decidim-admin/app/views/decidim/admin/participatory_processes/_form.html.erb +103 -72
  92. data/decidim-admin/app/views/decidim/admin/participatory_processes/edit.html.erb +15 -17
  93. data/decidim-admin/app/views/decidim/admin/participatory_processes/index.html.erb +52 -43
  94. data/decidim-admin/app/views/decidim/admin/participatory_processes/new.html.erb +6 -5
  95. data/decidim-admin/app/views/decidim/admin/scopes/edit.html.erb +9 -7
  96. data/decidim-admin/app/views/decidim/admin/scopes/index.html.erb +37 -36
  97. data/decidim-admin/app/views/decidim/admin/scopes/new.html.erb +9 -6
  98. data/decidim-admin/app/views/decidim/admin/static_pages/_form.html.erb +3 -3
  99. data/decidim-admin/app/views/decidim/admin/static_pages/edit.html.erb +10 -8
  100. data/decidim-admin/app/views/decidim/admin/static_pages/index.html.erb +38 -37
  101. data/decidim-admin/app/views/decidim/admin/static_pages/new.html.erb +10 -8
  102. data/decidim-admin/app/views/decidim/admin/user_groups/index.html.erb +37 -31
  103. data/decidim-admin/app/views/decidim/admin/users/_form.html.erb +5 -4
  104. data/decidim-admin/app/views/decidim/admin/users/index.html.erb +57 -52
  105. data/decidim-admin/app/views/decidim/admin/users/new.html.erb +10 -7
  106. data/decidim-admin/app/views/layouts/decidim/admin/_application.html.erb +23 -36
  107. data/decidim-admin/app/views/layouts/decidim/admin/_callouts_full.html.erb +10 -0
  108. data/decidim-admin/app/views/layouts/decidim/admin/_language_chooser.html.erb +9 -9
  109. data/decidim-admin/app/views/layouts/decidim/admin/_main_nav.html.erb +11 -0
  110. data/decidim-admin/app/views/layouts/decidim/admin/_template_bottom.html.erb +2 -0
  111. data/decidim-admin/app/views/layouts/decidim/admin/_template_top.html.erb +3 -0
  112. data/decidim-admin/app/views/layouts/decidim/admin/_title_bar.html.erb +20 -0
  113. data/decidim-admin/app/views/layouts/decidim/admin/newsletters.erb +14 -0
  114. data/decidim-admin/app/views/layouts/decidim/admin/pages.html.erb +14 -0
  115. data/decidim-admin/app/views/layouts/decidim/admin/participatory_process.html.erb +55 -53
  116. data/decidim-admin/app/views/layouts/decidim/admin/participatory_process_groups.html.erb +14 -0
  117. data/decidim-admin/app/views/layouts/decidim/admin/participatory_processes.html.erb +14 -0
  118. data/decidim-admin/app/views/layouts/decidim/admin/settings.html.erb +19 -0
  119. data/decidim-admin/app/views/layouts/decidim/admin/users.html.erb +21 -0
  120. data/decidim-admin/config/i18n-tasks.yml +1 -0
  121. data/decidim-admin/config/locales/ca.yml +77 -22
  122. data/decidim-admin/config/locales/en.yml +75 -20
  123. data/decidim-admin/config/locales/es.yml +77 -22
  124. data/decidim-admin/config/locales/eu.yml +191 -16
  125. data/decidim-admin/config/locales/fi.yml +0 -29
  126. data/decidim-admin/config/routes.rb +1 -1
  127. data/decidim-admin/lib/decidim/admin/test/manage_attachments_examples.rb +11 -11
  128. data/decidim-admin/spec/commands/deliver_newsletter_spec.rb +4 -1
  129. data/decidim-admin/spec/commands/update_participatory_process_spec.rb +117 -0
  130. data/decidim-admin/spec/features/admin_invite_spec.rb +5 -1
  131. data/decidim-admin/spec/features/admin_manages_feature_permissions_spec.rb +1 -1
  132. data/decidim-admin/spec/features/admin_manages_features_spec.rb +10 -10
  133. data/decidim-admin/spec/features/admin_manages_newsletters_spec.rb +11 -9
  134. data/decidim-admin/spec/features/admin_manages_organization_admins_spec.rb +8 -5
  135. data/decidim-admin/spec/features/admin_manages_organization_scopes_spec.rb +7 -6
  136. data/decidim-admin/spec/features/admin_manages_organization_spec.rb +14 -0
  137. data/decidim-admin/spec/features/admin_manages_participatory_process_groups_spec.rb +6 -6
  138. data/decidim-admin/spec/features/admin_manages_participatory_processes_spec.rb +27 -5
  139. data/decidim-admin/spec/features/admin_manages_user_groups_spec.rb +1 -1
  140. data/decidim-admin/spec/features/participatory_process_admin_manages_participatory_process_admins_spec.rb +2 -2
  141. data/decidim-admin/spec/features/static_pages_spec.rb +9 -7
  142. data/decidim-admin/spec/jobs/newsletter_job_spec.rb +2 -0
  143. data/decidim-admin/spec/queries/process_admin_roles_for_process_spec.rb +4 -3
  144. data/decidim-admin/spec/shared/manage_process_admins_examples.rb +31 -8
  145. data/decidim-admin/spec/shared/manage_process_attachments_examples.rb +1 -1
  146. data/decidim-admin/spec/shared/manage_process_categories_examples.rb +11 -26
  147. data/decidim-admin/spec/shared/manage_process_steps_examples.rb +8 -23
  148. data/decidim-admin/spec/shared/manage_processes_examples.rb +5 -33
  149. data/decidim-admin/spec/support/processes_menu_links_helpers.rb +1 -1
  150. data/decidim-admin/yarn.lock +4 -0
  151. data/decidim-api/app/controllers/decidim/api/application_controller.rb +2 -1
  152. data/decidim-budgets/app/controllers/decidim/budgets/admin/projects_controller.rb +1 -1
  153. data/decidim-budgets/app/controllers/decidim/budgets/line_items_controller.rb +21 -15
  154. data/decidim-budgets/app/views/decidim/budgets/admin/projects/_form.html.erb +34 -24
  155. data/decidim-budgets/app/views/decidim/budgets/admin/projects/edit.html.erb +3 -5
  156. data/decidim-budgets/app/views/decidim/budgets/admin/projects/index.html.erb +52 -34
  157. data/decidim-budgets/app/views/decidim/budgets/admin/projects/new.html.erb +4 -5
  158. data/decidim-budgets/app/views/decidim/budgets/projects/_filters.html.erb +1 -1
  159. data/decidim-budgets/config/i18n-tasks.yml +1 -0
  160. data/decidim-budgets/config/locales/ca.yml +5 -0
  161. data/decidim-budgets/config/locales/en.yml +5 -0
  162. data/decidim-budgets/config/locales/es.yml +5 -0
  163. data/decidim-budgets/config/locales/eu.yml +27 -0
  164. data/decidim-budgets/spec/features/orders_spec.rb +22 -0
  165. data/decidim-budgets/spec/shared/manage_attachments_examples.rb +1 -1
  166. data/decidim-budgets/spec/shared/manage_projects_examples.rb +10 -8
  167. data/decidim-comments/README.md +33 -0
  168. data/decidim-comments/app/assets/javascripts/decidim/comments/bundle.js +0 -0
  169. data/decidim-comments/app/assets/javascripts/decidim/comments/comments.js.erb +2 -0
  170. data/decidim-comments/app/frontend/application/{apollo_client.js → apollo_client.ts} +5 -5
  171. data/decidim-comments/app/frontend/application/application.component.test.tsx +36 -0
  172. data/decidim-comments/app/frontend/application/application.component.tsx +37 -0
  173. data/decidim-comments/app/frontend/application/icon.component.test.tsx +49 -0
  174. data/decidim-comments/app/frontend/application/icon.component.tsx +35 -0
  175. data/decidim-comments/app/frontend/comments/{add_comment_form.component.test.jsx → add_comment_form.component.test.tsx} +98 -92
  176. data/decidim-comments/app/frontend/comments/{add_comment_form.component.jsx → add_comment_form.component.tsx} +152 -153
  177. data/decidim-comments/app/frontend/comments/{comment.component.test.jsx → comment.component.test.tsx} +59 -71
  178. data/decidim-comments/app/frontend/comments/{comment.component.jsx → comment.component.tsx} +114 -116
  179. data/decidim-comments/app/frontend/comments/comment_order_selector.component.test.tsx +21 -0
  180. data/decidim-comments/app/frontend/comments/comment_order_selector.component.tsx +88 -0
  181. data/decidim-comments/app/frontend/comments/comment_thread.component.test.tsx +65 -0
  182. data/decidim-comments/app/frontend/comments/comment_thread.component.tsx +70 -0
  183. data/decidim-comments/app/frontend/comments/{comments.component.test.jsx → comments.component.test.tsx} +38 -81
  184. data/decidim-comments/app/frontend/comments/{comments.component.jsx → comments.component.tsx} +49 -63
  185. data/decidim-comments/app/frontend/comments/down_vote_button.component.test.tsx +39 -0
  186. data/decidim-comments/app/frontend/comments/down_vote_button.component.tsx +89 -0
  187. data/decidim-comments/app/frontend/comments/up_vote_button.component.test.tsx +39 -0
  188. data/decidim-comments/app/frontend/comments/up_vote_button.component.tsx +89 -0
  189. data/decidim-comments/app/frontend/comments/vote_button.component.tsx +36 -0
  190. data/decidim-comments/app/frontend/comments/{vote_button_component.test.jsx → vote_button_component.test.tsx} +16 -20
  191. data/decidim-comments/app/frontend/entry.ts +19 -0
  192. data/decidim-comments/app/frontend/{comments → fragments}/add_comment_form_commentable.fragment.graphql +1 -1
  193. data/decidim-comments/app/frontend/{comments → fragments}/add_comment_form_session.fragment.graphql +1 -1
  194. data/decidim-comments/app/frontend/{comments → fragments}/comment.fragment.graphql +3 -1
  195. data/decidim-comments/app/frontend/{comments → fragments}/comment_data.fragment.graphql +6 -3
  196. data/decidim-comments/app/frontend/{comments → fragments}/comment_thread.fragment.graphql +3 -1
  197. data/decidim-comments/app/frontend/{comments/down_vote.fragment.graphql → fragments/down_vote_button.fragment.graphql} +2 -2
  198. data/decidim-comments/app/frontend/{comments/up_vote.fragment.graphql → fragments/up_vote_button.fragment.graphql} +2 -2
  199. data/decidim-comments/app/frontend/{comments/add_comment_form.mutation.graphql → mutations/add_comment.mutation.graphql} +3 -1
  200. data/decidim-comments/app/frontend/{comments → mutations}/down_vote.mutation.graphql +3 -1
  201. data/decidim-comments/app/frontend/{comments → mutations}/up_vote.mutation.graphql +3 -1
  202. data/decidim-comments/app/frontend/{comments → queries}/comments.query.graphql +4 -1
  203. data/decidim-comments/app/frontend/support/{asset_url.js → asset_url.ts} +1 -1
  204. data/decidim-comments/app/frontend/support/{generate_comments_data.js → generate_comments_data.ts} +11 -6
  205. data/decidim-comments/app/frontend/support/{generate_user_data.js → generate_user_data.ts} +2 -2
  206. data/decidim-comments/app/frontend/support/{generate_user_group_data.js → generate_user_group_data.ts} +2 -2
  207. data/decidim-comments/app/frontend/support/graphql_transformer.js +32 -0
  208. data/decidim-comments/app/frontend/support/load_translations.ts +44 -0
  209. data/decidim-comments/app/frontend/support/{require_all.js → require_all.ts} +1 -1
  210. data/decidim-comments/app/frontend/support/{resolve_graphql_query.js → resolve_graphql_query.ts} +7 -7
  211. data/decidim-comments/app/frontend/support/schema.ts +119 -0
  212. data/decidim-comments/config/locales/eu.yml +29 -5
  213. data/decidim-dev/Rakefile +2 -0
  214. data/decidim-dev/config/locales/eu.yml +1 -1
  215. data/decidim-dev/decidim-dev.gemspec +1 -1
  216. data/decidim-dev/lib/decidim/dev/test/rspec_support/capybara.rb +1 -4
  217. data/decidim-dev/lib/decidim/dev/test/rspec_support/feature.rb +1 -0
  218. data/decidim-dev/lib/decidim/dev/test/rspec_support/helpers.rb +1 -1
  219. data/decidim-dev/lib/decidim/dev/test/rspec_support/phantomjs_polyfills/phantomjs-shim.js +215 -0
  220. data/decidim-dev/lib/decidim/dev/test/rspec_support/translation_helpers.rb +1 -1
  221. data/decidim-dev/lib/generators/decidim/dummy_generator.rb +5 -0
  222. data/decidim-meetings/app/controllers/decidim/meetings/admin/application_controller.rb +1 -1
  223. data/decidim-meetings/app/views/decidim/meetings/admin/meeting_closes/_form.html.erb +30 -19
  224. data/decidim-meetings/app/views/decidim/meetings/admin/meeting_closes/edit.html.erb +3 -5
  225. data/decidim-meetings/app/views/decidim/meetings/admin/meetings/_form.html.erb +46 -35
  226. data/decidim-meetings/app/views/decidim/meetings/admin/meetings/edit.html.erb +5 -6
  227. data/decidim-meetings/app/views/decidim/meetings/admin/meetings/index.html.erb +72 -51
  228. data/decidim-meetings/app/views/decidim/meetings/admin/meetings/new.html.erb +4 -6
  229. data/decidim-meetings/app/views/decidim/meetings/meetings/_filters.html.erb +1 -1
  230. data/decidim-meetings/config/locales/ca.yml +2 -1
  231. data/decidim-meetings/config/locales/en.yml +2 -1
  232. data/decidim-meetings/config/locales/es.yml +2 -1
  233. data/decidim-meetings/config/locales/eu.yml +25 -5
  234. data/decidim-meetings/config/locales/fi.yml +0 -1
  235. data/decidim-meetings/spec/shared/manage_attachments_examples.rb +1 -1
  236. data/decidim-meetings/spec/shared/manage_meetings_examples.rb +18 -16
  237. data/decidim-pages/app/views/decidim/pages/admin/pages/_form.html.erb +10 -3
  238. data/decidim-pages/app/views/decidim/pages/admin/pages/edit.html.erb +3 -5
  239. data/decidim-pages/config/locales/eu.yml +4 -2
  240. data/decidim-pages/spec/features/admin_spec.rb +1 -1
  241. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposals_controller.rb +1 -1
  242. data/decidim-proposals/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +14 -8
  243. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/_form.html.erb +28 -20
  244. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/index.html.erb +60 -43
  245. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/new.html.erb +3 -5
  246. data/decidim-proposals/app/views/decidim/proposals/proposals/_filters.html.erb +1 -1
  247. data/decidim-proposals/config/locales/ca.yml +3 -0
  248. data/decidim-proposals/config/locales/en.yml +4 -1
  249. data/decidim-proposals/config/locales/es.yml +3 -0
  250. data/decidim-proposals/config/locales/eu.yml +42 -19
  251. data/decidim-proposals/config/locales/fi.yml +0 -1
  252. data/decidim-proposals/spec/shared/manage_proposals_examples.rb +15 -13
  253. data/decidim-results/README.md +1 -1
  254. data/decidim-results/app/controllers/decidim/results/admin/results_controller.rb +1 -1
  255. data/decidim-results/app/views/decidim/results/admin/results/_form.html.erb +30 -21
  256. data/decidim-results/app/views/decidim/results/admin/results/edit.html.erb +4 -5
  257. data/decidim-results/app/views/decidim/results/admin/results/index.html.erb +40 -25
  258. data/decidim-results/app/views/decidim/results/admin/results/new.html.erb +4 -5
  259. data/decidim-results/app/views/decidim/results/results/_filters.html.erb +1 -1
  260. data/decidim-results/config/locales/ca.yml +2 -0
  261. data/decidim-results/config/locales/en.yml +2 -0
  262. data/decidim-results/config/locales/es.yml +2 -0
  263. data/decidim-results/config/locales/eu.yml +22 -4
  264. data/decidim-results/spec/shared/manage_results_examples.rb +10 -8
  265. data/decidim-system/config/locales/eu.yml +60 -2
  266. data/lib/generators/decidim/app_generator.rb +0 -4
  267. data/lib/generators/decidim/install_generator.rb +0 -4
  268. data/lib/generators/decidim/templates/README.md.erb +18 -18
  269. data/package.json +47 -22
  270. data/tsconfig.json +17 -0
  271. data/tslint.json +12 -0
  272. data/webpack.config.js +8 -6
  273. data/webpack.d.ts +5 -0
  274. data/yarn.lock +1461 -781
  275. metadata +119 -83
  276. data/decidim-admin/app/assets/stylesheets/decidim/admin/_actions.scss +0 -8
  277. data/decidim-admin/app/assets/stylesheets/decidim/admin/_forms.scss +0 -10
  278. data/decidim-admin/app/assets/stylesheets/decidim/admin/_foundation_and_overrides.scss +0 -53
  279. data/decidim-admin/app/assets/stylesheets/decidim/admin/_icons.scss +0 -9
  280. data/decidim-admin/app/assets/stylesheets/decidim/admin/_layout.scss +0 -27
  281. data/decidim-admin/app/assets/stylesheets/decidim/admin/_settings.scss +0 -566
  282. data/decidim-admin/app/assets/stylesheets/decidim/admin/_tables.scss +0 -21
  283. data/decidim-admin/app/views/layouts/decidim/admin/_login_items.html.erb +0 -8
  284. data/decidim-admin/app/views/layouts/decidim/admin/_sidebar.html.erb +0 -22
  285. data/decidim-comments/app/frontend/application/application.component.jsx +0 -37
  286. data/decidim-comments/app/frontend/application/application.component.test.jsx +0 -33
  287. data/decidim-comments/app/frontend/application/icon.component.jsx +0 -26
  288. data/decidim-comments/app/frontend/application/icon.component.test.jsx +0 -53
  289. data/decidim-comments/app/frontend/comments/comment_order_selector.component.jsx +0 -72
  290. data/decidim-comments/app/frontend/comments/comment_order_selector.component.test.jsx +0 -20
  291. data/decidim-comments/app/frontend/comments/comment_thread.component.jsx +0 -75
  292. data/decidim-comments/app/frontend/comments/comment_thread.component.test.jsx +0 -83
  293. data/decidim-comments/app/frontend/comments/down_vote_button.component.jsx +0 -98
  294. data/decidim-comments/app/frontend/comments/down_vote_button.component.test.jsx +0 -48
  295. data/decidim-comments/app/frontend/comments/featured_comment.component.jsx +0 -23
  296. data/decidim-comments/app/frontend/comments/featured_comment.component.test.jsx +0 -15
  297. data/decidim-comments/app/frontend/comments/up_vote_button.component.jsx +0 -98
  298. data/decidim-comments/app/frontend/comments/up_vote_button.component.test.jsx +0 -48
  299. data/decidim-comments/app/frontend/comments/vote_button.component.jsx +0 -32
  300. data/decidim-comments/app/frontend/entry.js +0 -17
  301. data/decidim-comments/app/frontend/entry.test.js +0 -31
  302. data/decidim-comments/app/frontend/support/load_translations.js +0 -23
  303. data/decidim-comments/app/frontend/support/stub_component.js +0 -29
  304. data/decidim-dev/lib/decidim/dev/test/rspec_support/phantomjs_polyfills/bind-polyfill.js +0 -18
  305. data/decidim-dev/lib/decidim/dev/test/rspec_support/phantomjs_polyfills/object-assign-polyfill.js +0 -24
  306. data/karma.conf.js +0 -45
  307. data/lib/generators/decidim/review_app_generator.rb +0 -53
  308. data/lib/generators/decidim/templates/app.json.erb +0 -16
@@ -1,184 +1,190 @@
1
- /* eslint-disable no-unused-expressions */
2
- import { shallow, mount } from 'enzyme';
1
+ import { mount, ReactWrapper, shallow } from "enzyme";
2
+ import * as React from "react";
3
3
 
4
- import { AddCommentForm } from './add_comment_form.component';
4
+ import { AddCommentForm } from "./add_comment_form.component";
5
5
 
6
- import generateUserData from '../support/generate_user_data';
7
- import generateUserGroupData from '../support/generate_user_group_data';
6
+ import generateUserData from "../support/generate_user_data";
7
+ import generateUserGroupData from "../support/generate_user_group_data";
8
+ import { loadLocaleTranslations } from "../support/load_translations";
8
9
 
9
10
  describe("<AddCommentForm />", () => {
10
- let session = null;
11
+ let session: any = null;
11
12
  const commentable = {
12
13
  id: "1",
13
- type: "Decidim::DummyResource"
14
+ type: "Decidim::DummyResource",
14
15
  };
15
- const addCommentStub = () => {
16
+ const addCommentStub = (): any => {
16
17
  return null;
17
- }
18
+ };
18
19
 
19
20
  beforeEach(() => {
21
+ loadLocaleTranslations("en");
20
22
  session = {
21
23
  user: generateUserData(),
22
- verifiedUserGroups: []
24
+ verifiedUserGroups: [],
25
+ };
26
+ window.DecidimComments = {
27
+ assets: {
28
+ "icons.svg": "/assets/icons.svg",
29
+ },
23
30
  };
24
31
  });
25
32
 
26
33
  it("should render a div with class add-comment", () => {
27
34
  const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
28
- expect(wrapper.find('div.add-comment')).to.present();
35
+ expect(wrapper.find("div.add-comment")).toBeDefined();
29
36
  });
30
37
 
31
38
  it("should have a reference to body textarea", () => {
32
39
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
33
- expect(wrapper.instance().bodyTextArea).to.be.ok;
40
+ expect((wrapper.instance() as AddCommentForm).bodyTextArea).toBeDefined();
34
41
  });
35
42
 
36
43
  it("should initialize with a state property disabled as true", () => {
37
44
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
38
- expect(wrapper).to.have.state('disabled', true);
45
+ expect(wrapper.state()).toHaveProperty("disabled", true);
39
46
  });
40
47
 
41
48
  it("should have a default prop showTitle as true", () => {
42
49
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
43
- expect(wrapper).to.have.prop('showTitle').equal(true);
50
+ expect(wrapper.props()).toHaveProperty("showTitle", true);
44
51
  });
45
52
 
46
53
  it("should not render the title if prop showTitle is false", () => {
47
54
  const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} showTitle={false} />);
48
- expect(wrapper.find('h5.section-heading')).not.to.be.present();
55
+ expect(wrapper.find("h5.section-heading").exists()).toBeFalsy();
49
56
  });
50
57
 
51
58
  it("should have a default prop submitButtonClassName as 'button button--sc'", () => {
52
59
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
53
- expect(wrapper).to.have.prop('submitButtonClassName').equal('button button--sc');
60
+ expect(wrapper.props()).toHaveProperty("submitButtonClassName", "button button--sc");
54
61
  });
55
62
 
56
63
  it("should have a default prop maxLength of 1000", () => {
57
64
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
58
- expect(wrapper).to.have.prop('maxLength').equal(1000);
65
+ expect(wrapper.props()).toHaveProperty("maxLength", 1000);
59
66
  });
60
67
 
61
-
62
68
  it("should use prop submitButtonClassName as a className prop for submit button", () => {
63
69
  const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} submitButtonClassName="button small hollow" />);
64
- expect(wrapper.find('button[type="submit"]')).to.have.className('button');
65
- expect(wrapper.find('button[type="submit"]')).to.have.className('small');
66
- expect(wrapper.find('button[type="submit"]')).to.have.className('hollow');
70
+ expect(wrapper.find('button[type="submit"]').hasClass("button")).toBeTruthy();
71
+ expect(wrapper.find('button[type="submit"]').hasClass("small")).toBeTruthy();
72
+ expect(wrapper.find('button[type="submit"]').hasClass("hollow")).toBeTruthy();
67
73
  });
68
74
 
69
75
  it("should enable the submit button if textarea is not blank", () => {
70
76
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
71
- wrapper.find('textarea').simulate('change', {
77
+ wrapper.find("textarea").simulate("change", {
72
78
  target: {
73
- value: 'This is a comment'
74
- }
79
+ value: "This is a comment",
80
+ },
75
81
  });
76
- expect(wrapper.find('button[type="submit"]')).not.to.be.disabled();
82
+ expect(wrapper.find('button[type="submit"]').props()).not.toHaveProperty("disabled", true);
77
83
  });
78
84
 
79
85
  it("should disable the submit button if textarea is blank", () => {
80
86
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
81
- wrapper.find('textarea').simulate('change', {
87
+ wrapper.find("textarea").simulate("change", {
82
88
  target: {
83
- value: 'This will be deleted'
84
- }
89
+ value: "This will be deleted",
90
+ },
85
91
  });
86
- wrapper.find('textarea').simulate('change', {
92
+ wrapper.find("textarea").simulate("change", {
87
93
  target: {
88
- value: ''
89
- }
94
+ value: "",
95
+ },
90
96
  });
91
- expect(wrapper.find('button[type="submit"]')).to.be.disabled();
97
+ expect(wrapper.find('button[type="submit"]').props()).toHaveProperty("disabled", true);
92
98
  });
93
99
 
94
100
  it("should not render a div with class 'opinion-toggle'", () => {
95
101
  const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
96
- expect(wrapper.find('.opinion-toggle')).not.to.be.present();
102
+ expect(wrapper.find(".opinion-toggle").exists()).toBeFalsy();
97
103
  });
98
104
 
99
105
  describe("submitting the form", () => {
100
- let addComment = null;
101
- let onCommentAdded = null;
102
- let wrapper = null;
103
- let message = null;
106
+ let addComment: jasmine.Spy;
107
+ let onCommentAdded: jasmine.Spy ;
108
+ let wrapper: ReactWrapper<any, {}>;
109
+ let message: any = null;
104
110
 
105
111
  beforeEach(() => {
106
- addComment = sinon.spy();
107
- onCommentAdded = sinon.spy();
112
+ addComment = jasmine.createSpy("addComment");
113
+ onCommentAdded = jasmine.createSpy("onCommentAdded");
108
114
  wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} onCommentAdded={onCommentAdded} />);
109
- message = 'This will be submitted';
110
- wrapper.instance().bodyTextArea.value = message;
115
+ message = "This will be submitted";
116
+ (wrapper.instance() as AddCommentForm).bodyTextArea.value = message;
111
117
  });
112
118
 
113
119
  it("should call addComment prop with the textarea value and state property alignment", () => {
114
- wrapper.find('form').simulate('submit');
115
- expect(addComment).to.calledWith({ body: message, alignment: 0 });
120
+ wrapper.find("form").simulate("submit");
121
+ expect(addComment).toHaveBeenCalledWith({ body: message, alignment: 0 });
116
122
  });
117
123
 
118
124
  it("should reset textarea", () => {
119
- wrapper.find('form').simulate('submit');
120
- expect(wrapper.find('textarea')).to.have.value('');
125
+ wrapper.find("form").simulate("submit");
126
+ expect((wrapper.find("textarea").get(0) as any).value).toBe("");
121
127
  });
122
128
 
123
129
  it("should prevent default form submission", () => {
124
- const preventDefault = sinon.spy();
125
- wrapper.find('form').simulate('submit', { preventDefault });
126
- expect(preventDefault).to.have.been.called;
130
+ const preventDefault = jasmine.createSpy("preventDefault");
131
+ wrapper.find("form").simulate("submit", { preventDefault });
132
+ expect(preventDefault).toHaveBeenCalled();
127
133
  });
128
134
 
129
135
  it("should call the prop onCommentAdded function", () => {
130
- wrapper.find('form').simulate('submit');
131
- expect(onCommentAdded).to.have.been.called;
136
+ wrapper.find("form").simulate("submit");
137
+ expect(onCommentAdded).toHaveBeenCalled();
132
138
  });
133
139
  });
134
140
 
135
141
  it("should initialize state with a property alignment and value 0", () => {
136
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
137
- expect(wrapper).to.have.state('alignment').equal(0);
142
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable={true} />);
143
+ expect(wrapper.state()).toHaveProperty("alignment", 0);
138
144
  });
139
145
 
140
146
  describe("when receiving an optional prop arguable with value true", () => {
141
147
  it("should render a div with class 'opinion-toggle'", () => {
142
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
143
- expect(wrapper.find('.opinion-toggle')).to.be.present();
148
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable={true} />);
149
+ expect(wrapper.find(".opinion-toggle")).toBeDefined();
144
150
  });
145
151
 
146
152
  it("should set state alignment to 1 if user clicks ok button and change its class", () => {
147
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
148
- wrapper.find('.opinion-toggle--ok').simulate('click');
149
- expect(wrapper.find('.opinion-toggle--ok')).to.have.className('is-active');
150
- expect(wrapper).to.have.state('alignment').equal(1);
153
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable={true} />);
154
+ wrapper.find(".opinion-toggle--ok").simulate("click");
155
+ expect(wrapper.find(".opinion-toggle--ok").hasClass("is-active")).toBeTruthy();
156
+ expect(wrapper.state()).toHaveProperty("alignment", 1);
151
157
  });
152
158
 
153
159
  it("should set state alignment to -11 if user clicks ko button and change its class", () => {
154
- const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable />);
155
- wrapper.find('.opinion-toggle--ko').simulate('click');
156
- expect(wrapper.find('.opinion-toggle--ko')).to.have.className('is-active');
157
- expect(wrapper).to.have.state('alignment').equal(-1);
160
+ const wrapper = shallow(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} arguable={true} />);
161
+ wrapper.find(".opinion-toggle--ko").simulate("click");
162
+ expect(wrapper.find(".opinion-toggle--ko").hasClass("is-active")).toBeTruthy();
163
+ expect(wrapper.state()).toHaveProperty("alignment", -1);
158
164
  });
159
165
 
160
166
  describe("submitting the form", () => {
161
- let addComment = null;
162
- let wrapper = null;
163
- let message = null;
167
+ let wrapper: ReactWrapper<any, {}>;
168
+ let addComment: jasmine.Spy;
169
+ let message: string;
164
170
 
165
171
  beforeEach(() => {
166
- addComment = sinon.spy();
167
- wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} arguable />);
168
- message = 'This will be submitted';
169
- wrapper.instance().bodyTextArea.value = message;
172
+ addComment = jasmine.createSpy("addComment");
173
+ wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} arguable={true} />);
174
+ message = "This will be submitted";
175
+ (wrapper.instance() as AddCommentForm).bodyTextArea.value = message;
170
176
  });
171
177
 
172
178
  it("should call addComment prop with the state's property alignment", () => {
173
- wrapper.find('button.opinion-toggle--ko').simulate('click');
174
- wrapper.find('form').simulate('submit');
175
- expect(addComment).to.calledWith({ body: message, alignment: -1 });
179
+ wrapper.find("button.opinion-toggle--ko").simulate("click");
180
+ wrapper.find("form").simulate("submit");
181
+ expect(addComment).toHaveBeenCalledWith({ body: message, alignment: -1 });
176
182
  });
177
183
 
178
184
  it("should reset the state to its initial state", () => {
179
- wrapper.find('button.opinion-toggle--ok').simulate('click');
180
- wrapper.find('form').simulate('submit');
181
- expect(wrapper).to.have.state('alignment').eq(0);
185
+ wrapper.find("button.opinion-toggle--ok").simulate("click");
186
+ wrapper.find("form").simulate("submit");
187
+ expect(wrapper.state()).toHaveProperty("alignment", 0);
182
188
  });
183
189
  });
184
190
  });
@@ -187,48 +193,48 @@ describe("<AddCommentForm />", () => {
187
193
  beforeEach(() => {
188
194
  session.verifiedUserGroups = [
189
195
  generateUserGroupData(),
190
- generateUserGroupData()
196
+ generateUserGroupData(),
191
197
  ];
192
198
  });
193
199
 
194
200
  it("should have a reference to user_group_id select", () => {
195
201
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
196
- expect(wrapper.instance().userGroupIdSelect).to.be.ok;
202
+ expect((wrapper.instance() as AddCommentForm).userGroupIdSelect).toBeDefined();
197
203
  });
198
204
 
199
205
  it("should render a select with option tags for each verified user group", () => {
200
206
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
201
- expect(wrapper.find('select')).to.have.exactly(3).descendants('option');
207
+ expect(wrapper.find("select").children("option").length).toBe(3);
202
208
  });
203
209
 
204
210
  describe("submitting the form", () => {
205
- let addComment = null;
206
- let wrapper = null;
207
- let message = null;
208
- let userGroupId = null;
211
+ let addComment: jasmine.Spy;
212
+ let wrapper: ReactWrapper<any, {}>;
213
+ let message: string;
214
+ let userGroupId: string;
209
215
 
210
216
  beforeEach(() => {
211
- addComment = sinon.spy();
217
+ addComment = jasmine.createSpy("addComment");
212
218
  wrapper = mount(<AddCommentForm addComment={addComment} session={session} commentable={commentable} />);
213
- message = 'This will be submitted';
219
+ message = "This will be submitted";
214
220
  userGroupId = session.verifiedUserGroups[1].id;
215
- wrapper.instance().bodyTextArea.value = message;
216
- wrapper.instance().userGroupIdSelect.value = userGroupId;
221
+ (wrapper.instance() as AddCommentForm).bodyTextArea.value = message;
222
+ (wrapper.instance() as AddCommentForm).userGroupIdSelect.value = userGroupId;
217
223
  });
218
224
 
219
225
  it("should call addComment prop with the body textarea, alignment and user_group_id select values", () => {
220
- wrapper.find('form').simulate('submit');
221
- expect(addComment).to.calledWith({ body: message, alignment: 0, userGroupId });
226
+ wrapper.find("form").simulate("submit");
227
+ expect(addComment).toHaveBeenCalledWith({ body: message, alignment: 0, userGroupId });
222
228
  });
223
229
 
224
230
  describe("when user_group_id is blank", () => {
225
231
  beforeEach(() => {
226
- wrapper.instance().userGroupIdSelect.value = '';
232
+ (wrapper.instance() as AddCommentForm).userGroupIdSelect.value = "";
227
233
  });
228
234
 
229
235
  it("should call addComment prop with the body textarea and alignment", () => {
230
- wrapper.find('form').simulate('submit');
231
- expect(addComment).to.calledWith({ body: message, alignment: 0 });
236
+ wrapper.find("form").simulate("submit");
237
+ expect(addComment).toHaveBeenCalledWith({ body: message, alignment: 0 });
232
238
  });
233
239
  });
234
240
  });
@@ -241,7 +247,7 @@ describe("<AddCommentForm />", () => {
241
247
 
242
248
  it("display a message to sign in or sign up", () => {
243
249
  const wrapper = mount(<AddCommentForm addComment={addCommentStub} session={session} commentable={commentable} />);
244
- expect(wrapper.find('span')).to.include.text("sign up");
250
+ expect(wrapper.find("span").text()).toContain("sign up");
245
251
  });
246
252
  });
247
253
  });
@@ -1,39 +1,69 @@
1
1
  /* eslint-disable no-return-assign, react/no-unused-prop-types, max-lines */
2
- import { Component, PropTypes } from 'react';
3
- import { graphql } from 'react-apollo';
4
- import gql from 'graphql-tag';
5
- import { I18n, Translate } from 'react-i18nify';
6
- import uuid from 'uuid';
7
- import classnames from 'classnames';
8
-
9
- import Icon from '../application/icon.component';
10
-
11
- import addCommentMutation from './add_comment_form.mutation.graphql';
12
- import commentThreadFragment from './comment_thread.fragment.graphql'
13
- import commentFragment from './comment.fragment.graphql';
14
- import commentDataFragment from './comment_data.fragment.graphql';
15
- import upVoteFragment from './up_vote.fragment.graphql';
16
- import downVoteFragment from './down_vote.fragment.graphql';
17
- import addCommentFormSessionFragment from './add_comment_form_session.fragment.graphql';
18
- import addCommentFormCommentableFragment from './add_comment_form_commentable.fragment.graphql';
2
+ import * as classnames from "classnames";
3
+ import * as React from "react";
4
+ import { graphql } from "react-apollo";
5
+ import * as uuid from "uuid";
6
+
7
+ import Icon from "../application/icon.component";
8
+
9
+ const { I18n, Translate } = require("react-i18nify");
10
+
11
+ import {
12
+ AddCommentFormCommentableFragment,
13
+ AddCommentFormSessionFragment,
14
+ AddCommentMutation,
15
+ CommentFragment,
16
+ GetCommentsQuery,
17
+ } from "../support/schema";
18
+
19
+ interface AddCommentFormProps {
20
+ session: AddCommentFormSessionFragment & {
21
+ user: any;
22
+ } | null;
23
+ commentable: AddCommentFormCommentableFragment;
24
+ showTitle?: boolean;
25
+ submitButtonClassName?: string;
26
+ autoFocus?: boolean;
27
+ maxLength?: number;
28
+ arguable?: boolean;
29
+ addComment?: (data: { body: string, alignment: number, userGroupId?: string }) => void;
30
+ onCommentAdded?: () => void;
31
+ }
32
+
33
+ interface AddCommentFormState {
34
+ disabled: boolean;
35
+ error: boolean;
36
+ alignment: number;
37
+ }
19
38
 
20
39
  /**
21
40
  * Renders a form to create new comments.
22
41
  * @class
23
42
  * @augments Component
24
43
  */
25
- export class AddCommentForm extends Component {
26
- constructor(props) {
44
+ export class AddCommentForm extends React.Component<AddCommentFormProps, AddCommentFormState> {
45
+ public static defaultProps = {
46
+ showTitle: true,
47
+ submitButtonClassName: "button button--sc",
48
+ arguable: false,
49
+ autoFocus: false,
50
+ maxLength: 1000,
51
+ };
52
+
53
+ public bodyTextArea: HTMLTextAreaElement;
54
+ public userGroupIdSelect: HTMLSelectElement;
55
+
56
+ constructor(props: AddCommentFormProps) {
27
57
  super(props);
28
58
 
29
59
  this.state = {
30
60
  disabled: true,
31
61
  error: false,
32
- alignment: 0
62
+ alignment: 0,
33
63
  };
34
64
  }
35
65
 
36
- render() {
66
+ public render() {
37
67
  return (
38
68
  <div className="add-comment">
39
69
  {this._renderHeading()}
@@ -49,13 +79,13 @@ export class AddCommentForm extends Component {
49
79
  * @private
50
80
  * @returns {Void|DOMElement} - The heading or an empty element
51
81
  */
52
- _renderHeading() {
82
+ private _renderHeading() {
53
83
  const { showTitle } = this.props;
54
84
 
55
85
  if (showTitle) {
56
86
  return (
57
87
  <h5 className="section-heading">
58
- { I18n.t("components.add_comment_form.title") }
88
+ {I18n.t("components.add_comment_form.title")}
59
89
  </h5>
60
90
  );
61
91
  }
@@ -68,7 +98,7 @@ export class AddCommentForm extends Component {
68
98
  * @private
69
99
  * @returns {Void|DOMElement} - The message or an empty element.
70
100
  */
71
- _renderAccountMessage() {
101
+ private _renderAccountMessage() {
72
102
  const { session } = this.props;
73
103
 
74
104
  if (!session) {
@@ -78,7 +108,7 @@ export class AddCommentForm extends Component {
78
108
  value="components.add_comment_form.account_message"
79
109
  sign_in_url="/users/sign_in"
80
110
  sign_up_url="/users/sign_up"
81
- dangerousHTML
111
+ dangerousHTML={true}
82
112
  />
83
113
  </p>
84
114
  );
@@ -92,21 +122,23 @@ export class AddCommentForm extends Component {
92
122
  * @private
93
123
  * @returns {Void|DOMElement} - The add comment form on an empty element.
94
124
  */
95
- _renderForm() {
125
+ private _renderForm() {
96
126
  const { session, submitButtonClassName, commentable: { id, type } } = this.props;
97
127
  const { disabled } = this.state;
98
128
 
99
129
  if (session) {
100
130
  return (
101
- <form onSubmit={(evt) => this._addComment(evt)}>
131
+ <form onSubmit={this.addComment}>
102
132
  {this._renderCommentAs()}
103
133
  <div className="field">
104
- <label className="show-for-sr" htmlFor={`add-comment-${type}-${id}`}>{ I18n.t("components.add_comment_form.form.body.label") }</label>
134
+ <label className="show-for-sr" htmlFor={`add-comment-${type}-${id}`}>{I18n.t("components.add_comment_form.form.body.label")}</label>
105
135
  {this._renderTextArea()}
106
136
  {this._renderTextAreaError()}
107
- <button type="submit"
108
- className={submitButtonClassName}
109
- disabled={disabled}>
137
+ <button
138
+ type="submit"
139
+ className={submitButtonClassName}
140
+ disabled={disabled}
141
+ >
110
142
  {I18n.t("components.add_comment_form.form.submit")}
111
143
  </button>
112
144
  </div>
@@ -122,13 +154,13 @@ export class AddCommentForm extends Component {
122
154
  * @private
123
155
  * @returns {Void|DOMElement} - The heading or an empty element
124
156
  */
125
- _renderTextArea() {
157
+ private _renderTextArea() {
126
158
  const { commentable: { id, type }, autoFocus, maxLength } = this.props;
127
159
  const { error } = this.state;
128
- const className = classnames({ 'is-invalid-input': error });
160
+ const className = classnames({ "is-invalid-input": error });
129
161
 
130
- let textAreaProps = {
131
- ref: (textarea) => {this.bodyTextArea = textarea},
162
+ let textAreaProps: any = {
163
+ ref: (textarea: HTMLTextAreaElement) => {this.bodyTextArea = textarea; },
132
164
  id: `add-comment-${type}-${id}`,
133
165
  className,
134
166
  rows: "4",
@@ -136,10 +168,11 @@ export class AddCommentForm extends Component {
136
168
  required: "required",
137
169
  pattern: `^(.){0,${maxLength}}$`,
138
170
  placeholder: I18n.t("components.add_comment_form.form.body.placeholder"),
139
- onChange: (evt) => this._checkCommentBody(evt.target.value)
171
+ onChange: (evt: React.ChangeEvent<HTMLTextAreaElement>) => this._checkCommentBody(evt.target.value),
140
172
  };
173
+
141
174
  if (autoFocus) {
142
- textAreaProps.autoFocus = 'autoFocus';
175
+ textAreaProps.autoFocus = "autoFocus";
143
176
  }
144
177
 
145
178
  return (
@@ -152,14 +185,14 @@ export class AddCommentForm extends Component {
152
185
  * @private
153
186
  * @returns {Void|DOMElement} - The error or an empty element
154
187
  */
155
- _renderTextAreaError() {
188
+ private _renderTextAreaError() {
156
189
  const { maxLength } = this.props;
157
190
  const { error } = this.state;
158
191
 
159
192
  if (error) {
160
193
  return (
161
194
  <span className="form-error is-visible">
162
- { I18n.t("components.add_comment_form.form.form_error", { length: maxLength }) }
195
+ {I18n.t("components.add_comment_form.form.form_error", { length: maxLength })}
163
196
  </span>
164
197
  );
165
198
  }
@@ -167,23 +200,29 @@ export class AddCommentForm extends Component {
167
200
  return null;
168
201
  }
169
202
 
203
+ private setAlignment = (alignment: number) => {
204
+ return () => {
205
+ this.setState({ alignment });
206
+ };
207
+ }
208
+
170
209
  /**
171
210
  * Render opinion buttons or not based on the arguable prop
172
211
  * @private
173
212
  * @returns {Void|DOMElement} - Returns nothing or a wrapper with buttons
174
213
  */
175
- _renderOpinionButtons() {
214
+ private _renderOpinionButtons() {
176
215
  const { session, arguable } = this.props;
177
216
  const { alignment } = this.state;
178
- const buttonClassName = classnames('button', 'tiny', 'button--muted');
179
- const okButtonClassName = classnames(buttonClassName, 'opinion-toggle--ok', {
180
- 'is-active': alignment === 1
217
+ const buttonClassName = classnames("button", "tiny", "button--muted");
218
+ const okButtonClassName = classnames(buttonClassName, "opinion-toggle--ok", {
219
+ "is-active": alignment === 1,
181
220
  });
182
- const koButtonClassName = classnames(buttonClassName, 'opinion-toggle--ko', {
183
- 'is-active': alignment === -1
221
+ const koButtonClassName = classnames(buttonClassName, "opinion-toggle--ko", {
222
+ "is-active": alignment === -1,
184
223
  });
185
- const neutralButtonClassName = classnames(buttonClassName, 'opinion-toggle--meh', {
186
- 'is-active': alignment === 0
224
+ const neutralButtonClassName = classnames(buttonClassName, "opinion-toggle--meh", {
225
+ "is-active": alignment === 0,
187
226
  });
188
227
 
189
228
  if (session && arguable) {
@@ -191,19 +230,19 @@ export class AddCommentForm extends Component {
191
230
  <div className="opinion-toggle button-group">
192
231
  <button
193
232
  className={okButtonClassName}
194
- onClick={() => this.setState({ alignment: 1 })}
233
+ onClick={this.setAlignment(1)}
195
234
  >
196
235
  <Icon iconExtraClassName="" name="icon-thumb-up" />
197
236
  </button>
198
237
  <button
199
238
  className={neutralButtonClassName}
200
- onClick={() => this.setState({ alignment: 0 })}
239
+ onClick={this.setAlignment(0)}
201
240
  >
202
- { I18n.t("components.add_comment_form.opinion.neutral") }
241
+ {I18n.t("components.add_comment_form.opinion.neutral")}
203
242
  </button>
204
243
  <button
205
244
  className={koButtonClassName}
206
- onClick={() => this.setState({ alignment: -1 })}
245
+ onClick={this.setAlignment(-1)}
207
246
  >
208
247
  <Icon iconExtraClassName="" name="icon-thumb-down" />
209
248
  </button>
@@ -214,34 +253,39 @@ export class AddCommentForm extends Component {
214
253
  return null;
215
254
  }
216
255
 
256
+ private setUserGroupIdSelect = (select: HTMLSelectElement) => {this.userGroupIdSelect = select; };
257
+
217
258
  /**
218
259
  * Render a select with an option for each user's verified group
219
260
  * @private
220
261
  * @returns {Void|DOMElement} - Returns nothing or a form field.
221
262
  */
222
- _renderCommentAs() {
263
+ private _renderCommentAs() {
223
264
  const { session, commentable: { id, type } } = this.props;
224
- const { user, verifiedUserGroups } = session;
225
265
 
226
- if (verifiedUserGroups.length > 0) {
227
- return (
228
- <div className="field">
229
- <label htmlFor={`add-comment-${type}-${id}-user-group-id`}>
230
- { I18n.t('components.add_comment_form.form.user_group_id.label') }
231
- </label>
232
- <select
233
- ref={(select) => {this.userGroupIdSelect = select}}
234
- id={`add-comment-${type}-${id}-user-group-id`}
235
- >
236
- <option value="">{ user.name }</option>
237
- {
238
- verifiedUserGroups.map((userGroup) => (
239
- <option key={userGroup.id} value={userGroup.id}>{userGroup.name}</option>
240
- ))
241
- }
242
- </select>
243
- </div>
244
- );
266
+ if (session) {
267
+ const { user, verifiedUserGroups } = session;
268
+
269
+ if (verifiedUserGroups.length > 0) {
270
+ return (
271
+ <div className="field">
272
+ <label htmlFor={`add-comment-${type}-${id}-user-group-id`}>
273
+ {I18n.t("components.add_comment_form.form.user_group_id.label")}
274
+ </label>
275
+ <select
276
+ ref={this.setUserGroupIdSelect}
277
+ id={`add-comment-${type}-${id}-user-group-id`}
278
+ >
279
+ <option value="">{user.name}</option>
280
+ {
281
+ verifiedUserGroups.map((userGroup) => (
282
+ <option key={userGroup.id} value={userGroup.id}>{userGroup.name}</option>
283
+ ))
284
+ }
285
+ </select>
286
+ </div>
287
+ );
288
+ }
245
289
  }
246
290
 
247
291
  return null;
@@ -253,9 +297,9 @@ export class AddCommentForm extends Component {
253
297
  * @param {string} body - The comment's body
254
298
  * @returns {Void} - Returns nothing
255
299
  */
256
- _checkCommentBody(body) {
300
+ private _checkCommentBody(body: string) {
257
301
  const { maxLength } = this.props;
258
- this.setState({ disabled: body === '', error: body === '' || body.length > maxLength });
302
+ this.setState({ disabled: body === "", error: body === "" || (maxLength !== undefined && body.length > maxLength) });
259
303
  }
260
304
 
261
305
  /**
@@ -265,20 +309,22 @@ export class AddCommentForm extends Component {
265
309
  * @param {DOMEvent} evt - The form's submission event
266
310
  * @returns {Void} - Returns nothing
267
311
  */
268
- _addComment(evt) {
312
+ private addComment = (evt: React.FormEvent<HTMLFormElement>) => {
269
313
  const { alignment } = this.state;
270
314
  const { addComment, onCommentAdded } = this.props;
271
- let addCommentParams = { body: this.bodyTextArea.value, alignment };
315
+ let addCommentParams: { body: string, alignment: number, userGroupId?: string } = { body: this.bodyTextArea.value, alignment };
272
316
 
273
317
  evt.preventDefault();
274
318
 
275
- if (this.userGroupIdSelect && this.userGroupIdSelect.value !== '') {
319
+ if (this.userGroupIdSelect && this.userGroupIdSelect.value !== "") {
276
320
  addCommentParams.userGroupId = this.userGroupIdSelect.value;
277
321
  }
278
322
 
279
- addComment(addCommentParams);
323
+ if (addComment) {
324
+ addComment(addCommentParams);
325
+ }
280
326
 
281
- this.bodyTextArea.value = '';
327
+ this.bodyTextArea.value = "";
282
328
  this.setState({ alignment: 0 });
283
329
 
284
330
  if (onCommentAdded) {
@@ -287,80 +333,33 @@ export class AddCommentForm extends Component {
287
333
  }
288
334
  }
289
335
 
290
- AddCommentForm.propTypes = {
291
- addComment: PropTypes.func.isRequired,
292
- session: PropTypes.shape({
293
- user: PropTypes.shape({
294
- name: PropTypes.string.isRequired
295
- }),
296
- verifiedUserGroups: PropTypes.arrayOf(
297
- PropTypes.shape({
298
- name: PropTypes.string.isRequired
299
- })
300
- ).isRequired
301
- }),
302
- commentable: PropTypes.shape({
303
- id: PropTypes.string.isRequired,
304
- type: PropTypes.string.isRequired
305
- }),
306
- showTitle: PropTypes.bool.isRequired,
307
- submitButtonClassName: PropTypes.string.isRequired,
308
- onCommentAdded: PropTypes.func,
309
- arguable: PropTypes.bool,
310
- autoFocus: PropTypes.bool,
311
- maxLength: PropTypes.number.isRequired
312
- };
313
-
314
- AddCommentForm.defaultProps = {
315
- onCommentAdded: function() {},
316
- showTitle: true,
317
- submitButtonClassName: 'button button--sc',
318
- arguable: false,
319
- autoFocus: false,
320
- maxLength: 1000
321
- };
322
-
323
- AddCommentForm.fragments = {
324
- session: gql`
325
- ${addCommentFormSessionFragment}
326
- `,
327
- commentable: gql`
328
- ${addCommentFormCommentableFragment}
329
- `
330
- };
331
-
332
- const AddCommentFormWithMutation = graphql(gql`
333
- ${addCommentMutation}
334
- ${commentThreadFragment}
335
- ${commentFragment}
336
- ${commentDataFragment}
337
- ${upVoteFragment}
338
- ${downVoteFragment}
339
- `, {
336
+ const addCommentMutation = require("../mutations/add_comment.mutation.graphql");
337
+
338
+ const AddCommentFormWithMutation = graphql(addCommentMutation, {
340
339
  props: ({ ownProps, mutate }) => ({
341
- addComment: ({ body, alignment, userGroupId }) => mutate({
340
+ addComment: ({ body, alignment, userGroupId }: { body: string, alignment: number, userGroupId: string }) => mutate({
342
341
  variables: {
343
342
  commentableId: ownProps.commentable.id,
344
343
  commentableType: ownProps.commentable.type,
345
344
  body,
346
345
  alignment,
347
- userGroupId
346
+ userGroupId,
348
347
  },
349
348
  optimisticResponse: {
350
349
  commentable: {
351
- __typename: 'CommentableMutation',
350
+ __typename: "CommentableMutation",
352
351
  addComment: {
353
- __typename: 'Comment',
352
+ __typename: "Comment",
354
353
  id: uuid(),
355
354
  sgid: uuid(),
356
355
  type: "Decidim::Comments::Comment",
357
356
  createdAt: new Date().toISOString(),
358
357
  body,
359
- alignment: alignment,
358
+ alignment,
360
359
  author: {
361
- __typename: 'User',
360
+ __typename: "User",
362
361
  name: ownProps.session.user.name,
363
- avatarUrl: ownProps.session.user.avatarUrl
362
+ avatarUrl: ownProps.session.user.avatarUrl,
364
363
  },
365
364
  comments: [],
366
365
  hasComments: false,
@@ -369,32 +368,32 @@ const AddCommentFormWithMutation = graphql(gql`
369
368
  upVoted: false,
370
369
  downVotes: 0,
371
370
  downVoted: false,
372
- alreadyReported: false
373
- }
374
- }
371
+ alreadyReported: false,
372
+ },
373
+ },
375
374
  },
376
375
  updateQueries: {
377
- GetComments: (prev, { mutationResult: { data } }) => {
376
+ GetComments: (prev: GetCommentsQuery, { mutationResult: { data } }: { mutationResult: { data: AddCommentMutation }}) => {
378
377
  const { id, type } = ownProps.commentable;
379
- const newComment = data.commentable.addComment;
378
+ const newComment = data.commentable && data.commentable.addComment;
380
379
  let comments = [];
381
380
 
382
- const commentReducer = (comment) => {
381
+ const commentReducer = (comment: CommentFragment): CommentFragment => {
383
382
  const replies = comment.comments || [];
384
383
 
385
- if (comment.id === id) {
384
+ if (newComment && comment.id === id) {
386
385
  return {
387
386
  ...comment,
388
387
  hasComments: true,
389
388
  comments: [
390
389
  ...replies,
391
- newComment
392
- ]
390
+ newComment,
391
+ ],
393
392
  };
394
393
  }
395
394
  return {
396
395
  ...comment,
397
- comments: replies.map(commentReducer)
396
+ comments: replies.map(commentReducer),
398
397
  };
399
398
  };
400
399
 
@@ -403,7 +402,7 @@ const AddCommentFormWithMutation = graphql(gql`
403
402
  } else {
404
403
  comments = [
405
404
  ...prev.commentable.comments,
406
- newComment
405
+ newComment,
407
406
  ];
408
407
  }
409
408
 
@@ -411,13 +410,13 @@ const AddCommentFormWithMutation = graphql(gql`
411
410
  ...prev,
412
411
  commentable: {
413
412
  ...prev.commentable,
414
- comments
415
- }
413
+ comments,
414
+ },
416
415
  };
417
- }
418
- }
419
- })
420
- })
416
+ },
417
+ },
418
+ }),
419
+ }),
421
420
  })(AddCommentForm);
422
421
 
423
422
  export default AddCommentFormWithMutation;