decidim-core 0.31.5 → 0.32.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -13
  3. data/app/cells/decidim/amendable/amend_button_card_cell.rb +1 -1
  4. data/app/cells/decidim/content_blocks/announcement_settings_form/show.erb +5 -0
  5. data/app/cells/decidim/content_blocks/announcement_settings_form_cell.rb +17 -0
  6. data/app/cells/decidim/content_blocks/how_to_participate/show.erb +1 -1
  7. data/app/cells/decidim/content_blocks/participatory_space_announcement_cell.rb +1 -3
  8. data/app/cells/decidim/content_blocks/participatory_space_hero_cell.rb +1 -5
  9. data/app/cells/decidim/{participatory_space_private_user_cell.rb → member_cell.rb} +1 -1
  10. data/app/cells/decidim/onboarding_action_message_cell.rb +1 -0
  11. data/app/cells/decidim/profile/details.erb +1 -1
  12. data/app/cells/decidim/profile_cell.rb +1 -0
  13. data/app/cells/decidim/profile_member_of/show.erb +13 -0
  14. data/app/cells/decidim/profile_member_of_cell.rb +35 -0
  15. data/app/cells/decidim/tags_cell.rb +1 -1
  16. data/app/cells/decidim/versions_list/show.erb +25 -0
  17. data/app/commands/decidim/create_follow.rb +3 -5
  18. data/app/commands/decidim/create_user_report.rb +1 -1
  19. data/app/commands/decidim/destroy_account.rb +3 -3
  20. data/app/commands/decidim/search.rb +5 -5
  21. data/app/controllers/concerns/decidim/ajax_permission_handler.rb +1 -1
  22. data/app/controllers/concerns/decidim/direct_upload.rb +3 -2
  23. data/app/controllers/concerns/decidim/ephemeral_session_checker.rb +1 -1
  24. data/app/controllers/concerns/decidim/force_authentication.rb +10 -1
  25. data/app/controllers/concerns/decidim/impersonate_users.rb +4 -0
  26. data/app/controllers/concerns/decidim/locale_switcher.rb +52 -2
  27. data/app/controllers/concerns/decidim/needs_tos_accepted.rb +1 -1
  28. data/app/controllers/concerns/decidim/participatory_space/has_members_page.rb +27 -0
  29. data/app/controllers/concerns/decidim/participatory_space_context.rb +1 -5
  30. data/app/controllers/decidim/account_controller.rb +1 -1
  31. data/app/controllers/decidim/amendments_controller.rb +5 -4
  32. data/app/controllers/decidim/components/base_controller.rb +20 -3
  33. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -1
  34. data/app/controllers/decidim/devise/passwords_controller.rb +1 -1
  35. data/app/controllers/decidim/devise/registrations_controller.rb +1 -1
  36. data/app/controllers/decidim/devise/sessions_controller.rb +1 -1
  37. data/app/controllers/decidim/download_your_data_controller.rb +1 -1
  38. data/app/controllers/decidim/editor_images_controller.rb +1 -1
  39. data/app/controllers/decidim/follows_controller.rb +4 -3
  40. data/app/controllers/decidim/geolocation_controller.rb +1 -1
  41. data/app/controllers/decidim/likes_controller.rb +2 -1
  42. data/app/controllers/decidim/locales_controller.rb +3 -11
  43. data/app/controllers/decidim/messaging/conversations_controller.rb +3 -3
  44. data/app/controllers/decidim/newsletters_controller.rb +1 -1
  45. data/app/controllers/decidim/notifications_settings_controller.rb +1 -1
  46. data/app/controllers/decidim/notifications_subscriptions_controller.rb +1 -1
  47. data/app/controllers/decidim/open_data_controller.rb +1 -1
  48. data/app/controllers/decidim/report_users_controller.rb +2 -2
  49. data/app/controllers/decidim/reports_controller.rb +1 -1
  50. data/app/controllers/decidim/user_activities_controller.rb +1 -2
  51. data/app/events/decidim/welcome_notification_event.rb +1 -1
  52. data/app/forms/decidim/upload_validation_form.rb +1 -1
  53. data/app/helpers/concerns/decidim/flash_helper_extensions.rb +1 -0
  54. data/app/helpers/decidim/active_link_to_helper.rb +136 -0
  55. data/app/helpers/decidim/amendments_helper.rb +3 -3
  56. data/app/helpers/decidim/check_boxes_tree_helper.rb +2 -2
  57. data/app/helpers/decidim/component_path_helper.rb +9 -4
  58. data/app/helpers/decidim/filters_helper.rb +0 -2
  59. data/app/helpers/decidim/menu_helper.rb +3 -3
  60. data/app/helpers/decidim/meta_tags_helper.rb +0 -2
  61. data/app/helpers/decidim/paginate_helper.rb +14 -1
  62. data/app/helpers/decidim/sanitize_helper.rb +1 -0
  63. data/app/helpers/decidim/short_link_helper.rb +5 -5
  64. data/app/helpers/decidim/translations_helper.rb +4 -4
  65. data/app/jobs/decidim/delete_inactive_participants_job.rb +2 -2
  66. data/app/mailers/decidim/application_mailer.rb +7 -2
  67. data/app/mailers/decidim/decidim_devise_mailer.rb +1 -0
  68. data/app/mailers/decidim/reported_mailer.rb +4 -1
  69. data/app/models/decidim/action_log.rb +1 -1
  70. data/app/models/decidim/attachment.rb +2 -2
  71. data/app/models/decidim/authorization_transfer.rb +1 -1
  72. data/app/models/decidim/component.rb +6 -9
  73. data/app/models/decidim/content_block.rb +3 -3
  74. data/app/models/decidim/newsletter.rb +2 -2
  75. data/app/models/decidim/participatory_space/member.rb +55 -0
  76. data/app/models/decidim/resource_link.rb +1 -1
  77. data/app/models/decidim/short_link.rb +1 -1
  78. data/app/packs/src/decidim/controllers/assign_role/assign_role.test.js +80 -0
  79. data/app/packs/src/decidim/controllers/assign_role/controller.js +27 -0
  80. data/app/packs/src/decidim/controllers/main_menu/controller.js +115 -0
  81. data/app/packs/src/decidim/controllers/main_menu/main_menu.test.js +185 -0
  82. data/app/packs/src/decidim/datepicker/generate_datepicker.js +1 -13
  83. data/app/packs/src/decidim/editor/common/suggestion.js +1 -1
  84. data/app/packs/src/decidim/editor/extensions/decidim_kit/index.js +4 -1
  85. data/app/packs/src/decidim/editor/extensions/image/index.js +8 -5
  86. data/app/packs/src/decidim/editor/extensions/link/index.js +8 -0
  87. data/app/packs/src/decidim/editor/extensions/mention/index.js +18 -5
  88. data/app/packs/src/decidim/editor/extensions/mention_resource/index.js +16 -7
  89. data/app/packs/src/decidim/editor/test/extensions/decidim_kit.test.js +13 -4
  90. data/app/packs/src/decidim/editor/test/extensions/heading.test.js +1 -1
  91. data/app/packs/src/decidim/editor/test/extensions/image.test.js +26 -28
  92. data/app/packs/src/decidim/editor/test/extensions/mention.test.js +6 -2
  93. data/app/packs/src/decidim/editor/test/extensions/video_embed.test.js +27 -15
  94. data/app/packs/src/decidim/editor/test/helpers.js +8 -10
  95. data/app/packs/src/decidim/editor/test/toolbar/full.test.js +15 -4
  96. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_block.js +8 -4
  97. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_formatting.js +5 -1
  98. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_indent.js +9 -5
  99. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_link.js +5 -1
  100. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_list.js +8 -4
  101. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_styling.js +13 -9
  102. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_content_styling.js +6 -2
  103. data/app/packs/src/decidim/index.js +0 -1
  104. data/app/packs/stylesheets/decidim/_cards.scss +2 -1
  105. data/app/packs/stylesheets/decidim/_datepicker.scss +0 -8
  106. data/app/packs/stylesheets/decidim/_dropdown.scss +21 -31
  107. data/app/packs/stylesheets/decidim/_footer.scss +0 -36
  108. data/app/packs/stylesheets/decidim/_forms.scss +5 -1
  109. data/app/packs/stylesheets/decidim/_header.scss +148 -46
  110. data/app/packs/stylesheets/decidim/_language_chooser.scss +79 -0
  111. data/app/packs/stylesheets/decidim/application.scss +1 -0
  112. data/app/presenters/decidim/admin_log/participatory_space/member_presenter.rb +40 -0
  113. data/app/presenters/decidim/admin_log/static_page_resource_presenter.rb +1 -1
  114. data/app/presenters/decidim/authorization_transfer_presenter.rb +1 -1
  115. data/app/presenters/decidim/breadcrumb_root_menu_item_presenter.rb +3 -3
  116. data/app/presenters/decidim/log/user_presenter.rb +1 -0
  117. data/app/presenters/decidim/log/value_types/access_mode_presenter.rb +45 -0
  118. data/app/presenters/decidim/participatory_space/member_presenter.rb +48 -0
  119. data/app/presenters/decidim/resource_locator_presenter.rb +22 -3
  120. data/app/queries/decidim/last_activity.rb +1 -1
  121. data/app/serializers/decidim/exporters/participatory_space_serializer.rb +15 -1
  122. data/app/services/decidim/email_notification_generator.rb +2 -0
  123. data/app/services/decidim/notification_generator.rb +4 -0
  124. data/app/uploaders/decidim/organization_favicon_uploader.rb +4 -9
  125. data/app/validators/uploader_image_dimensions_validator.rb +13 -11
  126. data/app/views/decidim/homepage/show.html.erb +1 -1
  127. data/app/views/decidim/messaging/conversations/_new_conversation_button.html.erb +1 -1
  128. data/app/views/decidim/messaging/conversations/_reply.html.erb +1 -1
  129. data/app/views/decidim/messaging/conversations/new.html.erb +1 -1
  130. data/app/views/decidim/messaging/conversations/show.html.erb +2 -2
  131. data/app/views/decidim/participatory_space/members/_member.html.erb +1 -0
  132. data/app/views/decidim/shared/_results_per_page.html.erb +1 -1
  133. data/app/views/decidim/shared/filters/_check_boxes_tree.html.erb +1 -1
  134. data/app/views/decidim/shared/filters/_dropdown_label.html.erb +4 -3
  135. data/app/views/devise/mailer/{invite_private_user.html.erb → invite_member.html.erb} +2 -2
  136. data/app/views/devise/mailer/{invite_private_user.text.erb → invite_member.text.erb} +2 -2
  137. data/app/views/layouts/decidim/_head.html.erb +2 -2
  138. data/app/views/layouts/decidim/_wrapper.html.erb +1 -1
  139. data/app/views/layouts/decidim/footer/_focus_mode_main.html.erb +0 -1
  140. data/app/views/layouts/decidim/footer/_main.html.erb +0 -1
  141. data/app/views/layouts/decidim/header/_focus_mode_back_button.html.erb +4 -1
  142. data/app/views/layouts/decidim/header/_main.html.erb +17 -8
  143. data/app/views/layouts/decidim/header/_main_language_chooser.html.erb +24 -0
  144. data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +56 -56
  145. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +2 -2
  146. data/app/views/layouts/decidim/header/_main_links_mobile_item_account.html.erb +3 -1
  147. data/app/views/layouts/decidim/header/_main_menu_mobile.html.erb +18 -5
  148. data/app/views/layouts/decidim/header/_main_search.html.erb +3 -3
  149. data/app/views/layouts/decidim/header/_menu.html.erb +5 -5
  150. data/app/views/layouts/decidim/header/_menu_breadcrumb_desktop.html.erb +13 -12
  151. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile.html.erb +28 -0
  152. data/config/initializers/invisible_captcha.rb +1 -0
  153. data/config/locales/ar.yml +3 -10
  154. data/config/locales/bg.yml +3 -10
  155. data/config/locales/ca-IT.yml +33 -19
  156. data/config/locales/ca.yml +33 -19
  157. data/config/locales/cs.yml +22 -19
  158. data/config/locales/de.yml +13 -18
  159. data/config/locales/el.yml +3 -7
  160. data/config/locales/en.yml +33 -19
  161. data/config/locales/es-MX.yml +33 -19
  162. data/config/locales/es-PY.yml +33 -19
  163. data/config/locales/es.yml +33 -19
  164. data/config/locales/eu.yml +33 -19
  165. data/config/locales/fi-plain.yml +33 -19
  166. data/config/locales/fi.yml +33 -19
  167. data/config/locales/fr-CA.yml +33 -19
  168. data/config/locales/fr.yml +33 -19
  169. data/config/locales/ga-IE.yml +3 -1
  170. data/config/locales/gl.yml +3 -7
  171. data/config/locales/hu.yml +3 -10
  172. data/config/locales/id-ID.yml +3 -7
  173. data/config/locales/it.yml +3 -10
  174. data/config/locales/ja.yml +18 -19
  175. data/config/locales/lb.yml +3 -8
  176. data/config/locales/lt.yml +3 -10
  177. data/config/locales/lv.yml +3 -7
  178. data/config/locales/nl.yml +3 -8
  179. data/config/locales/no.yml +3 -8
  180. data/config/locales/pl.yml +3 -10
  181. data/config/locales/pt-BR.yml +17 -19
  182. data/config/locales/pt.yml +3 -8
  183. data/config/locales/ro-RO.yml +10 -18
  184. data/config/locales/ru.yml +0 -6
  185. data/config/locales/sk.yml +33 -19
  186. data/config/locales/sv.yml +33 -19
  187. data/config/locales/tr-TR.yml +3 -8
  188. data/config/locales/uk.yml +0 -3
  189. data/config/locales/zh-CN.yml +3 -7
  190. data/config/locales/zh-TW.yml +3 -8
  191. data/config/routes.rb +139 -76
  192. data/db/data/20251213075429_rename_members_in_action_log.rb +22 -0
  193. data/db/data/20260319145808_rename_send_to_members_in_newsletter.rb +27 -0
  194. data/db/migrate/20200730142511_add_file_upload_settings_to_decidim_organizations.rb +13 -15
  195. data/db/migrate/20250523104311_move_cta_to_hero_content_block.rb +1 -1
  196. data/db/migrate/20250527122040_move_highlighted_content_banner_settings_to_content_block.rb +1 -1
  197. data/db/migrate/20250819110800_convert_private_exports_id_to_uuid.rb +2 -2
  198. data/db/migrate/20251112132305_remove_legacy_images_from_core_module.rb +27 -0
  199. data/db/migrate/20251205122428_rename_participatory_space_private_users_to_members.rb +9 -0
  200. data/db/migrate/20251216185133_rename_privatable_to_to_participatory_space_in_members.rb +10 -0
  201. data/db/migrate/20260208201405_remove_user_group_core.rb +21 -0
  202. data/db/migrate/20260208201406_remove_user_group_memberships.rb +30 -0
  203. data/db/migrate/20260208201407_remove_user_group_organizations.rb +11 -0
  204. data/db/migrate/20260217152425_remove_enable_participatory_space_filters_from_decidim_organizations.rb +7 -0
  205. data/db/migrate/20260314081619_add_index_on_decidim_members.rb +25 -0
  206. data/decidim-core.gemspec +25 -28
  207. data/lib/decidim/acts_as_tree.rb +12 -12
  208. data/lib/decidim/admin_filters_registry.rb +1 -1
  209. data/lib/decidim/amendable.rb +5 -1
  210. data/lib/decidim/api/functions/category_list.rb +1 -0
  211. data/lib/decidim/api/functions/component_finder_base.rb +1 -1
  212. data/lib/decidim/api/functions/component_list.rb +3 -1
  213. data/lib/decidim/api/functions/component_list_base.rb +1 -0
  214. data/lib/decidim/api/functions/participatory_space_finder_base.rb +1 -1
  215. data/lib/decidim/api/functions/participatory_space_list_base.rb +1 -0
  216. data/lib/decidim/api/functions/user_entity_list.rb +0 -1
  217. data/lib/decidim/api/interfaces/amendable_entity_interface.rb +1 -0
  218. data/lib/decidim/api/interfaces/amendable_interface.rb +1 -0
  219. data/lib/decidim/api/interfaces/attachable_collection_interface.rb +1 -0
  220. data/lib/decidim/api/interfaces/attachable_interface.rb +1 -0
  221. data/lib/decidim/api/interfaces/author_interface.rb +2 -1
  222. data/lib/decidim/api/interfaces/authorable_interface.rb +1 -0
  223. data/lib/decidim/api/interfaces/categories_container_interface.rb +1 -0
  224. data/lib/decidim/api/interfaces/categorizable_interface.rb +1 -0
  225. data/lib/decidim/api/interfaces/coauthorable_interface.rb +1 -0
  226. data/lib/decidim/api/interfaces/component_interface.rb +9 -8
  227. data/lib/decidim/api/interfaces/fingerprint_interface.rb +1 -0
  228. data/lib/decidim/api/interfaces/followable_interface.rb +2 -0
  229. data/lib/decidim/api/interfaces/likeable_interface.rb +2 -2
  230. data/lib/decidim/api/interfaces/localizable_interface.rb +1 -0
  231. data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -0
  232. data/lib/decidim/api/interfaces/referable_interface.rb +1 -0
  233. data/lib/decidim/api/interfaces/scopable_interface.rb +1 -0
  234. data/lib/decidim/api/interfaces/taxonomizable_interface.rb +1 -0
  235. data/lib/decidim/api/interfaces/timestamps_interface.rb +1 -0
  236. data/lib/decidim/api/interfaces/traceable_interface.rb +1 -0
  237. data/lib/decidim/api/types/attachment_collection_type.rb +5 -3
  238. data/lib/decidim/api/types/attachment_type.rb +11 -4
  239. data/lib/decidim/api/types/badge_score_type.rb +28 -0
  240. data/lib/decidim/api/types/moderation_type.rb +25 -0
  241. data/lib/decidim/api/types/participant_details_type.rb +17 -0
  242. data/lib/decidim/api/types/reportable_type.rb +18 -0
  243. data/lib/decidim/api/types/reportable_user_type.rb +17 -0
  244. data/lib/decidim/api/types/static_page_topic_type.rb +16 -0
  245. data/lib/decidim/api/types/static_page_type.rb +22 -0
  246. data/lib/decidim/api/types/taxonomy_type.rb +2 -0
  247. data/lib/decidim/api/types/user_moderation_type.rb +36 -0
  248. data/lib/decidim/api/types/user_type.rb +13 -0
  249. data/lib/decidim/asset_router/pipeline.rb +1 -1
  250. data/lib/decidim/asset_router/storage.rb +9 -9
  251. data/lib/decidim/assets/tailwind/tailwind.config.js.erb +3 -0
  252. data/lib/decidim/attribute_encryptor.rb +51 -10
  253. data/lib/decidim/attribute_object/model.rb +0 -5
  254. data/lib/decidim/block_registry.rb +1 -1
  255. data/lib/decidim/coauthorable.rb +2 -1
  256. data/lib/decidim/command.rb +1 -0
  257. data/lib/decidim/content_processor.rb +1 -1
  258. data/lib/decidim/core/api.rb +8 -0
  259. data/lib/decidim/core/content_blocks/registry_manager.rb +0 -6
  260. data/lib/decidim/core/engine.rb +2 -34
  261. data/lib/decidim/core/menu.rb +0 -26
  262. data/lib/decidim/core/test/factories.rb +29 -18
  263. data/lib/decidim/core/test/shared_examples/access_mode_restricted_participatory_spaces.rb +172 -0
  264. data/lib/decidim/core/test/shared_examples/access_mode_transparent_participatory_spaces.rb +241 -0
  265. data/lib/decidim/core/test/shared_examples/comments_examples.rb +115 -111
  266. data/lib/decidim/core/test/shared_examples/fingerprint_examples.rb +2 -0
  267. data/lib/decidim/core/test/shared_examples/has_members.rb +63 -0
  268. data/lib/decidim/core/test/shared_examples/manage_share_tokens_examples.rb +1 -1
  269. data/lib/decidim/core/test/shared_examples/map_examples.rb +1 -1
  270. data/lib/decidim/core/test/shared_examples/participant_details_examples.rb +16 -0
  271. data/lib/decidim/core/test/shared_examples/participatory_space_members_page_examples.rb +10 -10
  272. data/lib/decidim/core/test/shared_examples/resource_liked_event_examples.rb +1 -1
  273. data/lib/decidim/core/test/shared_examples/resource_locator_presenter_examples.rb +7 -7
  274. data/lib/decidim/core/test/shared_examples/searchable_participatory_space_examples.rb +3 -3
  275. data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +2 -2
  276. data/lib/decidim/core/test/shared_examples/simple_event.rb +1 -0
  277. data/lib/decidim/core/test/shared_examples/translated_event_examples.rb +2 -2
  278. data/lib/decidim/core/test/shared_examples/versions_controller_examples.rb +1 -1
  279. data/lib/decidim/core/test.rb +2 -1
  280. data/lib/decidim/core/version.rb +1 -1
  281. data/lib/decidim/core.rb +222 -335
  282. data/lib/decidim/download_your_data_serializers/{download_your_data_participatory_space_private_user_serializer.rb → download_your_data_member_serializer.rb} +6 -6
  283. data/lib/decidim/download_your_data_serializers.rb +2 -2
  284. data/lib/decidim/engine_router.rb +31 -6
  285. data/lib/decidim/exporters/csv.rb +1 -1
  286. data/lib/decidim/filter_form_builder.rb +1 -1
  287. data/{app/controllers/concerns → lib}/decidim/form_factory.rb +11 -1
  288. data/lib/decidim/has_settings.rb +2 -2
  289. data/lib/decidim/legacy_form_builder.rb +1 -0
  290. data/lib/decidim/locale_router_detector.rb +41 -0
  291. data/lib/decidim/maintenance/import_models/scope.rb +1 -1
  292. data/lib/decidim/maintenance/import_models.rb +0 -2
  293. data/lib/decidim/map/frontend.rb +1 -1
  294. data/lib/decidim/map/geocoding.rb +2 -2
  295. data/lib/decidim/map/static_map.rb +10 -22
  296. data/lib/decidim/map/utility.rb +3 -6
  297. data/lib/decidim/map.rb +2 -2
  298. data/lib/decidim/menu.rb +1 -1
  299. data/lib/decidim/menu_item.rb +1 -0
  300. data/lib/decidim/menu_registry.rb +1 -1
  301. data/lib/decidim/moderation_tools.rb +1 -1
  302. data/lib/decidim/newsletter_encryptor.rb +5 -8
  303. data/lib/decidim/organization_settings.rb +2 -2
  304. data/lib/decidim/participable.rb +1 -0
  305. data/lib/decidim/participatory_space/has_members.rb +61 -0
  306. data/lib/decidim/participatory_space_manifest.rb +0 -4
  307. data/lib/decidim/participatory_space_resourceable.rb +10 -1
  308. data/lib/decidim/routes/locale_redirects.rb +47 -0
  309. data/lib/decidim/routes.rb +7 -0
  310. data/lib/decidim/search_resource_fields_mapper.rb +3 -4
  311. data/lib/decidim/seeds.rb +17 -0
  312. data/lib/decidim/shakapacker/runner.rb +4 -4
  313. data/lib/decidim/shakapacker/shakapacker.yml +28 -3
  314. data/lib/decidim/taxonomizable.rb +1 -1
  315. data/lib/decidim/translatable_attributes.rb +9 -3
  316. data/lib/decidim/view_model.rb +4 -0
  317. data/lib/devise/models/decidim_newsletterable.rb +1 -1
  318. data/lib/tasks/decidim_procfile.rake +1 -1
  319. data/lib/tasks/decidim_tasks.rake +0 -1
  320. data/lib/tasks/decidim_taxonomies.rake +1 -1
  321. data/lib/tasks/upgrade/decidim_active_storage_migration_tasks.rake +0 -1
  322. data/lib/tasks/upgrade/decidim_encryption.rake +12 -0
  323. data/lib/tasks/upgrade/decidim_migrate_wysiwyg_content.rake +0 -1
  324. data/lib/tasks/upgrade/decidim_remove_deleted_users_left_data_tasks.rake +1 -1
  325. data/lib/tasks/upgrade/{fix_deleted_private_follows.rake → fix_deleted_members_follows.rake} +5 -5
  326. data/lib/tasks/upgrade/migrations.rake +0 -1
  327. data/lib/tasks/upgrade/user_groups_migration.rake +0 -1
  328. metadata +206 -130
  329. data/app/cells/decidim/content_blocks/global_menu/show.erb +0 -11
  330. data/app/cells/decidim/content_blocks/global_menu_cell.rb +0 -35
  331. data/app/cells/decidim/content_blocks/menu_breadcrumb_last_activity/show.erb +0 -10
  332. data/app/cells/decidim/content_blocks/menu_breadcrumb_last_activity_cell.rb +0 -49
  333. data/app/cells/decidim/participatory_space_dropdown_metadata/links.erb +0 -9
  334. data/app/cells/decidim/participatory_space_dropdown_metadata/metadata.erb +0 -1
  335. data/app/cells/decidim/participatory_space_dropdown_metadata/show.erb +0 -11
  336. data/app/cells/decidim/participatory_space_dropdown_metadata_cell.rb +0 -26
  337. data/app/controllers/concerns/decidim/has_members_page.rb +0 -25
  338. data/app/jobs/decidim/newsletters_opt_in_job.rb +0 -11
  339. data/app/jobs/decidim/user_report_job.rb +0 -11
  340. data/app/models/decidim/participatory_space_private_user.rb +0 -51
  341. data/app/packs/src/decidim/controllers/sticky_header/controller.js +0 -116
  342. data/app/packs/src/decidim/controllers/sticky_header/sticky_header.test.js +0 -320
  343. data/app/packs/src/decidim/dropdown_menu.js +0 -18
  344. data/app/packs/src/decidim/identity_selector_dialog.js +0 -33
  345. data/app/presenters/decidim/admin_log/participatory_space_private_user_presenter.rb +0 -38
  346. data/app/presenters/decidim/participatory_space_private_user_presenter.rb +0 -46
  347. data/app/views/decidim/participatory_space_private_users/_participatory_space_private_user.html.erb +0 -1
  348. data/app/views/layouts/decidim/footer/_main_language_chooser.html.erb +0 -26
  349. data/app/views/layouts/decidim/header/_menu_breadcrumb_items.html.erb +0 -34
  350. data/app/views/layouts/decidim/header/_menu_breadcrumb_main_dropdown_desktop.html.erb +0 -10
  351. data/app/views/layouts/decidim/header/_menu_breadcrumb_main_dropdown_mobile.html.erb +0 -17
  352. data/app/views/layouts/decidim/header/_menu_breadcrumb_main_dropdown_top_left.html.erb +0 -2
  353. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +0 -33
  354. data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +0 -25
  355. data/lib/decidim/core/test/shared_examples/has_private_users.rb +0 -63
  356. data/lib/decidim/core/test/shared_examples/participatory_space_dropdown_metadata_cell_examples.rb +0 -81
  357. data/lib/decidim/has_private_users.rb +0 -57
  358. data/lib/decidim/maintenance/import_models/assembly_type.rb +0 -49
  359. data/lib/decidim/maintenance/import_models/participatory_process_type.rb +0 -21
  360. data/lib/decidim/query_extensions.rb +0 -64
  361. /data/app/cells/decidim/{participatory_space_private_user → member}/show.erb +0 -0
@@ -72,7 +72,10 @@ module Decidim
72
72
  end
73
73
 
74
74
  def author_profile_url
75
- @author_profile_url ||= @author.is_a?(Decidim::UserBaseEntity) && !@author.deleted? ? decidim.profile_url(@author.nickname, host: @organization.host) : nil
75
+ @author_profile_url ||= if @author.is_a?(Decidim::UserBaseEntity) && !@author.deleted?
76
+ decidim.profile_url(@author.nickname,
77
+ host: @organization.host)
78
+ end
76
79
  end
77
80
 
78
81
  def original_language(reportable)
@@ -262,7 +262,7 @@ module Decidim
262
262
  participatory_space_lazy.present? &&
263
263
  !resource_lazy.try(:deleted?) &&
264
264
  !resource_lazy.try(:hidden?) &&
265
- (participatory_space_lazy.try(:is_transparent?) || !resource_lazy.respond_to?(:can_participate?) || resource_lazy.try(:can_participate?, user))
265
+ (participatory_space_lazy.try(:transparent?) || !resource_lazy.respond_to?(:can_participate?) || resource_lazy.try(:can_participate?, user))
266
266
  rescue NameError => e
267
267
  Rails.logger.warn "Failed resource for #{self.class.name}(id=#{id}): #{e.message}"
268
268
 
@@ -158,9 +158,9 @@ module Decidim
158
158
  end
159
159
 
160
160
  def private_download_required?
161
- return attached_to.private_space? if attached_to.respond_to?(:private_space?)
161
+ return attached_to.restricted? if attached_to.respond_to?(:restricted?)
162
162
 
163
- attached_to.respond_to?(:component) && attached_to.component&.private_non_transparent_space?
163
+ attached_to.respond_to?(:component) && attached_to.component&.restricted_space?
164
164
  end
165
165
  end
166
166
  end
@@ -214,7 +214,7 @@ module Decidim
214
214
  # }
215
215
  # }
216
216
  #
217
- # @return [Hash<String => Hash<Symbol => Integer, ActiveModel::Name>>] The
217
+ # @return [Hash{String => Hash{Symbol => Integer, ActiveModel::Name}}] The
218
218
  # information hash created for the transfer.
219
219
  def information
220
220
  {}.tap do |types|
@@ -64,6 +64,7 @@ module Decidim
64
64
  {
65
65
  :host => organization.host,
66
66
  :component_id => id,
67
+ :locale => I18n.locale,
67
68
  :"#{participatory_space.underscored_name}_slug" => participatory_space.slug
68
69
  }
69
70
  end
@@ -102,22 +103,18 @@ module Decidim
102
103
  def can_participate_in_space?(user)
103
104
  return false unless published?
104
105
  return false unless participatory_space.published?
105
- return true unless participatory_space.try(:private_space?)
106
+
107
+ return true unless participatory_space.respond_to?(:restricted?) && participatory_space.restricted?
106
108
  return false unless user
107
109
 
108
110
  participatory_space.can_participate?(user)
109
111
  end
110
112
  alias can_participate? can_participate_in_space?
111
113
 
112
- def private_non_transparent_space?
113
- return false unless participatory_space.respond_to?(:private_space?)
114
- return false unless participatory_space.private_space?
114
+ def restricted_space?
115
+ return false unless participatory_space.respond_to?(:restricted?)
115
116
 
116
- if participatory_space.respond_to?(:is_transparent?)
117
- !participatory_space.is_transparent?
118
- else
119
- true
120
- end
117
+ participatory_space.restricted?
121
118
  end
122
119
 
123
120
  # Public: Public URL for component with given share token as query parameter
@@ -36,7 +36,7 @@ module Decidim
36
36
  # Returns an object that responds to the settings defined in the content
37
37
  # block manifest.
38
38
  def settings
39
- manifest.settings.schema.new(self[:settings])
39
+ manifest.settings.schema.new(self[:settings], organization&.default_locale)
40
40
  end
41
41
 
42
42
  def reload(*)
@@ -123,8 +123,8 @@ module Decidim
123
123
  private
124
124
 
125
125
  def manifest_attachments
126
- @manifest_attachments ||= manifest.images.each_with_object({}) do |attachment_config, list|
127
- list[attachment_config[:name]] = attachments.find_or_initialize_by(name: attachment_config[:name])
126
+ @manifest_attachments ||= manifest.images.to_h do |attachment_config|
127
+ [attachment_config[:name], attachments.find_or_initialize_by(name: attachment_config[:name])]
128
128
  end
129
129
  end
130
130
 
@@ -50,8 +50,8 @@ module Decidim
50
50
  extended_data["participatory_space_types"]
51
51
  end
52
52
 
53
- def sent_to_private_members?
54
- extended_data["send_to_private_members"]
53
+ def sent_to_members?
54
+ extended_data["send_to_members"]
55
55
  end
56
56
 
57
57
  def template
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ParticipatorySpace
5
+ # This class gives a given User access to a given private Member
6
+ class Member < ApplicationRecord
7
+ include Decidim::DownloadYourData
8
+ include ParticipatorySpaceUser
9
+ include Decidim::TranslatableResource
10
+
11
+ belongs_to :participatory_space, polymorphic: true
12
+
13
+ translatable_fields :role
14
+
15
+ delegate :email, :name, to: :user
16
+
17
+ validates :user, uniqueness: { scope: [:participatory_space_id, :participatory_space_type] }
18
+
19
+ scope :by_participatory_space, ->(participatory_space) { where(participatory_space_id: participatory_space.id, participatory_space_type: participatory_space.class.to_s) }
20
+ scope :published, -> { where(published: true) }
21
+
22
+ def self.user_collection(user)
23
+ where(decidim_user_id: user.id)
24
+ end
25
+
26
+ def self.member_ids_for_participatory_spaces(spaces)
27
+ joins(:user).where(participatory_space: spaces).distinct.pluck(:decidim_user_id)
28
+ end
29
+
30
+ def self.export_serializer
31
+ Decidim::DownloadYourDataSerializers::DownloadYourDataMemberSerializer
32
+ end
33
+
34
+ def self.log_presenter_class_for(_log)
35
+ Decidim::AdminLog::ParticipatorySpace::MemberPresenter
36
+ end
37
+
38
+ ransacker :invitation_sent_at do
39
+ Arel.sql(%{("invitation_sent_at")::text})
40
+ end
41
+
42
+ def self.ransackable_attributes(auth_object = nil)
43
+ return [] unless auth_object&.admin?
44
+
45
+ %w(name nickname email invitation_accepted_at last_sign_in_at invitation_sent_at role)
46
+ end
47
+
48
+ def self.ransackable_associations(_auth_object = nil)
49
+ %w(user)
50
+ end
51
+
52
+ def target_space_association = :participatory_space
53
+ end
54
+ end
55
+ end
@@ -7,7 +7,7 @@ module Decidim
7
7
  # In order to group the relations between resources you need to specify a name.
8
8
  #
9
9
  # Some examples could be: proposals that have been created in a meeting or
10
- # projects that are a result of merging different proposals.
10
+ # results that are the merge of different proposals.
11
11
  class ResourceLink < ApplicationRecord
12
12
  belongs_to :from, polymorphic: true
13
13
  belongs_to :to, polymorphic: true
@@ -94,7 +94,7 @@ module Decidim
94
94
  #
95
95
  # @return [String] The target full URL that the short link should link to.
96
96
  def target_url
97
- url_helpers.send("#{route_name}_url", **params)
97
+ url_helpers.send("#{route_name}_url", { locale: I18n.locale }.reverse_merge(params))
98
98
  end
99
99
 
100
100
  private
@@ -0,0 +1,80 @@
1
+ /* global global, jest */
2
+
3
+ import { Application } from "@hotwired/stimulus";
4
+ import AssignRoleController from "src/decidim/controllers/assign_role/controller";
5
+
6
+ describe("AssignRoleController", () => {
7
+ let application = null;
8
+ let element = null;
9
+ let controller = null;
10
+
11
+ beforeEach(() => {
12
+ document.body.innerHTML = `
13
+ <div data-controller="assign-role" data-role="navigation"></div>
14
+ `;
15
+
16
+ application = Application.start();
17
+ application.register("assign-role", AssignRoleController);
18
+
19
+ element = document.querySelector('[data-controller="assign-role"]');
20
+
21
+ return new Promise((resolve) => {
22
+ setTimeout(() => {
23
+ controller = application.getControllerForElementAndIdentifier(element, "assign-role");
24
+ resolve();
25
+ }, 0);
26
+ });
27
+ });
28
+
29
+ afterEach(() => {
30
+ application.stop();
31
+ document.body.innerHTML = "";
32
+ jest.useRealTimers();
33
+ jest.restoreAllMocks();
34
+ });
35
+
36
+ describe("connect", () => {
37
+ it("sets the role attribute after the delay", () => {
38
+ controller.disconnect();
39
+ jest.useFakeTimers();
40
+
41
+ controller.connect();
42
+
43
+ expect(element.getAttribute("role")).toBeNull();
44
+ jest.advanceTimersByTime(300);
45
+ expect(element.getAttribute("role")).toBe("navigation");
46
+ });
47
+
48
+ it("returns early when no data-role is provided", () => {
49
+ controller.disconnect();
50
+
51
+ element.dataset.role = "";
52
+ const setTimeoutSpy = jest.spyOn(global, "setTimeout");
53
+ jest.useFakeTimers();
54
+
55
+ controller.connect();
56
+
57
+ expect(element.getAttribute("role")).toBeNull();
58
+ expect(setTimeoutSpy).not.toHaveBeenCalled();
59
+ jest.advanceTimersByTime(300);
60
+ expect(element.getAttribute("role")).toBeNull();
61
+ });
62
+ });
63
+
64
+ describe("disconnect", () => {
65
+ it("clears any pending timeout", () => {
66
+ controller.disconnect();
67
+ jest.useFakeTimers();
68
+
69
+ const clearTimeoutSpy = jest.spyOn(global, "clearTimeout");
70
+
71
+ controller.connect();
72
+ controller.disconnect();
73
+
74
+ expect(clearTimeoutSpy).toHaveBeenCalled();
75
+
76
+ jest.advanceTimersByTime(300);
77
+ expect(element.getAttribute("role")).toBeNull();
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,27 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ /**
4
+ * This controller is used to set a role attribute for any element where is being assigned.
5
+ * It requires a data-role attribute with the value of the role attribute to be set.
6
+ * We are using to change the value "menu" of role attribute set by a11y on div dropdown-menu-account and
7
+ * dropdown-menu-account-mobile which are inappropriate for accessibility
8
+ */
9
+ export default class extends Controller {
10
+ connect() {
11
+ const role = this.element.dataset.role
12
+
13
+ if (!role) {
14
+ return
15
+ }
16
+
17
+ this.timeoutId = setTimeout(() => {
18
+ this.element.setAttribute("role", role)
19
+ }, 300)
20
+ }
21
+
22
+ disconnect() {
23
+ if (this.timeoutId) {
24
+ clearTimeout(this.timeoutId)
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,115 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ const OPEN_DELAY_MS = 50
4
+
5
+ /**
6
+ * Main menu dropdown controller and traps page scroll while the menu is open.
7
+ *
8
+ * Expected markup:
9
+ * - The controller element has a `data-target` attribute with the menu container id.
10
+ * - The menu container uses `aria-hidden="true|false"` for visibility.
11
+ * - An optional close button exists with id `main-dropdown-summary-desktop-close`.
12
+ */
13
+ export default class extends Controller {
14
+ connect() {
15
+ this.menuButton = this.element
16
+ this.menuContainer = document.getElementById(this.element.dataset.target)
17
+ this.closeButton = document.getElementById(this.element.dataset.closeButton)
18
+
19
+ if (!this.menuContainer) {
20
+ return;
21
+ }
22
+
23
+ this.handleContainerClick = this.handleContainerClick.bind(this)
24
+ this.handleButtonClick = this.handleButtonClick.bind(this)
25
+ this.handleKeydown = this.handleKeydown.bind(this)
26
+ this.handleCloseButtonClick = this.handleCloseButtonClick.bind(this)
27
+
28
+ this.menuButton.addEventListener("click", this.handleButtonClick)
29
+ this.menuContainer.addEventListener("click", this.handleContainerClick)
30
+
31
+
32
+ document.addEventListener("keydown", this.handleKeydown)
33
+ if (this.closeButton) {
34
+ this.closeButton.addEventListener("click", this.handleCloseButtonClick)
35
+ }
36
+ }
37
+
38
+ disconnect() {
39
+ if (!this.menuContainer) {
40
+ return;
41
+ }
42
+
43
+ this.menuButton.removeEventListener("click", this.handleButtonClick)
44
+ this.menuContainer.removeEventListener("click", this.handleContainerClick)
45
+ document.removeEventListener("keydown", this.handleKeydown)
46
+ if (this.closeButton) {
47
+ this.closeButton.removeEventListener("click", this.handleCloseButtonClick)
48
+ }
49
+ if (!this.isHidden()) {
50
+ this.closeMenu();
51
+ }
52
+ }
53
+
54
+
55
+ handleContainerClick(event) {
56
+ if (this.isHidden()) {
57
+ return;
58
+ }
59
+ if (event.target !== this.menuContainer) {
60
+ return;
61
+ }
62
+ this.closeMenu()
63
+ }
64
+
65
+ handleButtonClick() {
66
+ if (!this.isHidden()) {
67
+ return;
68
+ }
69
+
70
+ setTimeout(() => {
71
+ this.openMenu()
72
+ window.scrollTo({ top: 0, behavior: "smooth" })
73
+ }, OPEN_DELAY_MS)
74
+ }
75
+
76
+ handleKeydown(event) {
77
+ if (event.key !== "Escape") {
78
+ return;
79
+ }
80
+ if (this.isHidden()) {
81
+ return;
82
+ }
83
+
84
+ this.closeMenu()
85
+ }
86
+
87
+ handleCloseButtonClick() {
88
+ if (this.isHidden()) {
89
+ return;
90
+ }
91
+
92
+ this.closeMenu();
93
+ }
94
+
95
+ isHidden() {
96
+ return this.menuContainer.getAttribute("aria-hidden") === "true"
97
+ }
98
+
99
+ openMenu() {
100
+ if (typeof this.previousBodyOverflow === "undefined") {
101
+ this.previousBodyOverflow = document.body.style.overflow;
102
+ }
103
+ document.body.style.overflow = "hidden"
104
+ this.element.setAttribute("aria-expanded", "true")
105
+ this.menuContainer.setAttribute("aria-hidden", "false")
106
+ this.menuContainer.setAttribute("aria-modal", "true")
107
+ }
108
+
109
+ closeMenu() {
110
+ document.body.style.overflow = this.previousBodyOverflow ?? ""
111
+ this.element.setAttribute("aria-expanded", "false")
112
+ this.menuContainer.setAttribute("aria-hidden", "true")
113
+ this.menuContainer.removeAttribute("aria-modal")
114
+ }
115
+ }
@@ -0,0 +1,185 @@
1
+ /* global jest */
2
+ import { Application } from "@hotwired/stimulus"
3
+ import MainMenuController from "src/decidim/controllers/main_menu/controller"
4
+
5
+ describe("MainMenuController", () => {
6
+ let application = null
7
+ let controller = null
8
+ let menuButton = null
9
+ let menuContainer = null
10
+ let closeButton = null
11
+
12
+ const buildDom = () => {
13
+ document.body.innerHTML = `
14
+ <button
15
+ data-controller="main-menu"
16
+ data-target="main-menu-container"
17
+ data-close-button="main-menu-close"
18
+ aria-expanded="false"
19
+ >
20
+ Menu
21
+ </button>
22
+ <div id="main-menu-container" aria-hidden="true">
23
+ <div id="main-menu-item"></div>
24
+ </div>
25
+ <button id="main-menu-close">Close</button>
26
+ `
27
+ }
28
+
29
+ const startController = () => new Promise((resolve) => {
30
+ setTimeout(() => {
31
+ controller = application.getControllerForElementAndIdentifier(menuButton, "main-menu")
32
+ resolve()
33
+ }, 0)
34
+ })
35
+
36
+ beforeEach(() => {
37
+ application = Application.start()
38
+ application.register("main-menu", MainMenuController)
39
+ buildDom()
40
+
41
+ menuButton = document.querySelector('[data-controller="main-menu"]')
42
+ menuContainer = document.getElementById("main-menu-container")
43
+ closeButton = document.getElementById("main-menu-close")
44
+
45
+ jest.spyOn(window, "scrollTo").mockImplementation(() => {})
46
+
47
+ return startController()
48
+ })
49
+
50
+ afterEach(() => {
51
+ application.stop()
52
+ document.body.innerHTML = ""
53
+ jest.useRealTimers()
54
+ jest.restoreAllMocks()
55
+ })
56
+
57
+ describe("connect", () => {
58
+ it("binds expected event listeners", () => {
59
+ const buttonSpy = jest.spyOn(menuButton, "addEventListener")
60
+ const documentSpy = jest.spyOn(document, "addEventListener")
61
+ const closeSpy = jest.spyOn(closeButton, "addEventListener")
62
+
63
+ controller.disconnect()
64
+ controller.connect()
65
+
66
+ expect(buttonSpy).toHaveBeenCalledWith("click", controller.handleButtonClick)
67
+ expect(documentSpy).toHaveBeenCalledWith("keydown", controller.handleKeydown)
68
+ expect(closeSpy).toHaveBeenCalledWith("click", controller.handleCloseButtonClick)
69
+ })
70
+
71
+ it("returns early when menu container is missing", async () => {
72
+ document.body.innerHTML = `
73
+ <button data-controller="main-menu" data-target="missing-menu"></button>
74
+ `
75
+
76
+ application.stop()
77
+ application = Application.start()
78
+ application.register("main-menu", MainMenuController)
79
+
80
+ const missingButton = document.querySelector('[data-controller="main-menu"]')
81
+ const missingController = await new Promise((resolve) => {
82
+ setTimeout(() => {
83
+ resolve(application.getControllerForElementAndIdentifier(missingButton, "main-menu"))
84
+ }, 0)
85
+ })
86
+
87
+ expect(missingController.menuContainer).toBeNull()
88
+ expect(() => missingController.disconnect()).not.toThrow()
89
+ })
90
+ })
91
+
92
+ describe("handleButtonClick", () => {
93
+ it("opens the menu after the delay and scrolls to top", () => {
94
+ jest.useFakeTimers()
95
+ document.body.style.overflow = "scroll"
96
+
97
+ controller.handleButtonClick()
98
+ jest.advanceTimersByTime(50)
99
+
100
+ expect(menuButton.getAttribute("aria-expanded")).toBe("true")
101
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("false")
102
+ expect(document.body.style.overflow).toBe("hidden")
103
+ expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: "smooth" })
104
+ })
105
+
106
+ it("does nothing when menu is already open", () => {
107
+ jest.useFakeTimers()
108
+ controller.openMenu()
109
+
110
+ controller.handleButtonClick()
111
+ jest.advanceTimersByTime(50)
112
+
113
+ expect(window.scrollTo).not.toHaveBeenCalled()
114
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("false")
115
+ })
116
+ })
117
+
118
+ describe("handleKeydown", () => {
119
+ it("closes the menu on Escape", () => {
120
+ document.body.style.overflow = "scroll"
121
+ controller.openMenu()
122
+
123
+ controller.handleKeydown({ key: "Escape" })
124
+
125
+ expect(menuButton.getAttribute("aria-expanded")).toBe("false")
126
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("true")
127
+ expect(document.body.style.overflow).toBe("scroll")
128
+ })
129
+
130
+ it("ignores non-escape keys", () => {
131
+ controller.openMenu()
132
+
133
+ controller.handleKeydown({ key: "Enter" })
134
+
135
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("false")
136
+ })
137
+ })
138
+
139
+ describe("handleContainerClick", () => {
140
+ it("closes the menu when clicking the container", () => {
141
+ controller.openMenu()
142
+
143
+ controller.handleContainerClick({ target: menuContainer })
144
+
145
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("true")
146
+ })
147
+
148
+ it("does not close the menu when clicking inside the container", () => {
149
+ const childItem = document.getElementById("main-menu-item")
150
+ controller.openMenu()
151
+
152
+ controller.handleContainerClick({ target: childItem })
153
+
154
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("false")
155
+ })
156
+ })
157
+
158
+ describe("handleCloseButtonClick", () => {
159
+ it("closes the menu when open", () => {
160
+ controller.openMenu()
161
+
162
+ controller.handleCloseButtonClick()
163
+
164
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("true")
165
+ })
166
+ })
167
+
168
+ describe("disconnect", () => {
169
+ it("removes listeners and closes the menu if open", () => {
170
+ const buttonSpy = jest.spyOn(menuButton, "removeEventListener")
171
+ const documentSpy = jest.spyOn(document, "removeEventListener")
172
+ const closeSpy = jest.spyOn(closeButton, "removeEventListener")
173
+
174
+ document.body.style.overflow = "scroll"
175
+ controller.openMenu()
176
+ controller.disconnect()
177
+
178
+ expect(buttonSpy).toHaveBeenCalledWith("click", controller.handleButtonClick)
179
+ expect(documentSpy).toHaveBeenCalledWith("keydown", controller.handleKeydown)
180
+ expect(closeSpy).toHaveBeenCalledWith("click", controller.handleCloseButtonClick)
181
+ expect(menuContainer.getAttribute("aria-hidden")).toBe("true")
182
+ expect(document.body.style.overflow).toBe("scroll")
183
+ })
184
+ })
185
+ })
@@ -46,13 +46,6 @@ export default function generateDatePicker(input, row, formats) {
46
46
  closeCalendar.setAttribute("class", "datepicker__close-calendar button button__transparent-secondary button__xs");
47
47
  closeCalendar.setAttribute("type", "button");
48
48
 
49
- const pickCalendar = document.createElement("button");
50
- pickCalendar.innerText = i18n.select;
51
- pickCalendar.setAttribute("class", "datepicker__pick-calendar button button__secondary button__xs");
52
- pickCalendar.setAttribute("disabled", true);
53
- pickCalendar.setAttribute("type", "button");
54
-
55
- datePickerContainer.appendChild(pickCalendar);
56
49
  datePickerContainer.appendChild(closeCalendar);
57
50
 
58
51
  dateColumn.appendChild(datePickerContainer);
@@ -103,15 +96,10 @@ export default function generateDatePicker(input, row, formats) {
103
96
  let pickedDate = null;
104
97
 
105
98
  datePicker.addEventListener("selectDate", (event) => {
106
- pickCalendar.removeAttribute("disabled");
107
99
  pickedDate = event.detail;
108
- });
109
-
110
- pickCalendar.addEventListener("click", (event) => {
111
- event.preventDefault();
100
+ prevDate = pickedDate;
112
101
 
113
102
  date.value = displayDate(datePicker.value, formats);
114
- prevDate = pickedDate;
115
103
  if (input.type === "date") {
116
104
  input.value = `${pickedDate}`;
117
105
  } else if (input.type === "datetime-local") {
@@ -183,7 +183,7 @@ export const createSuggestionRenderer = (node, { itemConverter } = {}) => () =>
183
183
  export const createNodeView = (self) => {
184
184
  return ({ node }) => {
185
185
  const dom = document.createElement("span");
186
- dom.textContent = self.options.renderLabel({ options: self.options, node });
186
+ dom.textContent = self.options.renderText({ options: self.options, node });
187
187
 
188
188
  const { id, label } = node.attrs;
189
189
  dom.dataset.suggestion = node.type.name;
@@ -43,7 +43,10 @@ export default Extension.create({
43
43
  heading: false,
44
44
  bold: false,
45
45
  orderedList: false,
46
- codeBlock: false
46
+ codeBlock: false,
47
+ link: false,
48
+ underline: false,
49
+ trailingNode: false
47
50
  }),
48
51
  CharacterCount.configure(this.options.characterCount),
49
52
  Link.configure({ openOnClick: false, ...this.options.link }),
@@ -210,13 +210,16 @@ export default Image.extend({
210
210
  return true;
211
211
  },
212
212
 
213
- handleDoubleClick() {
214
- if (!editor.isActive("image")) {
215
- return false;
213
+ handleDoubleClick(view, pos) {
214
+ const { state } = view;
215
+ const node = state.doc.nodeAt(pos);
216
+
217
+ if (node && node.type.name === "image") {
218
+ editor.chain().focus().imageDialog().run();
219
+ return true;
216
220
  }
217
221
 
218
- editor.chain().focus().imageDialog().run();
219
- return true;
222
+ return false;
220
223
  },
221
224
 
222
225
  handleDOMEvents: {